Expanded on same-loop-handler-signature code, added staticgen.pl.
[xuni.git] / src / loop.c
blob11d45ce5d1ce40af52c33923e30af3bb6e41c8e3
1 /*! \file loop.c
3 */
5 #include <stdlib.h>
7 #include "SDL_framerate.h"
9 #include "graphics.h"
10 #include "gui.h"
11 #include "loop.h"
12 #include "memory.h"
13 #include "resource/resource.h"
15 #include "widget/dump.h"
16 #include "widget/widgets.h"
17 #include "widget/listbox.h"
19 static int call_perform_click_func(struct xuni_t *xuni,
20 struct panel_data_t *panel, struct widget_t *widget, panel_type_t *mode);
22 static SDL_Cursor *sdl_cursor_from_xpm(const char *image[]);
23 static void set_widget_cursor(struct xuni_t *xuni);
25 static void set_loop_caption(panel_type_t mode, struct xuni_t *xuni);
26 static void setup_theme_cursor(struct xuni_t *xuni, struct widget_t *panel);
27 static int get_event(int frameupdate, SDL_Event *event);
28 static int process_event(SDL_Event *event, panel_type_t *mode,
29 struct xuni_t *xuni);
30 static void scroll_listbox(struct xuni_t *xuni, int dir);
31 static int set_loop_widget_sel(panel_type_t mode, int xp, int yp,
32 int click, struct xuni_t *xuni);
33 static int find_widget_accelerator(struct xuni_accelerator_t *accel,
34 SDL_keysym *key, struct xuni_t *xuni, panel_type_t *mode);
35 static int check_sdl_key_mod(SDLMod mod, SDLMod now);
36 static int event_mouse_button_up(int xp, int yp, panel_type_t *mode,
37 struct xuni_t *xuni);
38 static int perform_loop_click(struct xuni_t *xuni, panel_type_t *mode,
39 struct widget_t *widget);
40 static int event_key_down(struct xuni_t *xuni, SDL_keysym *keysym,
41 panel_type_t *mode, int *handled);
43 /*! Calls the registered initialization functions for \a widget and all of its
44 sub-widgets. Only operates on widgets of type panel.
46 \param xuni A pointer to the main xuni structure.
47 \param widget The widget to call the initialization function for, if it is
48 a panel widget.
49 \param settings A pointer to the resource tree.
51 void call_init_funcs(struct xuni_t *xuni, struct widget_t *widget,
52 struct resource_t *settings) {
54 size_t x;
56 if(!widget) return;
58 if(widget->type == WIDGET_PANEL
59 && widget->p.panel->event[PANEL_EVENT_INIT].handler) {
61 widget->p.panel->event[PANEL_EVENT_INIT].p.init.settings = settings;
62 widget->p.panel->event[PANEL_EVENT_INIT].p.init.panel = widget;
64 (*widget->p.panel->event[PANEL_EVENT_INIT].handler)
65 (xuni, widget->p.panel);
68 if(widget->compose) {
69 for(x = 0; x < widget->compose->widgets; x ++) {
70 call_init_funcs(xuni, widget->compose->widget[x], settings);
75 void call_free_funcs(struct xuni_t *xuni, struct widget_t *widget) {
76 size_t x;
78 if(!widget) return;
80 if(widget->type == WIDGET_PANEL
81 && widget->p.panel->event[PANEL_EVENT_FREE].handler) {
83 (*widget->p.panel->event[PANEL_EVENT_FREE].handler)
84 (xuni, widget->p.panel);
87 if(widget->compose) {
88 for(x = 0; x < widget->compose->widgets; x ++) {
89 call_free_funcs(xuni, widget->compose->widget[x]);
94 /* !!! Note that because of this function, the handler member of the structure
95 passed to panel event handlers is not defined.
97 int panel_event_recursive(struct xuni_t *xuni, struct panel_data_t *data,
98 enum panel_event_type_t type, struct widget_t *widget) {
100 int r = 0;
101 size_t x;
103 if(!widget) return 0;
105 if(widget->type == WIDGET_PANEL
106 && widget->p.panel->event[type].handler) {
108 (*widget->p.panel->event[type].handler)(xuni, data);
111 if(widget->compose) {
112 for(x = 0; x < widget->compose->widgets; x ++) {
113 r = panel_event_recursive(xuni, data, type,
114 widget->compose->widget[x]) || r;
118 return r;
121 static int call_perform_click_func(struct xuni_t *xuni,
122 struct panel_data_t *panel, struct widget_t *widget, panel_type_t *mode) {
124 perform_widget_click(xuni, widget);
126 /*print_widget_backtrace(xuni->gui->sel.p.widget);*/
128 /* !!! quick fix to disallow clicking on a panel. Without this, for
129 example, clicking on the options graphics panel triggers the
130 fullscreen checkbox, as
131 PANEL_OPTIONS_GRAPHICS == WID_GRAPHICS_FULLSCREEN */
132 if(widget && panel->event[PANEL_EVENT_CLICK].handler
133 && widget_nameid_access(xuni->gui->widget, *mode) != widget) {
135 panel->event[PANEL_EVENT_CLICK].p.click.mode = mode;
136 panel->event[PANEL_EVENT_CLICK].p.click.widget = widget;
138 return (*panel->event[PANEL_EVENT_CLICK].handler)(xuni, panel);
141 return 0;
144 void call_deactivate_func(struct xuni_t *xuni, struct widget_t *widget) {
145 struct widget_t *base = widget;
147 while(base && base->type != WIDGET_PANEL) base = base->base;
149 if(base && base->p.panel->event[PANEL_EVENT_DEACTIVATE].handler) {
150 base->p.panel->event[PANEL_EVENT_DEACTIVATE].p.deactivate.widget
151 = widget;
153 (*base->p.panel->event[PANEL_EVENT_DEACTIVATE].handler)
154 (xuni, base->p.panel);
158 /*#define HIDE_COMBOBOX_POPUPS*/
160 int default_panel_sel(struct xuni_t *xuni, struct panel_data_t *data) {
161 struct panel_event_sel_t *sel = &data->event[PANEL_EVENT_SEL].p.sel;
163 return set_default_widget_sel(xuni, sel->mode, sel->xp, sel->yp,
164 sel->click, data->data);
167 /* !!! this function is obsolete */
168 int set_default_widget_sel(struct xuni_t *xuni, panel_type_t mode,
169 int xp, int yp, int click, void *vdata) {
171 #ifdef HIDE_COMBOBOX_POPUPS
172 struct widget_t *widget = xuni->gui->sel.p.widget;
173 #endif
174 int r;
176 r = set_widget_sel_repaint(&xuni->gui->sel, xp, yp, click,
177 widget_nameid_access(xuni->gui->widget, mode));
179 /*if(xuni->gui->sel.p.widget) {
180 printf("%s\n", xuni->gui->sel.p.widget->name);
183 #ifdef HIDE_COMBOBOX_POPUPS
184 /* Hide combobox popups that have become unselected. */
185 if(widget && widget != xuni->gui->sel.p.widget) {
186 struct widget_t *w = xuni->gui->sel.p.widget, *t;
188 /* See if any comboboxes that were previously selected still are. */
189 while(w) {
190 if(w->visibility & WIDGET_VISIBILITY_VISIBLE
191 && w->type == WIDGET_COMBOBOX) {
193 for(t = widget; t; t = t->base) {
194 if(t == w) break;
197 if(t) break;
200 w = w->base;
203 /* If no comboboxes were selected and remain selected, hide the popups
204 for all comboboxes that were selected.
206 if(!w) {
207 do {
208 if(widget->type == WIDGET_COMBOBOX) {
209 widget->compose->widget[WID_COMBOBOX_DROPDOWN]->visibility
210 &= ~WIDGET_VISIBILITY_VISIBLE;
213 widget = widget->base;
214 } while(widget);
217 #endif
219 return r;
222 #if !1
223 void set_panel_callbacks(struct widget_t *widget, void *vdata,
224 int frameupdate,
225 panel_event_func_t init_func,
226 panel_event_func_t start_func,
227 panel_event_func_t event_func,
228 panel_event_func_t set_widget_sel_func,
229 panel_event_func_t perform_click_func,
230 panel_event_func_t deactivate_func,
231 panel_event_func_t paint_func,
232 panel_event_func_t free_func) {
234 widget->p.panel->data = vdata;
235 widget->p.panel->frameupdate = frameupdate;
237 widget->p.panel->event[PANEL_EVENT_INIT].handler = init_func;
238 widget->p.panel->event[PANEL_EVENT_START].handler = start_func;
239 widget->p.panel->event[PANEL_EVENT_EVENT].handler = event_func;
240 widget->p.panel->event[PANEL_EVENT_SEL].handler = set_widget_sel_func;
241 widget->p.panel->event[PANEL_EVENT_CLICK].handler = perform_click_func;
242 widget->p.panel->event[PANEL_EVENT_DEACTIVATE].handler = deactivate_func;
243 widget->p.panel->event[PANEL_EVENT_PAINT].handler = paint_func;
244 widget->p.panel->event[PANEL_EVENT_FREE].handler = free_func;
246 widget->p.panel->nameid = 0;
248 widget->p.panel->accel = 0;
250 #endif
252 void execute_callback(struct xuni_t *xuni, struct xuni_callback_t *callback) {
253 if(callback && callback->func) {
254 (*callback->func)(callback->vdata, xuni);
258 /*! The main xuni loop. Indirectly calls all panel event-handling functions,
259 including initialization functions, event handlers, repaint functions,
260 etc.
262 This function should only be called once, after everything has been loaded
263 and set up. It should only return when the program is exiting -- but it
264 will return, so don't neglect that freeing code.
266 \param xuni The xuni structure, with most of the data required to display
267 and manage a Graphical User Interface (GUI, of course).
268 \param always A callback function that is executed for every loop of the
269 xuni event loop. Note that this can be called considerably more
270 frequently than repaint functions.
271 \param mode The panel to initially display.
273 void main_loop(struct xuni_t *xuni, struct xuni_callback_t *always,
274 panel_type_t mode) {
276 panel_type_t pmode = (size_t)-1;
277 struct panel_data_t *panel
278 = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
279 FPSmanager fpsm;
280 SDL_Event event;
281 int repaint = 1;
283 /*call_init_funcs(data, smode, font, theme, gui, settings);*/
285 SDL_initFramerate(&fpsm);
286 SDL_setFramerate(&fpsm, 30);
288 while(mode != (size_t)-1) {
289 execute_callback(xuni, always);
291 /*dump_widgets_need_repaint(xuni->gui->widget);*/
293 if(get_event(widget_nameid_access(xuni->gui->widget, mode)
294 ->p.panel->frameupdate, &event)) {
296 do {
297 repaint = process_event(&event, &mode, xuni) || repaint;
299 if(mode == (size_t)-1) break;
300 } while(SDL_PollEvent(&event));
302 if(mode == (size_t)-1) break;
305 if(pmode != mode) { /* !!! process_event() is called before the start_func */
306 panel = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
307 set_loop_caption(mode, xuni); /* !!! also pass pmode */
309 pmode = mode;
311 repaint = 1;
314 /* !!! this needs to go in a hover callback function of sorts */
315 set_widget_cursor(xuni);
317 if(xuni->smode->focus) {
318 if(repaint || panel->frameupdate) {
319 if(panel->event[PANEL_EVENT_PAINT].handler) {
320 panel->event[PANEL_EVENT_PAINT].p.paint.mode = mode;
322 (*panel->event[PANEL_EVENT_PAINT].handler)(xuni, panel);
327 repaint = 0;
329 if(!xuni->smode->focus || panel->frameupdate) {
330 SDL_framerateDelay(&fpsm);
334 set_caption("Quitting . . .");
336 /*panel_event_recursive(xuni, 0, PANEL_EVENT_FREE, xuni->gui->widget);*/
337 call_free_funcs(xuni, xuni->gui->widget);
340 /* An XPM image of a CURSOR_TEXT, used when the mouse is inside textboxes. */
341 static const char *text_cursor[] = {
342 /* width height num_colors chars_per_pixel */
343 " 16 16 3 1",
344 /* colors */
345 "X c #000000",
346 ". c #ffffff",
347 " c None",
348 /* pixels */
349 " XXXXX ",
350 " X.....X ",
351 " XX.XX ",
352 " X.X ",
353 " X.X ",
354 " X.X ",
355 " X.X ",
356 " X.X ",
357 " X.X ",
358 " X.X ",
359 " X.X ",
360 " X.X ",
361 " X.X ",
362 " XX.XX ",
363 " X.....X ",
364 " XXXXX ",
365 "7,7"
368 static SDL_Cursor *sdl_cursor_from_xpm(const char *image[]) {
369 int i, x, y;
370 Uint8 data[4*16] = {0};
371 Uint8 mask[4*16] = {0};
372 int hot_x, hot_y;
374 i = -1;
375 for(x = 4; x < 16 + 4; x ++) {
376 for(y = 0; y < 16; y ++) {
377 if(y % 8) {
378 data[i] <<= 1;
379 mask[i] <<= 1;
381 else i ++;
383 switch (image[x][y]) {
384 case 'X':
385 data[i] |= 0x01;
386 mask[i] |= 0x01;
387 break;
388 case '.':
389 mask[i] |= 0x01;
390 break;
391 case ' ':
392 break;
397 sscanf(image[x], "%d,%d", &hot_x, &hot_y);
399 return SDL_CreateCursor(data, mask, 16, 16, hot_x, hot_y);
402 static void set_widget_cursor(struct xuni_t *xuni) {
403 enum cursor_t now;
405 if(xuni->gui->sel.p.widget
406 && xuni->gui->sel.p.widget->type == WIDGET_TEXTBOX) {
408 now = CURSOR_TEXT;
410 else now = CURSOR_NORMAL;
412 if(xuni->theme->cursors.which != now) {
413 xuni->theme->cursors.which = now;
415 if(xuni->theme->cursors.which) {
416 if(!xuni->theme->cursors.text) {
417 xuni->theme->cursors.text = sdl_cursor_from_xpm(text_cursor);
418 /* save the normal cursor before it is overwritten */
419 xuni->theme->cursors.normal = SDL_GetCursor();
422 SDL_SetCursor(xuni->theme->cursors.text);
424 else {
425 if(!xuni->theme->cursors.normal) {
426 xuni->theme->cursors.normal = SDL_GetCursor();
429 SDL_SetCursor(xuni->theme->cursors.normal);
434 static void set_loop_caption(panel_type_t mode, struct xuni_t *xuni) {
435 struct widget_t *panel = widget_nameid_access(xuni->gui->widget, mode);
436 struct panel_data_t *paneldata = panel->p.panel;
437 int xp, yp;
439 show_cursor(1);
441 clear_gui(xuni, mode, 0);
443 /* !!! set all other panels to invisible */
444 widget_nameid_access(xuni->gui->widget, mode)->visibility
445 |= WIDGET_VISIBILITY_VISIBLE;
447 if(paneldata->event[PANEL_EVENT_START].handler) {
448 (*paneldata->event[PANEL_EVENT_START].handler)(xuni, paneldata);
451 setup_theme_cursor(xuni, widget_nameid_access(xuni->gui->widget, mode));
452 SDL_GetMouseState(&xp, &yp);
453 set_loop_widget_sel(mode, xp, yp, xuni->gui->sel.clickin, xuni);
456 static void setup_theme_cursor(struct xuni_t *xuni, struct widget_t *panel) {
457 struct widget_t *cursor;
459 if(!panel->p.panel->frameupdate) return;
461 cursor = get_theme_widget(xuni, THEME_DEFAULT_CURSOR);
462 if(!cursor) return;
464 prepare_paint_image(xuni, cursor);
466 if(cursor->p.image->image) {
467 show_cursor(0);
471 static int get_event(int frameupdate, SDL_Event *event) {
472 return frameupdate ? SDL_PollEvent(event) : SDL_WaitEvent(event);
475 /* handled should not be used in some cases. For example, a widget might be
476 deselected by a mouse motion event, but a xuni application (like the
477 resource editor) might want to know about the mouse motion event just the
478 same.
480 /*! Handles an event from the SDL. What happens depends on the event itself.
481 However, this function will always either deal with the event itself, call
482 the appropriate event handler function for the currently visible panel, or
483 perhaps do a little bit of both.
485 Some handled events include clicking inside widgets (implemented by
486 calling set_loop_widget_sel()), pressing special keypresses like
487 printscreen (see event_key_down()), and selecting widgets when the mouse
488 moves (set_loop_widget_sel()).
490 \param event The SDL_Event that needs to be dealt with.
491 \param mode The currently visible panel, which could be changed.
492 \param xuni The main xuni_t structure.
493 \return True if the screen needs to be repainted.
495 static int process_event(SDL_Event *event, panel_type_t *mode,
496 struct xuni_t *xuni) {
498 struct panel_data_t *panel
499 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
500 int repaint = 0, handled = 0;
502 switch(event->type) {
503 case SDL_MOUSEBUTTONDOWN:
504 if(event->button.button == SDL_BUTTON_WHEELUP) {
505 scroll_listbox(xuni, -1);
507 repaint = 1;
508 handled = 1;
510 else if(event->button.button == SDL_BUTTON_WHEELDOWN) {
511 scroll_listbox(xuni, 1);
513 repaint = 1;
514 handled = 1;
517 if(event->button.button != 1) break;
519 repaint = set_loop_widget_sel(*mode, event->button.x, event->button.y,
520 1, xuni);
522 if(repaint) handled = 1;
523 break;
524 case SDL_MOUSEBUTTONUP:
525 if(event->button.button != 1) break;
527 repaint = event_mouse_button_up(event->button.x, event->button.y,
528 mode, xuni);
530 if(repaint) handled = 1;
531 break;
532 case SDL_MOUSEMOTION:
533 repaint = set_loop_widget_sel(*mode, event->button.x, event->button.y,
534 xuni->gui->sel.clickin, xuni);
536 /*if(repaint) handled = 1;*/
537 break;
538 case SDL_VIDEORESIZE:
539 if(!resize_screen(xuni->smode, &event->resize)) {
540 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_RESCALE);
542 xuni->gui->widget->p.panel
543 ->event[PANEL_EVENT_EVENT].p.event.mode = mode;
544 xuni->gui->widget->p.panel
545 ->event[PANEL_EVENT_EVENT].p.event.event = event;
547 panel_event_recursive(xuni, xuni->gui->widget->p.panel,
548 PANEL_EVENT_EVENT, xuni->gui->widget);
551 if(focus_changed(xuni->smode, SDL_APPACTIVE)) {
552 if(xuni->smode->focus) {
553 set_loop_caption(*mode, xuni);
555 else {
556 show_cursor(1);
560 handled = 1;
561 repaint = 1;
562 break;
563 case SDL_VIDEOEXPOSE:
564 repaint = 1;
565 handled = 1;
566 break;
567 case SDL_KEYDOWN:
568 repaint = event_key_down(xuni, &event->key.keysym, mode, &handled);
570 break;
571 case SDL_KEYUP:
572 break;
573 case SDL_ACTIVEEVENT:
574 if(focus_changed(xuni->smode, SDL_APPACTIVE | SDL_APPINPUTFOCUS)) {
575 if(xuni->smode->focus) {
576 /*int x, y;
577 SDL_GetMouseState(&x, &y);
578 printf("activated: %i,%i\n", x, y);*/
579 /* !!! this is only for the cursor */
580 set_loop_caption(*mode, xuni);
581 repaint = 1;
583 else {
584 /* preserve any textbox data etc in the active widget */
585 clear_active(xuni, &xuni->gui->active, 1);
586 show_cursor(1);
590 handled = 1;
591 break;
592 default:
593 break;
596 if(!handled && panel->event[PANEL_EVENT_EVENT].handler) {
597 panel->event[PANEL_EVENT_EVENT].p.event.mode = mode;
598 panel->event[PANEL_EVENT_EVENT].p.event.event = event;
600 repaint |= (*panel->event[PANEL_EVENT_EVENT].handler)(xuni, panel);
603 return repaint;
606 /* !!! bad hack */
607 static void scroll_listbox(struct xuni_t *xuni, int dir) {
608 int step = 5;
610 if(xuni->gui->sel.p.widget
611 && xuni->gui->sel.p.widget->type == WIDGET_LISTBOX) {
613 move_scrollbar(xuni, xuni->gui->sel.p.widget->compose->widget
614 [WID_LISTBOX_VSCROLL], step * dir);
616 widget_event(xuni, xuni->gui->sel.p.widget, WIDGET_EVENT_REPOSITION);
619 if(xuni->gui->sel.p.widget
620 && xuni->gui->sel.p.widget->type == WIDGET_SCROLLBAR) {
622 move_scrollbar(xuni, xuni->gui->sel.p.widget, step * dir);
624 widget_event(xuni, xuni->gui->sel.p.widget->base,
625 WIDGET_EVENT_REPOSITION);
628 if(xuni->gui->sel.p.widget && xuni->gui->sel.p.widget->base
629 && xuni->gui->sel.p.widget->base->type == WIDGET_SCROLLBAR) {
631 move_scrollbar(xuni, xuni->gui->sel.p.widget->base, step * dir);
633 widget_event(xuni, xuni->gui->sel.p.widget->base->base,
634 WIDGET_EVENT_REPOSITION);
638 static int set_loop_widget_sel(panel_type_t mode, int xp, int yp,
639 int click, struct xuni_t *xuni) {
641 struct panel_data_t *panel
642 = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
644 if(!panel->event[PANEL_EVENT_SEL].handler) return 0;
646 panel->event[PANEL_EVENT_SEL].p.sel.mode = mode;
647 panel->event[PANEL_EVENT_SEL].p.sel.xp = xp;
648 panel->event[PANEL_EVENT_SEL].p.sel.yp = yp;
649 panel->event[PANEL_EVENT_SEL].p.sel.click = click;
651 return (*panel->event[PANEL_EVENT_SEL].handler)(xuni, panel);
654 /*! Determines whether any accelerator key combinations have been or are being
655 pressed, and, if so, handles the click event.
657 \param accel This panel's accelerator array. Searched for accelerator
658 combinations matching the current keyboard state.
659 \param key The current keyboard state. Compared with accelerator
660 combinations for the current panel for matches.
661 \param xuni The xuni structure. Used to get a struct widget_t pointer to
662 the current panel.
663 \param mode The currently active panel. Only passed so that
664 call_perform_click_func() may be called.
666 static int find_widget_accelerator(struct xuni_accelerator_t *accel,
667 SDL_keysym *key, struct xuni_t *xuni, panel_type_t *mode) {
669 size_t x;
670 SDL_keysym *akey;
672 if(!accel) return 0;
674 for(x = 0; x < accel->n; x ++) {
675 akey = &accel->key[x].key;
676 if(akey->sym && akey->sym == key->sym
677 && check_sdl_key_mod(akey->mod, key->mod)) {
679 call_perform_click_func(xuni,
680 widget_nameid_access(xuni->gui->widget, *mode)->p.panel,
681 accel->key[x].widget, mode);
683 return 1;
687 return 0;
690 /* !!! needs to be reworked to allow OR, AND, NOT, etc. */
691 static int check_sdl_key_mod(SDLMod mod, SDLMod now) {
692 if(mod != KMOD_NONE) {
693 if((mod & KMOD_CTRL) == KMOD_CTRL) {
694 if(now & KMOD_CTRL) mod &= ~KMOD_CTRL;
695 else return 0;
697 if((mod & KMOD_SHIFT) == KMOD_SHIFT) {
698 if(now & KMOD_SHIFT) mod &= ~KMOD_SHIFT;
699 else return 0;
701 if((mod & KMOD_ALT) == KMOD_ALT) {
702 if(now & KMOD_ALT) mod &= ~KMOD_ALT;
703 else return 0;
707 if(now != mod) return 0;
709 return 1;
712 static int event_mouse_button_up(int xp, int yp, panel_type_t *mode,
713 struct xuni_t *xuni) {
715 struct panel_data_t *panel
716 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
717 int repaint = 0, realclick;
719 if(xuni->gui->sel.p.widget && xuni->gui->sel.clickin
720 && pos_in_rect(xp, yp, xuni->gui->sel.p.widget->pos)) {
722 /*xuni->gui->sel.clickin = 0;*/
723 realclick = !set_widget_sel(&xuni->gui->sel, xp, yp, 1,
724 widget_nameid_access(xuni->gui->widget, *mode));
726 if(realclick) {
727 /* !!! return value ignored: repaint is always set to 1 */
728 call_perform_click_func(xuni, panel, xuni->gui->sel.p.widget,
729 mode);
732 xuni->gui->sel.wasin = 1;
733 xuni->gui->sel.clickin = 0;
734 repaint = 1;
736 if(realclick) {
737 if(xuni->gui->sel.p.widget &&
738 widget_can_be_active(xuni->gui->sel.p.widget)) {
740 xuni->gui->active = xuni->gui->sel.p;
741 activate_widget(xuni->gui->active.widget, xuni, xp, yp);
742 enable_unicode(xuni->gui->active.widget);
744 else if(xuni->gui->active.widget) {
745 repaint = 1;
747 clear_active(xuni, &xuni->gui->active, 1);
750 if(*mode != (size_t)-1) {
751 set_loop_widget_sel(*mode, xp, yp, xuni->gui->sel.clickin,
752 xuni);
756 else {
757 repaint = set_loop_widget_sel(*mode, xp, yp, 0, xuni);
759 if(xuni->gui->active.widget &&
760 !pos_in_rect(xp, yp, xuni->gui->active.widget->pos)) {
762 repaint = 1;
764 clear_active(xuni, &xuni->gui->active, 1);
768 return repaint;
771 static int perform_loop_click(struct xuni_t *xuni, panel_type_t *mode,
772 struct widget_t *widget) {
774 struct panel_data_t *panel
775 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
776 int repaint, xp = 0, yp = 0; /* !!! */
778 /* !!! return value ignored: repaint is always set to 1 */
779 call_perform_click_func(xuni, panel, widget, mode);
781 xuni->gui->sel.wasin = 1;
782 xuni->gui->sel.clickin = 0;
783 repaint = 1;
785 if(xuni->gui->sel.p.widget &&
786 widget_can_be_active(xuni->gui->sel.p.widget)) {
788 xuni->gui->active = xuni->gui->sel.p;
789 activate_widget(xuni->gui->active.widget, xuni, xp, yp);
790 enable_unicode(xuni->gui->active.widget);
792 else if(xuni->gui->active.widget) {
793 repaint = 1;
795 clear_active(xuni, &xuni->gui->active, 1);
798 if(*mode != (size_t)-1) {
799 set_loop_widget_sel(*mode, xp, yp, xuni->gui->sel.clickin,
800 xuni);
803 return repaint;
806 /* Perhaps functions for handling these keypresses could be registered?
808 Note: a lot of debugging keypresses are here, usually F-keys.
810 /*! Handles the keypresses that are common to all xuni widgets. This includes:
811 - escape, which reverts widgets;
812 - tab and shift-tab, which cycle though widgets;
813 - enter, which might deactivate or select widgets; and
814 - printscreen, which saves a screenshot.
816 \param xuni A pointer to the main xuni structure.
817 \param keysym A structure describing the state of the keyboard, e.g. the
818 key that was pressed along with the state of any modifier keys etc.
819 \param mode The currently visible panel. This could be changed indirectly
820 in the call to perform_loop_click().
821 \param handled Set to true if the keypress described by \a keysym was
822 handled. If the keypress was handled, the xuni application will never
823 see the event.
824 \return True if a repaint of the screen is required. This happens when,
825 for example, the keypress resulted in a widget being deactivated.
827 static int event_key_down(struct xuni_t *xuni, SDL_keysym *keysym,
828 panel_type_t *mode, int *handled) {
830 int repaint = 0;
832 switch(keysym->sym) {
833 case SDLK_ESCAPE:
834 if(xuni->gui->active.widget) {
835 clear_active(xuni, &xuni->gui->active, 0);
837 *handled = 1;
838 repaint = 1;
841 break;
842 case SDLK_PRINT:
843 save_screenshot(xuni->smode->screen);
845 *handled = 1;
846 break;
847 case SDLK_RETURN:
848 if(keysym->mod & KMOD_ALT) {
849 if(!toggle_fullscreen(xuni->smode)) repaint = 1;
851 *handled = 1;
853 else if(xuni->gui->tab.panel != (size_t)-1
854 && xuni->gui->tab.sel != (size_t)-1) {
856 repaint |= perform_loop_click(xuni, mode,
857 widget_nameid_follow(xuni->gui->widget,
858 xuni->gui->tab.panel, xuni->gui->tab.sel,
859 (size_t)-1));
862 break;
863 case SDLK_TAB:
864 if(xuni->gui->tab.panel == (size_t)-1) break;
866 if(keysym->mod & KMOD_SHIFT) {
867 if(xuni->gui->tab.sel-- == (size_t)-1) {
868 xuni->gui->tab.sel =
869 widget_nameid_access(xuni->gui->widget,
870 xuni->gui->tab.panel)->compose->widgets - 1;
873 else {
874 if(++xuni->gui->tab.sel ==
875 widget_nameid_access(xuni->gui->widget,
876 xuni->gui->tab.panel)->compose->widgets) {
878 xuni->gui->tab.sel = (size_t)-1;
882 repaint = 1;
883 *handled = 1;
884 break;
885 case SDLK_F9:
886 if(keysym->mod & KMOD_ALT) {
887 if(!SDL_WM_IconifyWindow()) {
888 /* error !!! */
891 *handled = 1;
893 break;
894 /* keypresses for debugging purposes */
895 case SDLK_F8:
896 if(xuni->gui->sel.p.widget) {
897 print_widget_backtrace(xuni, xuni->gui->sel.p.widget);
900 *handled = 1;
901 break;
902 case SDLK_F7:
903 if(xuni->gui->active.widget) {
904 print_widget_backtrace(xuni, xuni->gui->active.widget);
907 *handled = 1;
908 break;
909 case SDLK_F6:
910 puts("====");
911 print_sel_widgets(xuni->gui->widget);
912 *handled = 1;
913 break;
914 case SDLK_F5:
916 int x, y, xrel, yrel;
917 Uint8 state;
919 state = SDL_GetMouseState(&x, &y);
920 SDL_GetRelativeMouseState(&xrel, &yrel);
922 printf("Mouse: at (%i,%i), moved (%i,%i), buttons %i%i%i\n",
923 x, y, xrel, yrel, state & SDL_BUTTON(1),
924 state & SDL_BUTTON(2), state & SDL_BUTTON(3));
926 SDL_WarpMouse(x, y);
928 break;
929 case SDLK_F4:
930 /* force a repaint */
931 repaint = 1;
932 break;
933 case SDLK_F3:
934 if(xuni->gui->edit.datawidget) {
935 printf("textbox data: \"%s\"\n",
936 xuni->gui->edit.datawidget->p.label->text);
938 else puts("No label selected");
939 break;
940 case SDLK_F2:
941 if(xuni->gui->sel.p.widget->type == WIDGET_TEXTBOX) {
942 printf("textbox leftpos: %i\n",
943 xuni->gui->sel.p.widget->p.textbox->leftpos);
945 break;
946 case SDLK_F1:
947 move_scrollbar(xuni, xuni->gui->widget, 1);
948 break;
949 default:
950 break;
953 if(*mode != (size_t)-1) {
954 if(!*handled && find_widget_accelerator(
955 widget_nameid_access(xuni->gui->widget, *mode)
956 ->p.panel->accel, keysym, xuni, mode)) {
958 *handled = 1;
961 if(xuni->gui->active.widget) {
962 repaint = widget_process_character(xuni, keysym);
964 update_widget(xuni, xuni->gui->active.widget);
968 if(repaint) *handled = 1;
970 return repaint;