diff options
Diffstat (limited to 'source/ui_atoms.c')
-rw-r--r-- | source/ui_atoms.c | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/source/ui_atoms.c b/source/ui_atoms.c new file mode 100644 index 0000000..211f337 --- /dev/null +++ b/source/ui_atoms.c @@ -0,0 +1,903 @@ +/* +Copyright (C) 2003-2006 Andrey Nazarov + +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 2 +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "ui_local.h" + +#ifndef UI_HARD_LINKED +/* declare imports for this module */ +cmdAPI_t cmd; +cvarAPI_t cvar; +fsAPI_t fs; +commonAPI_t com; +sysAPI_t sys; +refAPI_t ref; +keyAPI_t keys; +clientAPI_t client; +#endif + +uiStatic_t uis; + +cvar_t *ui_debug; +cvar_t *ui_open; +cvar_t *ui_background; + +// =========================================================================== + +static int EmptyCallback( int id, int msg, int param ) { + return QMS_NOTHANDLED; +} + +/* +================= +UI_PushMenu +================= +*/ +void UI_PushMenu( menuFrameWork_t *menu ) { + int i, keydest; + + cvar.Set( "cl_paused", "1" ); + + // if this menu is already present, drop back to that level + // to avoid stacking menus by hotkeys + for( i=0 ; i<uis.menuDepth ; i++ ) { + if( uis.layers[i] == menu ) { + break; + } + } + + if( i == uis.menuDepth ) { + if( uis.menuDepth >= MAX_MENU_DEPTH ) + Com_Error( ERR_FATAL, "UI_PushMenu: MAX_MENU_DEPTH" ); + uis.layers[uis.menuDepth++] = menu; + } else { + uis.menuDepth = i; + } + + for( i=uis.menuDepth-1 ; i>=0 ; i-- ) { + if( uis.layers[i]->transparent ) { + break; + } + } + + uis.transparent = qtrue; + if( i < 0 ) { + uis.transparent = qfalse; + } + + if( !menu->callback ) { + menu->callback = EmptyCallback; + } + + if( !uis.activeMenu ) { + uis.entersound = qtrue; + } + + uis.activeMenu = menu; + + keydest = keys.GetDest(); + if( keydest & KEY_CONSOLE ) { + keydest &= ~KEY_CONSOLE; + cmd.ExecuteText( EXEC_NOW, "toggleconsole\n" ); + } + keys.SetDest( keydest | KEY_MENU ); + + UI_DoHitTest(); +} + +/* +================= +UI_ForceMenuOff +================= +*/ +void UI_ForceMenuOff( void ) { + int i; + + for( i=0 ; i<uis.menuDepth ; i++ ) { + if( uis.layers[i] ) { + uis.layers[i]->callback( ID_MENU, QM_DESTROY, qtrue ); + } + } + + keys.SetDest( keys.GetDest() & ~KEY_MENU ); + uis.menuDepth = 0; + uis.activeMenu = NULL; + uis.transparent = qfalse; + //keys.ClearStates(); + if( !( keys.GetDest() & KEY_CONSOLE ) ) { + cvar.Set( "cl_paused", "0" ); + } +} + +/* +================= +UI_PopMenu +================= +*/ +void UI_PopMenu( void ) { + int i; + + if( uis.menuDepth < 1 ) + Com_Error( ERR_FATAL, "UI_PopMenu: depth < 1" ); + + if( --uis.menuDepth == 0 ) { + UI_ForceMenuOff(); + return; + } + + uis.layers[uis.menuDepth]->callback( ID_MENU, QM_DESTROY, qfalse ); + uis.layers[uis.menuDepth - 1]->callback( ID_MENU, QM_DESTROY_CHILD, qfalse ); + + uis.activeMenu = uis.layers[uis.menuDepth - 1]; + + for( i=uis.menuDepth-1 ; i>=0 ; i-- ) { + if( uis.layers[i]->transparent ) { + break; + } + } + + uis.transparent = qtrue; + if( i < 0 ) { + uis.transparent = qfalse; + } + + UI_DoHitTest(); + + +} + +/* +================= +UI_IsTransparent +================= +*/ +qboolean UI_IsTransparent( void ) { + if( !( keys.GetDest() & KEY_MENU ) ) { + return qtrue; + } + + if( !uis.activeMenu ) { + return qtrue; + } + + return uis.transparent; +} + +/* +================= +UI_OpenMenu +================= +*/ +void UI_OpenMenu( uiMenu_t menu ) { + // close any existing menus + UI_ForceMenuOff(); + + switch( menu ) { + case UIMENU_MAIN: + if( ui_open->integer ) { + M_Menu_Main_f(); + } + break; + case UIMENU_MAIN_FORCE: + M_Menu_Main_f(); + break; + case UIMENU_INGAME: + M_Menu_Ingame_f(); + break; + case UIMENU_NONE: + break; + default: + Com_Error( ERR_FATAL, "UI_OpenMenu: bad menu" ); + break; + } +} + +void UI_ErrorMenu( comErrorType_t type, const char *text ) { + // close any existing menus + UI_ForceMenuOff(); + + if( !ui_open->integer ) { + return; + } + + M_Menu_Main_f(); + M_Menu_Error_f( type, text ); +} + + +//============================================================================= + +/* +================= +UI_FormatColumns +================= +*/ +char *UI_FormatColumns( int numArgs, ... ) { + va_list argptr; + char *buffer, *p; + int i, totalLength; + char *strings[MAX_COLUMNS]; + int lengths[MAX_COLUMNS]; + + if( ( unsigned )numArgs > MAX_COLUMNS ) { + Com_Error( ERR_FATAL, "UI_FormatColumns: too many columns" ); + } + + totalLength = 0; + + va_start( argptr, numArgs ); + for( i = 0; i < numArgs; i++ ) { + strings[i] = va_arg( argptr, char * ); + lengths[i] = strlen( strings[i] ) + 1; + totalLength += lengths[i]; + } + va_end( argptr ); + + buffer = p = UI_Malloc( totalLength + 1 ); + for( i = 0; i < numArgs; i++ ) { + memcpy( p, strings[i], lengths[i] ); + p += lengths[i]; + } + + *p = 0; + + return buffer; + +} + +/* +================= +UI_CopyString +================= +*/ +char *UI_CopyString( const char *in ) { + char *out; + + if( !in ) { + return NULL; + } + + out = UI_Malloc( strlen( in ) + 1 ); + strcpy( out, in ); + + return out; +} + +/* +================= +UI_SetupDefaultBanner +================= +*/ +void UI_SetupDefaultBanner( menuStatic_t *banner, const char *name ) { + banner->generic.type = MTYPE_STATIC; + banner->generic.name = name; + banner->generic.uiFlags = UI_CENTER|UI_ALTCOLOR; + + banner->generic.x = uis.glconfig.vidWidth / 2; + banner->generic.y = 8; +} + +/* +================= +UI_CursorInRect +================= +*/ +qboolean UI_CursorInRect( vrect_t *rect, int mx, int my ) { + if( mx > rect->x && mx < rect->x + rect->width && + my > rect->y && my < rect->y + rect->height ) + { + return qtrue; + } + + return qfalse; +} + +void UI_DrawString( int x, int y, const color_t color, uint32 flags, const char *string ) { + if( color ) { + ref.SetColor( DRAW_COLOR_RGBA, color ); + } + + if( ( flags & UI_CENTER ) == UI_CENTER ) { + x -= Q_DrawStrlen( string ) * 8 / 2; + } else if( flags & UI_RIGHT ) { + x -= Q_DrawStrlen( string ) * 8; + } + + ref.DrawString( x, y, flags, MAX_STRING_CHARS, string, uis.fontHandle ); + if( color ) { + ref.SetColor( DRAW_COLOR_CLEAR, NULL ); + } +} + +void UI_DrawChar( int x, int y, uint32 flags, int ch ) { + ref.DrawChar( x, y, flags, ch, uis.fontHandle ); +} + +void UI_StringDimensions( vrect_t *rc, uint32 flags, const char *string ) { + rc->height = 8; + rc->width = 8 * Q_DrawStrlen( string ); + + if( ( flags & UI_CENTER ) == UI_CENTER ) { + rc->x -= rc->width / 2; + } else if( flags & UI_RIGHT ) { + rc->x -= rc->width; + } +} + + +//============================================================================= +/* Menu Subsystem */ + +void M_Menu_Network_f( void ); + +/* +================= +UI_DoHitTest +================= +*/ +qboolean UI_DoHitTest( void ) { + menuCommon_t *item; + + if( !uis.activeMenu ) { + return qfalse; + } + + if( !( item = Menu_HitTest( uis.activeMenu, uis.mouseCoords[0], uis.mouseCoords[1] ) ) ) { + return qfalse; + } + if( !UI_IsItemSelectable( item ) ) { + return qfalse; + } + + Menu_MouseMove( item ); + + if( item->flags & QMF_HASFOCUS ) { + return qfalse; + } + + Menu_SetFocus( item ); + + return qtrue; +} + +/* +================= +UI_MouseMove +================= +*/ +void UI_MouseMove( int mx, int my ) { + if( !uis.activeMenu ) { + return; + } + + if( !mx && !my ) { + return; + } + + uis.mouseCoords[0] += mx; + uis.mouseCoords[1] += my; + + clamp( uis.mouseCoords[0], 0, uis.glconfig.vidWidth ); + clamp( uis.mouseCoords[1], 0, uis.glconfig.vidHeight ); + + if( UI_DoHitTest() ) { + // TODO: add new mousemove sound + // cl.StartLocalSound( "misc/menu2.wav" ); + } +} + + +/* +================= +UI_Draw +================= +*/ +void UI_Draw( int realtime ) { + int i; + + uis.realtime = realtime; + + if( !( keys.GetDest() & KEY_MENU ) ) { + return; + } + + if( !uis.activeMenu ) { + return; + } + + ref.SetColor( DRAW_COLOR_CLEAR, NULL ); + if( uis.glconfig.renderer == GL_RENDERER_SOFTWARE ) { + ref.SetClipRect( DRAW_CLIP_MASK, &uis.clipRect ); + } + + if( !uis.transparent ) { + // no transparent menus + if( uis.backgroundHandle ) { + ref.DrawStretchPic( 0, 0, uis.glconfig.vidWidth, uis.glconfig.vidHeight, + uis.backgroundHandle ); + } else { + ref.DrawFill( 0, 0, uis.glconfig.vidWidth, uis.glconfig.vidHeight, 0 ); + } + + if( uis.activeMenu->draw ) { + uis.activeMenu->draw( uis.activeMenu ); + } else { + Menu_Draw( uis.activeMenu ); + } + } else { + // draw all layers + for( i = 0; i < uis.menuDepth; i++ ) { + if( !uis.layers[i]->transparent ) { + if( uis.backgroundHandle ) { + ref.DrawStretchPic( 0, 0, uis.glconfig.vidWidth, uis.glconfig.vidHeight, + uis.backgroundHandle ); + } else { + ref.DrawFill( 0, 0, uis.glconfig.vidWidth, uis.glconfig.vidHeight, 0 ); + } + } + + if( uis.layers[i]->draw ) { + uis.layers[i]->draw( uis.layers[i] ); + } else { + Menu_Draw( uis.layers[i] ); + } + } + } + + ref.DrawStretchPic( uis.mouseCoords[0] - uis.cursorWidth / 2, + uis.mouseCoords[1] - uis.cursorHeight / 2, + uis.cursorWidth, uis.cursorHeight, uis.cursorHandle ); + + if( ui_debug->integer ) { + Menu_HitTest( uis.activeMenu, 0, 0 ); + UI_DrawString( uis.glconfig.vidWidth - 4, 4, NULL, UI_RIGHT, + va( "%3i %3i", uis.mouseCoords[0], uis.mouseCoords[1] ) ); + } + + // delay playing the enter sound until after the + // menu has been drawn, to avoid delay while + // caching images + if( uis.entersound ) { + uis.entersound = qfalse; + client.StartLocalSound( "misc/menu1.wav" ); + } + + if( uis.glconfig.renderer == GL_RENDERER_SOFTWARE ) { + ref.SetClipRect( DRAW_CLIP_DISABLED, NULL ); + } + ref.SetColor( DRAW_COLOR_CLEAR, NULL ); +} + +/* +================= +Default_MenuKey +================= +*/ +int Default_MenuKey( menuFrameWork_t *m, int key ) { + menuCommon_t *item; + + switch( key ) { + case K_ESCAPE: + UI_PopMenu(); + return QMS_OUT; + + case K_KP_UPARROW: + case K_UPARROW: + case 'k': + return Menu_AdjustCursor( m, -1 ); + + case K_KP_DOWNARROW: + case K_DOWNARROW: + case K_TAB: + case 'j': + return Menu_AdjustCursor( m, 1 ); + + case K_KP_LEFTARROW: + case K_LEFTARROW: + case K_MWHEELDOWN: + case 'h': + return Menu_SlideItem( m, -1 ); + + case K_KP_RIGHTARROW: + case K_RIGHTARROW: + case K_MWHEELUP: + case 'l': + return Menu_SlideItem( m, 1 ); + + case K_MOUSE1: + case K_MOUSE2: + case K_MOUSE3: + item = Menu_HitTest( m, uis.mouseCoords[0], uis.mouseCoords[1] ); + if( !item ) { + return QMS_NOTHANDLED; + } + + if( !( item->flags & QMF_HASFOCUS ) ) { + return QMS_NOTHANDLED; + } + + // fall through + + case K_JOY1: + case K_JOY2: + case K_JOY3: + case K_JOY4: + case K_AUX1: + case K_AUX2: + case K_AUX3: + case K_AUX4: + case K_AUX5: + case K_AUX6: + case K_AUX7: + case K_AUX8: + case K_AUX9: + case K_AUX10: + case K_AUX11: + case K_AUX12: + case K_AUX13: + case K_AUX14: + case K_AUX15: + case K_AUX16: + case K_AUX17: + case K_AUX18: + case K_AUX19: + case K_AUX20: + case K_AUX21: + case K_AUX22: + case K_AUX23: + case K_AUX24: + case K_AUX25: + case K_AUX26: + case K_AUX27: + case K_AUX28: + case K_AUX29: + case K_AUX30: + case K_AUX31: + case K_AUX32: + case K_KP_ENTER: + case K_ENTER: + return Menu_SelectItem( m ); + } + + return QMS_NOTHANDLED; +} + +/* +================= +UI_Keydown +================= +*/ +void UI_Keydown( int key ) { + menuCommon_t *item; + int sound; + + if( !uis.activeMenu ) { + return; + } + + if( ( sound = uis.activeMenu->callback( ID_MENU, QM_KEY, key ) ) == QMS_NOTHANDLED ) { + if( ( item = Menu_ItemAtCursor( uis.activeMenu ) ) == NULL || + ( sound = Menu_KeyEvent( item, key ) ) == QMS_NOTHANDLED ) + { + sound = Default_MenuKey( uis.activeMenu, key ); + } + } + + switch( sound ) { + case QMS_IN: + client.StartLocalSound( "misc/menu1.wav" ); + break; + case QMS_MOVE: + client.StartLocalSound( "misc/menu2.wav" ); + break; + case QMS_OUT: + client.StartLocalSound( "misc/menu3.wav" ); + break; + case QMS_BEEP: + client.StartLocalSound( "misc/talk1.wav" ); + break; + case QMS_NOTHANDLED: + default: + break; + } + +} + +/* +================= +UI_CharEvent +================= +*/ +void UI_CharEvent( int key ) { + menuCommon_t *item; + int sound; + + if( !uis.activeMenu ) { + return; + } + + if( ( sound = uis.activeMenu->callback( ID_MENU, QM_CHAR, key ) ) == QMS_NOTHANDLED ) { + if( ( item = Menu_ItemAtCursor( uis.activeMenu ) ) == NULL || + ( sound = Menu_CharEvent( item, key ) ) == QMS_NOTHANDLED ) + { + return; + } + } + + switch( sound ) { + case QMS_IN: + client.StartLocalSound( "misc/menu1.wav" ); + break; + case QMS_MOVE: + client.StartLocalSound( "misc/menu2.wav" ); + break; + case QMS_OUT: + client.StartLocalSound( "misc/menu3.wav" ); + break; + case QMS_BEEP: + client.StartLocalSound( "misc/talk1.wav" ); + break; + case QMS_NOTHANDLED: + default: + break; + } + +} + +typedef struct uicmd_s { + const char *name; + void (*func)( void ); +} uicmd_t; + +static const uicmd_t uicmds[] = { + { "menu_main", M_Menu_Main_f }, + { "menu_game", M_Menu_Game_f }, + { "menu_loadgame", M_Menu_LoadGame_f }, + { "menu_savegame", M_Menu_SaveGame_f }, + { "menu_addressbook", M_Menu_AddressBook_f }, + { "menu_startserver", M_Menu_StartServer_f }, + { "menu_dmoptions", M_Menu_DMOptions_f }, + { "menu_playerconfig", M_Menu_PlayerConfig_f }, + { "menu_downloadoptions", M_Menu_DownloadOptions_f }, + { "menu_multiplayer", M_Menu_Multiplayer_f }, + { "menu_network", M_Menu_Network_f }, + { "menu_video", M_Menu_Video_f }, + { "menu_options", M_Menu_Options_f }, + { "menu_keys", M_Menu_Keys_f }, + { "menu_quit", M_Menu_Credits_f }, + { "menu_close", UI_ForceMenuOff }, + + { NULL, NULL } +}; + +static void ui_background_changed( cvar_t *self ) { + if( self->string[0] ) { + uis.backgroundHandle = ref.RegisterPic( self->string ); + } +} + +void UI_ModeChanged( void ) { + UI_ForceMenuOff(); + + ref.GetConfig( &uis.glconfig ); + if( uis.glconfig.renderer == GL_RENDERER_SOFTWARE ) { + uis.clipRect.left = 0; + uis.clipRect.top = 0; + uis.clipRect.right = uis.glconfig.vidWidth; + uis.clipRect.bottom = uis.glconfig.vidHeight; + } + +} + +/* +================= +UI_Init +================= +*/ +qboolean UI_Init( void ) { + const uicmd_t *uicmd; + + memset( &uis, 0, sizeof( uis ) ); + + for( uicmd = uicmds; uicmd->name; uicmd++ ) { + cmd.AddCommand( uicmd->name, uicmd->func ); + } + + ui_debug = cvar.Get( "ui_debug", "0", 0 ); + ui_open = cvar.Get( "ui_open", "0", CVAR_ARCHIVE ); + ui_background = cvar.Get( "ui_background", "", 0 ); + + uis.fontHandle = ref.RegisterFont( "conchars" ); + uis.cursorHandle = ref.RegisterPic( "ch1" ); + ref.DrawGetPicSize( &uis.cursorWidth, &uis.cursorHeight, uis.cursorHandle ); + + if( uis.glconfig.renderer != GL_RENDERER_SOFTWARE ) { + if( ui_background->string[0] ) { + uis.backgroundHandle = ref.RegisterPic( ui_background->string ); + } + ui_background->changed = ui_background_changed; + } + + return qtrue; +} + +/* +================= +UI_Shutdown +================= +*/ +void UI_Shutdown( void ) { + const uicmd_t *uicmd; + + ui_background->changed = NULL; + + PlayerModel_Free(); + + for( uicmd = uicmds; uicmd->name; uicmd++ ) { + cmd.RemoveCommand( uicmd->name ); + } +} + +#ifndef UI_HARD_LINKED + +// this is only here so the functions in q_shared.c can link + +void Com_Printf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_ALL, text ); +} + +void Com_DPrintf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_DEVELOPER, text ); +} + +void Com_WPrintf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_WARNING, text ); +} + +void Com_EPrintf( const char *fmt, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( text, sizeof( text ), fmt, argptr ); + va_end( argptr ); + + com.Print( PRINT_ERROR, text ); +} + +void Com_Error( comErrorType_t type, const char *error, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + + com.Error( type, text ); +} + +#endif + +/* +================= +UI_FillAPI +================= +*/ +static void UI_FillAPI( uiAPI_t *api ) { + api->Init = UI_Init; + api->Shutdown = UI_Shutdown; + api->ModeChanged = UI_ModeChanged; + api->Draw = UI_Draw; + api->DrawLoading = UI_DrawLoading; + api->MouseMove = UI_MouseMove; + api->Keydown = UI_Keydown; + api->CharEvent = UI_CharEvent; + api->OpenMenu = UI_OpenMenu; + api->ErrorMenu = UI_ErrorMenu; + api->AddToServerList = UI_AddToServerList; + api->IsTransparent = UI_IsTransparent; +} + +/* +================= +UI_APISetupCallback +================= +*/ +static qboolean UI_APISetupCallback( api_type_t type, void *api ) { + switch( type ) { + case API_UI: + UI_FillAPI( ( uiAPI_t * )api ); + break; + default: + return qfalse; + } + + return qtrue; +} + +#ifndef UI_HARD_LINKED + +/* +@@@@@@@@@@@@@@@@@@@@@ +moduleEntry + +@@@@@@@@@@@@@@@@@@@@@ +*/ +EXPORTED void *moduleEntry( int query, void *data ) { + moduleInfo_t *info; + moduleCapability_t caps; + APISetupCallback_t callback; + + switch( query ) { + case MQ_GETINFO: + info = ( moduleInfo_t * )data; + info->api_version = MODULES_APIVERSION; + Q_strncpyz( info->fullname, "User interface library", + sizeof( info->fullname ) ); + Q_strncpyz( info->author, "Andrey Nazarov", sizeof( info->author ) ); + return ( void * )qtrue; + + case MQ_GETCAPS: + caps = MCP_UI; + return ( void * )caps; + + case MQ_SETUPAPI: + if( ( callback = ( APISetupCallback_t )data ) == NULL ) { + return NULL; + } + callback( API_CMD, &cmd ); + callback( API_CVAR, &cvar ); + callback( API_FS, &fs ); + callback( API_COMMON, &com ); + callback( API_SYSTEM, &sys ); + callback( API_REFRESH, &ref ); + callback( API_KEYS, &keys ); + callback( API_CLIENT, &client ); + + return ( void * )UI_APISetupCallback; + + } + + /* quiet compiler warning */ + return NULL; +} + +#endif + |