/* vgui_draw.c - vgui draw methods Copyright (C) 2011 Uncle Mike This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include #include "common.h" #include "client.h" #include "vgui_draw.h" #include "vgui_api.h" #include "library.h" #include "keydefs.h" #include "ref_common.h" #include "input.h" #include "platform/platform.h" CVAR_DEFINE_AUTO( vgui_utf8, "0", FCVAR_ARCHIVE, "enable utf-8 support for vgui text" ); static void GAME_EXPORT *VGUI_EngineMalloc( size_t size ); static void GAME_EXPORT VGUI_GetMousePos( int *, int * ); static void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor ); static byte GAME_EXPORT VGUI_GetColor( int, int ); static int GAME_EXPORT VGUI_UtfProcessChar( int in ); static qboolean GAME_EXPORT VGUI_IsInGame( void ); static struct { qboolean initialized; vgui_support_interface_t dllFuncs; VGUI_DefaultCursor cursor; HINSTANCE hInstance; enum VGUI_KeyCode virtualKeyTrans[256]; } vgui = { false, { 0 }, // not initialized yet -1 }; static void GAME_EXPORT *VGUI_EngineMalloc( size_t size ) { return Z_Malloc( size ); } static qboolean GAME_EXPORT VGUI_IsInGame( void ) { return cls.state == ca_active && cls.key_dest == key_game; } static void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y ) { float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; int x, y; Platform_GetMousePos( &x, &y ); *_x = x / xscale; *_y = y / yscale; } static void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor ) { if( vgui.cursor != cursor ) Platform_SetCursorType( cursor ); } static byte GAME_EXPORT VGUI_GetColor( int i, int j ) { return g_color_table[i][j]; } static int GAME_EXPORT VGUI_UtfProcessChar( int in ) { if( vgui_utf8.value ) return Con_UtfProcessCharForce( in ); return in; } qboolean VGui_IsActive( void ) { return vgui.initialized; } static vgui_support_api_t gEngfuncs = { NULL, // VGUI_DrawInit, NULL, // VGUI_DrawShutdown, NULL, // VGUI_SetupDrawingText, NULL, // VGUI_SetupDrawingRect, NULL, // VGUI_SetupDrawingImage, NULL, // VGUI_BindTexture, NULL, // VGUI_EnableTexture, NULL, // VGUI_CreateTexture, NULL, // VGUI_UploadTexture, NULL, // VGUI_UploadTextureBlock, NULL, // VGUI_DrawQuad, NULL, // VGUI_GetTextureSizes, NULL, // VGUI_GenerateTexture, VGUI_EngineMalloc, VGUI_CursorSelect, VGUI_GetColor, VGUI_IsInGame, Key_EnableTextInput, VGUI_GetMousePos, VGUI_UtfProcessChar, Platform_GetClipboardText, Platform_SetClipboardText, Platform_GetKeyModifiers, COM_LoadLibrary, COM_FreeLibrary, COM_GetProcAddress, }; static void VGui_FillAPIFromRef( vgui_support_api_t *to, const ref_interface_t *from ) { to->DrawInit = from->VGUI_DrawInit; to->DrawShutdown = from->VGUI_DrawShutdown; to->SetupDrawingText = from->VGUI_SetupDrawingText; to->SetupDrawingRect = from->VGUI_SetupDrawingRect; to->SetupDrawingImage = from->VGUI_SetupDrawingImage; to->BindTexture = from->VGUI_BindTexture; to->EnableTexture = from->VGUI_EnableTexture; to->CreateTexture = from->VGUI_CreateTexture; to->UploadTexture = from->VGUI_UploadTexture; to->UploadTextureBlock = from->VGUI_UploadTextureBlock; to->DrawQuad = from->VGUI_DrawQuad; to->GetTextureSizes = from->VGUI_GetTextureSizes; to->GenerateTexture = from->VGUI_GenerateTexture; } void VGui_RegisterCvars( void ) { Cvar_RegisterVariable( &vgui_utf8 ); } static HINSTANCE VGui_LoadSupportLibrary( void ) { string vguiloader; string vguilib; HINSTANCE hInstance; // HACKHACK: try to load path from custom path // to support having different versions of VGUI if( Sys_GetParmFromCmdLine( "-vguilib", vguilib ) && !COM_LoadLibrary( vguilib, false, false )) { Con_Reportf( S_WARN "VGUI preloading failed. Default library will be used! Reason: %s", COM_GetLibraryError()); } if( !Sys_GetParmFromCmdLine( "-vguiloader", vguiloader )) { Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, sizeof( vguiloader )); } hInstance = COM_LoadLibrary( vguiloader, false, false ); if( !hInstance ) { if( FS_FileExists( vguiloader, false )) Con_Reportf( S_ERROR "Failed to load vgui_support library: %s\n", COM_GetLibraryError() ); else Con_Reportf( "vgui_support: not found\n" ); } return hInstance; } static qboolean VGui_ProbeNewAPI( HINSTANCE hInstance, vgui_support_interface_t *iface, const vgui_support_api_t *api ) { const int version = VGUI_SUPPORT_API_VERSION; static vgui_support_api_t localapi; VGUISUPPORTAPI F; F = COM_GetProcAddress( hInstance, GET_VGUI_SUPPORT_API ); if( !F ) return false; // keep local temporary copy, do not let vgui_support to mess up here memcpy( &localapi, api, sizeof( localapi )); if( F( version, iface, &localapi ) != version ) return false; Con_Reportf( "vgui_support: initialized new API\n" ); return true; } static qboolean VGui_ProbeOldAPI( HINSTANCE hInstance, vgui_support_interface_t *iface, const vgui_support_api_t *api, qboolean client ) { static legacy_vguiapi_t localapi; LEGACY_VGUISUPPORTAPI F; F = COM_GetProcAddress( hInstance, client ? LEGACY_CLIENT_GET_VGUI_SUPPORT_API : LEGACY_GET_VGUI_SUPPORT_API ); if( !F ) return false; memset( &localapi, 0, sizeof( localapi )); localapi.DrawInit = api->DrawInit; localapi.DrawShutdown = api->DrawShutdown; localapi.SetupDrawingText = api->SetupDrawingText; localapi.SetupDrawingRect = api->SetupDrawingRect; localapi.SetupDrawingImage = api->SetupDrawingImage; localapi.BindTexture = api->BindTexture; localapi.EnableTexture = api->EnableTexture; localapi.CreateTexture = api->CreateTexture; localapi.UploadTexture = api->UploadTexture; localapi.UploadTextureBlock = api->UploadTextureBlock; localapi.DrawQuad = api->DrawQuad; localapi.GetTextureSizes = api->GetTextureSizes; localapi.GenerateTexture = api->GenerateTexture; localapi.EngineMalloc = api->EngineMalloc; localapi.CursorSelect = api->CursorSelect; localapi.GetColor = api->GetColor; localapi.IsInGame = api->IsInGame; localapi.EnableTextInput = api->EnableTextInput; localapi.GetCursorPos = api->GetCursorPos; localapi.ProcessUtfChar = api->ProcessUtfChar; localapi.GetClipboardText = api->GetClipboardText; localapi.SetClipboardText = api->SetClipboardText; localapi.GetKeyModifiers = api->GetKeyModifiers; F( &localapi ); localapi.initialized = true; iface->Startup = localapi.Startup; iface->Shutdown = localapi.Shutdown; iface->GetPanel = localapi.GetPanel; iface->Paint = localapi.Paint; iface->Mouse = localapi.Mouse; iface->Key = localapi.Key; iface->MouseMove = localapi.MouseMove; iface->TextInput = localapi.TextInput; Con_Reportf( "vgui_support: initialized legacy API in %s module\n", client ? "client" : "support" ); return true; } qboolean VGui_LoadProgs( HINSTANCE hInstance ) { LEGACY_VGUISUPPORTAPI F; qboolean client = hInstance != NULL; // not loading interface from client.dll, load vgui_support.dll instead if( !client ) { hInstance = vgui.hInstance = VGui_LoadSupportLibrary(); if( !hInstance ) return false; } // prepare api funcs VGui_FillAPIFromRef( &gEngfuncs, &ref.dllFuncs ); // try new API first if( !VGui_ProbeNewAPI( vgui.hInstance, &vgui.dllFuncs, &gEngfuncs )) { // try legacy API next if( !VGui_ProbeOldAPI( vgui.hInstance, &vgui.dllFuncs, &gEngfuncs, client )) { Con_Reportf( S_ERROR "Failed to find VGUI support API entry point in %s module\n", client ? "client" : "support" ); return false; } } vgui.initialized = true; return true; } /* ================ VGui_Startup ================ */ void VGui_Startup( HINSTANCE clientInstance, int width, int height ) { // vgui not initialized from both support and client modules, skip if( !vgui.initialized ) return; height = Q_max( 480, height ); if( width <= 640 ) width = 640; else if( width <= 800 ) width = 800; else if( width <= 1024 ) width = 1024; else if( width <= 1152 ) width = 1152; else if( width <= 1280 ) width = 1280; else if( width <= 1600 ) width = 1600; if( !clientInstance ) { if( vgui.dllFuncs.Startup ) vgui.dllFuncs.Startup( width, height ); } else { if( vgui.dllFuncs.ClientStartup ) vgui.dllFuncs.ClientStartup( clientInstance, width, height ); } } /* ================ VGui_Shutdown Unload vgui_support library and call VGui_Shutdown ================ */ void VGui_Shutdown( void ) { if( vgui.dllFuncs.Shutdown ) vgui.dllFuncs.Shutdown(); if( vgui.hInstance ) COM_FreeLibrary( vgui.hInstance ); vgui.hInstance = NULL; vgui.initialized = false; } static void VGUI_InitKeyTranslationTable( void ) { static qboolean initialized = false; if( initialized ) return; initialized = true; // set virtual key translation table memset( vgui.virtualKeyTrans, -1, sizeof( vgui.virtualKeyTrans ) ); // TODO: engine keys are not enough here! // make crossplatform way to pass SDL keys here vgui.virtualKeyTrans['0'] = KEY_0; vgui.virtualKeyTrans['1'] = KEY_1; vgui.virtualKeyTrans['2'] = KEY_2; vgui.virtualKeyTrans['3'] = KEY_3; vgui.virtualKeyTrans['4'] = KEY_4; vgui.virtualKeyTrans['5'] = KEY_5; vgui.virtualKeyTrans['6'] = KEY_6; vgui.virtualKeyTrans['7'] = KEY_7; vgui.virtualKeyTrans['8'] = KEY_8; vgui.virtualKeyTrans['9'] = KEY_9; vgui.virtualKeyTrans['A'] = vgui.virtualKeyTrans['a'] = KEY_A; vgui.virtualKeyTrans['B'] = vgui.virtualKeyTrans['b'] = KEY_B; vgui.virtualKeyTrans['C'] = vgui.virtualKeyTrans['c'] = KEY_C; vgui.virtualKeyTrans['D'] = vgui.virtualKeyTrans['d'] = KEY_D; vgui.virtualKeyTrans['E'] = vgui.virtualKeyTrans['e'] = KEY_E; vgui.virtualKeyTrans['F'] = vgui.virtualKeyTrans['f'] = KEY_F; vgui.virtualKeyTrans['G'] = vgui.virtualKeyTrans['g'] = KEY_G; vgui.virtualKeyTrans['H'] = vgui.virtualKeyTrans['h'] = KEY_H; vgui.virtualKeyTrans['I'] = vgui.virtualKeyTrans['i'] = KEY_I; vgui.virtualKeyTrans['J'] = vgui.virtualKeyTrans['j'] = KEY_J; vgui.virtualKeyTrans['K'] = vgui.virtualKeyTrans['k'] = KEY_K; vgui.virtualKeyTrans['L'] = vgui.virtualKeyTrans['l'] = KEY_L; vgui.virtualKeyTrans['M'] = vgui.virtualKeyTrans['m'] = KEY_M; vgui.virtualKeyTrans['N'] = vgui.virtualKeyTrans['n'] = KEY_N; vgui.virtualKeyTrans['O'] = vgui.virtualKeyTrans['o'] = KEY_O; vgui.virtualKeyTrans['P'] = vgui.virtualKeyTrans['p'] = KEY_P; vgui.virtualKeyTrans['Q'] = vgui.virtualKeyTrans['q'] = KEY_Q; vgui.virtualKeyTrans['R'] = vgui.virtualKeyTrans['r'] = KEY_R; vgui.virtualKeyTrans['S'] = vgui.virtualKeyTrans['s'] = KEY_S; vgui.virtualKeyTrans['T'] = vgui.virtualKeyTrans['t'] = KEY_T; vgui.virtualKeyTrans['U'] = vgui.virtualKeyTrans['u'] = KEY_U; vgui.virtualKeyTrans['V'] = vgui.virtualKeyTrans['v'] = KEY_V; vgui.virtualKeyTrans['W'] = vgui.virtualKeyTrans['w'] = KEY_W; vgui.virtualKeyTrans['X'] = vgui.virtualKeyTrans['x'] = KEY_X; vgui.virtualKeyTrans['Y'] = vgui.virtualKeyTrans['y'] = KEY_Y; vgui.virtualKeyTrans['Z'] = vgui.virtualKeyTrans['z'] = KEY_Z; vgui.virtualKeyTrans[K_KP_5 - 5] = KEY_PAD_0; vgui.virtualKeyTrans[K_KP_5 - 4] = KEY_PAD_1; vgui.virtualKeyTrans[K_KP_5 - 3] = KEY_PAD_2; vgui.virtualKeyTrans[K_KP_5 - 2] = KEY_PAD_3; vgui.virtualKeyTrans[K_KP_5 - 1] = KEY_PAD_4; vgui.virtualKeyTrans[K_KP_5 - 0] = KEY_PAD_5; vgui.virtualKeyTrans[K_KP_5 + 1] = KEY_PAD_6; vgui.virtualKeyTrans[K_KP_5 + 2] = KEY_PAD_7; vgui.virtualKeyTrans[K_KP_5 + 3] = KEY_PAD_8; vgui.virtualKeyTrans[K_KP_5 + 4] = KEY_PAD_9; vgui.virtualKeyTrans[K_KP_SLASH] = KEY_PAD_DIVIDE; vgui.virtualKeyTrans['*'] = KEY_PAD_MULTIPLY; vgui.virtualKeyTrans[K_KP_MINUS] = KEY_PAD_MINUS; vgui.virtualKeyTrans[K_KP_PLUS] = KEY_PAD_PLUS; vgui.virtualKeyTrans[K_KP_ENTER] = KEY_PAD_ENTER; vgui.virtualKeyTrans[K_KP_NUMLOCK] = KEY_NUMLOCK; vgui.virtualKeyTrans['['] = KEY_LBRACKET; vgui.virtualKeyTrans[']'] = KEY_RBRACKET; vgui.virtualKeyTrans[';'] = KEY_SEMICOLON; vgui.virtualKeyTrans['`'] = KEY_BACKQUOTE; vgui.virtualKeyTrans[','] = KEY_COMMA; vgui.virtualKeyTrans['.'] = KEY_PERIOD; vgui.virtualKeyTrans['-'] = KEY_MINUS; vgui.virtualKeyTrans['='] = KEY_EQUAL; vgui.virtualKeyTrans['/'] = KEY_SLASH; vgui.virtualKeyTrans['\\'] = KEY_BACKSLASH; vgui.virtualKeyTrans['\''] = KEY_APOSTROPHE; vgui.virtualKeyTrans[K_TAB] = KEY_TAB; vgui.virtualKeyTrans[K_ENTER] = KEY_ENTER; vgui.virtualKeyTrans[K_SPACE] = KEY_SPACE; vgui.virtualKeyTrans[K_CAPSLOCK] = KEY_CAPSLOCK; vgui.virtualKeyTrans[K_BACKSPACE] = KEY_BACKSPACE; vgui.virtualKeyTrans[K_ESCAPE] = KEY_ESCAPE; vgui.virtualKeyTrans[K_INS] = KEY_INSERT; vgui.virtualKeyTrans[K_DEL] = KEY_DELETE; vgui.virtualKeyTrans[K_HOME] = KEY_HOME; vgui.virtualKeyTrans[K_END] = KEY_END; vgui.virtualKeyTrans[K_PGUP] = KEY_PAGEUP; vgui.virtualKeyTrans[K_PGDN] = KEY_PAGEDOWN; vgui.virtualKeyTrans[K_PAUSE] = KEY_BREAK; vgui.virtualKeyTrans[K_SHIFT] = KEY_LSHIFT; // SHIFT -> left SHIFT vgui.virtualKeyTrans[K_ALT] = KEY_LALT; // ALT -> left ALT vgui.virtualKeyTrans[K_CTRL] = KEY_LCONTROL; // CTRL -> left CTRL vgui.virtualKeyTrans[K_WIN] = KEY_LWIN; vgui.virtualKeyTrans[K_UPARROW] = KEY_UP; vgui.virtualKeyTrans[K_LEFTARROW] = KEY_LEFT; vgui.virtualKeyTrans[K_DOWNARROW] = KEY_DOWN; vgui.virtualKeyTrans[K_RIGHTARROW] = KEY_RIGHT; vgui.virtualKeyTrans[K_F1] = KEY_F1; vgui.virtualKeyTrans[K_F2] = KEY_F2; vgui.virtualKeyTrans[K_F3] = KEY_F3; vgui.virtualKeyTrans[K_F4] = KEY_F4; vgui.virtualKeyTrans[K_F5] = KEY_F5; vgui.virtualKeyTrans[K_F6] = KEY_F6; vgui.virtualKeyTrans[K_F7] = KEY_F7; vgui.virtualKeyTrans[K_F8] = KEY_F8; vgui.virtualKeyTrans[K_F9] = KEY_F9; vgui.virtualKeyTrans[K_F10] = KEY_F10; vgui.virtualKeyTrans[K_F11] = KEY_F11; vgui.virtualKeyTrans[K_F12] = KEY_F12; } static enum VGUI_KeyCode VGUI_MapKey( int keyCode ) { VGUI_InitKeyTranslationTable(); if( keyCode >= 0 && keyCode < ARRAYSIZE( vgui.virtualKeyTrans )) return vgui.virtualKeyTrans[keyCode]; return (enum VGUI_KeyCode)-1; } void VGui_MouseEvent( int key, int clicks ) { enum VGUI_MouseAction mact; enum VGUI_MouseCode code; if( !vgui.dllFuncs.Mouse ) return; switch( key ) { case K_MOUSE1: code = MOUSE_LEFT; break; case K_MOUSE2: code = MOUSE_RIGHT; break; case K_MOUSE3: code = MOUSE_MIDDLE; break; default: return; } if( clicks >= 2 ) mact = MA_DOUBLE; else if( clicks == 1 ) mact = MA_PRESSED; else mact = MA_RELEASED; vgui.dllFuncs.Mouse( mact, code ); } void VGui_MWheelEvent( int y ) { if( !vgui.dllFuncs.Mouse ) return; vgui.dllFuncs.Mouse( MA_WHEEL, y ); } void VGui_KeyEvent( int key, int down ) { enum VGUI_KeyCode code; if( !vgui.dllFuncs.Key ) return; if(( code = VGUI_MapKey( key )) < 0 ) return; if( down ) { vgui.dllFuncs.Key( KA_PRESSED, code ); vgui.dllFuncs.Key( KA_TYPED, code ); } else vgui.dllFuncs.Key( KA_RELEASED, code ); } void VGui_MouseMove( int x, int y ) { if( vgui.dllFuncs.MouseMove ) { float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; vgui.dllFuncs.MouseMove( x / xscale, y / yscale ); } } void VGui_Paint( void ) { if( vgui.dllFuncs.Paint ) vgui.dllFuncs.Paint(); } void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType ) { vgui.cursor = cursorType; } void *GAME_EXPORT VGui_GetPanel( void ) { if( vgui.dllFuncs.GetPanel ) return vgui.dllFuncs.GetPanel(); return NULL; } void VGui_ReportTextInput( const char *text ) { if( vgui.dllFuncs.TextInput ) vgui.dllFuncs.TextInput( text ); }