--- /dev/null
+
+#include "quakedef.h"
+
+cvar_t ui_showname = {0, "ui_showname", "0"};
+
+#define ITEM_CLICKABLE 1
+#define ITEM_DRAWABLE 2
+
+#define UIKEY_LEFT 1
+#define UIKEY_RIGHT 2
+#define UIKEY_UP 3
+#define UIKEY_DOWN 4
+#define UIKEY_ENTER 5
+
+#define UI_MOUSEBUTTONS 3
+
+static int ui_alive, ui_active;
+static float ui_mouse_x, ui_mouse_y;
+static int ui_mousebutton[UI_MOUSEBUTTONS], ui_mouseclick;
+static int ui_keyui, ui_keyitem;
+static ui_item_t *ui_keyrealitem;
+
+static ui_t *ui_list[MAX_UI_COUNT];
+
+static qpic_t *ui_mousepointer;
+
+void ui_start(void)
+{
+ ui_mousepointer = Draw_CachePic("ui/mousepointer.lmp");
+ ui_mouse_x = vid.width * 0.5;
+ ui_mouse_y = vid.height * 0.5;
+ ui_alive = true;
+}
+
+void ui_shutdown(void)
+{
+ ui_mousepointer = NULL;
+ ui_alive = false;
+}
+
+void ui_newmap(void)
+{
+}
+
+void ui_init(void)
+{
+ Cvar_RegisterVariable(&ui_showname);
+ R_RegisterModule("UI", ui_start, ui_shutdown, ui_newmap);
+}
+
+void ui_mouseupdate(float x, float y)
+{
+ if (ui_alive)
+ {
+ ui_mouse_x = bound(0, x, vid.width);
+ ui_mouse_y = bound(0, y, vid.height);
+ }
+}
+
+void ui_mouseupdaterelative(float x, float y)
+{
+ if (ui_alive)
+ {
+ ui_mouse_x += x;
+ ui_mouse_y += y;
+ ui_mouse_x = bound(0, ui_mouse_x, vid.width);
+ ui_mouse_y = bound(0, ui_mouse_y, vid.height);
+ }
+}
+
+ui_t *ui_create(void)
+{
+ ui_t *ui;
+ ui = qmalloc(sizeof(*ui));
+ if (ui == NULL)
+ Sys_Error("ui_create: unable to allocate memory for new ui\n");
+ memset(ui, 0, sizeof(*ui));
+ return ui;
+}
+
+void ui_free(ui_t *ui)
+{
+ if (ui)
+ qfree(ui);
+}
+
+void ui_clear(ui_t *ui)
+{
+ ui->item_count = 0;
+}
+
+void ui_item
+(
+ ui_t *ui, char *basename, int number,
+ float x, float y, qpic_t *pic,
+ float left, float top, float width, float height,
+ void(*leftkey)(void *nativedata1, void *nativedata2, float data1, float data2),
+ void(*rightkey)(void *nativedata1, void *nativedata2, float data1, float data2),
+ void(*enterkey)(void *nativedata1, void *nativedata2, float data1, float data2),
+ void(*mouseclick)(void *nativedata1, void *nativedata2, float data1, float data2, float xfrac, float yfrac),
+ void *nativedata1, void *nativedata2, float data1, float data2
+)
+{
+ int i;
+ ui_item_t *it;
+ char itemname[32];
+ snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
+ for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
+ if (it->name == NULL || !strncmp(itemname, it->name, 32))
+ break;
+ if (i == ui->item_count)
+ {
+ if (i == MAX_UI_ITEMS)
+ {
+ Con_Printf("ui_item: ran out of UI item slots\n");
+ return;
+ }
+ ui->item_count++;
+ }
+ strncpy(it->name, itemname, 32);
+ it->flags = 0;
+ if (leftkey || rightkey || enterkey || mouseclick)
+ it->flags |= ITEM_CLICKABLE;
+ if (pic)
+ it->flags |= ITEM_DRAWABLE;
+ it->draw_x = x;
+ it->draw_y = y;
+ it->click_x = x + left;
+ it->click_y = y + top;
+ it->click_x2 = it->click_x + width;
+ it->click_y2 = it->click_y + height;
+ it->leftkey = leftkey;
+ it->rightkey = rightkey;
+ it->enterkey = enterkey;
+ it->mouseclick = mouseclick;
+ if (it->mouseclick == NULL)
+ it->mouseclick = (void *)it->enterkey;
+ if (it->leftkey == NULL)
+ it->leftkey = it->enterkey;
+ if (it->rightkey == NULL)
+ it->rightkey = it->enterkey;
+ it->nativedata1 = nativedata1;
+ it->nativedata2 = nativedata2;
+}
+
+void ui_item_remove(ui_t *ui, char *basename, int number)
+{
+ int i;
+ ui_item_t *it;
+ char itemname[32];
+ snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
+ for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
+ if (it->name && !strncmp(itemname, it->name, 32))
+ break;
+ if (i < ui->item_count)
+ it->name[0] = 0;
+}
+
+ui_item_t *ui_hititem(float x, float y)
+{
+ int i, j;
+ ui_item_t *it;
+ ui_t *ui;
+ for (j = 0;j < MAX_UI_COUNT;j++)
+ if ((ui = ui_list[j]))
+ for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
+ if (it->name[0] && (it->flags & ITEM_CLICKABLE))
+ if (x >= it->click_x && y >= it->click_y && x < it->click_x2 && y < it->click_y2)
+ return it;
+ return NULL;
+}
+
+int ui_uiactive(ui_t *ui)
+{
+ int i;
+ for (i = 0;i < MAX_UI_COUNT;i++)
+ if (ui_list[i] == ui)
+ return true;
+ return false;
+}
+
+void ui_activate(ui_t *ui, int yes)
+{
+ int i;
+ if (yes)
+ {
+ if (ui_uiactive(ui))
+ return;
+
+ for (i = 0;i < MAX_UI_COUNT;i++)
+ {
+ if (ui_list[i] == NULL)
+ {
+ ui_list[i] = ui;
+ return;
+ }
+ }
+
+ Con_Printf("ui_activate: ran out of active ui list items\n");
+ }
+ else
+ {
+ for (i = 0;i < MAX_UI_COUNT;i++)
+ {
+ if (ui_list[i] == ui)
+ {
+ ui_list[i] = NULL;
+ return;
+ }
+ }
+ }
+}
+
+int ui_isactive(void)
+{
+ int j;
+ ui_t *ui;
+ if (ui_alive)
+ {
+ for (j = 0;j < MAX_UI_COUNT;j++)
+ if ((ui = ui_list[j]))
+ if (ui->item_count)
+ return true;
+ }
+ return false;
+}
+
+#define UI_QUEUE_SIZE 256
+static byte ui_keyqueue[UI_QUEUE_SIZE];
+static int ui_keyqueuepos = 0;
+
+void ui_leftkeyupdate(int pressed)
+{
+ static int key = false;
+ if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
+ ui_keyqueue[ui_keyqueuepos++] = UIKEY_LEFT;
+ key = pressed;
+}
+
+void ui_rightkeyupdate(int pressed)
+{
+ static int key = false;
+ if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
+ ui_keyqueue[ui_keyqueuepos++] = UIKEY_RIGHT;
+ key = pressed;
+}
+
+void ui_upkeyupdate(int pressed)
+{
+ static int key = false;
+ if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
+ ui_keyqueue[ui_keyqueuepos++] = UIKEY_UP;
+ key = pressed;
+}
+
+void ui_downkeyupdate(int pressed)
+{
+ static int key = false;
+ if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
+ ui_keyqueue[ui_keyqueuepos++] = UIKEY_DOWN;
+ key = pressed;
+}
+
+void ui_mousebuttonupdate(int button, int pressed)
+{
+ if (button < 0 || button >= UI_MOUSEBUTTONS)
+ return;
+ if (button == 0 && ui_mousebutton[button] && !pressed)
+ ui_mouseclick = true;
+ ui_mousebutton[button] = pressed;
+}
+
+void ui_update(void)
+{
+ ui_item_t *startitem, *it;
+ if (ui_alive)
+ {
+ ui_mouse_x = bound(0, ui_mouse_x, vid.width);
+ ui_mouse_y = bound(0, ui_mouse_y, vid.height);
+
+ if ((ui_active = ui_isactive()))
+ {
+ // validate currently selected item
+ if(ui_list[ui_keyui] == NULL)
+ {
+ while (ui_list[ui_keyui] == NULL)
+ ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
+ ui_keyitem = 0;
+ }
+ ui_keyitem = bound(0, ui_keyitem, ui_list[ui_keyui]->item_count - 1);
+ startitem = ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
+ if ((ui_keyrealitem->flags & ITEM_CLICKABLE) == 0)
+ {
+ do
+ {
+ // FIXME: cycle through UIs as well as items in a UI
+ ui_keyitem = (ui_keyitem - 1) % ui_list[ui_keyui]->item_count - 1;
+ ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
+ }
+ while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
+ }
+
+ if (ui_keyqueuepos)
+ {
+ int i;
+ for (i = 0;i < ui_keyqueuepos;i++)
+ {
+ startitem = ui_keyrealitem;
+ switch(ui_keyqueue[i])
+ {
+ case UIKEY_UP:
+ do
+ {
+ ui_keyitem--;
+ if (ui_keyitem < 0)
+ {
+ do
+ ui_keyui = (ui_keyui - 1) % MAX_UI_COUNT;
+ while(ui_list[ui_keyui] == NULL);
+ ui_keyitem = ui_list[ui_keyui]->item_count - 1;
+ }
+ ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
+ }
+ while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
+ break;
+ case UIKEY_DOWN:
+ do
+ {
+ ui_keyitem++;
+ if (ui_keyitem >= ui_list[ui_keyui]->item_count)
+ {
+ do
+ ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
+ while(ui_list[ui_keyui] == NULL);
+ ui_keyitem = 0;
+ }
+ ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
+ }
+ while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
+ break;
+ case UIKEY_LEFT:
+ if (ui_keyrealitem->leftkey)
+ ui_keyrealitem->leftkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
+ break;
+ case UIKEY_RIGHT:
+ if (ui_keyrealitem->rightkey)
+ ui_keyrealitem->rightkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
+ break;
+ case UIKEY_ENTER:
+ if (ui_keyrealitem->enterkey)
+ ui_keyrealitem->enterkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
+ break;
+ }
+ }
+ }
+ ui_keyqueuepos = 0;
+
+ if (ui_mouseclick && (it = ui_hititem(ui_mouse_x, ui_mouse_y)) && it->mouseclick)
+ it->mouseclick(it->nativedata1, it->nativedata2, it->data1, it->data2, ui_mouse_x - it->click_x, ui_mouse_y - it->click_y);
+ }
+ }
+ ui_mouseclick = false;
+}
+
+void ui_draw(void)
+{
+ int i, j;
+ ui_item_t *it;
+ ui_t *ui;
+ if (ui_alive && ui_active)
+ {
+ for (j = 0;j < MAX_UI_COUNT;j++)
+ if ((ui = ui_list[j]))
+ if (ui->item_count)
+ for (i = 0, it = ui->items;i < ui->item_count;i++, it++)
+ if (it->flags & ITEM_DRAWABLE)
+ Draw_Pic(it->draw_x, it->draw_y, it->draw_pic);
+
+ if ((it = ui_hititem(ui_mouse_x, ui_mouse_y)))
+ {
+ Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
+ if (ui_showname.value)
+ Draw_String(ui_mouse_x, ui_mouse_y + 16, it->name, 9999);
+ }
+
+ it = ui_keyrealitem;
+ Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
+
+ Draw_Pic(ui_mouse_x, ui_mouse_y, ui_mousepointer);
+ }
+}