2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
25 /*---------------------------------------------------------------------------*/
29 #define GUI_TYPE 0xFFFE
62 const GLfloat
*color0
;
63 const GLfloat
*color1
;
68 /*---------------------------------------------------------------------------*/
70 const GLfloat gui_wht
[4] = { 1.0f
, 1.0f
, 1.0f
, 1.0f
};
71 const GLfloat gui_yel
[4] = { 1.0f
, 1.0f
, 0.0f
, 1.0f
};
72 const GLfloat gui_red
[4] = { 1.0f
, 0.0f
, 0.0f
, 1.0f
};
73 const GLfloat gui_grn
[4] = { 0.0f
, 1.0f
, 0.0f
, 1.0f
};
74 const GLfloat gui_blu
[4] = { 0.0f
, 0.0f
, 1.0f
, 1.0f
};
75 const GLfloat gui_blk
[4] = { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
76 const GLfloat gui_gry
[4] = { 0.3f
, 0.3f
, 0.3f
, 1.0f
};
78 /*---------------------------------------------------------------------------*/
80 static struct widget widget
[MAXWIDGET
];
83 static TTF_Font
*font
[3] = { NULL
, NULL
, NULL
};
85 static GLuint digit_text
[3][11];
86 static GLuint digit_list
[3][11];
87 static int digit_w
[3][11];
88 static int digit_h
[3][11];
92 /*---------------------------------------------------------------------------*/
94 static int gui_hot(int id
)
96 return (widget
[id
].type
& GUI_STATE
);
99 /*---------------------------------------------------------------------------*/
101 * Initialize a display list containing a rectangle (x, y, w, h) to
102 * which a rendered-font texture may be applied. Colors c0 and c1
103 * determine the top-to-bottom color gradiant of the text.
106 static GLuint
gui_list(int x
, int y
,
107 int w
, int h
, const float *c0
, const float *c1
)
109 GLuint list
= glGenLists(1);
114 int W
, H
, d
= h
/ 16;
116 /* Assume the applied texture size is rect size rounded to power-of-two. */
118 image_size(&W
, &H
, w
, h
);
120 s0
= 0.5f
* (W
- w
) / W
;
121 t0
= 0.5f
* (H
- h
) / H
;
125 glNewList(list
, GL_COMPILE
);
129 glColor4f(0.0f
, 0.0f
, 0.0f
, 0.5f
);
130 glTexCoord2f(s0
, t1
); glVertex2i(x
+ d
, y
- d
);
131 glTexCoord2f(s1
, t1
); glVertex2i(x
+ w
+ d
, y
- d
);
132 glTexCoord2f(s1
, t0
); glVertex2i(x
+ w
+ d
, y
+ h
- d
);
133 glTexCoord2f(s0
, t0
); glVertex2i(x
+ d
, y
+ h
- d
);
136 glTexCoord2f(s0
, t1
); glVertex2i(x
, y
);
137 glTexCoord2f(s1
, t1
); glVertex2i(x
+ w
, y
);
140 glTexCoord2f(s1
, t0
); glVertex2i(x
+ w
, y
+ h
);
141 glTexCoord2f(s0
, t0
); glVertex2i(x
, y
+ h
);
151 * Initialize a display list containing a rounded-corner rectangle (x,
152 * y, w, h). Generate texture coordinates to properly apply a texture
153 * map to the rectangle as though the corners were not rounded.
156 static GLuint
gui_rect(int x
, int y
, int w
, int h
, int f
, int r
)
158 GLuint list
= glGenLists(1);
163 glNewList(list
, GL_COMPILE
);
165 glBegin(GL_QUAD_STRIP
);
169 for (i
= 0; i
<= n
; i
++)
171 float a
= 0.5f
* V_PI
* (float) i
/ (float) n
;
172 float s
= r
* fsinf(a
);
173 float c
= r
* fcosf(a
);
176 float Ya
= y
+ h
+ ((f
& GUI_NW
) ? (s
- r
) : 0);
177 float Yb
= y
+ ((f
& GUI_SW
) ? (r
- s
) : 0);
179 glTexCoord2f((X
- x
) / w
, 1 - (Ya
- y
) / h
);
182 glTexCoord2f((X
- x
) / w
, 1 - (Yb
- y
) / h
);
186 /* ... Right side. */
188 for (i
= 0; i
<= n
; i
++)
190 float a
= 0.5f
* V_PI
* (float) i
/ (float) n
;
191 float s
= r
* fsinf(a
);
192 float c
= r
* fcosf(a
);
194 float X
= x
+ w
- r
+ s
;
195 float Ya
= y
+ h
+ ((f
& GUI_NE
) ? (c
- r
) : 0);
196 float Yb
= y
+ ((f
& GUI_SE
) ? (r
- c
) : 0);
198 glTexCoord2f((X
- x
) / w
, 1 - (Ya
- y
) / h
);
201 glTexCoord2f((X
- x
) / w
, 1 - (Yb
- y
) / h
);
212 /*---------------------------------------------------------------------------*/
216 const float *c0
= gui_yel
;
217 const float *c1
= gui_red
;
219 int i
, j
, h
= config_get_d(CONFIG_HEIGHT
);
221 /* Initialize font rendering. */
225 memset(widget
, 0, sizeof (struct widget
) * MAXWIDGET
);
227 /* Load small, medium, and large typefaces. */
229 font
[GUI_SML
] = TTF_OpenFont(config_data(GUI_FACE
), h
/ 24);
230 font
[GUI_MED
] = TTF_OpenFont(config_data(GUI_FACE
), h
/ 12);
231 font
[GUI_LRG
] = TTF_OpenFont(config_data(GUI_FACE
), h
/ 6);
234 /* Initialize the global pause GUI. */
236 if ((pause_id
= gui_pause(0)))
237 gui_layout(pause_id
, 0, 0);
239 /* Initialize digit glyphs and lists for counters and clocks. */
241 for (i
= 0; i
< 3; i
++)
245 /* Draw digits 0 throught 9. */
247 for (j
= 0; j
< 10; j
++)
249 text
[0] = '0' + (char) j
;
252 digit_text
[i
][j
] = make_image_from_font(NULL
, NULL
,
256 digit_list
[i
][j
] = gui_list(-digit_w
[i
][j
] / 2,
259 +digit_h
[i
][j
], c0
, c1
);
262 /* Draw the colon for the clock. */
264 digit_text
[i
][j
] = make_image_from_font(NULL
, NULL
,
268 digit_list
[i
][j
] = gui_list(-digit_w
[i
][10] / 2,
271 +digit_h
[i
][10], c0
, c1
);
282 /* Release any remaining widget texture and display list indices. */
284 for (id
= 1; id
< MAXWIDGET
; id
++)
286 if (glIsTexture(widget
[id
].text_img
))
287 glDeleteTextures(1, &widget
[id
].text_img
);
289 if (glIsList(widget
[id
].text_obj
))
290 glDeleteLists(widget
[id
].text_obj
, 1);
291 if (glIsList(widget
[id
].rect_obj
))
292 glDeleteLists(widget
[id
].rect_obj
, 1);
294 widget
[id
].type
= GUI_FREE
;
295 widget
[id
].text_img
= 0;
296 widget
[id
].text_obj
= 0;
297 widget
[id
].rect_obj
= 0;
302 /* Release all digit textures and display lists. */
304 for (i
= 0; i
< 3; i
++)
305 for (j
= 0; j
< 11; j
++)
307 if (glIsTexture(digit_text
[i
][j
]))
308 glDeleteTextures(1, &digit_text
[i
][j
]);
310 if (glIsList(digit_list
[i
][j
]))
311 glDeleteLists(digit_list
[i
][j
], 1);
314 /* Release all loaded fonts and finalize font rendering. */
316 if (font
[GUI_LRG
]) TTF_CloseFont(font
[GUI_LRG
]);
317 if (font
[GUI_MED
]) TTF_CloseFont(font
[GUI_MED
]);
318 if (font
[GUI_SML
]) TTF_CloseFont(font
[GUI_SML
]);
323 /*---------------------------------------------------------------------------*/
325 static int gui_widget(int pd
, int type
)
329 /* Find an unused entry in the widget table. */
331 for (id
= 1; id
< MAXWIDGET
; id
++)
332 if (widget
[id
].type
== GUI_FREE
)
334 /* Set the type and default properties. */
336 widget
[id
].type
= type
;
337 widget
[id
].token
= 0;
338 widget
[id
].value
= 0;
340 widget
[id
].rect
= GUI_NW
| GUI_SW
| GUI_NE
| GUI_SE
;
343 widget
[id
].text_img
= 0;
344 widget
[id
].text_obj
= 0;
345 widget
[id
].rect_obj
= 0;
346 widget
[id
].color0
= gui_wht
;
347 widget
[id
].color1
= gui_wht
;
348 widget
[id
].scale
= 1.0f
;
350 /* Insert the new widget into the parents's widget list. */
355 widget
[id
].cdr
= widget
[pd
].car
;
367 fprintf(stderr
, "Out of widget IDs\n");
372 int gui_harray(int pd
) { return gui_widget(pd
, GUI_HARRAY
); }
373 int gui_varray(int pd
) { return gui_widget(pd
, GUI_VARRAY
); }
374 int gui_hstack(int pd
) { return gui_widget(pd
, GUI_HSTACK
); }
375 int gui_vstack(int pd
) { return gui_widget(pd
, GUI_VSTACK
); }
376 int gui_filler(int pd
) { return gui_widget(pd
, GUI_FILLER
); }
378 /*---------------------------------------------------------------------------*/
380 void gui_set_image(int id
, const char *file
)
382 if (glIsTexture(widget
[id
].text_img
))
383 glDeleteTextures(1, &widget
[id
].text_img
);
385 widget
[id
].text_img
= make_image_from_file(NULL
, NULL
, NULL
, NULL
, file
);
388 void gui_set_label(int id
, const char *text
)
392 if (glIsTexture(widget
[id
].text_img
))
393 glDeleteTextures(1, &widget
[id
].text_img
);
394 if (glIsList(widget
[id
].text_obj
))
395 glDeleteLists(widget
[id
].text_obj
, 1);
397 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
, &w
, &h
,
398 text
, font
[widget
[id
].size
]);
399 widget
[id
].text_obj
= gui_list(-w
/ 2, -h
/ 2, w
, h
,
400 widget
[id
].color0
, widget
[id
].color1
);
403 void gui_set_count(int id
, int value
)
405 widget
[id
].value
= value
;
408 void gui_set_clock(int id
, int value
)
410 widget
[id
].value
= value
;
413 void gui_set_multi(int id
, const char *text
)
422 /* Copy each delimited string to a line buffer. */
424 for (p
= text
, j
= 0; *p
&& j
< 8; j
++)
426 strncpy(s
[j
], p
, (n
= strcspn(p
, "\\")));
429 if (*(p
+= n
) == '\\') p
++;
432 /* Set the label value for each line. */
434 for (i
= j
- 1, jd
= widget
[id
].car
; i
>= 0 && jd
; i
--, jd
= widget
[jd
].cdr
)
435 gui_set_label(jd
, s
[i
]);
438 /*---------------------------------------------------------------------------*/
440 int gui_image(int pd
, const char *file
, int w
, int h
)
444 if ((id
= gui_widget(pd
, GUI_IMAGE
)))
446 widget
[id
].text_img
= make_image_from_file(NULL
, NULL
,
454 int gui_start(int pd
, const char *text
, int size
, int token
, int value
)
458 if ((id
= gui_state(pd
, text
, size
, token
, value
)))
464 int gui_state(int pd
, const char *text
, int size
, int token
, int value
)
468 if ((id
= gui_widget(pd
, GUI_STATE
)))
470 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
,
474 widget
[id
].size
= size
;
475 widget
[id
].token
= token
;
476 widget
[id
].value
= value
;
481 int gui_label(int pd
, const char *text
, int size
, int rect
, const float *c0
,
486 if ((id
= gui_widget(pd
, GUI_LABEL
)))
488 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
,
492 widget
[id
].size
= size
;
493 widget
[id
].color0
= c0
? c0
: gui_yel
;
494 widget
[id
].color1
= c1
? c1
: gui_red
;
495 widget
[id
].rect
= rect
;
500 int gui_count(int pd
, int value
, int size
, int rect
)
504 if ((id
= gui_widget(pd
, GUI_COUNT
)))
506 for (i
= value
; i
; i
/= 10)
507 widget
[id
].w
+= digit_w
[size
][0];
509 widget
[id
].h
= digit_h
[size
][0];
510 widget
[id
].value
= value
;
511 widget
[id
].size
= size
;
512 widget
[id
].color0
= gui_yel
;
513 widget
[id
].color1
= gui_red
;
514 widget
[id
].rect
= rect
;
519 int gui_clock(int pd
, int value
, int size
, int rect
)
523 if ((id
= gui_widget(pd
, GUI_CLOCK
)))
525 widget
[id
].w
= digit_w
[size
][0] * 6;
526 widget
[id
].h
= digit_h
[size
][0];
527 widget
[id
].value
= value
;
528 widget
[id
].size
= size
;
529 widget
[id
].color0
= gui_yel
;
530 widget
[id
].color1
= gui_red
;
531 widget
[id
].rect
= rect
;
536 int gui_space(int pd
)
540 if ((id
= gui_widget(pd
, GUI_SPACE
)))
548 int gui_pause(int pd
)
550 const char *text
= "Paused";
553 if ((id
= gui_widget(pd
, GUI_PAUSE
)))
555 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
,
558 text
, font
[GUI_LRG
]);
559 widget
[id
].color0
= gui_wht
;
560 widget
[id
].color1
= gui_wht
;
561 widget
[id
].value
= 0;
562 widget
[id
].size
= GUI_LRG
;
563 widget
[id
].rect
= GUI_ALL
;
568 /*---------------------------------------------------------------------------*/
570 * Create a multi-line text box using a vertical array of labels.
571 * Parse the text for '\' characters and treat them as line-breaks.
572 * Preserve the rect specifation across the entire array.
575 int gui_multi(int pd
, const char *text
, int size
, int rect
, const float *c0
,
580 if (text
&& (id
= gui_varray(pd
)))
590 /* Copy each delimited string to a line buffer. */
592 for (p
= text
, j
= 0; *p
&& j
< 8; j
++)
594 strncpy(s
[j
], p
, (n
= strcspn(p
, "\\")));
598 if (*(p
+= n
) == '\\') p
++;
601 /* Set the curves for the first and last lines. */
605 r
[0] |= rect
& (GUI_NW
| GUI_NE
);
606 r
[j
- 1] |= rect
& (GUI_SW
| GUI_SE
);
609 /* Create a label widget for each line. */
611 for (i
= 0; i
< j
; i
++)
612 gui_label(id
, s
[i
], size
, r
[i
], c0
, c1
);
617 /*---------------------------------------------------------------------------*/
619 * The bottom-up pass determines the area of all widgets. The minimum
620 * width and height of a leaf widget is given by the size of its
621 * contents. Array and stack widths and heights are computed
622 * recursively from these.
625 static void gui_widget_up(int id
);
627 static void gui_harray_up(int id
)
631 /* Find the widest child width and the highest child height. */
633 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
637 if (widget
[id
].h
< widget
[jd
].h
)
638 widget
[id
].h
= widget
[jd
].h
;
639 if (widget
[id
].w
< widget
[jd
].w
)
640 widget
[id
].w
= widget
[jd
].w
;
645 /* Total width is the widest child width times the child count. */
650 static void gui_varray_up(int id
)
654 /* Find the widest child width and the highest child height. */
656 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
660 if (widget
[id
].h
< widget
[jd
].h
)
661 widget
[id
].h
= widget
[jd
].h
;
662 if (widget
[id
].w
< widget
[jd
].w
)
663 widget
[id
].w
= widget
[jd
].w
;
668 /* Total height is the highest child height times the child count. */
673 static void gui_hstack_up(int id
)
677 /* Find the highest child height. Sum the child widths. */
679 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
683 if (widget
[id
].h
< widget
[jd
].h
)
684 widget
[id
].h
= widget
[jd
].h
;
686 widget
[id
].w
+= widget
[jd
].w
;
690 static void gui_vstack_up(int id
)
694 /* Find the widest child width. Sum the child heights. */
696 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
700 if (widget
[id
].w
< widget
[jd
].w
)
701 widget
[id
].w
= widget
[jd
].w
;
703 widget
[id
].h
+= widget
[jd
].h
;
707 static void gui_paused_up(int id
)
709 /* Store width and height for later use in text rendering. */
711 widget
[id
].x
= widget
[id
].w
;
712 widget
[id
].y
= widget
[id
].h
;
714 /* The pause widget fills the screen. */
716 widget
[id
].w
= config_get_d(CONFIG_WIDTH
);
717 widget
[id
].h
= config_get_d(CONFIG_HEIGHT
);
720 static void gui_button_up(int id
)
722 /* Store width and height for later use in text rendering. */
724 widget
[id
].x
= widget
[id
].w
;
725 widget
[id
].y
= widget
[id
].h
;
727 if (widget
[id
].w
< widget
[id
].h
&& widget
[id
].w
> 0)
728 widget
[id
].w
= widget
[id
].h
;
731 /* Padded text elements look a little nicer. */
733 if (widget
[id
].w
< config_get_d(CONFIG_WIDTH
))
734 widget
[id
].w
+= radius
;
735 if (widget
[id
].h
< config_get_d(CONFIG_HEIGHT
))
736 widget
[id
].h
+= radius
;
739 static void gui_widget_up(int id
)
742 switch (widget
[id
].type
& GUI_TYPE
)
744 case GUI_HARRAY
: gui_harray_up(id
); break;
745 case GUI_VARRAY
: gui_varray_up(id
); break;
746 case GUI_HSTACK
: gui_hstack_up(id
); break;
747 case GUI_VSTACK
: gui_vstack_up(id
); break;
748 case GUI_PAUSE
: gui_paused_up(id
); break;
749 default: gui_button_up(id
); break;
753 /*---------------------------------------------------------------------------*/
755 * The top-down layout pass distributes available area as computed
756 * during the bottom-up pass. Widgets use their area and position to
757 * initialize rendering state.
760 static void gui_widget_dn(int id
, int x
, int y
, int w
, int h
);
762 static void gui_harray_dn(int id
, int x
, int y
, int w
, int h
)
764 int jd
, i
= 0, c
= 0;
771 /* Count children. */
773 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
776 /* Distribute horizontal space evenly to all children. */
778 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
, i
++)
780 int x0
= x
+ i
* w
/ c
;
781 int x1
= x
+ (i
+ 1) * w
/ c
;
783 gui_widget_dn(jd
, x0
, y
, x1
- x0
, h
);
787 static void gui_varray_dn(int id
, int x
, int y
, int w
, int h
)
789 int jd
, i
= 0, c
= 0;
796 /* Count children. */
798 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
801 /* Distribute vertical space evenly to all children. */
803 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
, i
++)
805 int y0
= y
+ i
* h
/ c
;
806 int y1
= y
+ (i
+ 1) * h
/ c
;
808 gui_widget_dn(jd
, x
, y0
, w
, y1
- y0
);
812 static void gui_hstack_dn(int id
, int x
, int y
, int w
, int h
)
814 int jd
, jx
= x
, jw
= 0, c
= 0;
821 /* Measure the total width requested by non-filler children. */
823 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
824 if ((widget
[jd
].type
& GUI_TYPE
) == GUI_FILLER
)
829 /* Give non-filler children their requested space. */
830 /* Distribute the rest evenly among filler children. */
832 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
834 if ((widget
[jd
].type
& GUI_TYPE
) == GUI_FILLER
)
835 gui_widget_dn(jd
, jx
, y
, (w
- jw
) / c
, h
);
837 gui_widget_dn(jd
, jx
, y
, widget
[jd
].w
, h
);
843 static void gui_vstack_dn(int id
, int x
, int y
, int w
, int h
)
845 int jd
, jy
= y
, jh
= 0, c
= 0;
852 /* Measure the total height requested by non-filler children. */
854 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
855 if ((widget
[jd
].type
& GUI_TYPE
) == GUI_FILLER
)
860 /* Give non-filler children their requested space. */
861 /* Distribute the rest evenly among filler children. */
863 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
865 if ((widget
[jd
].type
& GUI_TYPE
) == GUI_FILLER
)
866 gui_widget_dn(jd
, x
, jy
, w
, (h
- jh
) / c
);
868 gui_widget_dn(jd
, x
, jy
, w
, widget
[jd
].h
);
874 static void gui_filler_dn(int id
, int x
, int y
, int w
, int h
)
876 /* Filler expands to whatever size it is given. */
884 static void gui_button_dn(int id
, int x
, int y
, int w
, int h
)
886 /* Recall stored width and height for text rendering. */
888 int W
= widget
[id
].x
;
889 int H
= widget
[id
].y
;
890 int R
= widget
[id
].rect
;
891 int r
= ((widget
[id
].type
& GUI_TYPE
) == GUI_PAUSE
? radius
* 4 : radius
);
893 const float *c0
= widget
[id
].color0
;
894 const float *c1
= widget
[id
].color1
;
901 /* Create display lists for the text area and rounded rectangle. */
903 widget
[id
].text_obj
= gui_list(-W
/ 2, -H
/ 2, W
, H
, c0
, c1
);
904 widget
[id
].rect_obj
= gui_rect(-w
/ 2, -h
/ 2, w
, h
, R
, r
);
907 static void gui_widget_dn(int id
, int x
, int y
, int w
, int h
)
910 switch (widget
[id
].type
& GUI_TYPE
)
912 case GUI_HARRAY
: gui_harray_dn(id
, x
, y
, w
, h
); break;
913 case GUI_VARRAY
: gui_varray_dn(id
, x
, y
, w
, h
); break;
914 case GUI_HSTACK
: gui_hstack_dn(id
, x
, y
, w
, h
); break;
915 case GUI_VSTACK
: gui_vstack_dn(id
, x
, y
, w
, h
); break;
916 case GUI_FILLER
: gui_filler_dn(id
, x
, y
, w
, h
); break;
917 case GUI_SPACE
: gui_filler_dn(id
, x
, y
, w
, h
); break;
918 default: gui_button_dn(id
, x
, y
, w
, h
); break;
922 /*---------------------------------------------------------------------------*/
924 * During GUI layout, we make a bottom-up pass to determine total area
925 * requirements for the widget tree. We position this area to the
926 * sides or center of the screen. Finally, we make a top-down pass to
927 * distribute this area to each widget.
930 void gui_layout(int id
, int xd
, int yd
)
934 int w
, W
= config_get_d(CONFIG_WIDTH
);
935 int h
, H
= config_get_d(CONFIG_HEIGHT
);
943 else if (xd
> 0) x
= (W
- w
);
944 else x
= (W
- w
) / 2;
947 else if (yd
> 0) y
= (H
- h
);
948 else y
= (H
- h
) / 2;
950 gui_widget_dn(id
, x
, y
, w
, h
);
953 int gui_search(int id
, int x
, int y
)
957 /* Search the hierarchy for the widget containing the given point. */
959 if (id
&& (widget
[id
].x
<= x
&& x
< widget
[id
].x
+ widget
[id
].w
&&
960 widget
[id
].y
<= y
&& y
< widget
[id
].y
+ widget
[id
].h
))
965 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
966 if ((kd
= gui_search(jd
, x
, y
)))
973 * Activate a widget, allowing it to behave as a normal state widget.
974 * This may be used to create image buttons, or cause an array of
975 * widgets to behave as a single state widget.
977 int gui_active(int id
, int token
, int value
)
979 widget
[id
].type
|= GUI_STATE
;
980 widget
[id
].token
= token
;
981 widget
[id
].value
= value
;
986 int gui_delete(int id
)
990 /* Recursively delete all subwidgets. */
992 gui_delete(widget
[id
].cdr
);
993 gui_delete(widget
[id
].car
);
995 /* Release any GL resources held by this widget. */
997 if (glIsTexture(widget
[id
].text_img
))
998 glDeleteTextures(1, &widget
[id
].text_img
);
1000 if (glIsList(widget
[id
].text_obj
))
1001 glDeleteLists(widget
[id
].text_obj
, 1);
1002 if (glIsList(widget
[id
].rect_obj
))
1003 glDeleteLists(widget
[id
].rect_obj
, 1);
1005 /* Mark this widget unused. */
1007 widget
[id
].type
= GUI_FREE
;
1008 widget
[id
].text_img
= 0;
1009 widget
[id
].text_obj
= 0;
1010 widget
[id
].rect_obj
= 0;
1017 /*---------------------------------------------------------------------------*/
1019 static void gui_paint_rect(int id
, int st
)
1022 static const GLfloat back
[4][4] = {
1023 { 0.1f
, 0.1f
, 0.1f
, 0.5f
}, /* off and inactive */
1024 { 0.3f
, 0.3f
, 0.3f
, 0.5f
}, /* off and active */
1025 { 0.7f
, 0.3f
, 0.0f
, 0.5f
}, /* on and inactive */
1026 { 1.0f
, 0.7f
, 0.3f
, 0.5f
}, /* on and active */
1029 static const GLfloat back
[4][4] = {
1030 { 0.1f
, 0.1f
, 0.1f
, 0.5f
}, /* off and inactive */
1031 { 0.5f
, 0.5f
, 0.5f
, 0.8f
}, /* off and active */
1032 { 1.0f
, 0.7f
, 0.3f
, 0.5f
}, /* on and inactive */
1033 { 1.0f
, 0.7f
, 0.3f
, 0.8f
}, /* on and active */
1038 /* Use the widget status to determine the background color. */
1041 i
= st
| (((widget
[id
].value
) ? 2 : 0) |
1042 ((id
== active
) ? 1 : 0));
1044 switch (widget
[id
].type
& GUI_TYPE
)
1056 /* Recursively paint all subwidgets. */
1058 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1059 gui_paint_rect(jd
, i
);
1065 /* Draw a leaf's background, colored by widget state. */
1069 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1070 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1072 glColor4fv(back
[i
]);
1073 glCallList(widget
[id
].rect_obj
);
1081 /*---------------------------------------------------------------------------*/
1083 static void gui_paint_text(int id
);
1085 static void gui_paint_array(int id
)
1091 GLfloat cx
= widget
[id
].x
+ widget
[id
].w
/ 2.0f
;
1092 GLfloat cy
= widget
[id
].y
+ widget
[id
].h
/ 2.0f
;
1093 GLfloat ck
= widget
[id
].scale
;
1095 glTranslatef(+cx
, +cy
, 0.0f
);
1096 glScalef(ck
, ck
, ck
);
1097 glTranslatef(-cx
, -cy
, 0.0f
);
1099 /* Recursively paint all subwidgets. */
1101 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1107 static void gui_paint_image(int id
)
1109 /* Draw the widget rect, textured using the image. */
1113 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1114 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1116 glScalef(widget
[id
].scale
,
1120 glBindTexture(GL_TEXTURE_2D
, widget
[id
].text_img
);
1121 glColor4fv(gui_wht
);
1122 glCallList(widget
[id
].rect_obj
);
1127 static void gui_paint_count(int id
)
1129 int j
, i
= widget
[id
].size
;
1133 glColor4fv(gui_wht
);
1135 /* Translate to the widget center, and apply the pulse scale. */
1137 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1138 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1140 glScalef(widget
[id
].scale
,
1144 if (widget
[id
].value
)
1146 /* Translate left by half the total width of the rendered value. */
1148 for (j
= widget
[id
].value
; j
; j
/= 10)
1149 glTranslatef((GLfloat
) +digit_w
[i
][j
% 10] / 2.0f
, 0.0f
, 0.0f
);
1151 glTranslatef((GLfloat
) -digit_w
[i
][0] / 2.0f
, 0.0f
, 0.0f
);
1153 /* Render each digit, moving right after each. */
1155 for (j
= widget
[id
].value
; j
; j
/= 10)
1157 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][j
% 10]);
1158 glCallList(digit_list
[i
][j
% 10]);
1159 glTranslatef((GLfloat
) -digit_w
[i
][j
% 10], 0.0f
, 0.0f
);
1164 /* If the value is zero, just display a zero in place. */
1166 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][0]);
1167 glCallList(digit_list
[i
][0]);
1173 static void gui_paint_clock(int id
)
1175 int i
= widget
[id
].size
;
1176 int mt
= (widget
[id
].value
/ 6000) / 10;
1177 int mo
= (widget
[id
].value
/ 6000) % 10;
1178 int st
= ((widget
[id
].value
% 6000) / 100) / 10;
1179 int so
= ((widget
[id
].value
% 6000) / 100) % 10;
1180 int ht
= ((widget
[id
].value
% 6000) % 100) / 10;
1181 int ho
= ((widget
[id
].value
% 6000) % 100) % 10;
1183 GLfloat dx_large
= (GLfloat
) digit_w
[i
][0];
1184 GLfloat dx_small
= (GLfloat
) digit_w
[i
][0] * 0.75f
;
1188 glColor4fv(gui_wht
);
1190 /* Translate to the widget center, and apply the pulse scale. */
1192 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1193 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1195 glScalef(widget
[id
].scale
,
1199 /* Translate left by half the total width of the rendered value. */
1202 glTranslatef(-2.25f
* dx_large
, 0.0f
, 0.0f
);
1204 glTranslatef(-1.75f
* dx_large
, 0.0f
, 0.0f
);
1206 /* Render the minutes counter. */
1210 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][mt
]);
1211 glCallList(digit_list
[i
][mt
]);
1212 glTranslatef(dx_large
, 0.0f
, 0.0f
);
1215 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][mo
]);
1216 glCallList(digit_list
[i
][mo
]);
1217 glTranslatef(dx_small
, 0.0f
, 0.0f
);
1219 /* Render the colon. */
1221 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][10]);
1222 glCallList(digit_list
[i
][10]);
1223 glTranslatef(dx_small
, 0.0f
, 0.0f
);
1225 /* Render the seconds counter. */
1227 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][st
]);
1228 glCallList(digit_list
[i
][st
]);
1229 glTranslatef(dx_large
, 0.0f
, 0.0f
);
1231 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][so
]);
1232 glCallList(digit_list
[i
][so
]);
1233 glTranslatef(dx_small
, 0.0f
, 0.0f
);
1235 /* Render hundredths counter half size. */
1237 glScalef(0.5f
, 0.5f
, 1.0f
);
1239 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][ht
]);
1240 glCallList(digit_list
[i
][ht
]);
1241 glTranslatef(dx_large
, 0.0f
, 0.0f
);
1243 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][ho
]);
1244 glCallList(digit_list
[i
][ho
]);
1249 static void gui_paint_label(int id
)
1251 /* Draw the widget text box, textured using the glyph. */
1255 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1256 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1258 glScalef(widget
[id
].scale
,
1262 glBindTexture(GL_TEXTURE_2D
, widget
[id
].text_img
);
1263 glCallList(widget
[id
].text_obj
);
1268 static void gui_paint_text(int id
)
1270 switch (widget
[id
].type
& GUI_TYPE
)
1272 case GUI_SPACE
: break;
1273 case GUI_FILLER
: break;
1274 case GUI_HARRAY
: gui_paint_array(id
); break;
1275 case GUI_VARRAY
: gui_paint_array(id
); break;
1276 case GUI_HSTACK
: gui_paint_array(id
); break;
1277 case GUI_VSTACK
: gui_paint_array(id
); break;
1278 case GUI_IMAGE
: gui_paint_image(id
); break;
1279 case GUI_COUNT
: gui_paint_count(id
); break;
1280 case GUI_CLOCK
: gui_paint_clock(id
); break;
1281 default: gui_paint_label(id
); break;
1285 void gui_paint(int id
)
1289 glPushAttrib(GL_LIGHTING_BIT
|
1290 GL_COLOR_BUFFER_BIT
|
1291 GL_DEPTH_BUFFER_BIT
);
1292 config_push_ortho();
1295 glEnable(GL_COLOR_MATERIAL
);
1296 glDisable(GL_LIGHTING
);
1297 glDisable(GL_DEPTH_TEST
);
1299 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1301 glPushAttrib(GL_TEXTURE_BIT
);
1303 glDisable(GL_TEXTURE_2D
);
1304 gui_paint_rect(id
, 0);
1310 config_pop_matrix();
1315 void gui_blank(void)
1317 gui_paint(pause_id
);
1320 /*---------------------------------------------------------------------------*/
1322 void gui_dump(int id
, int d
)
1330 switch (widget
[id
].type
& GUI_TYPE
)
1332 case GUI_HARRAY
: type
= "harray"; break;
1333 case GUI_VARRAY
: type
= "varray"; break;
1334 case GUI_HSTACK
: type
= "hstack"; break;
1335 case GUI_VSTACK
: type
= "vstack"; break;
1336 case GUI_FILLER
: type
= "filler"; break;
1337 case GUI_IMAGE
: type
= "image"; break;
1338 case GUI_LABEL
: type
= "label"; break;
1339 case GUI_COUNT
: type
= "count"; break;
1340 case GUI_CLOCK
: type
= "clock"; break;
1343 for (i
= 0; i
< d
; i
++)
1346 printf("%04d %s\n", id
, type
);
1348 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1349 gui_dump(jd
, d
+ 1);
1353 void gui_pulse(int id
, float k
)
1355 if (id
) widget
[id
].scale
= k
;
1358 void gui_timer(int id
, float dt
)
1364 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1367 if (widget
[id
].scale
- 1.0f
< dt
)
1368 widget
[id
].scale
= 1.0f
;
1370 widget
[id
].scale
-= dt
;
1374 int gui_point(int id
, int x
, int y
)
1376 /* Short-circuit check the current active widget. */
1378 int jd
= gui_search(active
, x
, y
);
1380 /* If not still active, search the hierarchy for a new active widget. */
1383 jd
= gui_search(id
, x
, y
);
1385 /* If the active widget has changed, return the new active id. */
1387 if (jd
== 0 || jd
== active
)
1398 int gui_token(int id
)
1400 return id
? widget
[id
].token
: 0;
1403 int gui_value(int id
)
1405 return id
? widget
[id
].value
: 0;
1408 void gui_toggle(int id
)
1410 widget
[id
].value
= widget
[id
].value
? 0 : 1;
1413 /*---------------------------------------------------------------------------*/
1415 static int gui_vert_test(int id
, int jd
)
1417 /* Determine whether widget id is in vertical contact with widget jd. */
1419 if (id
&& gui_hot(id
) && jd
&& gui_hot(jd
))
1421 int i0
= widget
[id
].x
;
1422 int i1
= widget
[id
].x
+ widget
[id
].w
;
1423 int j0
= widget
[jd
].x
;
1424 int j1
= widget
[jd
].x
+ widget
[jd
].w
;
1426 /* Is widget id's top edge is in contact with jd's bottom edge? */
1428 if (widget
[id
].y
+ widget
[id
].h
== widget
[jd
].y
)
1430 /* Do widgets id and jd overlap horizontally? */
1432 if (j0
<= i0
&& i0
< j1
) return 1;
1433 if (j0
< i1
&& i1
<= j1
) return 1;
1434 if (i0
<= j0
&& j0
< i1
) return 1;
1435 if (i0
< j1
&& j1
<= i1
) return 1;
1441 static int gui_horz_test(int id
, int jd
)
1443 /* Determine whether widget id is in horizontal contact with widget jd. */
1445 if (id
&& gui_hot(id
) && jd
&& gui_hot(jd
))
1447 int i0
= widget
[id
].y
;
1448 int i1
= widget
[id
].y
+ widget
[id
].h
;
1449 int j0
= widget
[jd
].y
;
1450 int j1
= widget
[jd
].y
+ widget
[jd
].h
;
1452 /* Is widget id's right edge in contact with jd's left edge? */
1454 if (widget
[id
].x
+ widget
[id
].w
== widget
[jd
].x
)
1456 /* Do widgets id and jd overlap vertically? */
1458 if (j0
<= i0
&& i0
< j1
) return 1;
1459 if (j0
< i1
&& i1
<= j1
) return 1;
1460 if (i0
<= j0
&& j0
< i1
) return 1;
1461 if (i0
< j1
&& j1
<= i1
) return 1;
1467 /*---------------------------------------------------------------------------*/
1469 static int gui_stick_L(int id
, int dd
)
1473 /* Find a widget to the left of widget dd. */
1475 if (gui_horz_test(id
, dd
))
1478 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1479 if ((kd
= gui_stick_L(jd
, dd
)))
1485 static int gui_stick_R(int id
, int dd
)
1489 /* Find a widget to the right of widget dd. */
1491 if (gui_horz_test(dd
, id
))
1494 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1495 if ((kd
= gui_stick_R(jd
, dd
)))
1501 static int gui_stick_D(int id
, int dd
)
1505 /* Find a widget below widget dd. */
1507 if (gui_vert_test(id
, dd
))
1510 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1511 if ((kd
= gui_stick_D(jd
, dd
)))
1517 static int gui_stick_U(int id
, int dd
)
1521 /* Find a widget above widget dd. */
1523 if (gui_vert_test(dd
, id
))
1526 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1527 if ((kd
= gui_stick_U(jd
, dd
)))
1534 int gui_stick(int id
, int x
, int y
)
1536 /* Flag the axes to prevent uncontrolled scrolling. */
1538 static int xflag
= 1;
1539 static int yflag
= 1;
1543 /* Find a new active widget in the direction of joystick motion. */
1545 if (x
&& -JOY_MID
<= x
&& x
<= +JOY_MID
)
1547 else if (x
< -JOY_MID
&& xflag
&& (jd
= gui_stick_L(id
, active
)))
1549 else if (x
> +JOY_MID
&& xflag
&& (jd
= gui_stick_R(id
, active
)))
1552 if (y
&& -JOY_MID
<= y
&& y
<= +JOY_MID
)
1554 else if (y
< -JOY_MID
&& yflag
&& (jd
= gui_stick_U(id
, active
)))
1556 else if (y
> +JOY_MID
&& yflag
&& (jd
= gui_stick_D(id
, active
)))
1559 /* If the active widget has changed, return the new active id. */
1561 if (jd
== 0 || jd
== active
)
1567 /*---------------------------------------------------------------------------*/