Home

Resume

Blog

Teikitu


/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/*  »Project«   Teikitu Gaming System (TgS) (∂)
    »File«      TgS (WIN) Kernel - Module.c
    »Author«    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
    »Version«   4.51 / »GUID« A9981407-3EC9-42AF-8B6F-8BE6DD919615                                                                                                        */
/*   -------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
/*  Copyright: © 2002-2017, Andrew Aye.  All Rights Reserved.
    This software is free for non-commercial use.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
      following conditions are met:
        Redistribution of source code must retain this copyright notice, this list of conditions and the following disclaimers.
        Redistribution in binary form must reproduce this copyright notice, this list of conditions and the following disclaimers in the documentation and other materials
          provided with the distribution.
    The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
    The intellectual property rights of the algorithms used reside with Andrew Aye.
    You may not use this software, in whole or in part, in support of any commercial product without the express written consent of the author.
    There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".                                                   */
/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* == Kernel ============================================================================================================================================================ */

/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions and Data                                                                                                                                         */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

static TgUINT32                             tgKN_Update_Win_Thread( C_TgUINTPTR );

static TgMS_HINSTANCE                       s_hDLL_User32;

static TgBOOL                               s_bRun_Thread;
static TgTHREAD_ID                          s_tiThread;
static STg1_MP_MX                           s_sInstance_Lock;




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Unit Test Functions                                                                                                                                        */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

static TgVOID                               tgTST_KN_Device_Change_Check( C_TgSINT32, C_TgSINT32, C_TgSINT32 );
static TgVOID                               tgTST_KN_Query_Render_Display_Rect( P_TgSINT32, P_TgSINT32, C_TgSINT32, C_TgBOOL );
static TgVOID                               tgTST_KN_Query_Render_Display_Min_Rect( P_TgSINT32, P_TgSINT32, C_TgSINT32, C_TgBOOL );
static TgVOID                               tgTST_KN_Query_Render_Display_Max_Rect( P_TgSINT32, P_TgSINT32, C_TgSINT32, C_TgBOOL );
static TgVOID                               tgTST_KN_Query_Render_Select_Rect( P_TgSINT32, P_TgSINT32, C_TgSINT32, C_TgBOOL );
static TgSINT32                             tgTST_KN_Query_Monitor_Index( TgVOID );




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Private Functions                                                                                                                                                     */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgKN_PM_Module_Init --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgKN_PM_Module_Init( TgVOID )
{
    /* Clear the platform specific data */
    g_szKN_ExeTitle[0] = 0;
    g_szKN_ClassName[0] = 0;
    g_pfnKN_WndProc = 0;
    g_iKN_Display_W = 0;
    g_iKN_Display_H = 0;
    g_iKN_Window_X0 = 0;
    g_iKN_Window_Y0 = 0;
    g_iKN_Window_X1 = 0;
    g_iKN_Window_Y1 = 0;
    g_uiKN_Window_Style = 0;
    g_bKN_Minimized = 0;
    g_bKN_FullScreen = 0;
    g_bKN_FullScreen_Lock = 0;
    g_bKN_WindowSize_Fixed = 0;
    g_bKN_WindowSize_Lock = 0;
    g_bKN_MuteInBackground = 0;
    g_bKN_RunInBackground = 0;
    g_bKN_HasGDIMenu = 0;
    g_bKN_ClearDisplay = TgTRUE;
    g_bKN_PauseOnSize = 0;
    g_bKN_Monitor_Index = 0;
    g_bKN_Monitor_Portrait = TgFALSE;

    g_pfnKN_GUI_Keyboard_Handler = nullptr;
    g_pfnKN_GUI_Mouse_Handler = nullptr;

    g_pfnDevice_Change_Check = nullptr;
    g_pfnQuery_Render_Display_Rect = nullptr;
    g_pfnQuery_Render_Display_Min_Rect = nullptr;
    g_pfnQuery_Render_Display_Max_Rect = nullptr;
    g_pfnQuery_Render_Select_Rect = nullptr;
    g_pfnQuery_Monitor_Index = nullptr;

    s_hDLL_User32 = nullptr;
    s_bRun_Thread = TgFALSE;
    s_tiThread = KTgTHREAD_ID__INVALID;

    /* Load the User functions from the DLL */
    if (!tgGB_Load_Lib( &s_hDLL_User32, TgT("user32.dll") ))
    {
        TgCRITICAL_MSGF( 0, TgT( "%-16.16s(%-32.32s): Unable to load User32 DLL.\n" ), TgT("Kernel"), TgT("Platform") );
        return (KTgE_FAIL);
    };

    if (
           TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnRegisterClassEx, s_hDLL_User32, TFCN( RegisterClassEx ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnUnregisterClass, s_hDLL_User32, TFCN( UnregisterClass ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnCreateWindowEx, s_hDLL_User32, TFCN( CreateWindowEx ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnDestroyWindow, s_hDLL_User32, "DestroyWindow" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnShowWindow, s_hDLL_User32, "ShowWindow" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnUpdateWindow, s_hDLL_User32, "UpdateWindow" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnGetDC, s_hDLL_User32, "GetDC" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnReleaseDC, s_hDLL_User32, "ReleaseDC" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnSetWindowText, s_hDLL_User32, TFCN( SetWindowText ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnGetWindowRect, s_hDLL_User32, "GetWindowRect" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnGetClientRect, s_hDLL_User32, "GetClientRect" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnFindWindow, s_hDLL_User32, TFCN( FindWindow ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnIsIconic, s_hDLL_User32, "IsIconic" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnSetForegroundWindow, s_hDLL_User32, "SetForegroundWindow" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnScreenToClient, s_hDLL_User32, "ScreenToClient" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnSetCursor, s_hDLL_User32, "SetCursor" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnPeekMessage, s_hDLL_User32, TFCN( PeekMessage ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnGetMessage, s_hDLL_User32, TFCN( GetMessage ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnTranslateMessage, s_hDLL_User32, "TranslateMessage" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnPostQuitMessage, s_hDLL_User32, "PostQuitMessage" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnMonitorFromWindow, s_hDLL_User32, "MonitorFromWindow" )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnDefWindowProc, s_hDLL_User32, TFCN( DefWindowProc ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnDispatchMessage, s_hDLL_User32, TFCN( DispatchMessage ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnSendMessage, s_hDLL_User32, TFCN( SendMessage ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnPostMessage, s_hDLL_User32, TFCN( PostMessage ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnGetWindowLong, s_hDLL_User32, TFCN( GetWindowLongPtr ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnSetWindowLong, s_hDLL_User32, TFCN( SetWindowLongPtr ) )
        || TgTRUE != tgGB_Get_Func_Address( (TgMS_FARPROC*)&g_pfnSetWindowPos, s_hDLL_User32, "SetWindowPos" )
        )
    {
        TgCRITICAL_MSGF( 0, TgT( "%-16.16s(%-32.32s): Unable to find function in User32 DLL.\n" ), TgT("Kernel"), TgT("Platform") );
        return (KTgE_FAIL);
    };

    return (KTgS_OK);
}


/* ---- tgKN_PM_Module_Boot --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgKN_PM_Module_Boot( TgVOID )
{
    TgSINT32                            iMonitor;
    TgSINT32                            iArg;
    CP_TgMS_RECT                        ptgRect;
    TgBOOL                              bFind_Inst = TgFALSE;
    TgSINT32                            iMonitor_W = 0, iMonitor_H = 0;

    if (nullptr == g_pfnCoInitializeEx)
    {
        TgCRITICAL_MSGF( 0, TgT( "%-16.16s(%-32.32s): Unable to find COM Init function in DLL.\n" ), TgT("Kernel"), TgT("Platform") );
        return (KTgE_FAIL);
    }
    else
    {
        TgSINT32                            iResult;

        iResult = g_pfnCoInitializeEx( nullptr, 0 );
        if (TgFAILED( iResult ))
        {
            TgCRITICAL_MSGF( 0, TgT( "%-16.16s(%-32.32s): Unable to initialize COM layer.\n" ), TgT("Kernel"), TgT("Platform") );
            return (KTgE_FAIL);
        };
    };

    /* Create defaults for any uninitialized values required by Windows OS */
    if (0 == g_szKN_ClassName[0])
    {
        tgSZ_Copy( g_szKN_ClassName, KTgKN_MAX_TITLE, TgT("Teikitu_Class") );
    };

    if (0 == g_szKN_ExeTitle[0])
    {
        tgSZ_Copy( g_szKN_ExeTitle, KTgKN_MAX_TITLE, TgT("Teikitu_Default") );
    };

    if (0 == g_uiKN_Window_Style)
    {
        g_uiKN_Window_Style = 0x00040000L;
    };

    /* Look for Unit Test argument to default the render related functions */
    iArg = tgGB_CMD_Query_Argument_Index( TgT("-//test/unit/kernel") );
    if (iArg >= 0)
    {
        g_pfnDevice_Change_Check = tgTST_KN_Device_Change_Check;
        g_pfnQuery_Render_Display_Rect = tgTST_KN_Query_Render_Display_Rect;
        g_pfnQuery_Render_Display_Min_Rect = tgTST_KN_Query_Render_Display_Min_Rect;
        g_pfnQuery_Render_Display_Max_Rect = tgTST_KN_Query_Render_Display_Max_Rect;
        g_pfnQuery_Render_Select_Rect = tgTST_KN_Query_Render_Select_Rect;
        g_pfnQuery_Monitor_Index = tgTST_KN_Query_Monitor_Index;

        iArg = tgGB_CMD_Query_Argument_Index( TgT("-resolution") );
        if (iArg >= 0 && iArg + 1 < tgGB_CMD_Query_Argument_Count())
        {
            g_iKN_Display_W = tgSZ_To_S32( tgGB_CMD_Query_Argument( iArg + 1 ) );
            g_iKN_Display_H = tgSZ_To_S32( tgGB_CMD_Query_Argument( iArg + 2 ) );;
        };
    };

    TgCRITICAL( nullptr != g_pfnDevice_Change_Check );
    TgCRITICAL( nullptr != g_pfnQuery_Render_Display_Rect );
    TgCRITICAL( nullptr != g_pfnQuery_Render_Display_Min_Rect );
    TgCRITICAL( nullptr != g_pfnQuery_Render_Display_Max_Rect );
    TgCRITICAL( nullptr != g_pfnQuery_Render_Select_Rect );
    TgCRITICAL( nullptr != g_pfnQuery_Monitor_Index );

    /* Check to see if multiple instances of this application are allowed */
    iArg = tgGB_CMD_Query_Argument_Index( TgT("-multiple_instance") );

    if (TgSUCCEEDED( tgCM_MP_MX_Init( &s_sInstance_Lock, g_szKN_ClassName ) ))
    {
        if (TgFAILED( tgCM_MP_MX_Enter( &s_sInstance_Lock ) ))
        {
            if (iArg < 0)
            {
                tgCM_MP_MX_Free( &s_sInstance_Lock );
            };

            bFind_Inst = TgTRUE;
        };
    }
    else
    {
        bFind_Inst = TgTRUE;
    }

    if (bFind_Inst)
    {
        if (iArg < 0)
        {
            TgMS_HWND hWnd = g_pfnFindWindow( g_szKN_ClassName, nullptr );
            if (hWnd)
            {
                if (g_pfnIsIconic( hWnd ))
                {
                    g_pfnShowWindow( hWnd, 9 );
                };

                g_pfnSetForegroundWindow( hWnd );
            };

            g_pfnCoUninitialize();

            return (KTgE_FAIL);
        };
    };

    /* Set the window padding (AdjustRect is a MS function, and therefore does not work - calculate manually) */
    tgKN_Set_Window_PadX( g_pfnGetSystemMetrics( 32 ) * 4 );
    tgKN_Set_Window_PadY( g_pfnGetSystemMetrics( 33 ) * 4 + g_pfnGetSystemMetrics( 4 ) );

    /* Find the dimensions of the work area for the selected monitor */
    iMonitor = g_pfnQuery_Monitor_Index ? g_pfnQuery_Monitor_Index() - 1 : 0;
    tgKN_Set_Monitor_Index( iMonitor );

    if (nullptr != (ptgRect = tgSI_Query_Monitor_Work_Rect( iMonitor )))
    {
        iMonitor_W = ptgRect->right - ptgRect->left;
        iMonitor_H = ptgRect->bottom - ptgRect->top;
        tgKN_Set_Monitor_Landscape( iMonitor_W >= iMonitor_H );
    };

    /* Get the display (client) dimensions from the render system (callback) */
    if (g_pfnQuery_Render_Display_Rect)
    {
        g_pfnQuery_Render_Display_Rect( &g_iKN_Display_W, &g_iKN_Display_H, iMonitor, tgKN_Query_Monitor_Landscape() );
    }

    /* By default place the window in the middle of the monitor */
    iArg = tgGB_CMD_Query_Argument_Index( TgT("-window_position") );
    if (iArg >= 0 && iArg + 1 < tgGB_CMD_Query_Argument_Count())
    {
        g_iKN_Window_X0 = tgSZ_To_S32( tgGB_CMD_Query_Argument( iArg + 1 ) );
        g_iKN_Window_Y0 = tgSZ_To_S32( tgGB_CMD_Query_Argument( iArg + 2 ) );;
    }
    else
    {
        g_iKN_Window_X0 = (iMonitor_W >> 1) - ((g_iKN_Display_W + tgKN_Query_Window_PadX()) >> 1);
        g_iKN_Window_Y0 = (iMonitor_H >> 1) - ((g_iKN_Display_H + tgKN_Query_Window_PadY()) >> 1);

        if (ptgRect)
        {
            g_iKN_Window_X0 += ptgRect->left;
            g_iKN_Window_Y0 += ptgRect->top;
        };
    };

    s_bRun_Thread = TgTRUE;
    tgAM_WRITE_FENCE();
    s_tiThread = tgTR_Create( tgKN_Update_Win_Thread, 0, 0, ETgTHREAD_PRIORITY__CRITICAL, TgT("KN Update Windows") );

    return (KTgS_OK);
}


/* ---- tgKN_PM_Module_Stop --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgKN_PM_Module_Stop( TgVOID )
{
    g_pfnDestroyWindow( g_hWnd );

    s_bRun_Thread = TgFALSE;
    tgAM_WRITE_FENCE();
    tgTR_Close( s_tiThread );
    s_tiThread = KTgTHREAD_ID__INVALID;

    tgCM_MP_MX_Free( &s_sInstance_Lock );
    g_pfnCoUninitialize();
}


/* ---- tgKN_PM_Module_Free --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgKN_PM_Module_Free( TgVOID )
{

}


/* ---- tgKN_PM_Module_Update ------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgKN_PM_Module_Update( C_TgFLOAT32 UNUSED_PARAM fDT )
{
    return (s_bRun_Thread ? KTgS_OK : KTgE_FAIL);
}




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions                                                                                                                                                  */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgKN_Update_Win_Thread ------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgUINT32 tgKN_Update_Win_Thread( C_TgUINTPTR UNUSED_PARAM uiUnused )
{
    TgMS_WNDCLASS                       wc;
    TgMS_HDC                            hdc;

    memset( &wc, 0x00, sizeof( TgMS_WNDCLASS ) );

    wc.m_uiSize                         = sizeof(TgMS_WNDCLASS);
    wc.m_uiStyle                        = 0x0040;
    wc.m_pfnWndProc                     = tgKN_PM_Base_Window_Update;
    wc.m_hInstance                      = g_hInstance;
    wc.m_pszClassName                   = tgKN_Query_Class_Name();

    if (0 == g_pfnRegisterClassEx( &wc ))
    {
        const DWORD dwError = g_pfnGetLastError();
        if (dwError != 1410L)
        {
            return (1);
        };
    };

    /* Create an instance of the window we just registered locate it at the bottom of the screen. */
    g_hWnd = g_pfnCreateWindowEx(
        0,
        wc.m_pszClassName,
        tgKN_Query_Exe_Title(),
        g_uiKN_Window_Style,
        tgKN_Query_Window_PosX(),
        tgKN_Query_Window_PosY(),
        tgKN_Query_Window_PadX() + tgKN_Query_Display_Width(),
        tgKN_Query_Window_PadY() + tgKN_Query_Display_Height(),
        0,
        0,
        g_hInstance,
        0
    );

    if (!g_hWnd)
    {
        g_pfnUnregisterClass( wc.m_pszClassName, g_hInstance );
        return (1);
    };

    g_pfnShowWindow( g_hWnd, 10 );
    g_pfnUpdateWindow( g_hWnd );

    tgKN_ReCalc_Display();
    tgKN_Display_Dimensions_In_Caption();

    hdc = g_pfnGetDC( g_hWnd );
    /* g_pfnPatBlt( hdc, 0, 0, tgKN_Query_Display_Width(), tgKN_Query_Display_Height(), 0x00000042 ); */
    g_pfnReleaseDC( g_hWnd, hdc );

    while (s_bRun_Thread)
    {
        TgMS_MSG                            msg;

        while (0 != g_pfnPeekMessage( &msg, g_hWnd, 0U, 0U, 0x0001 ))
        {
            g_pfnTranslateMessage( &msg );
            g_pfnDispatchMessage( &msg );

            if (0x0012 == msg.m_uiMessage) /* WM_QUIT */
            {
                s_bRun_Thread = TgFALSE;
            };
        };

        tgTR_Sleep( 15 );
    };

    g_pfnUnregisterClass( wc.m_pszClassName, g_hInstance );

    return (0);
}




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Unit Test Functions                                                                                                                                        */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

static C_TgSINT32 s_iTST_KN__W_List[] = { 320,640,720,720,800,1024,1152,1280,1280,1280,1280,1280,1360,1600,1600,1600,1680,1920,1920 };
static C_TgSINT32 s_iTST_KN__H_List[] = { 240,480,480,576,600,768,864,720,768,800,960,1024,768,900,1024,1200,1050,1080,1200 };

/* ---- tgTST_KN_Device_Change_Check ------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTST_KN_Device_Change_Check( C_TgSINT32 UNUSED_PARAM iMonitor, C_TgSINT32 UNUSED_PARAM iWidth, C_TgSINT32 UNUSED_PARAM iHeight )
{

}


/* ---- tgTST_KN_Query_Render_Display_Rect ------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTST_KN_Query_Render_Display_Rect( P_TgSINT32 piW, P_TgSINT32 piH, C_TgSINT32 UNUSED_PARAM iMonitor, C_TgBOOL bLandscape )
{
    if (bLandscape)
    {
        *piW = s_iTST_KN__W_List[0];
        *piH = s_iTST_KN__H_List[0];
    }
    else
    {
        *piH = s_iTST_KN__W_List[0];
        *piW = s_iTST_KN__H_List[0];
    };
}


/* ---- tgTST_KN_Query_Render_Display_Min_Rect -------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTST_KN_Query_Render_Display_Min_Rect( P_TgSINT32 piW, P_TgSINT32 piH, C_TgSINT32 UNUSED_PARAM iMonitor, C_TgBOOL bLandscape )
{
    if (bLandscape)
    {
        *piW = s_iTST_KN__W_List[0];
        *piH = s_iTST_KN__H_List[0];
    }
    else
    {
        *piH = s_iTST_KN__W_List[0];
        *piW = s_iTST_KN__H_List[0];
    };
}


/* ---- tgTST_KN_Query_Render_Display_Max_Rect -------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTST_KN_Query_Render_Display_Max_Rect( P_TgSINT32 piW, P_TgSINT32 piH, C_TgSINT32 UNUSED_PARAM iMonitor, C_TgBOOL bLandscape )
{
    if (bLandscape)
    {
        *piW = s_iTST_KN__W_List[(sizeof( s_iTST_KN__W_List ) / sizeof( s_iTST_KN__W_List[0] )) - 1];
        *piH = s_iTST_KN__H_List[(sizeof( s_iTST_KN__W_List ) / sizeof( s_iTST_KN__W_List[0] )) - 1];
    }
    else
    {
        *piH = s_iTST_KN__W_List[(sizeof( s_iTST_KN__W_List ) / sizeof( s_iTST_KN__W_List[0] )) - 1];
        *piW = s_iTST_KN__H_List[(sizeof( s_iTST_KN__W_List ) / sizeof( s_iTST_KN__W_List[0] )) - 1];
    };
}


/* ---- tgTST_KN_Query_Render_Select_Rect ------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgTST_KN_Query_Render_Select_Rect( P_TgSINT32 piW, P_TgSINT32 piH, C_TgSINT32 UNUSED_PARAM iMonitor, C_TgBOOL bLandscape )
{
    C_TgSINT32                          uiMaxModes = sizeof( s_iTST_KN__W_List ) / sizeof( s_iTST_KN__W_List[0] );
    TgSINT32                            iIndex = 0;
    TgSINT32                            iW = bLandscape ? *piW : *piH;
    TgSINT32                            iH = bLandscape ? *piH : *piW;

    if (s_iTST_KN__W_List[iIndex] < iW)
    {
        for (++iIndex; iIndex < uiMaxModes; ++iIndex)
        {
            if (s_iTST_KN__W_List[iIndex] > iW)
            {
                break;
            };
        };
        --iIndex;
    };

    iW = s_iTST_KN__W_List[iIndex];
    if (0 == iIndex)
    {
        for (++iIndex; iIndex < uiMaxModes; ++iIndex)
        {
            if (s_iTST_KN__W_List[iIndex] > iW)
            {
                break;
            };
        };
        --iIndex;
    };

    if (s_iTST_KN__H_List[iIndex] > iH)
    {
        for (; iIndex > 0; --iIndex)
        {
            if (s_iTST_KN__H_List[iIndex] < iH || s_iTST_KN__W_List[iIndex - 1] != iW)
            {
                break;
            };
        };
    };

    iH = s_iTST_KN__H_List[iIndex];
    if (0 == iIndex || s_iTST_KN__W_List[iIndex - 1] != iW)
    {
        for (++iIndex; iIndex < uiMaxModes && s_iTST_KN__W_List[iIndex] == iW && s_iTST_KN__H_List[iIndex] == iH; ++iIndex);
        --iIndex;
    };

    *piW = bLandscape ? iW : iH;
    *piH = bLandscape ? iH : iW;
}


/* ---- tgTST_KN_Query_Monitor_Index ------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgSINT32 tgTST_KN_Query_Monitor_Index( TgVOID )
{
    TgSINT32                            iMonitor = 1;
    TgSINT32                            iArg;

    iArg = tgGB_CMD_Query_Argument_Index( TgT("-monitor") );
    if (iArg >= 0 && iArg < tgGB_CMD_Query_Argument_Count())
    {
        iMonitor = tgCM_CLP_S32( tgSZ_To_S32( tgGB_CMD_Query_Argument( iArg + 1 ) ), 1, tgSI_Query_Monitor_Count() );
    };

    return (iMonitor);
}