rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
[xonotic/darkplaces.git] / ui.c
1
2 #include "quakedef.h"
3
4 cvar_t ui_showname = {0, "ui_showname", "0"};
5
6 #define ITEM_CLICKABLE 1
7 #define ITEM_DRAWABLE 2
8
9 #define UIKEY_LEFT 1
10 #define UIKEY_RIGHT 2
11 #define UIKEY_UP 3
12 #define UIKEY_DOWN 4
13 #define UIKEY_ENTER 5
14
15 #define UI_MOUSEBUTTONS 3
16
17 static int                      ui_alive, ui_active;
18 static float            ui_mouse_x, ui_mouse_y;
19 static int                      ui_mousebutton[UI_MOUSEBUTTONS], ui_mouseclick;
20 static int                      ui_keyui, ui_keyitem;
21 static ui_item_t        *ui_keyrealitem;
22
23 static ui_t *ui_list[MAX_UI_COUNT];
24
25 //static qpic_t *ui_mousepointer;
26 static rtexture_t *ui_mousepointertexture;
27
28 static byte pointerimage[256] =
29 {
30         "333333332......."
31         "26777761........"
32         "2655541........."
33         "265541.........."
34         "2654561........."
35         "26414561........"
36         "251.14561......."
37         "21...14561......"
38         "1.....141......."
39         ".......1........"
40         "................"
41         "................"
42         "................"
43         "................"
44         "................"
45         "................"
46 };
47
48 static rtexturepool_t *uitexturepool;
49
50 static void ui_start(void)
51 {
52         int i;
53         byte buffer[256][4];
54         uitexturepool = R_AllocTexturePool();
55 //      ui_mousepointer = Draw_CachePic("ui/mousepointer.lmp");
56         for (i = 0;i < 256;i++)
57         {
58                 if (pointerimage[i] == '.')
59                 {
60                         buffer[i][0] = 0;
61                         buffer[i][1] = 0;
62                         buffer[i][2] = 0;
63                         buffer[i][3] = 0;
64                 }
65                 else
66                 {
67                         buffer[i][0] = (pointerimage[i] - '0') * 16;
68                         buffer[i][1] = (pointerimage[i] - '0') * 16;
69                         buffer[i][2] = (pointerimage[i] - '0') * 16;
70                         buffer[i][3] = 255;
71                 }
72         }
73         ui_mousepointertexture = R_LoadTexture(uitexturepool, "mousepointer", 16, 16, &buffer[0][0], TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE);
74         ui_mouse_x = vid.conwidth * 0.5;
75         ui_mouse_y = vid.conheight * 0.5;
76         ui_alive = true;
77 }
78
79 static void ui_shutdown(void)
80 {
81 //      ui_mousepointer = NULL;
82         ui_mousepointertexture = NULL;
83         ui_alive = false;
84         R_FreeTexturePool(&uitexturepool);
85 }
86
87 static void ui_newmap(void)
88 {
89 }
90
91 static mempool_t *uimempool;
92
93 void ui_init(void)
94 {
95         uimempool = Mem_AllocPool("UI");
96
97         Cvar_RegisterVariable(&ui_showname);
98         R_RegisterModule("UI", ui_start, ui_shutdown, ui_newmap);
99 }
100
101 void ui_mouseupdate(float x, float y)
102 {
103         if (ui_alive)
104         {
105                 ui_mouse_x = bound(0, x, vid.conwidth);
106                 ui_mouse_y = bound(0, y, vid.conheight);
107         }
108 }
109
110 void ui_mouseupdaterelative(float x, float y)
111 {
112         if (ui_alive)
113         {
114                 ui_mouse_x += x;
115                 ui_mouse_y += y;
116                 ui_mouse_x = bound(0, ui_mouse_x, vid.conwidth);
117                 ui_mouse_y = bound(0, ui_mouse_y, vid.conheight);
118         }
119 }
120
121 ui_t *ui_create(void)
122 {
123         ui_t *ui;
124         ui = Mem_Alloc(uimempool, sizeof(*ui));
125         if (ui == NULL)
126                 Sys_Error("ui_create: unable to allocate memory for new ui\n");
127         memset(ui, 0, sizeof(*ui));
128         return ui;
129 }
130
131 void ui_free(ui_t *ui)
132 {
133         if (ui)
134                 Mem_Free(ui);
135 }
136
137 void ui_clear(ui_t *ui)
138 {
139         ui->item_count = 0;
140 }
141
142 void ui_item
143 (
144         ui_t *ui, char *basename, int number,
145         float x, float y, qpic_t *pic, char *string,
146         float left, float top, float width, float height,
147         void(*leftkey)(void *nativedata1, void *nativedata2, float data1, float data2),
148         void(*rightkey)(void *nativedata1, void *nativedata2, float data1, float data2),
149         void(*enterkey)(void *nativedata1, void *nativedata2, float data1, float data2),
150         void(*mouseclick)(void *nativedata1, void *nativedata2, float data1, float data2, float xfrac, float yfrac),
151         void *nativedata1, void *nativedata2, float data1, float data2
152 )
153 {
154         int i;
155         ui_item_t *it;
156         char itemname[32];
157         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
158         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
159                 if (it->name == NULL || !strncmp(itemname, it->name, 32))
160                         break;
161         if (i == ui->item_count)
162         {
163                 if (i == MAX_UI_ITEMS)
164                 {
165                         Con_Printf("ui_item: ran out of UI item slots\n");
166                         return;
167                 }
168                 ui->item_count++;
169         }
170         memset(it, 0, sizeof(ui_item_t));
171         strncpy(it->name, itemname, 32);
172         it->flags = 0;
173         if (pic || string)
174         {
175                 it->flags |= ITEM_DRAWABLE;
176                 it->draw_pic = pic;
177                 it->draw_string = string;
178                 it->draw_x = x;
179                 it->draw_y = y;
180         }
181         if (leftkey || rightkey || enterkey || mouseclick)
182         {
183                 it->flags |= ITEM_CLICKABLE;
184                 it->click_x = x + left;
185                 it->click_y = y + top;
186                 it->click_x2 = it->click_x + width;
187                 it->click_y2 = it->click_y + height;
188                 it->leftkey = leftkey;
189                 it->rightkey = rightkey;
190                 it->enterkey = enterkey;
191                 it->mouseclick = mouseclick;
192                 if (it->mouseclick == NULL)
193                         it->mouseclick = (void *)it->enterkey;
194                 if (it->leftkey == NULL)
195                         it->leftkey = it->enterkey;
196                 if (it->rightkey == NULL)
197                         it->rightkey = it->enterkey;
198                 it->nativedata1 = nativedata1;
199                 it->nativedata2 = nativedata2;
200         }
201 }
202
203 void ui_item_remove(ui_t *ui, char *basename, int number)
204 {
205         int i;
206         ui_item_t *it;
207         char itemname[32];
208         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
209         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
210                 if (it->name && !strncmp(itemname, it->name, 32))
211                         break;
212         if (i < ui->item_count)
213                 it->name[0] = 0;
214 }
215
216 ui_item_t *ui_hititem(float x, float y)
217 {
218         int i, j;
219         ui_item_t *it;
220         ui_t *ui;
221         for (j = 0;j < MAX_UI_COUNT;j++)
222                 if ((ui = ui_list[j]))
223                         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
224                                 if (it->name[0] && (it->flags & ITEM_CLICKABLE))
225                                         if (x >= it->click_x && y >= it->click_y && x < it->click_x2 && y < it->click_y2)
226                                                 return it;
227         return NULL;
228 }
229
230 int ui_uiactive(ui_t *ui)
231 {
232         int i;
233         for (i = 0;i < MAX_UI_COUNT;i++)
234                 if (ui_list[i] == ui)
235                         return true;
236         return false;
237 }
238
239 void ui_activate(ui_t *ui, int yes)
240 {
241         int i;
242         if (yes)
243         {
244                 if (ui_uiactive(ui))
245                         return;
246
247                 for (i = 0;i < MAX_UI_COUNT;i++)
248                 {
249                         if (ui_list[i] == NULL)
250                         {
251                                 ui_list[i] = ui;
252                                 return;
253                         }
254                 }
255
256                 Con_Printf("ui_activate: ran out of active ui list items\n");
257         }
258         else
259         {
260                 for (i = 0;i < MAX_UI_COUNT;i++)
261                 {
262                         if (ui_list[i] == ui)
263                         {
264                                 ui_list[i] = NULL;
265                                 return;
266                         }
267                 }
268         }
269 }
270
271 int ui_isactive(void)
272 {
273         int j;
274         ui_t *ui;
275         if (ui_alive)
276         {
277                 for (j = 0;j < MAX_UI_COUNT;j++)
278                         if ((ui = ui_list[j]))
279                                 if (ui->item_count)
280                                         return true;
281         }
282         return false;
283 }
284
285 #define UI_QUEUE_SIZE 256
286 static byte ui_keyqueue[UI_QUEUE_SIZE];
287 static int ui_keyqueuepos = 0;
288
289 void ui_leftkeyupdate(int pressed)
290 {
291         static int key = false;
292         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
293                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_LEFT;
294         key = pressed;
295 }
296
297 void ui_rightkeyupdate(int pressed)
298 {
299         static int key = false;
300         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
301                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_RIGHT;
302         key = pressed;
303 }
304
305 void ui_upkeyupdate(int pressed)
306 {
307         static int key = false;
308         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
309                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_UP;
310         key = pressed;
311 }
312
313 void ui_downkeyupdate(int pressed)
314 {
315         static int key = false;
316         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
317                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_DOWN;
318         key = pressed;
319 }
320
321 void ui_mousebuttonupdate(int button, int pressed)
322 {
323         if (button < 0 || button >= UI_MOUSEBUTTONS)
324                 return;
325         if (button == 0 && ui_mousebutton[button] && !pressed)
326                 ui_mouseclick = true;
327         ui_mousebutton[button] = pressed;
328 }
329
330 void ui_update(void)
331 {
332         ui_item_t *startitem, *it;
333         if (ui_alive)
334         {
335                 ui_mouse_x = bound(0, ui_mouse_x, vid.conwidth);
336                 ui_mouse_y = bound(0, ui_mouse_y, vid.conheight);
337
338                 if ((ui_active = ui_isactive()))
339                 {
340                         // validate currently selected item
341                         if(ui_list[ui_keyui] == NULL)
342                         {
343                                 while (ui_list[ui_keyui] == NULL)
344                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
345                                 ui_keyitem = 0;
346                         }
347                         ui_keyitem = bound(0, ui_keyitem, ui_list[ui_keyui]->item_count - 1);
348                         startitem = ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
349                         if ((ui_keyrealitem->flags & ITEM_CLICKABLE) == 0)
350                         {
351                                 do
352                                 {
353                                         // FIXME: cycle through UIs as well as items in a UI
354                                         ui_keyitem = (ui_keyitem - 1) % ui_list[ui_keyui]->item_count - 1;
355                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
356                                 }
357                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
358                         }
359
360                         if (ui_keyqueuepos)
361                         {
362                                 int i;
363                                 for (i = 0;i < ui_keyqueuepos;i++)
364                                 {
365                                         startitem = ui_keyrealitem;
366                                         switch(ui_keyqueue[i])
367                                         {
368                                         case UIKEY_UP:
369                                                 do
370                                                 {
371                                                         ui_keyitem--;
372                                                         if (ui_keyitem < 0)
373                                                         {
374                                                                 do
375                                                                         ui_keyui = (ui_keyui - 1) % MAX_UI_COUNT;
376                                                                 while(ui_list[ui_keyui] == NULL);
377                                                                 ui_keyitem = ui_list[ui_keyui]->item_count - 1;
378                                                         }
379                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
380                                                 }
381                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
382                                                 break;
383                                         case UIKEY_DOWN:
384                                                 do
385                                                 {
386                                                         ui_keyitem++;
387                                                         if (ui_keyitem >= ui_list[ui_keyui]->item_count)
388                                                         {
389                                                                 do
390                                                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
391                                                                 while(ui_list[ui_keyui] == NULL);
392                                                                 ui_keyitem = 0;
393                                                         }
394                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
395                                                 }
396                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
397                                                 break;
398                                         case UIKEY_LEFT:
399                                                 if (ui_keyrealitem->leftkey)
400                                                         ui_keyrealitem->leftkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
401                                                 break;
402                                         case UIKEY_RIGHT:
403                                                 if (ui_keyrealitem->rightkey)
404                                                         ui_keyrealitem->rightkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
405                                                 break;
406                                         case UIKEY_ENTER:
407                                                 if (ui_keyrealitem->enterkey)
408                                                         ui_keyrealitem->enterkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
409                                                 break;
410                                         }
411                                 }
412                         }
413                         ui_keyqueuepos = 0;
414
415                         if (ui_mouseclick && (it = ui_hititem(ui_mouse_x, ui_mouse_y)) && it->mouseclick)
416                                 it->mouseclick(it->nativedata1, it->nativedata2, it->data1, it->data2, ui_mouse_x - it->click_x, ui_mouse_y - it->click_y);
417         }
418         }
419         ui_mouseclick = false;
420 }
421
422 void ui_draw(void)
423 {
424         int i, j;
425         ui_item_t *it;
426         ui_t *ui;
427         if (ui_alive && ui_active)
428         {
429                 for (j = 0;j < MAX_UI_COUNT;j++)
430                         if ((ui = ui_list[j]))
431                                 if (ui->item_count)
432                                         for (i = 0, it = ui->items;i < ui->item_count;i++, it++)
433                                                 if (it->flags & ITEM_DRAWABLE)
434                                                 {
435                                                         if (it->draw_pic)
436                                                                 Draw_Pic(it->draw_x, it->draw_y, it->draw_pic);
437                                                         if (it->draw_string)
438                                                                 Draw_String(it->draw_x, it->draw_y, it->draw_string, 9999);
439                                                 }
440
441                 if ((it = ui_hititem(ui_mouse_x, ui_mouse_y)))
442                 {
443                         if (it->draw_pic)
444                                 Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
445                         if (it->draw_string)
446                                 Draw_AdditiveString(it->draw_x, it->draw_y, it->draw_string, 9999);
447                         if (ui_showname.integer)
448                                 Draw_String(ui_mouse_x, ui_mouse_y + 16, it->name, 9999);
449         }
450
451                 it = ui_keyrealitem;
452                 if (it->draw_pic)
453                         Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
454                 if (it->draw_string)
455                         Draw_AdditiveString(it->draw_x, it->draw_y, it->draw_string, 9999);
456
457 //              Draw_Pic(ui_mouse_x, ui_mouse_y, ui_mousepointer);
458                 Draw_GenericPic(ui_mousepointertexture, 1, 1, 1, 1, ui_mouse_x, ui_mouse_y, 16, 16);
459         }
460 }