Minor syntactical changes for readability.
[xuni.git] / src / loop.c
blob410b7c0fcd38e3a7305f20754a7677a6fbbb6c47
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, size_t mode);
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, &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 */
445 widget_nameid_access(xuni->gui->widget, mode)->visibility
446 |= WIDGET_VISIBILITY_VISIBLE;
448 if(paneldata->event[PANEL_EVENT_START].handler) {
449 (*paneldata->event[PANEL_EVENT_START].handler)(xuni, paneldata);
452 setup_theme_cursor(xuni, widget_nameid_access(xuni->gui->widget, mode));
453 SDL_GetMouseState(&xp, &yp);
454 set_loop_widget_sel(mode, xp, yp, xuni->gui->sel.clickin, xuni);
457 static void setup_theme_cursor(struct xuni_t *xuni, struct widget_t *panel) {
458 struct widget_t *cursor;
460 if(!panel->p.panel->frameupdate) return;
462 cursor = get_theme_widget(xuni, THEME_DEFAULT_CURSOR);
463 if(!cursor) return;
465 prepare_paint_image(xuni, cursor);
467 if(cursor->p.image->image) {
468 show_cursor(0);
472 static int get_event(int frameupdate, SDL_Event *event) {
473 return frameupdate ? SDL_PollEvent(event) : SDL_WaitEvent(event);
476 /* handled should not be used in some cases. For example, a widget might be
477 deselected by a mouse motion event, but a xuni application (like the
478 resource editor) might want to know about the mouse motion event just the
479 same.
481 /*! Handles an event from the SDL. What happens depends on the event itself.
482 However, this function will always either deal with the event itself, call
483 the appropriate event handler function for the currently visible panel, or
484 perhaps do a little bit of both.
486 Some handled events include clicking inside widgets (implemented by
487 calling set_loop_widget_sel()), pressing special keypresses like
488 printscreen (see event_key_down()), and selecting widgets when the mouse
489 moves (set_loop_widget_sel()).
491 \param event The SDL_Event that needs to be dealt with.
492 \param mode The currently visible panel, which could be changed.
493 \param xuni The main xuni_t structure.
494 \return True if the screen needs to be repainted.
496 static int process_event(SDL_Event *event, panel_type_t *mode,
497 struct xuni_t *xuni) {
499 struct panel_data_t *panel
500 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
501 int repaint = 0, handled = 0;
503 switch(event->type) {
504 case SDL_MOUSEBUTTONDOWN:
505 if(event->button.button == SDL_BUTTON_WHEELUP) {
506 scroll_listbox(xuni, -1, *mode);
508 repaint = 1;
509 handled = 1;
511 else if(event->button.button == SDL_BUTTON_WHEELDOWN) {
512 scroll_listbox(xuni, 1, *mode);
514 repaint = 1;
515 handled = 1;
518 if(event->button.button != 1) break;
520 repaint = set_loop_widget_sel(*mode, event->button.x, event->button.y,
521 1, xuni);
523 if(repaint) handled = 1;
524 break;
525 case SDL_MOUSEBUTTONUP:
526 if(event->button.button != 1) break;
528 repaint = event_mouse_button_up(event->button.x, event->button.y,
529 mode, xuni);
531 if(repaint) handled = 1;
532 break;
533 case SDL_MOUSEMOTION:
534 repaint = set_loop_widget_sel(*mode, event->button.x, event->button.y,
535 xuni->gui->sel.clickin, xuni);
537 /*if(repaint) handled = 1;*/
538 break;
539 case SDL_VIDEORESIZE:
540 if(!resize_screen(xuni->smode, &event->resize)) {
541 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_RESCALE);
543 xuni->gui->widget->p.panel
544 ->event[PANEL_EVENT_EVENT].p.event.mode = mode;
545 xuni->gui->widget->p.panel
546 ->event[PANEL_EVENT_EVENT].p.event.event = event;
548 panel_event_recursive(xuni, xuni->gui->widget->p.panel,
549 PANEL_EVENT_EVENT, xuni->gui->widget);
552 if(focus_changed(xuni->smode, SDL_APPACTIVE)) {
553 if(xuni->smode->focus) {
554 set_loop_caption(*mode, xuni);
556 else {
557 show_cursor(1);
561 /*handled = 1;*/
562 repaint = 1;
563 break;
564 case SDL_VIDEOEXPOSE:
565 repaint = 1;
566 handled = 1;
567 break;
568 case SDL_KEYDOWN:
569 repaint = event_key_down(xuni, &event->key.keysym, mode, &handled);
571 break;
572 case SDL_KEYUP:
573 break;
574 case SDL_ACTIVEEVENT:
575 if(focus_changed(xuni->smode, SDL_APPACTIVE | SDL_APPINPUTFOCUS)) {
576 if(xuni->smode->focus) {
577 /*int x, y;
578 SDL_GetMouseState(&x, &y);
579 printf("activated: %i,%i\n", x, y);*/
580 /* !!! this is only for the cursor */
581 set_loop_caption(*mode, xuni);
582 repaint = 1;
584 else {
585 /* preserve any textbox data etc in the active widget */
586 clear_active(xuni, &xuni->gui->active, 1);
587 show_cursor(1);
591 handled = 1;
592 break;
593 default:
594 break;
597 if(!handled && panel->event[PANEL_EVENT_EVENT].handler) {
598 panel->event[PANEL_EVENT_EVENT].p.event.mode = mode;
599 panel->event[PANEL_EVENT_EVENT].p.event.event = event;
601 repaint |= (*panel->event[PANEL_EVENT_EVENT].handler)(xuni, panel);
604 return repaint;
607 /* !!! bad hack */
608 static void scroll_listbox(struct xuni_t *xuni, int dir, size_t mode) {
609 int step = 5;
610 int moved = 0;
612 if(xuni->gui->sel.p.widget
613 && xuni->gui->sel.p.widget->type == WIDGET_LISTBOX) {
615 move_scrollbar(xuni, xuni->gui->sel.p.widget->compose->widget
616 [WID_LISTBOX_VSCROLL], step * dir);
618 widget_event(xuni, xuni->gui->sel.p.widget, WIDGET_EVENT_REPOSITION);
619 moved = 1;
622 if(xuni->gui->sel.p.widget
623 && xuni->gui->sel.p.widget->type == WIDGET_SCROLLBAR) {
625 move_scrollbar(xuni, xuni->gui->sel.p.widget, step * dir);
627 widget_event(xuni, xuni->gui->sel.p.widget->base,
628 WIDGET_EVENT_REPOSITION);
629 moved = 1;
632 if(xuni->gui->sel.p.widget && xuni->gui->sel.p.widget->base
633 && xuni->gui->sel.p.widget->base->type == WIDGET_SCROLLBAR) {
635 move_scrollbar(xuni, xuni->gui->sel.p.widget->base, step * dir);
637 widget_event(xuni, xuni->gui->sel.p.widget->base->base,
638 WIDGET_EVENT_REPOSITION);
639 moved = 1;
642 if(moved) {
643 int xp, yp;
645 SDL_GetMouseState(&xp, &yp);
647 set_loop_widget_sel(mode, xp, yp, xuni->gui->sel.clickin, xuni);
651 static int set_loop_widget_sel(panel_type_t mode, int xp, int yp,
652 int click, struct xuni_t *xuni) {
654 struct panel_data_t *panel
655 = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
657 if(!panel->event[PANEL_EVENT_SEL].handler) return 0;
659 panel->event[PANEL_EVENT_SEL].p.sel.mode = mode;
660 panel->event[PANEL_EVENT_SEL].p.sel.xp = xp;
661 panel->event[PANEL_EVENT_SEL].p.sel.yp = yp;
662 panel->event[PANEL_EVENT_SEL].p.sel.click = click;
664 return (*panel->event[PANEL_EVENT_SEL].handler)(xuni, panel);
667 /*! Determines whether any accelerator key combinations have been or are being
668 pressed, and, if so, handles the click event.
670 \param accel This panel's accelerator array. Searched for accelerator
671 combinations matching the current keyboard state.
672 \param key The current keyboard state. Compared with accelerator
673 combinations for the current panel for matches.
674 \param xuni The xuni structure. Used to get a struct widget_t pointer to
675 the current panel.
676 \param mode The currently active panel. Only passed so that
677 call_perform_click_func() may be called.
679 static int find_widget_accelerator(struct xuni_accelerator_t *accel,
680 SDL_keysym *key, struct xuni_t *xuni, panel_type_t *mode) {
682 size_t x;
683 SDL_keysym *akey;
685 if(!accel) return 0;
687 for(x = 0; x < accel->n; x ++) {
688 akey = &accel->key[x].key;
689 if(akey->sym && akey->sym == key->sym
690 && check_sdl_key_mod(akey->mod, key->mod)) {
692 call_perform_click_func(xuni,
693 widget_nameid_access(xuni->gui->widget, *mode)->p.panel,
694 accel->key[x].widget, mode);
696 return 1;
700 return 0;
703 /* !!! needs to be reworked to allow OR, AND, NOT, etc. */
704 static int check_sdl_key_mod(SDLMod mod, SDLMod now) {
705 if(mod != KMOD_NONE) {
706 if((mod & KMOD_CTRL) == KMOD_CTRL) {
707 if(now & KMOD_CTRL) mod &= ~KMOD_CTRL;
708 else return 0;
710 if((mod & KMOD_SHIFT) == KMOD_SHIFT) {
711 if(now & KMOD_SHIFT) mod &= ~KMOD_SHIFT;
712 else return 0;
714 if((mod & KMOD_ALT) == KMOD_ALT) {
715 if(now & KMOD_ALT) mod &= ~KMOD_ALT;
716 else return 0;
720 if(now != mod) return 0;
722 return 1;
725 static int event_mouse_button_up(int xp, int yp, panel_type_t *mode,
726 struct xuni_t *xuni) {
728 struct panel_data_t *panel
729 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
730 int repaint = 0, realclick;
732 if(xuni->gui->sel.p.widget && xuni->gui->sel.clickin
733 && pos_in_rect(xp, yp, xuni->gui->sel.p.widget->pos)) {
735 /*xuni->gui->sel.clickin = 0;*/
736 realclick = !set_widget_sel(&xuni->gui->sel, xp, yp, 1,
737 widget_nameid_access(xuni->gui->widget, *mode));
739 if(realclick) {
740 /* !!! return value ignored: repaint is always set to 1 */
741 call_perform_click_func(xuni, panel, xuni->gui->sel.p.widget,
742 mode);
745 xuni->gui->sel.wasin = 1;
746 xuni->gui->sel.clickin = 0;
747 repaint = 1;
749 if(realclick) {
750 if(xuni->gui->sel.p.widget &&
751 widget_can_be_active(xuni->gui->sel.p.widget)) {
753 xuni->gui->active = xuni->gui->sel.p;
754 activate_widget(xuni->gui->active.widget, xuni, xp, yp);
755 enable_unicode(xuni->gui->active.widget);
757 else if(xuni->gui->active.widget) {
758 repaint = 1;
760 clear_active(xuni, &xuni->gui->active, 1);
763 if(*mode != (size_t)-1) {
764 set_loop_widget_sel(*mode, xp, yp, xuni->gui->sel.clickin,
765 xuni);
769 else {
770 repaint = set_loop_widget_sel(*mode, xp, yp, 0, xuni);
772 if(xuni->gui->active.widget &&
773 !pos_in_rect(xp, yp, xuni->gui->active.widget->pos)) {
775 repaint = 1;
777 clear_active(xuni, &xuni->gui->active, 1);
781 return repaint;
784 static int perform_loop_click(struct xuni_t *xuni, panel_type_t *mode,
785 struct widget_t *widget) {
787 struct panel_data_t *panel
788 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
789 int repaint, xp = 0, yp = 0; /* !!! */
791 /* !!! return value ignored: repaint is always set to 1 */
792 call_perform_click_func(xuni, panel, widget, mode);
794 xuni->gui->sel.wasin = 1;
795 xuni->gui->sel.clickin = 0;
796 repaint = 1;
798 if(xuni->gui->sel.p.widget &&
799 widget_can_be_active(xuni->gui->sel.p.widget)) {
801 xuni->gui->active = xuni->gui->sel.p;
802 activate_widget(xuni->gui->active.widget, xuni, xp, yp);
803 enable_unicode(xuni->gui->active.widget);
805 else if(xuni->gui->active.widget) {
806 repaint = 1;
808 clear_active(xuni, &xuni->gui->active, 1);
811 if(*mode != (size_t)-1) {
812 set_loop_widget_sel(*mode, xp, yp, xuni->gui->sel.clickin,
813 xuni);
816 return repaint;
819 /* Perhaps functions for handling these keypresses could be registered?
821 Note: a lot of debugging keypresses are here, usually F-keys.
823 /*! Handles the keypresses that are common to all xuni widgets. This includes:
824 - escape, which reverts widgets;
825 - tab and shift-tab, which cycle though widgets;
826 - enter, which might deactivate or select widgets; and
827 - printscreen, which saves a screenshot.
829 \param xuni A pointer to the main xuni structure.
830 \param keysym A structure describing the state of the keyboard, e.g. the
831 key that was pressed along with the state of any modifier keys etc.
832 \param mode The currently visible panel. This could be changed indirectly
833 in the call to perform_loop_click().
834 \param handled Set to true if the keypress described by \a keysym was
835 handled. If the keypress was handled, the xuni application will never
836 see the event.
837 \return True if a repaint of the screen is required. This happens when,
838 for example, the keypress resulted in a widget being deactivated.
840 static int event_key_down(struct xuni_t *xuni, SDL_keysym *keysym,
841 panel_type_t *mode, int *handled) {
843 int repaint = 0;
845 switch(keysym->sym) {
846 case SDLK_ESCAPE:
847 if(xuni->gui->active.widget) {
848 clear_active(xuni, &xuni->gui->active, 0);
850 *handled = 1;
851 repaint = 1;
854 break;
855 case SDLK_PRINT:
856 save_screenshot(xuni->smode->screen);
858 *handled = 1;
859 break;
860 case SDLK_RETURN:
861 if(keysym->mod & KMOD_ALT) {
862 if(!toggle_fullscreen(xuni->smode)) repaint = 1;
864 *handled = 1;
866 else if(xuni->gui->tab.panel != (size_t)-1
867 && xuni->gui->tab.sel != (size_t)-1) {
869 repaint |= perform_loop_click(xuni, mode,
870 widget_nameid_follow(xuni->gui->widget,
871 xuni->gui->tab.panel, xuni->gui->tab.sel,
872 (size_t)-1));
875 break;
876 case SDLK_TAB:
877 if(xuni->gui->tab.panel == (size_t)-1) break;
879 if(keysym->mod & KMOD_SHIFT) {
880 if(xuni->gui->tab.sel-- == (size_t)-1) {
881 xuni->gui->tab.sel =
882 widget_nameid_access(xuni->gui->widget,
883 xuni->gui->tab.panel)->compose->widgets - 1;
886 else {
887 if(++xuni->gui->tab.sel ==
888 widget_nameid_access(xuni->gui->widget,
889 xuni->gui->tab.panel)->compose->widgets) {
891 xuni->gui->tab.sel = (size_t)-1;
895 repaint = 1;
896 *handled = 1;
897 break;
898 case SDLK_F9:
899 if(keysym->mod & KMOD_ALT) {
900 if(!SDL_WM_IconifyWindow()) {
901 /* error !!! */
903 else {
904 *handled = 1;
907 break;
908 /* keypresses for debugging purposes */
909 case SDLK_F8:
910 if(xuni->gui->sel.p.widget) {
911 print_widget_backtrace(xuni, xuni->gui->sel.p.widget);
914 *handled = 1;
915 break;
916 case SDLK_F7:
917 if(xuni->gui->active.widget) {
918 print_widget_backtrace(xuni, xuni->gui->active.widget);
921 *handled = 1;
922 break;
923 case SDLK_F6:
924 puts("====");
925 print_sel_widgets(xuni->gui->widget);
926 *handled = 1;
927 break;
928 case SDLK_F5:
930 int x, y, xrel, yrel;
931 Uint8 state;
933 state = SDL_GetMouseState(&x, &y);
934 SDL_GetRelativeMouseState(&xrel, &yrel);
936 printf("Mouse: at (%i,%i), moved (%i,%i), buttons %i%i%i\n",
937 x, y, xrel, yrel, state & SDL_BUTTON(1),
938 state & SDL_BUTTON(2), state & SDL_BUTTON(3));
940 SDL_WarpMouse(x, y);
942 break;
943 case SDLK_F4:
944 /* force a repaint */
945 repaint = 1;
946 break;
947 case SDLK_F3:
948 if(xuni->gui->edit.datawidget) {
949 printf("textbox data: \"%s\"\n",
950 xuni->gui->edit.datawidget->p.label->text);
952 else puts("No label selected");
953 break;
954 case SDLK_F2:
955 if(xuni->gui->sel.p.widget->type == WIDGET_TEXTBOX) {
956 printf("textbox leftpos: %i\n",
957 xuni->gui->sel.p.widget->p.textbox->leftpos);
959 break;
960 default:
961 break;
964 if(*mode != (size_t)-1) {
965 if(!*handled && find_widget_accelerator(
966 widget_nameid_access(xuni->gui->widget, *mode)
967 ->p.panel->accel, keysym, xuni, mode)) {
969 *handled = 1;
972 if(xuni->gui->active.widget) {
973 repaint = widget_process_character(xuni, keysym);
975 update_widget(xuni, xuni->gui->active.widget);
979 if(repaint) *handled = 1;
981 return repaint;