1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file toolkit.c
7  *
8  * @brief Handles windows and widgets.
9  */
10 
11 
12 #include "toolkit.h"
13 
14 #include "naev.h"
15 
16 #include <stdarg.h>
17 
18 #include "tk/toolkit_priv.h"
19 
20 #include "log.h"
21 #include "pause.h"
22 #include "opengl.h"
23 #include "input.h"
24 #include "nstd.h"
25 #include "dialogue.h"
26 #include "conf.h"
27 
28 
29 #define INPUT_DELAY      conf.repeat_delay /**< Delay before starting to repeat. */
30 #define INPUT_FREQ       conf.repeat_freq /**< Interval between repetition. */
31 
32 
33 static unsigned int genwid = 0; /**< Generates unique window ids, > 0 */
34 
35 
36 static int toolkit_open = 0; /**< 1 if toolkit is in use, 0 else. */
37 static int toolkit_delayCounter = 0; /**< Horrible hack around secondary loop. */
38 
39 
40 /*
41  * window stuff
42  */
43 #define MIN_WINDOWS  3 /**< Minimum windows to prealloc. */
44 static Window *windows = NULL; /**< Window linked list, not to be confused with MS windows. */
45 static int window_dead = 0; /**< There are dead windows lying around. */
46 
47 
48 /*
49  * simulate keypresses when holding
50  */
51 static SDLKey input_key             = 0; /**< Current pressed key. */
52 static SDLMod input_mod             = 0; /**< Current pressed modifier. */
53 static unsigned int input_keyTime   = 0; /**< Tick pressed. */
54 static int input_keyCounter         = 0; /**< Number of repetitions. */
55 static char input_text              = 0; /**< Current character. */
56 
57 
58 /*
59  * default outline colours
60  */
61 const glColour* toolkit_colLight = &cGrey90; /**< Light outline colour. */
62 const glColour* toolkit_col      = &cGrey70; /**< Normal outline colour. */
63 const glColour* toolkit_colDark  = &cGrey30; /**< Dark outline colour. */
64 
65 
66 /*
67  * VBO
68  */
69 static gl_vbo *toolkit_vbo; /**< Toolkit VBO. */
70 static GLsizei toolkit_vboColourOffset; /**< Colour offset. */
71 
72 
73 /*
74  * static prototypes
75  */
76 /* input */
77 static int toolkit_mouseEvent( Window *w, SDL_Event* event );
78 static int toolkit_mouseEventWidget( Window *w, Widget *wgt,
79       SDL_Event *event, int x, int y, int rx, int ry );
80 static int toolkit_keyEvent( Window *wdw, SDL_Event* event );
81 #if SDL_VERSION_ATLEAST(2,0,0)
82 static int toolkit_textEvent( Window *wdw, SDL_Event* event );
83 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
84 /* Focus */
85 static int toolkit_isFocusable( Widget *wgt );
86 static Widget* toolkit_getFocus( Window *wdw );
87 static void toolkit_expose( Window *wdw, int expose );
88 /* render */
89 static void window_renderBorder( Window* w );
90 /* Death. */
91 static void widget_kill( Widget *wgt );
92 static void window_kill( Window *wdw );
93 static void toolkit_purgeDead (void);
94 
95 
96 /**
97  * @brief Checks to see if the toolkit is open.
98  *
99  *    @return 1 if the toolkit is open.
100  */
toolkit_isOpen(void)101 int toolkit_isOpen (void)
102 {
103    return !!toolkit_open;
104 }
105 
106 
107 /**
108  * @brief Delays the toolkit purge by an iteration, useful for dialogues.
109  */
toolkit_delay(void)110 void toolkit_delay (void)
111 {
112    toolkit_delayCounter++;
113 }
114 
115 
116 /**
117  * @brief Sets the internal widget position.
118  *
119  *    @param wdw Window to which the widget belongs.
120  *    @param wgt Widget to set position of.
121  *    @param x X position to use.
122  *    @param y Y position to use.
123  */
toolkit_setPos(Window * wdw,Widget * wgt,int x,int y)124 void toolkit_setPos( Window *wdw, Widget *wgt, int x, int y )
125 {
126    /* X position. */
127    if (x < 0)
128       wgt->x = wdw->w - wgt->w + x;
129    else
130       wgt->x = (double) x;
131 
132    /* Y position. */
133    if (y < 0)
134       wgt->y = wdw->h - wgt->h + y;
135    else
136       wgt->y = (double) y;
137 }
138 
139 
140 /**
141  * @brief Moves a window to the specified coordinates.
142  *
143  *    @param x X position.
144  *    @param y Y position.
145  */
toolkit_setWindowPos(Window * wdw,int x,int y)146 void toolkit_setWindowPos( Window *wdw, int x, int y )
147 {
148    wdw->xrel = -1.;
149    wdw->yrel = -1.;
150 
151    /* x pos */
152    if (x == -1) { /* Center */
153       wdw->x = (SCREEN_W - wdw->w)/2.;
154       wdw->xrel = .5;
155    }
156    else if (x < 0)
157       wdw->x = SCREEN_W - wdw->w + (double) x;
158    else
159       wdw->x = (double) x;
160 
161    /* y pos */
162    if (y == -1) { /* Center */
163       wdw->y = (SCREEN_H - wdw->h)/2.;
164       wdw->yrel = .5;
165    }
166    else if (y < 0)
167       wdw->y = SCREEN_H - wdw->h + (double) y;
168    else
169       wdw->y = (double) y;
170 }
171 
172 
173 /**
174  * @brief Moves a window to the specified coordinates.
175  *
176  *    @param x X position.
177  *    @param y Y position.
178  */
window_move(unsigned int wid,int x,int y)179 void window_move( unsigned int wid, int x, int y )
180 {
181    Window *wdw;
182 
183    /* Get the window. */
184    wdw = window_wget(wid);
185    if (wdw == NULL)
186       return;
187 
188    toolkit_setWindowPos( wdw, x, y );
189 }
190 
191 
192 /**
193  * @brief Allocates room for a new widget.
194  *
195  *    @param w Window to create widget in.
196  *    @return Newly allocated widget.
197  */
window_newWidget(Window * w,const char * name)198 Widget* window_newWidget( Window* w, const char *name )
199 {
200    Widget *wgt, *wlast, *wtmp;
201    char *saved_name = NULL;
202 
203    /* NULL protection. */
204    if (w==NULL)
205       return NULL;
206 
207    /* Try to find one with the same name first. */
208    wlast = NULL;
209    for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next) {
210 
211       /* Must match name. */
212       if (strcmp(name, wgt->name)!=0) {
213          wlast = wgt;
214          continue;
215       }
216 
217       /* Should be destroyed. */
218       if (!wgt_isFlag( wgt, WGT_FLAG_KILL )) {
219          WARN("Trying to create widget '%s' over existing one that hasn't been destroyed",
220                name );
221          return NULL;
222       }
223 
224       /* Relink. */
225       if (wlast==NULL)
226          w->widgets  = wgt->next;
227       else
228          wlast->next = wgt->next;
229 
230       /* Prepare and return this widget. */
231       saved_name = wgt->name;
232       wgt->name  = NULL;
233       widget_cleanup(wgt);
234       break;
235    }
236 
237    /* Must grow widgets. */
238    if (wgt == NULL)
239       wgt = malloc( sizeof(Widget) );
240 
241    /* Sane defaults. */
242    memset( wgt, 0, sizeof(Widget) );
243    wgt->type   = WIDGET_NULL;
244    wgt->status = WIDGET_STATUS_NORMAL;
245    wgt->wdw    = w->id;
246    if (saved_name != NULL) /* Hack to avoid frees so _getFocus works in the same frame. */
247       wgt->name   = saved_name;
248    else
249       wgt->name   = strdup(name);
250    wgt->id     = ++w->idgen;
251 
252    /* Set up. */
253    wlast = NULL;
254    for (wtmp=w->widgets; wtmp!=NULL; wtmp=wtmp->next)
255       wlast = wtmp;
256    if (wlast == NULL)
257       w->widgets  = wgt;
258    else
259       wlast->next = wgt;
260 
261    return wgt;
262 }
263 
264 
265 /**
266  * @brief Gets a Window by ID.
267  *
268  *    @param wid ID of the window to get.
269  *    @return Window matching wid.
270  */
window_wget(const unsigned int wid)271 Window* window_wget( const unsigned int wid )
272 {
273    Window *w;
274    if (windows == NULL)
275       return NULL;
276    for (w = windows; w != NULL; w = w->next)
277       if (w->id == wid)
278          return w;
279    return NULL;
280 }
281 
282 
283 /**
284  * @brief Gets a widget from window id and widgetname.
285  *
286  *    @param wid ID of the window to get widget from.
287  *    @param name Name of the widget to get.
288  *    @return Widget matching name in the window.
289  */
window_getwgt(const unsigned int wid,const char * name)290 Widget* window_getwgt( const unsigned int wid, const char* name )
291 {
292    Window *wdw;
293    Widget *wgt;
294 
295    /* Get the window. */
296    wdw = window_wget(wid);
297    if (wdw == NULL)
298       return NULL;
299 
300    /* Find the widget. */
301    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
302       if (strcmp(wgt->name, name)==0)
303          return wgt;
304 
305    WARN("Widget '%s' not found in window '%u'!", name, wid );
306    return NULL;
307 }
308 
309 
310 /**
311  * @brief Gets the dimensions of a window.
312  *
313  *    @param wid ID of the window to get dimension of.
314  *    @param[out] w Width of the window or -1 on error.
315  *    @param[out] h Height of the window or -1 on error.
316  */
window_dimWindow(const unsigned int wid,int * w,int * h)317 void window_dimWindow( const unsigned int wid, int *w, int *h )
318 {
319    Window *wdw;
320 
321    /* Get the window. */
322    wdw = window_wget(wid);
323    if (wdw == NULL) {
324       *w = -1;
325       *h = -1;
326       return;
327    }
328 
329    /* Set dimensions. */
330    *w = wdw->w;
331    *h = wdw->h;
332 }
333 
334 
335 /**
336  * @brief Gets the dimensions of a widget.
337  *
338  *    @param wid ID of the window that contains the widget.
339  *    @param name Name of the widget to get dimensions of.
340  *    @param[out] w Width of the widget or -1 on error.
341  *    @param[out] h Height of the widget or -1 on error.
342  */
window_dimWidget(const unsigned int wid,char * name,int * w,int * h)343 void window_dimWidget( const unsigned int wid, char *name,  int *w, int *h )
344 {
345    Widget *wgt;
346 
347    /* Get widget. */
348    wgt = window_getwgt(wid, name);
349    if (wgt == NULL) {
350       *w = -1;
351       *h = -1;
352       return;
353    }
354 
355    *w = wgt->w;
356    *h = wgt->h;
357 }
358 
359 
360 /**
361  * @brief Gets a widget's position.
362  *
363  *    @param wid ID of the window to get widget from.
364  *    @param name Name of the widget to get position of.
365  *    @param[out] x X position of the widget.
366  *    @param[out] y Y position of the widget.
367  */
window_posWidget(const unsigned int wid,char * name,int * x,int * y)368 void window_posWidget( const unsigned int wid,
369       char* name, int *x, int *y )
370 {
371    Widget *wgt;
372 
373    /* Get widget. */
374    wgt = window_getwgt(wid,name);
375    if (wgt == NULL)
376       return;
377 
378    /* Return position. */
379    if (x != NULL)
380       (*x) = wgt->x;
381    if (y != NULL)
382       (*y) = wgt->y;
383 }
384 
385 
386 /**
387  * @brief Moves a widget.
388  *
389  *    @param wid ID of the window to get widget from.
390  *    @param name Name of the widget to set position to.
391  *    @param x New X position to set widget to.
392  *    @param y New Y position to set widget to.
393  */
window_moveWidget(const unsigned int wid,char * name,int x,int y)394 void window_moveWidget( const unsigned int wid,
395       char* name, int x, int y )
396 {
397    Window *wdw;
398    Widget *wgt;
399 
400    /* Get window. */
401    wdw = window_wget(wid);
402    if (wdw == NULL)
403       return;
404 
405    /* Get widget. */
406    wgt = window_getwgt(wid,name);
407    if (wgt == NULL)
408       return;
409 
410    /* Set position. */
411    toolkit_setPos( wdw, wgt, x, y );
412 }
413 
414 
415 /**
416  * @brief Checks to see if a window exists.
417  *
418  *    @param wdwname Name of the window to check.
419  *    @return 1 if it exists, 0 if it doesn't.
420  */
window_exists(const char * wdwname)421 int window_exists( const char* wdwname )
422 {
423    Window *w;
424    if (windows == NULL)
425       return 0;
426    for (w = windows; w != NULL; w = w->next)
427       if ((strcmp(w->name,wdwname)==0) && !window_isFlag(w, WINDOW_KILL))
428          return 1;
429    return 0; /* doesn't exist */
430 }
431 
432 
433 /**
434  * @brief Checks to see if a window with a certain ID exists.
435  *
436  *    @param wdwname Name of the window to check.
437  *    @return 1 if it exists, 0 if it doesn't.
438  */
window_existsID(const unsigned int wid)439 int window_existsID( const unsigned int wid )
440 {
441    Window *w;
442    if (windows == NULL)
443       return 0;
444    for (w = windows; w != NULL; w = w->next)
445       if ((w->id==wid) && !window_isFlag(w, WINDOW_KILL))
446          return 1;
447    return 0; /* doesn't exist */
448 }
449 
450 
451 /**
452  * @brief Gets the ID of a window.
453  *
454  *    @param wdwname Name of the window to get ID of.
455  *    @return ID of the window.
456  */
window_get(const char * wdwname)457 unsigned int window_get( const char* wdwname )
458 {
459    Window *w;
460    if (windows == NULL)
461       return 0;
462    for (w = windows; w != NULL; w = w->next)
463       if ((strcmp(w->name,wdwname)==0) && !window_isFlag(w, WINDOW_KILL))
464          return w->id;
465    return 0;
466 }
467 
468 
469 /**
470  * @brief Creates a window.
471  *
472  *    @param name Name of the window to create.
473  *    @param x X position of the window (-1 centers).
474  *    @param y Y position of the window (-1 centers).
475  *    @param w Width of the window (-1 fullscreen).
476  *    @param h Height of the window (-1 fullscreen).
477  *    @return Newly created window's ID.
478  */
window_create(const char * name,const int x,const int y,const int w,const int h)479 unsigned int window_create( const char* name,
480       const int x, const int y, const int w, const int h )
481 {
482    return window_createFlags( name, x, y, w, h, 0 );
483 }
484 
485 
486 /**
487  * @brief Creates a window.
488  *
489  *    @param name Name of the window to create.
490  *    @param x X position of the window (-1 centers).
491  *    @param y Y position of the window (-1 centers).
492  *    @param w Width of the window (-1 fullscreen).
493  *    @param h Height of the window (-1 fullscreen).
494  *    @param flags Initial flags to set.
495  *    @return Newly created window's ID.
496  */
window_createFlags(const char * name,const int x,const int y,const int w,const int h,unsigned int flags)497 unsigned int window_createFlags( const char* name,
498       const int x, const int y, const int w, const int h, unsigned int flags )
499 {
500    Window *wcur, *wlast, *wdw;
501 
502    /* Allocate memory. */
503    wdw = calloc( 1, sizeof(Window) );
504 
505    const int wid = (++genwid); /* unique id */
506 
507    /* Create the window. */
508 
509    wdw->id           = wid;
510    wdw->name         = strdup(name);
511 
512    /* Sane defaults. */
513    wdw->idgen        = -1;
514    wdw->focus        = -1;
515    wdw->xrel         = -1.;
516    wdw->yrel         = -1.;
517    wdw->flags        = flags;
518    wdw->exposed      = !window_isFlag(wdw, WINDOW_NOFOCUS);
519 
520    /* Dimensions. */
521    wdw->w            = (w == -1) ? SCREEN_W : (double) w;
522    wdw->h            = (h == -1) ? SCREEN_H : (double) h;
523    if ((w == -1) && (h == -1)) {
524       window_setFlag( wdw, WINDOW_FULLSCREEN );
525       wdw->x = 0.;
526       wdw->y = 0.;
527    }
528    else
529       toolkit_setWindowPos( wdw, x, y );
530 
531    if (toolkit_open==0) { /* toolkit is on */
532       input_mouseShow();
533       toolkit_open = 1; /* enable toolkit */
534       pause_game();
535       gl_defViewport(); /* Reset the default viewport */
536    }
537 
538    /* Clear key repeat. */
539    toolkit_clearKey();
540 
541    /* Add to list. */
542    wdw->next = NULL;
543    if (windows == NULL)
544       windows = wdw;
545    else {
546       /* Take focus from the old window. */
547       if (wdw->exposed) {
548          wcur = toolkit_getActiveWindow();
549          if (wcur != NULL)
550             toolkit_expose( wcur, 0 ); /* wcur is hidden */
551       }
552 
553       wlast = NULL;
554       for (wcur = windows; wcur != NULL; wcur = wcur->next) {
555          if ((strcmp(wcur->name,name)==0) && !window_isFlag(wcur, WINDOW_KILL) &&
556                !window_isFlag(wcur, WINDOW_NOFOCUS))
557             WARN("Window with name '%s' already exists!",wcur->name);
558          wlast = wcur;
559       }
560 
561       if (wlast != NULL)
562          wlast->next = wdw;
563    }
564 
565    return wid;
566 }
567 
568 
569 /**
570  * @brief Sets a window as a window's parent.
571  *
572  * When a window's parent closes, it closes the window also.
573  *
574  *    @param wid Window to set as child.
575  *    @param parent Window to set as parent.
576  */
window_setParent(unsigned int wid,unsigned int parent)577 void window_setParent( unsigned int wid, unsigned int parent )
578 {
579    Window *wdw;
580 
581    /* Get the window. */
582    wdw = window_wget( wid );
583    if (wdw == NULL)
584       return;
585 
586    /* Set the parent. */
587    wdw->parent = parent;
588 }
589 
590 
591 /**
592  * @brief Gets the window's parent.
593  *
594  *    @param wid Window to get parent of.
595  *    @return Parent of the window or 0 on error.
596  */
window_getParent(unsigned int wid)597 unsigned int window_getParent( unsigned int wid )
598 {
599    Window *wdw;
600 
601    /* Get the window. */
602    wdw = window_wget( wid );
603    if (wdw == NULL)
604       return 0;
605 
606    /* Get the parent. */
607    return wdw->parent;
608 }
609 
610 
611 /**
612  * @brief Sets the default close function of the window.
613  *
614  * This function is called when the window is closed.
615  *
616  *    @param wid Window to set close function of.
617  *    @param Function to trigger when window is closed, parameter is window id
618  *           and name.
619  */
window_onClose(unsigned int wid,void (* fptr)(unsigned int,char *))620 void window_onClose( unsigned int wid, void (*fptr)(unsigned int,char*) )
621 {
622    Window *wdw;
623 
624    /* Get the window. */
625    wdw = window_wget( wid );
626    if (wdw == NULL)
627       return;
628 
629    /* Set the close function. */
630    wdw->close_fptr = fptr;
631 }
632 
633 
634 /**
635  * @brief Sets the default accept function of the window.
636  *
637  * This function is called whenever 'enter' is pressed and the current widget
638  *  does not catch it.  NULL disables the accept function.
639  *
640  *    @param wid ID of the window to set the accept function.
641  *    @param accept Function to trigger when window is "accepted".  Parameter
642  *                  passed is window name.
643  */
window_setAccept(const unsigned int wid,void (* accept)(unsigned int,char *))644 void window_setAccept( const unsigned int wid, void (*accept)(unsigned int,char*) )
645 {
646    Window *wdw;
647 
648    /* Get the window. */
649    wdw = window_wget( wid );
650    if (wdw == NULL)
651       return;
652 
653    /* Set the accept function. */
654    wdw->accept_fptr = accept;
655 }
656 
657 
658 /**
659  * @brief Sets the default cancel function of the window.
660  *
661  * This function is called whenever 'escape' is hit and the current widget
662  *  does not catch it.  NULL disables the cancel function.
663  *
664  *    @param wid ID of the window to set cancel function.
665  *    @param cancel Function to trigger when window is "cancelled".  Parameter
666  *                  passed is window name.
667  */
window_setCancel(const unsigned int wid,void (* cancel)(unsigned int,char *))668 void window_setCancel( const unsigned int wid, void (*cancel)(unsigned int,char*) )
669 {
670    Window *wdw;
671 
672    /* Get the window. */
673    wdw = window_wget( wid );
674    if (wdw == NULL)
675       return;
676 
677    /* Set the cancel function. */
678    wdw->cancel_fptr = cancel;
679 }
680 
681 
682 /**
683  * @brief Sets custom data for a window.
684  *
685  *    @param wid Window to set custom data of.
686  *    @param data Data to set.
687  */
window_setData(unsigned int wid,void * data)688 void window_setData( unsigned int wid, void *data )
689 {
690    Window *wdw;
691 
692    /* Get the window. */
693    wdw = window_wget( wid );
694    if (wdw == NULL)
695       return;
696 
697    /* Set data. */
698    wdw->udata = data;
699 }
700 
701 
702 /**
703  * @brief Gets the custom data of a window.
704  *
705  *    @param wid Window to get custom data of.
706  *    @return The custom data or NULL if not applicable.
707  */
window_getData(unsigned int wid)708 void* window_getData( unsigned int wid )
709 {
710    Window *wdw;
711 
712    /* Get the window. */
713    wdw = window_wget( wid );
714    if (wdw == NULL)
715       return NULL;
716 
717    /* Get data. */
718    return wdw->udata;
719 }
720 
721 
722 /**
723  * @brief Sets or removes the border of a window.
724  *
725  * Default is enabled.
726  *
727  *    @param wid ID of the window to enable/disable border.
728  *    @param enable Whether or not to enable rendering of the border.
729  */
window_setBorder(unsigned int wid,int enable)730 void window_setBorder( unsigned int wid, int enable )
731 {
732    Window *wdw;
733 
734    /* Get the window. */
735    wdw = window_wget( wid );
736    if (wdw == NULL)
737       return;
738 
739    if (enable)
740       window_rmFlag( wdw, WINDOW_NOBORDER );
741    else
742       window_setFlag( wdw, WINDOW_NOBORDER );
743 }
744 
745 
746 /**
747  * @brief Sets the key handler for the window.
748  *
749  * This function is only called if neither the active widget nor the window
750  *  itself grabs the input.
751  */
window_handleKeys(const unsigned int wid,int (* keyhandler)(unsigned int,SDLKey,SDLMod))752 void window_handleKeys( const unsigned int wid,
753       int (*keyhandler)(unsigned int,SDLKey,SDLMod) )
754 {
755    Window *wdw;
756 
757    /* Get the window. */
758    wdw = window_wget( wid );
759    if (wdw == NULL)
760       return;
761 
762    /* Set key event handler function. */
763    wdw->keyevent = keyhandler;
764 }
765 
766 
767 /**
768  * @brief Sets the event handler for the window.
769  *
770  * This function is called every time the window receives input.
771  */
window_handleEvents(const unsigned int wid,int (* eventhandler)(unsigned int,SDL_Event *))772 void window_handleEvents( const unsigned int wid,
773       int (*eventhandler)(unsigned int,SDL_Event*) )
774 {
775    Window *wdw;
776 
777    /* Get the window. */
778    wdw = window_wget( wid );
779    if (wdw == NULL)
780       return;
781 
782    /* Set key event handler function. */
783    wdw->eventevent = eventhandler;
784 }
785 
786 
787 /**
788  * @brief Destroys a widget.
789  *
790  *    @param widget Widget to destroy.
791  */
widget_cleanup(Widget * widget)792 void widget_cleanup( Widget *widget )
793 {
794    /* Type specific clean up. */
795    if (widget->cleanup != NULL)
796       widget->cleanup(widget);
797 
798    /* General freeing. */
799    free(widget->name);
800 }
801 
802 
803 /**
804  * @brief Helper function to automatically close the window calling it.
805  *
806  *    @param wid Window to close.
807  *    @param str Unused.
808  */
window_close(unsigned int wid,char * str)809 void window_close( unsigned int wid, char *str )
810 {
811    (void) str;
812 
813    window_destroy( wid );
814 }
815 
816 
817 /**
818  * @brief Kills the window.
819  *
820  *    @param wid ID of window to destroy.
821  *    @return 1 if windows still need killing.
822  */
window_destroy(const unsigned int wid)823 void window_destroy( const unsigned int wid )
824 {
825    Window *wdw, *w;
826    if (windows == NULL)
827       return;
828    /* Destroy the window */
829    for (wdw = windows; wdw != NULL; wdw = wdw->next) {
830 
831       /* Not the window we're looking for. */
832       if (wdw->id != wid)
833          continue;
834 
835       /* Already being killed, skip. */
836       if (window_isFlag( wdw, WINDOW_KILL ))
837          continue;
838 
839       /* Mark children for death. */
840       for (w = windows; w != NULL; w = w->next)
841          if (w->parent == wid)
842             window_destroy( w->id );
843 
844       /* Mark for death. */
845       window_setFlag( wdw, WINDOW_KILL );
846       window_dead = 1;
847 
848       /* Run the close function first. */
849       if (wdw->close_fptr != NULL)
850          wdw->close_fptr( wdw->id, wdw->name );
851       wdw->close_fptr = NULL;
852 
853       /* Disable text input, etc. */
854       toolkit_focusClear( wdw );
855 
856       w = toolkit_getActiveWindow();
857       if (w == NULL)
858          break;
859 
860       toolkit_expose( w, 1 );
861       break;
862    }
863 }
864 
865 
866 /**
867  * @brief Kills the window.
868  *
869  *    @param wid ID of window to destroy.
870  */
window_kill(Window * wdw)871 static void window_kill( Window *wdw )
872 {
873    Widget *wgt, *wgtkill;
874 
875    /* Run the close function first. */
876    if (wdw->close_fptr != NULL)
877       wdw->close_fptr( wdw->id, wdw->name );
878    wdw->close_fptr = NULL;
879 
880    /* Destroy the window. */
881    if (wdw->name)
882       free(wdw->name);
883    wgt = wdw->widgets;
884    while (wgt != NULL) {
885       wgtkill = wgt;
886       wgt = wgtkill->next;
887       widget_kill(wgtkill);
888    }
889    free(wdw);
890 
891    /* Clear key repeat, since toolkit could miss the keyup event. */
892    toolkit_clearKey();
893 }
894 
895 
896 /**
897  * @brief Checks to see if a widget exists.
898  *
899  *    @param wid Window to check widget in.
900  *    @param wgtname Name of the widget to check;
901  *    @return 1 if the widget exists.
902  */
widget_exists(const unsigned int wid,const char * wgtname)903 int widget_exists( const unsigned int wid, const char* wgtname )
904 {
905    Window *w = window_wget(wid);
906    Widget *wgt;
907 
908    /* Get window. */
909    if (w==NULL) {
910       WARN("window '%d' does not exist", wid);
911       return 0;
912    }
913 
914    /* Check for widget. */
915    for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next)
916       if (strcmp(wgtname, wgt->name)==0)
917          return !wgt_isFlag(wgt, WGT_FLAG_KILL);
918 
919    return 0;
920 }
921 
922 
923 /**
924  * @brief Destroys a widget in a window.
925  *
926  *    @param wid Window to destroy widget in.
927  *    @param wgtname Name of the widget to destroy.
928  */
window_destroyWidget(unsigned int wid,const char * wgtname)929 void window_destroyWidget( unsigned int wid, const char* wgtname )
930 {
931    Window *wdw;
932    Widget *wgt;
933 
934    /* Get the window. */
935    wdw = window_wget( wid );
936    if (wdw == NULL)
937       return;
938 
939    /* Get the widget. */
940    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
941       if (strcmp(wgt->name, wgtname)==0)
942          break;
943 
944    if (wgt == NULL) {
945       WARN("Widget '%s' not found in window '%s'", wgtname, wdw->name );
946       return;
947    }
948 
949    /* Defocus. */
950    if (wdw->focus == wgt->id)
951       toolkit_defocusWidget( wdw, wgt );
952 
953    /* There's dead stuff now. */
954    window_dead = 1;
955    wgt_rmFlag( wgt, WGT_FLAG_FOCUSED );
956    wgt_setFlag( wgt, WGT_FLAG_KILL );
957 }
958 
959 
960 /**
961  * @brief Destroy a widget really.
962  */
widget_kill(Widget * wgt)963 static void widget_kill( Widget *wgt )
964 {
965    /* Clean up. */
966    widget_cleanup(wgt);
967    free(wgt);
968 }
969 
970 
971 /**
972  * @brief Draws an outline.
973  *
974  * If lc is NULL, colour will be flat.
975  *
976  *    @param x X position to draw at.
977  *    @param y Y position to draw at.
978  *    @param w Width.
979  *    @param h Height.
980  *    @param b Border width.
981  *    @param thick Thickness of the border.
982  *    @param c Colour.
983  *    @param lc Light colour.
984  */
toolkit_drawOutlineThick(int x,int y,int w,int h,int b,int thick,const glColour * c,const glColour * lc)985 void toolkit_drawOutlineThick( int x, int y, int w, int h, int b,
986       int thick, const glColour* c, const glColour* lc )
987 {
988    GLshort tri[5][4];
989    glColour colours[10];
990 
991    /* Set shade model. */
992    glShadeModel( (lc==NULL) ? GL_FLAT : GL_SMOOTH );
993 
994    x -= b - thick;
995    w += 2 * (b - thick);
996    y -= b - thick;
997    h += 2 * (b - thick);
998    lc = lc ? lc : c;
999 
1000    /* Left-up. */
1001    tri[0][0]     = x;         /* Inner */
1002    tri[0][1]     = y;
1003    tri[0][2]     = x-thick;   /* Outer */
1004    tri[0][3]     = y-thick;
1005    colours[0]    = *lc;
1006    colours[1]    = *lc;
1007 
1008    /* Left-down. */
1009    tri[1][0]     = x;         /* Inner. */
1010    tri[1][1]     = y + h;
1011    tri[1][2]     = x-thick;   /* Outer. */
1012    tri[1][3]     = y + h+thick;
1013    colours[2]    = *c;
1014    colours[3]    = *c;
1015 
1016    /* Right-down. */
1017    tri[2][0]     = x + w;       /* Inner. */
1018    tri[2][1]     = y + h;
1019    tri[2][2]     = x + w+thick; /* Outer. */
1020    tri[2][3]     = y + h+thick;
1021    colours[4]    = *c;
1022    colours[5]    = *c;
1023 
1024    /* Right-up. */
1025    tri[3][0]     = x + w;       /* Inner. */
1026    tri[3][1]     = y;
1027    tri[3][2]     = x + w+thick; /* Outer. */
1028    tri[3][3]     = y-thick;
1029    colours[6]    = *lc;
1030    colours[7]    = *lc;
1031 
1032    /* Left-up. */
1033    tri[4][0]     = x;         /* Inner */
1034    tri[4][1]     = y;
1035    tri[4][2]     = x-thick;   /* Outer */
1036    tri[4][3]     = y-thick;
1037    colours[8]    = *lc;
1038    colours[9]    = *lc;
1039 
1040    /* Upload to the VBO. */
1041    gl_vboSubData( toolkit_vbo, 0, sizeof(tri), tri );
1042    gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset, sizeof(colours), colours );
1043 
1044    /* Set up the VBO. */
1045    gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );
1046    gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1047          toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1048 
1049    /* Draw the VBO. */
1050    glDrawArrays( GL_TRIANGLE_STRIP, 0, 10 );
1051 
1052    /* Deactivate VBO. */
1053    gl_vboDeactivate();
1054 }
1055 
1056 
1057 /**
1058  * @brief Draws an outline.
1059  *
1060  * If lc is NULL, colour will be flat.
1061  *
1062  *    @param x X position to draw at.
1063  *    @param y Y position to draw at.
1064  *    @param w Width.
1065  *    @param h Height.
1066  *    @param b Border width.
1067  *    @param c Colour.
1068  *    @param lc Light colour.
1069  */
toolkit_drawOutline(int x,int y,int w,int h,int b,const glColour * c,const glColour * lc)1070 void toolkit_drawOutline( int x, int y, int w, int h, int b,
1071       const glColour* c, const glColour* lc )
1072 {
1073    GLshort lines[4][2];
1074    glColour colours[4];
1075 
1076    /* Set shade model. */
1077    glShadeModel( (lc==NULL) ? GL_FLAT : GL_SMOOTH );
1078 
1079    x -= b, w += 2 * b;
1080    y -= b, h += 2 * b;
1081    lc = lc ? lc : c;
1082 
1083    /* Lines. */
1084    lines[0][0]   = x - 1;  /* left-up */
1085    lines[0][1]   = y;
1086    colours[0]    = *lc;
1087 
1088    lines[1][0]   = x;      /* left-down */
1089    lines[1][1]   = y + h;
1090    colours[1]    = *c;
1091 
1092    lines[2][0]   = x + w;  /* right-down */
1093    lines[2][1]   = y + h;
1094    colours[2]    = *c;
1095 
1096    lines[3][0]   = x + w;  /* right-up */
1097    lines[3][1]   = y;
1098    colours[3]    = *lc;
1099 
1100    /* Upload to the VBO. */
1101    gl_vboSubData( toolkit_vbo, 0, sizeof(lines), lines );
1102    gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset, sizeof(colours), colours );
1103 
1104    /* Set up the VBO. */
1105    gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );
1106    gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1107          toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1108 
1109    /* Draw the VBO. */
1110    glDrawArrays( GL_LINE_LOOP, 0, 4 );
1111 
1112    /* Deactivate VBO. */
1113    gl_vboDeactivate();
1114 }
1115 /**
1116  * @brief Draws a rectangle.
1117  *
1118  * If lc is NULL, colour will be flat.
1119  *
1120  *    @param x X position to draw at.
1121  *    @param y Y position to draw at.
1122  *    @param w Width.
1123  *    @param h Height.
1124  *    @param c Colour.
1125  *    @param lc Light colour.
1126  */
toolkit_drawRect(int x,int y,int w,int h,const glColour * c,const glColour * lc)1127 void toolkit_drawRect( int x, int y, int w, int h,
1128       const glColour* c, const glColour* lc )
1129 {
1130    GLshort vertex[4][2];
1131    glColour colours[4];
1132 
1133    /* Set shade model. */
1134    glShadeModel( (lc) ? GL_SMOOTH : GL_FLAT );
1135 
1136    lc = lc == NULL ? c : lc;
1137 
1138    /* Set up vertices and colours. */
1139    vertex[0][0] = x;        /* left-up */
1140    vertex[0][1] = y;
1141    colours[0]   = *c;
1142 
1143    vertex[1][0] = x;        /* left-down */
1144    vertex[1][1] = y + h;
1145    colours[1]   = *lc;
1146 
1147    vertex[2][0] = x + w;    /* right-up */
1148    vertex[2][1] = y;
1149    colours[2]   = *c;
1150 
1151    vertex[3][0] = x + w;    /* right-down */
1152    vertex[3][1] = y + h;
1153    colours[3]   = *lc;
1154 
1155    /* Upload to the VBO. */
1156    gl_vboSubData( toolkit_vbo, 0, sizeof(vertex), vertex );
1157    gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset, sizeof(colours), colours );
1158 
1159    /* Set up the VBO. */
1160    gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY,
1161          0, 2, GL_SHORT, 0 );
1162    gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1163          toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1164 
1165    /* Draw the VBO. */
1166    glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1167 
1168    /* Deactivate VBO. */
1169    gl_vboDeactivate();
1170 }
1171 /**
1172  * @brief Draws an alt text.
1173  *
1174  *    @param bx X position to draw at.
1175  *    @param by Y position to draw at.
1176  *    @param alt Text to draw.
1177  */
toolkit_drawAltText(int bx,int by,const char * alt)1178 void toolkit_drawAltText( int bx, int by, const char *alt )
1179 {
1180    double w, h;
1181    double x, y, o;
1182    glColour c;
1183    glColour c2;
1184    int i, l;
1185    char *buf;
1186 
1187    /* Find the first newline. */
1188    i = 0;
1189    while (alt[i] != '\0' && alt[i] != '\n')
1190       i++;
1191 
1192    buf = malloc(i + 1);
1193    strncpy(buf, alt, i);
1194    buf[i] = '\0'; /* Null-terminate. */
1195 
1196    l = gl_printWidthRaw( &gl_smallFont, buf );
1197    free(buf);
1198 
1199    /* Get dimensions, rounding width up to nearest 20 px increment. */
1200    w = CLAMP(160., 240., ceil( l / 20. ) * 20.);
1201    h = gl_printHeightRaw( &gl_smallFont, w, alt );
1202 
1203    /* One check to make bigger. */
1204    if (h > 160. && w < 200.) {
1205       w = 200;
1206       h = gl_printHeightRaw( &gl_smallFont, w, alt );
1207    }
1208 
1209    /* Choose position. */
1210    x = bx + 10.;
1211    y = by - h - gl_smallFont.h - 10.;
1212    if (y < -SCREEN_H/2+10) {
1213       o  = -(SCREEN_H/2 + y) + 10;
1214       y += o;
1215    }
1216 
1217    /* Set colours. */
1218    c.r = cGrey80.r;
1219    c.g = cGrey80.g;
1220    c.b = cGrey80.b;
1221    c.a = 0.8;
1222    c2.r = cGrey30.r;
1223    c2.g = cGrey30.g;
1224    c2.b = cGrey30.b;
1225    c2.a = 0.7;
1226    toolkit_drawRect( x-1, y-5, w+6, h+6, &c2, NULL );
1227    toolkit_drawRect( x-3, y-3, w+6, h+6, &c, NULL );
1228    gl_printTextRaw( &gl_smallFont, w, h, x, y, &cBlack, alt );
1229 }
1230 
1231 
1232 /**
1233  * @brief Renders a window border.
1234  *
1235  *    @param w Window to render
1236  */
window_renderBorder(Window * w)1237 static void window_renderBorder( Window* w )
1238 {
1239    int i;
1240    GLshort cx, cy;
1241    double x, y;
1242    const glColour *lc, *c, *dc, *oc;
1243    GLshort vertex[31*4];
1244    GLfloat colours[31*4];
1245 
1246    /* position */
1247    x = w->x;
1248    y = w->y;
1249 
1250    /* colours */
1251    lc = &cGrey90;
1252    c = &cGrey70;
1253    dc = &cGrey50;
1254    oc = &cGrey30;
1255 
1256    /*
1257     * Case fullscreen.
1258     */
1259    if (window_isFlag( w, WINDOW_FULLSCREEN )) {
1260       /* Background. */
1261       toolkit_drawRect( x, y,          w->w, 0.6*w->h, dc, c );
1262       toolkit_drawRect( x, y+0.6*w->h, w->w, 0.4*w->h, c, NULL );
1263       /* Name. */
1264       gl_printMidRaw( &gl_defFont, w->w,
1265             x,
1266             y + w->h - 20.,
1267             &cBlack, w->name );
1268       return;
1269    }
1270 
1271    /*
1272     * window shaded bg
1273     */
1274    /* main body */
1275    toolkit_drawRect( x+21, y,          w->w-42., 0.6*w->h, dc, c );
1276    toolkit_drawRect( x+21, y+0.6*w->h, w->w-42., 0.4*w->h, c, NULL );
1277 
1278    glShadeModel(GL_SMOOTH);
1279    /* Both sides. */
1280    gl_vboActivateOffset( toolkit_vbo, GL_COLOR_ARRAY,
1281          toolkit_vboColourOffset, 4, GL_FLOAT, 0 );
1282    gl_vboActivateOffset( toolkit_vbo, GL_VERTEX_ARRAY, 0, 2, GL_SHORT, 0 );
1283    /* Colour is shared. */
1284    colours[0] = c->r;
1285    colours[1] = c->g;
1286    colours[2] = c->b;
1287    colours[3] = c->a;
1288    for (i=0; i<7; i++) {
1289       colours[4 + 4*i + 0] = dc->r;
1290       colours[4 + 4*i + 1] = dc->g;
1291       colours[4 + 4*i + 2] = dc->b;
1292       colours[4 + 4*i + 3] = dc->a;
1293    }
1294    for (i=0; i<8; i++) {
1295       colours[32 + 4*i + 0] = c->r;
1296       colours[32 + 4*i + 1] = c->g;
1297       colours[32 + 4*i + 2] = c->b;
1298       colours[32 + 4*i + 3] = c->a;
1299    }
1300    gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset,
1301          sizeof(GLfloat) * 4*16, colours );
1302    /* Left side vertex. */
1303    cx = x;
1304    cy = y;
1305    vertex[0]  = cx + 21;
1306    vertex[1]  = cy + 0.6*w->h;
1307    vertex[2]  = cx + 21;
1308    vertex[3]  = cy;
1309    vertex[4]  = cx + 15;
1310    vertex[5]  = cy + 1;
1311    vertex[6]  = cx + 10;
1312    vertex[7]  = cy + 3;
1313    vertex[8]  = cx + 6;
1314    vertex[9]  = cy + 6;
1315    vertex[10] = cx + 3;
1316    vertex[11] = cy + 10;
1317    vertex[12] = cx + 1;
1318    vertex[13] = cy + 15;
1319    vertex[14] = cx;
1320    vertex[15] = cy + 21;
1321    vertex[16] = cx;
1322    vertex[17] = cy + 0.6*w->h;
1323    vertex[18] = cx;
1324    cy = y + w->h;
1325    vertex[19] = cy - 21;
1326    vertex[20] = cx + 1;
1327    vertex[21] = cy - 15;
1328    vertex[22] = cx + 3;
1329    vertex[23] = cy - 10;
1330    vertex[24] = cx + 6;
1331    vertex[25] = cy - 6;
1332    vertex[26] = cx + 10;
1333    vertex[27] = cy - 3;
1334    vertex[28] = cx + 15;
1335    vertex[29] = cy - 1;
1336    vertex[30] = cx + 21;
1337    vertex[31] = cy;
1338    gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*16, vertex );
1339    glDrawArrays( GL_TRIANGLE_FAN, 0, 16 );
1340    /* Right side vertex. */
1341    cx = x + w->w;
1342    cy = y;
1343    vertex[0]  = cx - 21;
1344    vertex[1]  = cy + 0.6*w->h;
1345    vertex[2]  = cx - 21;
1346    vertex[3]  = cy;
1347    vertex[4]  = cx - 15;
1348    vertex[5]  = cy + 1;
1349    vertex[6]  = cx - 10;
1350    vertex[7]  = cy + 3;
1351    vertex[8]  = cx - 6;
1352    vertex[9]  = cy + 6;
1353    vertex[10] = cx - 3;
1354    vertex[11] = cy + 10;
1355    vertex[12] = cx - 1;
1356    vertex[13] = cy + 15;
1357    vertex[14] = cx;
1358    vertex[15] = cy + 21;
1359    vertex[16] = cx;
1360    vertex[17] = cy + 0.6*w->h;
1361    vertex[18] = cx;
1362    cy = y + w->h;
1363    vertex[19] = cy - 21;
1364    vertex[20] = cx - 1;
1365    vertex[21] = cy - 15;
1366    vertex[22] = cx - 3;
1367    vertex[23] = cy - 10;
1368    vertex[24] = cx - 6;
1369    vertex[25] = cy - 6;
1370    vertex[26] = cx - 10;
1371    vertex[27] = cy - 3;
1372    vertex[28] = cx - 15;
1373    vertex[29] = cy - 1;
1374    vertex[30] = cx - 21;
1375    vertex[31] = cy;
1376    gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*16, vertex );
1377    glDrawArrays( GL_TRIANGLE_FAN, 0, 16 );
1378 
1379 
1380    /*
1381     * inner outline
1382     */
1383    /* Colour. */
1384    for (i=0; i<7; i++) {
1385       colours[4*i + 0] = c->r;
1386       colours[4*i + 1] = c->g;
1387       colours[4*i + 2] = c->b;
1388       colours[4*i + 3] = c->a;
1389    }
1390    for (; i<7+16; i++) {
1391       colours[4*i + 0] = lc->r;
1392       colours[4*i + 1] = lc->g;
1393       colours[4*i + 2] = lc->b;
1394       colours[4*i + 3] = lc->a;
1395    }
1396    for (; i<7+16+8; i++) {
1397       colours[4*i + 0] = c->r;
1398       colours[4*i + 1] = c->g;
1399       colours[4*i + 2] = c->b;
1400       colours[4*i + 3] = c->a;
1401    }
1402    gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset,
1403          sizeof(GLfloat) * 4*31, colours );
1404    /* Vertex. */
1405    /* Left side. */
1406    cx = x + 1;
1407    cy = y + 1;
1408    vertex[0]  = cx + 21;
1409    vertex[1]  = cy;
1410    vertex[2]  = cx + 15;
1411    vertex[3]  = cy + 1;
1412    vertex[4]  = cx + 10;
1413    vertex[5]  = cy + 3;
1414    vertex[6]  = cx + 6;
1415    vertex[7]  = cy + 6;
1416    vertex[8]  = cx + 3;
1417    vertex[9]  = cy + 10;
1418    vertex[10] = cx + 1;
1419    vertex[11] = cy + 15;
1420    vertex[12] = cx;
1421    vertex[13] = cy + 21;
1422    vertex[14] = cx;
1423    vertex[15] = cy + 0.6*w->h - 1;
1424    cy = y + w->h - 1;
1425    vertex[16] = cx;
1426    vertex[17] = cy - 21;
1427    vertex[18] = cx + 1;
1428    vertex[19] = cy - 15;
1429    vertex[20] = cx + 3;
1430    vertex[21] = cy - 10;
1431    vertex[22] = cx + 6;
1432    vertex[23] = cy - 6;
1433    vertex[24] = cx + 10;
1434    vertex[25] = cy - 3;
1435    vertex[26] = cx + 15;
1436    vertex[27] = cy - 1;
1437    vertex[28] = cx + 21;
1438    vertex[29] = cy;
1439    /* Right side via top. */
1440    cx = x + w->w - 1;
1441    cy = y + w->h - 1;
1442    vertex[30] = cx - 21;
1443    vertex[31] = cy;
1444    vertex[32] = cx - 15;
1445    vertex[33] = cy - 1;
1446    vertex[34] = cx - 10;
1447    vertex[35] = cy - 3;
1448    vertex[36] = cx - 6;
1449    vertex[37] = cy - 6;
1450    vertex[38] = cx - 3;
1451    vertex[39] = cy - 10;
1452    vertex[40] = cx - 1;
1453    vertex[41] = cy - 15;
1454    vertex[42] = cx;
1455    vertex[43] = cy - 21;
1456    cy = y + 1;
1457    vertex[44] = cx;
1458    vertex[45] = cy + 0.6*w->h - 1;
1459    vertex[46] = cx;
1460    vertex[47] = cy + 21;
1461    vertex[48] = cx - 1;
1462    vertex[49] = cy + 15;
1463    vertex[50] = cx - 3;
1464    vertex[51] = cy + 10;
1465    vertex[52] = cx - 6;
1466    vertex[53] = cy + 6;
1467    vertex[54] = cx - 10;
1468    vertex[55] = cy + 3;
1469    vertex[56] = cx - 15;
1470    vertex[57] = cy + 1;
1471    vertex[58] = cx - 21;
1472    vertex[59] = cy;
1473    cx = x + 1;
1474    cy = y + 1;
1475    vertex[60] = cx + 21;
1476    vertex[61] = cy;
1477    gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*31, vertex );
1478    glDrawArrays( GL_LINE_LOOP, 0, 31 );
1479 
1480 
1481    /*
1482     * outer outline
1483     */
1484    glShadeModel(GL_FLAT);
1485    /* Colour. */
1486    for (i=0; i<31; i++) {
1487       colours[4*i + 0] = oc->r;
1488       colours[4*i + 1] = oc->g;
1489       colours[4*i + 2] = oc->b;
1490       colours[4*i + 3] = oc->a;
1491    }
1492    gl_vboSubData( toolkit_vbo, toolkit_vboColourOffset,
1493          sizeof(GLfloat) * 4*31, colours );
1494    /* Vertex. */
1495    /* Left side. */
1496    cx = x;
1497    cy = y;
1498    vertex[0]  = cx + 21;
1499    vertex[1]  = cy;
1500    vertex[2]  = cx + 15;
1501    vertex[3]  = cy + 1;
1502    vertex[4]  = cx + 10;
1503    vertex[5]  = cy + 3;
1504    vertex[6]  = cx + 6;
1505    vertex[7]  = cy + 6;
1506    vertex[8]  = cx + 3;
1507    vertex[9]  = cy + 10;
1508    vertex[10] = cx + 1;
1509    vertex[11] = cy + 15;
1510    vertex[12] = cx;
1511    vertex[13] = cy + 21;
1512    vertex[14] = cx;
1513    vertex[15] = cy + 0.6*w->h;
1514    cy = y + w->h;
1515    vertex[16] = cx;
1516    vertex[17] = cy - 21;
1517    vertex[18] = cx + 1;
1518    vertex[19] = cy - 15;
1519    vertex[20] = cx + 3;
1520    vertex[21] = cy - 10;
1521    vertex[22] = cx + 6;
1522    vertex[23] = cy - 6;
1523    vertex[24] = cx + 10;
1524    vertex[25] = cy - 3;
1525    vertex[26] = cx + 15;
1526    vertex[27] = cy - 1;
1527    vertex[28] = cx + 21;
1528    vertex[29] = cy;
1529    /* Right side via top. */
1530    cx = x + w->w;
1531    cy = y + w->h;
1532    vertex[30] = cx - 21;
1533    vertex[31] = cy;
1534    vertex[32] = cx - 15;
1535    vertex[33] = cy - 1;
1536    vertex[34] = cx - 10;
1537    vertex[35] = cy - 3;
1538    vertex[36] = cx - 6;
1539    vertex[37] = cy - 6;
1540    vertex[38] = cx - 3;
1541    vertex[39] = cy - 10;
1542    vertex[40] = cx - 1;
1543    vertex[41] = cy - 15;
1544    vertex[42] = cx;
1545    vertex[43] = cy - 21;
1546    cy = y;
1547    vertex[44] = cx;
1548    vertex[45] = cy + 0.6*w->h;
1549    vertex[46] = cx;
1550    vertex[47] = cy + 21;
1551    vertex[48] = cx - 1;
1552    vertex[49] = cy + 15;
1553    vertex[50] = cx - 3;
1554    vertex[51] = cy + 10;
1555    vertex[52] = cx - 6;
1556    vertex[53] = cy + 6;
1557    vertex[54] = cx - 10;
1558    vertex[55] = cy + 3;
1559    vertex[56] = cx - 15;
1560    vertex[57] = cy + 1;
1561    vertex[58] = cx - 21;
1562    vertex[59] = cy;
1563    cx = x;
1564    cy = y;
1565    vertex[60] = cx + 21;
1566    vertex[61] = cy;
1567    gl_vboSubData( toolkit_vbo, 0, sizeof(GLshort) * 2*31, vertex );
1568    glDrawArrays( GL_LINE_LOOP, 0, 31 );
1569 
1570    /* Clean up. */
1571    gl_vboDeactivate();
1572 
1573    /*
1574     * render window name
1575     */
1576    gl_printMidRaw( &gl_defFont, w->w,
1577          x,
1578          y + w->h - 20.,
1579          &cBlack, w->name );
1580 }
1581 
1582 
1583 /**
1584  * @brief Renders a window.
1585  *
1586  *    @param w Window to render.
1587  */
window_render(Window * w)1588 void window_render( Window *w )
1589 {
1590    double x, y, wid, hei;
1591    Widget *wgt;
1592 
1593    /* Do not render dead windows. */
1594    if (window_isFlag( w, WINDOW_KILL ))
1595       return;
1596 
1597    /* position */
1598    x = w->x;
1599    y = w->y;
1600 
1601    /* See if needs border. */
1602    if (!window_isFlag( w, WINDOW_NOBORDER ))
1603       window_renderBorder(w);
1604 
1605    /*
1606     * widgets
1607     */
1608    for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next)
1609       if (wgt->render != NULL)
1610          wgt->render( wgt, x, y );
1611 
1612    /*
1613     * focused widget
1614     */
1615    if (w->focus != -1) {
1616       wgt = toolkit_getFocus( w );
1617       if (wgt == NULL)
1618          return;
1619       x  += wgt->x;
1620       y  += wgt->y;
1621       wid = wgt->w;
1622       hei = wgt->h;
1623       toolkit_drawOutline( x, y, wid, hei, 3, &cBlack, NULL );
1624    }
1625 }
1626 
1627 
1628 /**
1629  * @brief Renders the window overlays.
1630  *
1631  *    @param w Window to render overlays of.
1632  */
window_renderOverlay(Window * w)1633 void window_renderOverlay( Window *w )
1634 {
1635    double x, y;
1636    Widget *wgt;
1637 
1638    /* Do not render dead windows. */
1639    if (window_isFlag( w, WINDOW_KILL ))
1640       return;
1641 
1642    /* position */
1643    x = w->x;
1644    y = w->y;
1645 
1646    /*
1647     * overlays
1648     */
1649    for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next)
1650       if (wgt->renderOverlay != NULL)
1651          wgt->renderOverlay( wgt, x, y );
1652 }
1653 
1654 
1655 /**
1656  * @brief Draws a scrollbar.
1657  *
1658  *    @param x X position of scrollbar.
1659  *    @param y Y position of scrollbar.
1660  *    @param w Width of the scrollbar.
1661  *    @param h Height of the scrollbar.
1662  *    @param pos Position at [0:1].
1663  */
toolkit_drawScrollbar(int x,int y,int w,int h,double pos)1664 void toolkit_drawScrollbar( int x, int y, int w, int h, double pos )
1665 {
1666    double sy;
1667 
1668    /* scrollbar background */
1669    toolkit_drawRect( x, y, w, h, toolkit_colDark, toolkit_col );
1670    /* toolkit_drawOutline( x, y, w, h,  0., toolkit_colDark, NULL ); */
1671    /* toolkit_drawOutline( x, y, w, h, 0., toolkit_colLight, toolkit_col ); */
1672 
1673    /* Bar itself. */
1674    sy = y + (h - 30.) * (1.-pos);
1675    toolkit_drawRect( x, sy, w, 30., toolkit_colLight, toolkit_col );
1676    toolkit_drawOutline( x + 1, sy, w - 1, 30., 0., toolkit_colDark, NULL );
1677 }
1678 
1679 
1680 /**
1681  * @brief Renders the windows.
1682  */
toolkit_render(void)1683 void toolkit_render (void)
1684 {
1685    Window *w;
1686 
1687    /* Render base. */
1688    for (w = windows; w!=NULL; w = w->next) {
1689       if (!window_isFlag(w, WINDOW_NORENDER) &&
1690             !window_isFlag(w, WINDOW_KILL)) {
1691          window_render(w);
1692          window_renderOverlay(w);
1693       }
1694    }
1695 }
1696 
1697 
1698 /**
1699  * @brief Toolkit input handled here.
1700  *
1701  *    @param event Event to handle.
1702  *    @return 1 if input was used, 0 if it wasn't.
1703  */
toolkit_input(SDL_Event * event)1704 int toolkit_input( SDL_Event* event )
1705 {
1706    Window *wdw;
1707 
1708    /* Get window that can be focused. */
1709    wdw = toolkit_getActiveWindow();
1710    if (wdw == NULL)
1711       return 0;
1712 
1713    /* Pass event to window. */
1714    return toolkit_inputWindow( wdw, event, 1 );
1715 }
1716 
1717 
1718 /**
1719  * @brief Toolkit window input is handled here.
1720  */
toolkit_inputWindow(Window * wdw,SDL_Event * event,int purge)1721 int toolkit_inputWindow( Window *wdw, SDL_Event *event, int purge )
1722 {
1723    int ret;
1724    Widget *wgt;
1725    ret = 0;
1726 
1727    /* See if widget needs event. */
1728    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
1729       if (wgt_isFlag( wgt, WGT_FLAG_RAWINPUT )) {
1730          if (wgt->rawevent != NULL) {
1731             ret = wgt->rawevent( wgt, event );
1732             if (ret != 0)
1733                return ret;
1734          }
1735       }
1736    }
1737 
1738    /* Event handler. */
1739    if (wdw->eventevent != NULL)
1740       wdw->eventevent( wdw->id, event );
1741 
1742    /* Hack in case window got destroyed in eventevent. */
1743    if (!window_isFlag(wdw, WINDOW_KILL)) {
1744 
1745       /* Pass it on. */
1746       switch (event->type) {
1747          case SDL_MOUSEMOTION:
1748          case SDL_MOUSEBUTTONDOWN:
1749          case SDL_MOUSEBUTTONUP:
1750 #if SDL_VERSION_ATLEAST(2,0,0)
1751          case SDL_MOUSEWHEEL:
1752 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1753             ret |= toolkit_mouseEvent(wdw, event);
1754             break;
1755 
1756          case SDL_KEYDOWN:
1757          case SDL_KEYUP:
1758             ret |= toolkit_keyEvent(wdw, event);
1759             break;
1760 
1761 #if SDL_VERSION_ATLEAST(2,0,0)
1762          case SDL_TEXTINPUT:
1763             ret |= toolkit_textEvent(wdw, event);
1764             break;
1765          case SDL_TEXTEDITING:
1766             break;
1767 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1768       }
1769    }
1770 
1771    /* Clean up the dead if needed. */
1772    if (purge && !dialogue_isOpen()) { /* Hack, since dialogues use secondary loop. */
1773       if (toolkit_delayCounter > 0)
1774          toolkit_delayCounter--;
1775       else
1776          toolkit_purgeDead();
1777    }
1778 
1779    return ret; /* don't block input */
1780 }
1781 
1782 
1783 /**
1784  * @brief Translates the mouse coordinates.
1785  *
1786  *    @param w Window to translate coords for.
1787  *    @param event Event to translate coords.
1788  *    @param[out] x Resulting X coord in window space.
1789  *    @param[out] y Resulting Y coord in window space.
1790  *    @param[out] rx Relative X movement (only valid for motion).
1791  *    @param[out] ry Relative Y movement (only valid for motion).
1792  *    @return The type of the event.
1793  */
toolkit_inputTranslateCoords(Window * w,SDL_Event * event,int * x,int * y,int * rx,int * ry)1794 Uint32 toolkit_inputTranslateCoords( Window *w, SDL_Event *event,
1795       int *x, int *y, int *rx, int *ry )
1796 {
1797    /* Extract the position as event. */
1798    if (event->type==SDL_MOUSEMOTION) {
1799       *x = event->motion.x;
1800       *y = event->motion.y;
1801    }
1802    else if ((event->type==SDL_MOUSEBUTTONDOWN) || (event->type==SDL_MOUSEBUTTONUP)) {
1803       *x = event->button.x;
1804       *y = event->button.y;
1805    }
1806 #if SDL_VERSION_ATLEAST(2,0,0)
1807    else if (event->type == SDL_MOUSEWHEEL)
1808       SDL_GetMouseState( x, y );
1809 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1810 
1811    /* Translate offset. */
1812    gl_windowToScreenPos( x, y, *x, *y );
1813 
1814    /* Transform to relative to window. */
1815    *x -= w->x;
1816    *y -= w->y;
1817 
1818    /* Relative only matter if mouse motion. */
1819    if (event->type==SDL_MOUSEMOTION) {
1820       *ry = (double)event->motion.yrel * gl_screen.mxscale;
1821       *rx = (double)event->motion.xrel * gl_screen.myscale;
1822    }
1823    else {
1824       *ry = 0;
1825       *rx = 0;
1826    }
1827 
1828    return event->type;
1829 }
1830 
1831 
1832 /**
1833  * @brief Handles the mouse events.
1834  *
1835  *    @param wdw Window receiving the mouse event.
1836  *    @param event Mouse event to handle.
1837  */
toolkit_mouseEvent(Window * w,SDL_Event * event)1838 static int toolkit_mouseEvent( Window *w, SDL_Event* event )
1839 {
1840    Widget *wgt;
1841    int x, y, rx, ry, ret;
1842 
1843    /* Translate mouse coords. */
1844    toolkit_inputTranslateCoords( w, event, &x, &y, &rx, &ry );
1845 
1846    /* Check each widget. */
1847    ret = 0;
1848    for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next) {
1849 
1850       /* custom widgets take it from here */
1851       if (wgt->type==WIDGET_CUST) {
1852          if (wgt->dat.cst.mouse)
1853             ret |= wgt->dat.cst.mouse( w->id, event, x-wgt->x, y-wgt->y, wgt->w, wgt->h,
1854                   wgt->dat.cst.userdata );
1855       }
1856       else
1857          ret |= toolkit_mouseEventWidget( w, wgt, event, x, y, rx, ry );
1858    }
1859 
1860    return ret;
1861 }
1862 
1863 
1864 /**
1865  * @brief Handle widget mouse input.
1866  *
1867  *    @param w Window to which widget belongs.
1868  *    @param wgt Widget receiving event.
1869  *    @param event Event received by the window.
1870  */
toolkit_mouseEventWidget(Window * w,Widget * wgt,SDL_Event * event,int x,int y,int rx,int ry)1871 static int toolkit_mouseEventWidget( Window *w, Widget *wgt,
1872       SDL_Event *event, int x, int y, int rx, int ry )
1873 {
1874    int ret, inbounds;
1875    Uint8 button;
1876 
1877    /* Widget translations. */
1878    x -= wgt->x;
1879    y -= wgt->y;
1880 
1881    /* Handle mouse event. */
1882    if (event->type == SDL_MOUSEMOTION)
1883       button = event->motion.state;
1884    else
1885       button = event->button.button;
1886 
1887    /* Check inbounds. */
1888    inbounds = !((x < 0) || (x >= wgt->w) || (y < 0) || (y >= wgt->h));
1889 
1890    /* Regular widgets. */
1891    ret = 0;
1892    switch (event->type) {
1893       case SDL_MOUSEMOTION:
1894          /* Change the status of the widget if mouse isn't down. */
1895 
1896          /* Not scrolling. */
1897          if (wgt->status != WIDGET_STATUS_SCROLLING) {
1898             if (inbounds) {
1899                if (wgt->status != WIDGET_STATUS_MOUSEDOWN)
1900                   wgt->status = WIDGET_STATUS_MOUSEOVER;
1901             }
1902             else
1903                wgt->status = WIDGET_STATUS_NORMAL;
1904          }
1905          else
1906             inbounds = 1; /* Scrolling is always inbounds. */
1907 
1908          /* If always gets the event. */
1909          if (wgt_isFlag( wgt, WGT_FLAG_ALWAYSMMOVE ))
1910             inbounds = 1;
1911 
1912          /* Try to give the event to the widget. */
1913          if (inbounds && (wgt->mmoveevent != NULL))
1914             ret |= (*wgt->mmoveevent)( wgt, x, y, rx, ry );
1915 
1916          break;
1917 
1918 #if SDL_VERSION_ATLEAST(2,0,0)
1919       case SDL_MOUSEWHEEL:
1920          if (!inbounds)
1921             break;
1922 
1923          /* Try to give the event to the widget. */
1924          if (wgt->mwheelevent != NULL)
1925             ret |= (*wgt->mwheelevent)( wgt, event->wheel );
1926 
1927          break;
1928 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
1929 
1930       case SDL_MOUSEBUTTONDOWN:
1931          if (!inbounds)
1932             break;
1933 
1934          /* Update the status. */
1935          if (button == SDL_BUTTON_LEFT)
1936             wgt->status = WIDGET_STATUS_MOUSEDOWN;
1937 
1938          if (toolkit_isFocusable(wgt)) {
1939             toolkit_focusClear( w );
1940             toolkit_focusWidget( w, wgt );
1941          }
1942 
1943          /* Try to give the event to the widget. */
1944          if (wgt->mclickevent != NULL)
1945             ret |= (*wgt->mclickevent)( wgt, button, x, y );
1946          break;
1947 
1948       case SDL_MOUSEBUTTONUP:
1949          /* Since basically only buttons are handled here, we ignore
1950           * it all except the left mouse button. */
1951          if (button != SDL_BUTTON_LEFT)
1952             break;
1953 
1954          if (wgt->status==WIDGET_STATUS_MOUSEDOWN) {
1955             /* Soft-disabled buttons will run anyway. */
1956             if ((wgt->type==WIDGET_BUTTON) && ((wgt->dat.btn.disabled==0) ||
1957                      (wgt->dat.btn.softdisable))) {
1958                if (wgt->dat.btn.fptr==NULL)
1959                   DEBUG("Toolkit: Button '%s' of Window '%s' "
1960                         "doesn't have a function trigger",
1961                         wgt->name, w->name );
1962                else {
1963                   (*wgt->dat.btn.fptr)(w->id, wgt->name);
1964                   ret = 1;
1965                }
1966             }
1967          }
1968 
1969          /* Signal scroll done if necessary. */
1970          if ((wgt->status == WIDGET_STATUS_SCROLLING) && (wgt->scrolldone != NULL))
1971             wgt->scrolldone( wgt );
1972 
1973          /* Always goes normal unless is below mouse. */
1974          if (inbounds)
1975             wgt->status = WIDGET_STATUS_MOUSEOVER;
1976          else
1977             wgt->status = WIDGET_STATUS_NORMAL;
1978 
1979          break;
1980    }
1981 
1982    return ret;
1983 }
1984 
1985 
1986 /**
1987  * @brief Maps modifier keysyms (ctrl, alt, shift) to SDLMods.
1988  *
1989  *    @param key Key to convert.
1990  *    @return The SDLMod corresponding to the key, or 0 if none correspond.
1991  */
toolkit_mapMod(SDLKey key)1992 static SDLMod toolkit_mapMod( SDLKey key )
1993 {
1994    switch(key) {
1995       case SDLK_LCTRL:
1996          return KMOD_LCTRL;
1997       case SDLK_RCTRL:
1998          return KMOD_RCTRL;
1999       case SDLK_LALT:
2000          return KMOD_LALT;
2001       case SDLK_RALT:
2002          return KMOD_RALT;
2003       case SDLK_LSHIFT:
2004          return KMOD_LSHIFT;
2005       case SDLK_RSHIFT:
2006          return KMOD_RSHIFT;
2007       default:
2008          return 0;
2009    }
2010 }
2011 
2012 /**
2013  * @brief Registers a key as down (for key repetition).
2014  *
2015  *    @param key Key to register as down.
2016  */
toolkit_regKey(SDLKey key,SDLKey c)2017 static void toolkit_regKey( SDLKey key, SDLKey c )
2018 {
2019    SDLMod mod;
2020 
2021    /* See if our key is in fact a modifier key, and if it is, convert it to a mod.
2022     * If it is indeed a mod, do not register a new key but add the modifier to the mod mask instead.
2023     */
2024    mod = toolkit_mapMod(key);
2025    if (mod)
2026       input_mod         |= mod;
2027    /* Don't reset values on repeat keydowns. */
2028    else if (input_key != key) {
2029       input_key         = key;
2030       input_keyTime     = SDL_GetTicks();
2031       input_keyCounter  = 0;
2032       input_text        = nstd_checkascii(c) ? c : 0;
2033    }
2034 }
2035 
2036 /**
2037  * @brief Unregisters a key.
2038  *
2039  *    @param key Key to unregister.
2040  */
toolkit_unregKey(SDLKey key)2041 static void toolkit_unregKey( SDLKey key )
2042 {
2043    SDLMod mod;
2044 
2045    /* See if our key is in fact a modifier key, and if it is, convert it to a mod.
2046     * If it is indeed a mod, do not unregister the key but subtract the modifier from the mod mask instead.
2047     */
2048    mod = toolkit_mapMod(key);
2049    if (mod)
2050       input_mod         &= ~mod;
2051    else
2052       toolkit_clearKey();
2053 }
2054 
2055 /**
2056  * @brief Clears the registered keys.
2057  */
toolkit_clearKey(void)2058 void toolkit_clearKey (void)
2059 {
2060    input_key         = 0;
2061    input_keyTime     = 0;
2062    input_keyCounter  = 0;
2063    input_text        = 0;
2064 }
2065 /**
2066  * @brief Handles keyboard events.
2067  *
2068  *    @param wdw Window receiving the key event.
2069  *    @param event Keyboard event to handle.
2070  *    @return 1 if the event is used, 0 if it isn't.
2071  */
toolkit_keyEvent(Window * wdw,SDL_Event * event)2072 static int toolkit_keyEvent( Window *wdw, SDL_Event* event )
2073 {
2074    Widget *wgt;
2075    SDLKey key;
2076    SDLMod mod;
2077    int handled = 0;
2078 #if !SDL_VERSION_ATLEAST(2,0,0)
2079    char buf[2];
2080 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
2081 
2082    /* Event info. */
2083    key = event->key.keysym.sym;
2084    mod = event->key.keysym.mod;
2085 
2086    /* Hack to simulate key repetition */
2087    if (event->type == SDL_KEYDOWN)
2088 #if SDL_VERSION_ATLEAST(2,0,0)
2089       toolkit_regKey(key, key);
2090 #else /* SDL_VERSION_ATLEAST(2,0,0) */
2091       toolkit_regKey(key, event->key.keysym.unicode);
2092 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
2093    else if (event->type == SDL_KEYUP)
2094       toolkit_unregKey(key);
2095 
2096    /* See if window is valid. */
2097    if (wdw == NULL)
2098       return 0;
2099 
2100    /* Get widget. */
2101    wgt = toolkit_getFocus( wdw );
2102 
2103    /* We only want keydown from now on. */
2104    if (event->type != SDL_KEYDOWN)
2105       return 0;
2106 
2107    /* Trigger event function if exists. */
2108    if (wgt != NULL) {
2109       if (wgt->keyevent != NULL) {
2110          if (wgt->keyevent( wgt, input_key, input_mod ))
2111             return 1;
2112       }
2113 #if !SDL_VERSION_ATLEAST(2,0,0)
2114       if (wgt->textevent != NULL) {
2115          buf[0] = key & 0x7f;
2116          buf[0] = event->key.keysym.unicode & 0x7f;
2117          buf[1] = '\0';
2118          if ((*wgt->textevent)( wgt, buf ))
2119             return 1;
2120       }
2121 #endif /* !SDL_VERSION_ATLEAST(2,0,0) */
2122    }
2123 
2124    /* Handle button hotkeys. */
2125    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
2126       if ((wgt->type == WIDGET_BUTTON) && (wgt->dat.btn.key != 0) &&
2127             (wgt->dat.btn.key == input_key))
2128          return (wgt->keyevent( wgt, SDLK_RETURN, input_mod ));
2129 
2130    /* Handle other cases where event might be used by the window. */
2131    switch (key) {
2132       case SDLK_RETURN:
2133       case SDLK_KP_ENTER:
2134          if (wdw->accept_fptr != NULL) {
2135             wdw->accept_fptr( wdw->id, wdw->name );
2136             return 1;
2137          }
2138          break;
2139 
2140       case SDLK_ESCAPE:
2141          if (wdw->cancel_fptr != NULL) {
2142             wdw->cancel_fptr( wdw->id, wdw->name );
2143             return 1;
2144          }
2145          break;
2146 
2147       default:
2148          break;
2149    }
2150 
2151    /* Finally the stuff gets passed to the custom key handler if it's defined. */
2152    if (wdw->keyevent != NULL)
2153       handled = (*wdw->keyevent)( wdw->id, input_key, input_mod );
2154 
2155    /* Placed here so it can be overriden in console for tab completion. */
2156    if (!handled && key == SDLK_TAB) {
2157       if (mod & (KMOD_LSHIFT | KMOD_RSHIFT))
2158          toolkit_prevFocus( wdw );
2159       else
2160          toolkit_nextFocus( wdw );
2161    }
2162 
2163    return 0;
2164 }
2165 #if SDL_VERSION_ATLEAST(2,0,0)
toolkit_textEvent(Window * wdw,SDL_Event * event)2166 static int toolkit_textEvent( Window *wdw, SDL_Event* event )
2167 {
2168    Widget *wgt;
2169 
2170    /* See if window is valid. */
2171    if (wdw == NULL)
2172       return 0;
2173 
2174    /* Get widget. */
2175    wgt = toolkit_getFocus( wdw );
2176 
2177    /* Trigger event function if exists. */
2178    if ((wgt != NULL) && (wgt->textevent != NULL)) {
2179       if ((*wgt->textevent)( wgt, event->text.text ))
2180          return 1;
2181    }
2182 
2183    return 0;
2184 }
2185 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
2186 
2187 
2188 /**
2189  * @brief Purges the dead windows.
2190  */
toolkit_purgeDead(void)2191 static void toolkit_purgeDead (void)
2192 {
2193    Window *wdw, *wlast, *wkill;
2194    Widget *wgt, *wgtlast, *wgtkill;
2195 
2196    /* Only clean up if necessary. */
2197    if (!window_dead)
2198       return;
2199 
2200    /* Must be windows. */
2201    if (windows == NULL)
2202       return;
2203 
2204    /* Destroy what is needed. */
2205    wlast = NULL;
2206    wdw   = windows;
2207    while (wdw != NULL) {
2208       if (window_isFlag( wdw, WINDOW_KILL )) {
2209          /* Save target. */
2210          wkill = wdw;
2211          /* Reattach linked list. */
2212          if (wlast == NULL)
2213             windows = wdw->next;
2214          else
2215             wlast->next = wdw->next;
2216          wdw = wlast;
2217          /* Kill target. */
2218          wkill->next = NULL;
2219          window_kill( wkill );
2220       }
2221       else {
2222          wgtlast = NULL;
2223          wgt     = wdw->widgets;
2224          while (wgt != NULL) {
2225             if (wgt_isFlag( wgt, WGT_FLAG_KILL )) {
2226                /* Save target. */
2227                wgtkill = wgt;
2228                /* Reattach linked list. */
2229                if (wgtlast == NULL)
2230                   wdw->widgets  = wgt->next;
2231                else
2232                   wgtlast->next = wgt->next;
2233                wgt = wgtlast;
2234                /* Kill target. */
2235                wgtkill->next = NULL;
2236                widget_kill( wgtkill );
2237             }
2238             /* Save position. */
2239             wgtlast = wgt;
2240             if (wgt == NULL)
2241                wgt = wdw->widgets;
2242             else
2243                wgt = wgt->next;
2244          }
2245       }
2246       /* Save position. */
2247       wlast = wdw;
2248       if (wdw == NULL)
2249          wdw = windows;
2250       else
2251          wdw = wdw->next;
2252    }
2253 
2254    /* Nothing left to purge. */
2255    window_dead = 0;
2256 }
2257 
2258 
2259 /**
2260  * @brief Updates the toolkit input for repeating keys.
2261  */
toolkit_update(void)2262 void toolkit_update (void)
2263 {
2264 #if !SDL_VERSION_ATLEAST(2,0,0)
2265    unsigned int t;
2266    Window *wdw;
2267    Widget *wgt;
2268    char buf[2];
2269    SDL_Event event;
2270    int ret;
2271 #endif /* !SDL_VERSION_ATLEAST(2,0,0) */
2272 
2273    /* Clean up the dead if needed. */
2274    if (!dialogue_isOpen()) { /* Hack, since dialogues use secondary loop. */
2275       if (toolkit_delayCounter > 0)
2276          toolkit_delayCounter--;
2277       else
2278          toolkit_purgeDead();
2279    }
2280 
2281    /* Killed all the windows. */
2282    if (windows == NULL) {
2283       input_mouseHide();
2284       toolkit_open = 0; /* disable toolkit */
2285       if (paused && !player_paused)
2286          unpause_game();
2287       return; /*  No need to handle anything else. */
2288    }
2289 
2290 #if !SDL_VERSION_ATLEAST(2,0,0)
2291    /* Must have a key pressed. */
2292    if (input_key == 0 && input_mod == 0)
2293       return;
2294 
2295    t = SDL_GetTicks();
2296 
2297    /* Should be repeating. */
2298    if (input_keyTime + INPUT_DELAY + input_keyCounter*INPUT_FREQ > t)
2299       return;
2300 
2301    /* Increment counter. */
2302    input_keyCounter++;
2303 
2304    /* Get the window. */
2305    wdw = toolkit_getActiveWindow();
2306    if (wdw == NULL)
2307       return;
2308 
2309    /* See if widget needs event. */
2310    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2311       if (wgt_isFlag( wgt, WGT_FLAG_RAWINPUT )) {
2312          if (wgt->rawevent != NULL) {
2313             event.type           = SDL_KEYDOWN;
2314             event.key.state      = SDL_PRESSED;
2315             event.key.keysym.sym = input_key;
2316             event.key.keysym.mod = input_mod;
2317             event.key.keysym.unicode = (uint8_t)input_text;
2318             ret = wgt->rawevent( wgt, &event );
2319             if (ret != 0)
2320                return;
2321          }
2322       }
2323    }
2324 
2325    /* Handle the focused widget. */
2326    wgt = toolkit_getFocus( wdw );
2327    if ((wgt != NULL) && (wgt->keyevent != NULL))
2328       wgt->keyevent( wgt, input_key, input_mod );
2329 
2330    if ((input_text != 0) && (wgt != NULL) && (wgt->textevent != NULL)) {
2331       buf[0] = input_text;
2332       buf[1] = '\0';
2333       wgt->textevent( wgt, buf );
2334    }
2335 #endif /* !SDL_VERSION_ATLEAST(2,0,0) */
2336 }
2337 
2338 
2339 /**
2340  * @brief Exposes or hides a window and notifies its widgets.
2341  *
2342  *    @param wgt Widget to change exposure of.
2343  *    @param expose Whether exposing or hiding.
2344  */
toolkit_expose(Window * wdw,int expose)2345 static void toolkit_expose( Window *wdw, int expose )
2346 {
2347    Widget *wgt;
2348 
2349    if (expose == wdw->exposed)
2350       return;
2351    else
2352       wdw->exposed = expose;
2353 
2354    if (expose)
2355       toolkit_focusSanitize( wdw );
2356    else
2357       toolkit_focusClear( wdw );
2358 
2359    if (wdw->focus != -1)
2360       return;
2361 
2362    /* Notify widgets (for tabbed children, etc.) */
2363    for (wgt = wdw->widgets; wgt != NULL; wgt = wgt->next)
2364       if (wgt->exposeevent != NULL)
2365          wgt->exposeevent( wgt, expose );
2366 }
2367 
2368 
2369 /**
2370  * @brief Clears the window focus.
2371  */
toolkit_focusClear(Window * wdw)2372 void toolkit_focusClear( Window *wdw )
2373 {
2374    Widget *wgt;
2375 
2376    if (wdw->focus == -1)
2377       return;
2378 
2379    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2380       if (wdw->focus == wgt->id)
2381          toolkit_defocusWidget( wdw, wgt );
2382 
2383       wgt_rmFlag( wgt, WGT_FLAG_FOCUSED );
2384    }
2385 }
2386 
2387 
2388 /**
2389  * @brief Sanitizes the focus of a window.
2390  *
2391  * Makes sure the window has a focusable widget focused.
2392  */
toolkit_focusSanitize(Window * wdw)2393 void toolkit_focusSanitize( Window *wdw )
2394 {
2395    Widget *wgt;
2396 
2397    /* Clear focus. */
2398    toolkit_focusClear( wdw );
2399 
2400    /* No focus is always sane. */
2401    if (wdw->focus == -1)
2402       return;
2403 
2404    /* Check focused widget. */
2405    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2406       if (wdw->focus == wgt->id) {
2407          /* Not focusable. */
2408          if (!toolkit_isFocusable(wgt)) {
2409             wdw->focus = -1;
2410             toolkit_nextFocus( wdw ); /* Get first focus. */
2411          }
2412          else
2413             toolkit_focusWidget( wdw, wgt );
2414 
2415          return;
2416       }
2417    }
2418 }
2419 
2420 
2421 /**
2422  * @brief Focus next widget.
2423  */
toolkit_nextFocus(Window * wdw)2424 void toolkit_nextFocus( Window *wdw )
2425 {
2426    Widget *wgt;
2427    int next;
2428 
2429    /* Clear focus. */
2430    toolkit_focusClear( wdw );
2431 
2432    /* See what to focus. */
2433    next = (wdw->focus == -1);
2434    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2435       if (!toolkit_isFocusable(wgt))
2436          continue;
2437 
2438       if (next) {
2439          toolkit_focusWidget( wdw, wgt );
2440          return;
2441       }
2442       else if (wdw->focus == wgt->id) {
2443          next = 1;
2444       }
2445    }
2446 
2447    /* Focus nothing. */
2448    wdw->focus = -1;
2449    return;
2450 }
2451 
2452 
2453 /**
2454  * @brief Focus previous widget.
2455  */
toolkit_prevFocus(Window * wdw)2456 void toolkit_prevFocus( Window *wdw )
2457 {
2458    Widget *wgt, *prev;
2459 
2460    /* Clear focus. */
2461    toolkit_focusClear( wdw );
2462 
2463    /* See what to focus. */
2464    prev = NULL;
2465    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next) {
2466       if (!toolkit_isFocusable(wgt))
2467          continue;
2468 
2469       /* See if we found the current one. */
2470       if (wdw->focus == wgt->id) {
2471          if (prev == NULL)
2472             wdw->focus = -1;
2473          else
2474             toolkit_focusWidget( wdw, prev );
2475 
2476          return;
2477       }
2478 
2479       /* Store last focusable widget. */
2480       prev = wgt;
2481    }
2482 
2483    /* Focus nothing. */
2484    if (prev == NULL)
2485       wdw->focus = -1;
2486    else
2487       toolkit_focusWidget( wdw, prev );
2488 
2489    return;
2490 }
2491 
2492 
2493 /**
2494  * @brief Focuses a widget in a window.
2495  */
toolkit_focusWidget(Window * wdw,Widget * wgt)2496 void toolkit_focusWidget( Window *wdw, Widget *wgt )
2497 {
2498    if (!toolkit_isFocusable(wgt))
2499       return;
2500 
2501    wdw->focus = wgt->id;
2502    wgt_setFlag( wgt, WGT_FLAG_FOCUSED );
2503    if (wgt->focusGain != NULL)
2504       wgt->focusGain( wgt );
2505 }
2506 
2507 
2508 /**
2509  * @brief Defocuses the focused widget in a window.
2510  */
toolkit_defocusWidget(Window * wdw,Widget * wgt)2511 void toolkit_defocusWidget( Window *wdw, Widget *wgt )
2512 {
2513    if (wdw->focus != wgt->id)
2514       return;
2515 
2516    wgt_rmFlag( wgt, WGT_FLAG_FOCUSED );
2517    if (wgt->focusLose != NULL)
2518       wgt->focusLose( wgt );
2519 }
2520 
2521 
2522 /**
2523  * @brief Checks to see if a widget is focusable.
2524  *
2525  *    @param wgt Widget to check if is focusable.
2526  *    @return 1 if it's focusable, 0 if it isn't.
2527  */
toolkit_isFocusable(Widget * wgt)2528 static int toolkit_isFocusable( Widget *wgt )
2529 {
2530    if (wgt==NULL)
2531       return 0;
2532 
2533    return wgt_isFlag(wgt, WGT_FLAG_CANFOCUS);
2534 }
2535 
2536 
2537 /**
2538  * @brief Gets the active window in the toolkit.
2539  *
2540  *    @return The active window in the toolkit.
2541  */
toolkit_getActiveWindow(void)2542 Window* toolkit_getActiveWindow (void)
2543 {
2544    Window *wdw, *wlast;
2545 
2546    /* Get window that can be focused. */
2547    wlast = NULL;
2548    for (wdw = windows; wdw!=NULL; wdw = wdw->next)
2549       if (!window_isFlag(wdw, WINDOW_NOFOCUS) &&
2550             !window_isFlag(wdw, WINDOW_KILL))
2551          wlast = wdw;
2552    return wlast;
2553 }
2554 
2555 
2556 /**
2557  * @brief Gets the focused widget in a window.
2558  *
2559  *    @param wdw The window to get the focused widget from.
2560  *    @return The focused widget.
2561  */
toolkit_getFocus(Window * wdw)2562 static Widget* toolkit_getFocus( Window *wdw )
2563 {
2564    Widget *wgt;
2565 
2566    /* No focus. */
2567    if (wdw->focus == -1)
2568       return NULL;
2569 
2570    /* Find focus. */
2571    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
2572       if (wdw->focus == wgt->id)
2573          return wgt;
2574 
2575    /* Not found. */
2576    toolkit_focusClear( wdw );
2577    wdw->focus = -1;
2578    return NULL;
2579 }
2580 
2581 
2582 /**
2583  * @brief Sets the focused widget in a window.
2584  *
2585  *    @param wid ID of the window to get widget from.
2586  *    @param name Name of the widget to set focus to.
2587  */
window_setFocus(const unsigned int wid,const char * wgtname)2588 void window_setFocus( const unsigned int wid, const char* wgtname )
2589 {
2590    Window *wdw;
2591    Widget *wgt;
2592 
2593    /* Get window. */
2594    wdw = window_wget(wid);
2595    if (wdw == NULL)
2596       return;
2597 
2598    /* Get widget. */
2599    wgt = window_getwgt(wid,wgtname);
2600    if (wgt == NULL)
2601       return;
2602 
2603    toolkit_focusClear( wdw );
2604    toolkit_focusWidget( wdw, wgt );
2605 }
2606 
2607 
2608 /**
2609  * @brief Gets the focused widget in a window.
2610  *
2611  *    @param wid ID of the window to get widget from.
2612  *    @return The focused widget's name.
2613  */
window_getFocus(const unsigned int wid)2614 char* window_getFocus( const unsigned int wid )
2615 {
2616    Window *wdw;
2617    Widget *wgt;
2618 
2619    /* Get window. */
2620    wdw = window_wget(wid);
2621    if (wdw == NULL)
2622       return NULL;
2623 
2624    /* Find focused widget. */
2625    for (wgt=wdw->widgets; wgt!=NULL; wgt=wgt->next)
2626       if (wgt->id == wdw->focus)
2627          return wgt->name;
2628 
2629    return NULL;
2630 }
2631 
2632 
2633 /**
2634  * @brief Raises a window (causes all other windows to appear below it).
2635  *
2636  *    @param wid Window to raise.
2637  */
window_raise(unsigned int wid)2638 void window_raise( unsigned int wid )
2639 {
2640    Window *wdw, *wtmp, *wprev, *wlast;
2641 
2642    wdw = window_wget(wid);
2643 
2644    /* Not found, or already top of the stack. */
2645    if (wdw == NULL || wdw->next == NULL)
2646       return;
2647 
2648    wprev = NULL;
2649    wlast = NULL;
2650 
2651    for (wtmp = windows; wtmp != NULL; wtmp = wtmp->next)
2652       if (wtmp->next == wdw)
2653          wprev = wtmp;
2654       else if (wtmp->next == NULL)
2655          wlast = wtmp;
2656 
2657    if (wprev != NULL)
2658       wprev->next = wdw->next; /* wdw-1 links to wdw+1 */
2659 
2660    if (wlast != NULL)
2661       wlast->next = wdw;       /* last links to wdw */
2662 
2663    wdw->next   = NULL;      /* wdw becomes new last window */
2664 
2665    wtmp = toolkit_getActiveWindow();
2666 
2667    /* No active window, or window is the same. */
2668    if (wtmp == NULL || wtmp == wdw)
2669       return;
2670 
2671    toolkit_expose( wtmp, 0 ); /* wtmp is hidden */
2672    toolkit_expose( wdw, 1 );  /* wdw is visible */
2673 }
2674 
2675 
2676 /**
2677  * @brief Lowers a window (causes all other windows to appear above it).
2678  *
2679  *    @param wid Window to lower.
2680  */
window_lower(unsigned int wid)2681 void window_lower( unsigned int wid )
2682 {
2683    Window *wdw, *wtmp, *wprev;
2684 
2685    wdw = window_wget(wid);
2686 
2687    /* Not found, or already bottom of the stack. */
2688    if (wdw == NULL || wdw == windows)
2689       return;
2690 
2691    wprev = NULL;
2692    for (wtmp = windows; wtmp != NULL; wtmp = wtmp->next)
2693       if (wtmp->next == wdw)
2694          wprev = wtmp;
2695 
2696    if (wprev != NULL)
2697       wprev->next = wdw->next; /* wdw-1 links to wdw+1 */
2698 
2699    wdw->next   = windows;   /* wdw links to first window */
2700    windows     = wdw;       /* wdw becomes new first window */
2701 
2702    wtmp = toolkit_getActiveWindow();
2703 
2704    /* No active window, or window is the same. */
2705    if (wtmp == NULL || wtmp == wdw)
2706       return;
2707 
2708    toolkit_expose( wtmp, 1 ); /* wtmp is visible */
2709    toolkit_expose( wdw, 0 );  /* wdw is hidden */
2710 }
2711 
2712 
2713 /**
2714  * @brief Repositions windows and their children if resolution changes.
2715  */
toolkit_reposition(void)2716 void toolkit_reposition (void)
2717 {
2718    Window *w, *wtmp;
2719    Widget *wgt;
2720    int i, xorig, yorig, xdiff, ydiff;
2721 
2722    for (w = windows; w != NULL; w = w->next) {
2723       /* Fullscreen windows must always be full size, though their widgets
2724        * don't auto-scale. */
2725       if (window_isFlag( w, WINDOW_FULLSCREEN )) {
2726          w->w = SCREEN_W;
2727          w->h = SCREEN_H;
2728          continue;
2729       }
2730 
2731       /* Skip if position is fixed. */
2732       if (w->xrel == -1. && w->yrel == -1.)
2733          continue;
2734 
2735       xdiff = 0.;
2736       ydiff = 0.;
2737 
2738       if (w->xrel != -1.) {
2739          xorig = w->x;
2740          w->x = (SCREEN_W - w->w) * w->xrel;
2741          xdiff = w->x - xorig;
2742       }
2743 
2744       if (w->yrel != -1.) {
2745          yorig = w->y;
2746          w->y = (SCREEN_H - w->h) * w->yrel;
2747          ydiff = w->y - yorig;
2748       }
2749 
2750       /* Tabwin children aren't in the stack and must be manually updated. */
2751       for (wgt=w->widgets; wgt!=NULL; wgt=wgt->next) {
2752          if (wgt->type != WIDGET_TABBEDWINDOW)
2753             continue;
2754 
2755          for (i=0; i<wgt->dat.tab.ntabs; i++) {
2756             wtmp = window_wget( wgt->dat.tab.windows[i] );
2757             wtmp->x += xdiff;
2758             wtmp->y += ydiff;
2759          }
2760       }
2761    }
2762 }
2763 
2764 
2765 /**
2766  * @brief Initializes the toolkit.
2767  *
2768  *    @return 0 on success.
2769  */
toolkit_init(void)2770 int toolkit_init (void)
2771 {
2772    GLsizei size;
2773 
2774    /* Create the VBO. */
2775    toolkit_vboColourOffset = sizeof(GLshort) * 2 * 31;
2776    size = (sizeof(GLshort)*2 + sizeof(GLfloat)*4) * 31;
2777    toolkit_vbo = gl_vboCreateStream( size, NULL );
2778 
2779    /* Disable the cursor. */
2780    input_mouseHide();
2781 
2782    return 0;
2783 }
2784 
2785 
2786 /**
2787  * @brief Exits the toolkit.
2788  */
toolkit_exit(void)2789 void toolkit_exit (void)
2790 {
2791    Window *wdw;
2792 
2793    /* Destroy the windows. */
2794    while (windows!=NULL) {
2795       wdw      = windows;
2796       windows  = windows->next;
2797       window_kill(wdw);
2798    }
2799    free(windows);
2800 
2801    /* Free the VBO. */
2802    gl_vboDestroy( toolkit_vbo );
2803    toolkit_vbo = NULL;
2804 }
2805 
2806