summaryrefslogtreecommitdiff
path: root/src/client/ui/ui.c
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2012-07-24 19:36:31 +0400
committerAndrey Nazarov <skuller@skuller.net>2012-07-24 20:55:04 +0400
commit0b5a71869a4fd08524ea9c2a986b4744d0913e30 (patch)
treef50eea3188d5a221a8d3e2455d6635eddec9e35a /src/client/ui/ui.c
parent009390601fe318af50cde0f7f3449ed65365ea29 (diff)
Rework source tree layout.
Instead of having a mix of semi-randomly prefixed .c and .h files at src/, split the sources and public headers into src/ and inc/ directories. Further partition them down per subsystem. Add include guards to public headers. Split some headers into multiple ones.
Diffstat (limited to 'src/client/ui/ui.c')
-rw-r--r--src/client/ui/ui.c706
1 files changed, 706 insertions, 0 deletions
diff --git a/src/client/ui/ui.c b/src/client/ui/ui.c
new file mode 100644
index 0000000..b795509
--- /dev/null
+++ b/src/client/ui/ui.c
@@ -0,0 +1,706 @@
+/*
+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.h"
+#include "client/input.h"
+#include "common/prompt.h"
+
+uiStatic_t uis;
+
+LIST_DECL(ui_menus);
+
+cvar_t *ui_debug;
+static cvar_t *ui_open;
+static cvar_t *ui_scale;
+
+// ===========================================================================
+
+/*
+=================
+UI_PushMenu
+=================
+*/
+void UI_PushMenu(menuFrameWork_t *menu)
+{
+ int i, j;
+
+ if (!menu) {
+ return;
+ }
+
+ // 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_EPrintf("UI_PushMenu: MAX_MENU_DEPTH exceeded\n");
+ return;
+ }
+ uis.layers[uis.menuDepth++] = menu;
+ } else {
+ for (j = i; j < uis.menuDepth; j++) {
+ UI_PopMenu();
+ }
+ uis.menuDepth = i + 1;
+ }
+
+ if (menu->push) {
+ if (!menu->push(menu)) {
+ return;
+ }
+ }
+
+ Menu_Init(menu);
+
+ Key_SetDest((Key_GetDest() & ~KEY_CONSOLE) | KEY_MENU);
+
+ Con_Close(qtrue);
+
+ if (!uis.activeMenu) {
+ // opening menu moves cursor to the nice location
+ IN_WarpMouse(menu->mins[0] / uis.scale, menu->mins[1] / uis.scale);
+
+ uis.mouseCoords[0] = menu->mins[0];
+ uis.mouseCoords[1] = menu->mins[1];
+
+ uis.entersound = qtrue;
+ }
+
+ uis.transparent |= menu->transparent;
+ uis.activeMenu = menu;
+
+ UI_DoHitTest();
+
+ if (menu->expose) {
+ menu->expose(menu);
+ }
+}
+
+static void UI_Resize(void)
+{
+ int i;
+
+#if USE_REF == REF_SOFT
+ uis.clipRect.left = 0;
+ uis.clipRect.top = 0;
+ uis.clipRect.right = r_config.width;
+ uis.clipRect.bottom = r_config.height;
+ uis.scale = 1;
+ uis.width = r_config.width;
+ uis.height = r_config.height;
+#else
+ Cvar_ClampValue(ui_scale, 1, 9);
+ uis.scale = 1 / ui_scale->value;
+ uis.width = r_config.width * uis.scale;
+ uis.height = r_config.height * uis.scale;
+#endif
+
+ for (i = 0; i < uis.menuDepth; i++) {
+ Menu_Init(uis.layers[i]);
+ }
+
+ //CL_WarpMouse(0, 0);
+}
+
+
+/*
+=================
+UI_ForceMenuOff
+=================
+*/
+void UI_ForceMenuOff(void)
+{
+ menuFrameWork_t *menu;
+ int i;
+
+ for (i = 0; i < uis.menuDepth; i++) {
+ menu = uis.layers[i];
+ if (menu->pop) {
+ menu->pop(menu);
+ }
+ }
+
+ Key_SetDest(Key_GetDest() & ~KEY_MENU);
+ uis.menuDepth = 0;
+ uis.activeMenu = NULL;
+ uis.transparent = qfalse;
+}
+
+/*
+=================
+UI_PopMenu
+=================
+*/
+void UI_PopMenu(void)
+{
+ menuFrameWork_t *menu;
+ int i;
+
+ if (uis.menuDepth < 1)
+ Com_Error(ERR_FATAL, "UI_PopMenu: depth < 1");
+
+ menu = uis.layers[--uis.menuDepth];
+ if (menu->pop) {
+ menu->pop(menu);
+ }
+
+ if (!uis.menuDepth) {
+ UI_ForceMenuOff();
+ return;
+ }
+
+ uis.activeMenu = uis.layers[uis.menuDepth - 1];
+
+ uis.transparent = qfalse;
+ for (i = uis.menuDepth - 1; i >= 0; i--) {
+ if (uis.layers[i]->transparent) {
+ uis.transparent = qtrue;
+ break;
+ }
+ }
+
+ UI_DoHitTest();
+}
+
+/*
+=================
+UI_IsTransparent
+=================
+*/
+qboolean UI_IsTransparent(void)
+{
+ if (!(Key_GetDest() & KEY_MENU)) {
+ return qtrue;
+ }
+
+ if (!uis.activeMenu) {
+ return qtrue;
+ }
+
+ return uis.transparent;
+}
+
+menuFrameWork_t *UI_FindMenu(const char *name)
+{
+ menuFrameWork_t *menu;
+
+ LIST_FOR_EACH(menuFrameWork_t, menu, &ui_menus, entry) {
+ if (!strcmp(menu->name, name)) {
+ return menu;
+ }
+ }
+ return NULL;
+}
+
+/*
+=================
+UI_OpenMenu
+=================
+*/
+void UI_OpenMenu(uiMenu_t type)
+{
+ menuFrameWork_t *menu = NULL;
+
+ if (!uis.initialized) {
+ return;
+ }
+
+ // close any existing menus
+ UI_ForceMenuOff();
+
+ switch (type) {
+ case UIMENU_DEFAULT:
+ if (ui_open->integer) {
+ menu = UI_FindMenu("main");
+ }
+ break;
+ case UIMENU_MAIN:
+ menu = UI_FindMenu("main");
+ break;
+ case UIMENU_GAME:
+ menu = UI_FindMenu("game");
+ if (!menu) {
+ menu = UI_FindMenu("main");
+ }
+ break;
+ case UIMENU_NONE:
+ break;
+ default:
+ Com_Error(ERR_FATAL, "UI_OpenMenu: bad menu");
+ break;
+ }
+
+ UI_PushMenu(menu);
+}
+
+//=============================================================================
+
+/*
+=================
+UI_FormatColumns
+=================
+*/
+void *UI_FormatColumns(int extrasize, ...)
+{
+ va_list argptr;
+ char *buffer, *p;
+ int i, j;
+ size_t total = 0;
+ char *strings[MAX_COLUMNS];
+ size_t lengths[MAX_COLUMNS];
+
+ va_start(argptr, extrasize);
+ for (i = 0; i < MAX_COLUMNS; i++) {
+ if ((p = va_arg(argptr, char *)) == NULL) {
+ break;
+ }
+ strings[i] = p;
+ total += lengths[i] = strlen(p) + 1;
+ }
+ va_end(argptr);
+
+ buffer = UI_Malloc(extrasize + total + 1);
+ p = buffer + extrasize;
+ for (j = 0; j < i; j++) {
+ memcpy(p, strings[j], lengths[j]);
+ p += lengths[j];
+ }
+ *p = 0;
+
+ return buffer;
+}
+
+char *UI_GetColumn(char *s, int n)
+{
+ int i;
+
+ for (i = 0; i < n && *s; i++) {
+ s += strlen(s) + 1;
+ }
+
+ return s;
+}
+
+/*
+=================
+UI_CursorInRect
+=================
+*/
+qboolean UI_CursorInRect(vrect_t *rect)
+{
+ if (uis.mouseCoords[0] < rect->x) {
+ return qfalse;
+ }
+ if (uis.mouseCoords[0] >= rect->x + rect->width) {
+ return qfalse;
+ }
+ if (uis.mouseCoords[1] < rect->y) {
+ return qfalse;
+ }
+ if (uis.mouseCoords[1] >= rect->y + rect->height) {
+ return qfalse;
+ }
+ return qtrue;
+}
+
+void UI_DrawString(int x, int y, int flags, const char *string)
+{
+ if ((flags & UI_CENTER) == UI_CENTER) {
+ x -= strlen(string) * CHAR_WIDTH / 2;
+ } else if (flags & UI_RIGHT) {
+ x -= strlen(string) * CHAR_WIDTH;
+ }
+
+ R_DrawString(x, y, flags, MAX_STRING_CHARS, string, uis.fontHandle);
+}
+
+void UI_DrawChar(int x, int y, int flags, int ch)
+{
+ R_DrawChar(x, y, flags, ch, uis.fontHandle);
+}
+
+void UI_StringDimensions(vrect_t *rc, int flags, const char *string)
+{
+ rc->height = CHAR_HEIGHT;
+ rc->width = CHAR_WIDTH * strlen(string);
+
+ if ((flags & UI_CENTER) == UI_CENTER) {
+ rc->x -= rc->width / 2;
+ } else if (flags & UI_RIGHT) {
+ rc->x -= rc->width;
+ }
+}
+
+void UI_DrawRect8(const vrect_t *rc, int border, int c)
+{
+ R_DrawFill8(rc->x, rc->y, border, rc->height, c); // left
+ R_DrawFill8(rc->x + rc->width - border, rc->y, border, rc->height, c); // right
+ R_DrawFill8(rc->x + border, rc->y, rc->width - border * 2, border, c); // top
+ R_DrawFill8(rc->x + border, rc->y + rc->height - border, rc->width - border * 2, border, c); // bottom
+}
+
+#if 0
+void UI_DrawRect32(const vrect_t *rc, int border, uint32_t color)
+{
+ R_DrawFill32(rc->x, rc->y, border, rc->height, color); // left
+ R_DrawFill32(rc->x + rc->width - border, rc->y, border, rc->height, color); // right
+ R_DrawFill32(rc->x + border, rc->y, rc->width - border * 2, border, color); // top
+ R_DrawFill32(rc->x + border, rc->y + rc->height - border, rc->width - border * 2, border, color); // bottom
+}
+#endif
+
+//=============================================================================
+/* Menu Subsystem */
+
+/*
+=================
+UI_DoHitTest
+=================
+*/
+qboolean UI_DoHitTest(void)
+{
+ menuCommon_t *item;
+
+ if (!uis.activeMenu) {
+ return qfalse;
+ }
+
+ if (!(item = Menu_HitTest(uis.activeMenu))) {
+ return qfalse;
+ }
+ if (!UI_IsItemSelectable(item)) {
+ return qfalse;
+ }
+
+ Menu_MouseMove(item);
+
+ if (item->flags & QMF_HASFOCUS) {
+ return qfalse;
+ }
+
+ Menu_SetFocus(item);
+
+ return qtrue;
+}
+
+/*
+=================
+UI_MouseEvent
+=================
+*/
+void UI_MouseEvent(int x, int y)
+{
+ clamp(x, 0, r_config.width - 1);
+ clamp(y, 0, r_config.height - 1);
+
+ uis.mouseCoords[0] = Q_rint(x * uis.scale);
+ uis.mouseCoords[1] = Q_rint(y * uis.scale);
+
+ UI_DoHitTest();
+}
+
+/*
+=================
+UI_Draw
+=================
+*/
+void UI_Draw(int realtime)
+{
+ int i;
+
+ uis.realtime = realtime;
+
+ if (!(Key_GetDest() & KEY_MENU)) {
+ return;
+ }
+
+ if (!uis.activeMenu) {
+ return;
+ }
+
+ R_ClearColor();
+#if USE_REF == REF_SOFT
+ R_SetClipRect(DRAW_CLIP_MASK, &uis.clipRect);
+#else
+ R_SetScale(&uis.scale);
+#endif
+
+ if (!uis.transparent) {
+ // draw top menu
+ 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]->draw) {
+ uis.layers[i]->draw(uis.layers[i]);
+ } else {
+ Menu_Draw(uis.layers[i]);
+ }
+ }
+ }
+
+ // draw custom cursor in fullscreen mode
+ if (r_config.flags & QVF_FULLSCREEN) {
+ R_DrawPic(uis.mouseCoords[0] - uis.cursorWidth / 2,
+ uis.mouseCoords[1] - uis.cursorHeight / 2, uis.cursorHandle);
+ }
+
+ if (ui_debug->integer) {
+ UI_DrawString(uis.width - 4, 4, 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;
+ S_StartLocalSound("misc/menu1.wav");
+ }
+
+#if USE_REF == REF_SOFT
+ R_SetClipRect(DRAW_CLIP_DISABLED, NULL);
+#else
+ R_SetScale(NULL);
+#endif
+ R_ClearColor();
+}
+
+void UI_StartSound(menuSound_t sound)
+{
+ switch (sound) {
+ case QMS_IN:
+ S_StartLocalSound("misc/menu1.wav");
+ break;
+ case QMS_MOVE:
+ S_StartLocalSound("misc/menu2.wav");
+ break;
+ case QMS_OUT:
+ S_StartLocalSound("misc/menu3.wav");
+ break;
+ case QMS_BEEP:
+ S_StartLocalSound("misc/talk1.wav");
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+=================
+UI_Keydown
+=================
+*/
+void UI_Keydown(int key)
+{
+ menuSound_t sound;
+
+ if (!uis.activeMenu) {
+ return;
+ }
+
+ sound = Menu_Keydown(uis.activeMenu, key);
+
+ UI_StartSound(sound);
+}
+
+/*
+=================
+UI_CharEvent
+=================
+*/
+void UI_CharEvent(int key)
+{
+ menuCommon_t *item;
+ menuSound_t sound;
+
+ if (!uis.activeMenu) {
+ return;
+ }
+
+ if ((item = Menu_ItemAtCursor(uis.activeMenu)) == NULL ||
+ (sound = Menu_CharEvent(item, key)) == QMS_NOTHANDLED) {
+ return;
+ }
+
+ UI_StartSound(sound);
+}
+
+static void UI_Menu_g(genctx_t *ctx)
+{
+ menuFrameWork_t *menu;
+
+ LIST_FOR_EACH(menuFrameWork_t, menu, &ui_menus, entry) {
+ if (!Prompt_AddMatch(ctx, menu->name)) {
+ break;
+ }
+ }
+}
+
+static void UI_PushMenu_c(genctx_t *ctx, int argnum)
+{
+ if (argnum == 1) {
+ UI_Menu_g(ctx);
+ }
+}
+
+static void UI_PushMenu_f(void)
+{
+ menuFrameWork_t *menu;
+ char *s;
+
+ if (Cmd_Argc() < 2) {
+ Com_Printf("Usage: %s <menu>\n", Cmd_Argv(0));
+ return;
+ }
+ s = Cmd_Argv(1);
+ menu = UI_FindMenu(s);
+ if (menu) {
+ UI_PushMenu(menu);
+ } else {
+ Com_Printf("No such menu: %s\n", s);
+ }
+}
+
+static void UI_PopMenu_f(void)
+{
+ if (uis.activeMenu) {
+ UI_PopMenu();
+ }
+}
+
+
+static const cmdreg_t c_ui[] = {
+ { "forcemenuoff", UI_ForceMenuOff },
+ { "pushmenu", UI_PushMenu_f, UI_PushMenu_c },
+ { "popmenu", UI_PopMenu_f },
+
+ { NULL, NULL }
+};
+
+static void ui_scale_changed(cvar_t *self)
+{
+ UI_Resize();
+}
+
+void UI_ModeChanged(void)
+{
+ ui_scale = Cvar_Get("ui_scale", "1", 0);
+ ui_scale->changed = ui_scale_changed;
+ UI_Resize();
+}
+
+static void UI_FreeMenus(void)
+{
+ menuFrameWork_t *menu, *next;
+
+ LIST_FOR_EACH_SAFE(menuFrameWork_t, menu, next, &ui_menus, entry) {
+ if (menu->free) {
+ menu->free(menu);
+ }
+ }
+ List_Init(&ui_menus);
+}
+
+
+/*
+=================
+UI_Init
+=================
+*/
+qboolean UI_Init(void)
+{
+ char buffer[MAX_QPATH];
+ int i;
+
+ Cmd_Register(c_ui);
+
+ ui_debug = Cvar_Get("ui_debug", "0", 0);
+ ui_open = Cvar_Get("ui_open", "0", 0);
+
+ UI_ModeChanged();
+
+ uis.fontHandle = R_RegisterFont("conchars");
+ uis.cursorHandle = R_RegisterPic("ch1");
+ R_GetPicSize(&uis.cursorWidth, &uis.cursorHeight, uis.cursorHandle);
+
+ for (i = 0; i < NUM_CURSOR_FRAMES; i++) {
+ Q_snprintf(buffer, sizeof(buffer), "m_cursor%d", i);
+ uis.bitmapCursors[i] = R_RegisterPic(buffer);
+ }
+
+ uis.color.background.u32 = MakeColor(0, 0, 0, 255);
+ uis.color.normal.u32 = MakeColor(15, 128, 235, 100);
+ uis.color.active.u32 = MakeColor(15, 128, 235, 100);
+ uis.color.selection.u32 = MakeColor(15, 128, 235, 100);
+ uis.color.disabled.u32 = MakeColor(127, 127, 127, 255);
+
+ strcpy(uis.weaponModel, "w_railgun.md2");
+
+ // load custom menus
+ UI_LoadScript();
+
+ // load built-in menus
+ M_Menu_PlayerConfig();
+ M_Menu_Servers();
+ M_Menu_Demos();
+
+ Com_DPrintf("Registered %d menus.\n", List_Count(&ui_menus));
+
+ uis.initialized = qtrue;
+
+ return qtrue;
+}
+
+/*
+=================
+UI_Shutdown
+=================
+*/
+void UI_Shutdown(void)
+{
+ if (!uis.initialized) {
+ return;
+ }
+ UI_ForceMenuOff();
+
+ ui_scale->changed = NULL;
+
+ PlayerModel_Free();
+
+ UI_FreeMenus();
+
+ Cmd_Deregister(c_ui);
+
+ memset(&uis, 0, sizeof(uis));
+
+ Z_LeakTest(TAG_UI);
+}
+
+