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 /*---------------------------------------------------------------------------*/
60 const GLfloat
*color0
;
61 const GLfloat
*color1
;
66 /*---------------------------------------------------------------------------*/
68 const GLfloat gui_wht
[4] = { 1.0f
, 1.0f
, 1.0f
, 1.0f
};
69 const GLfloat gui_yel
[4] = { 1.0f
, 1.0f
, 0.0f
, 1.0f
};
70 const GLfloat gui_red
[4] = { 1.0f
, 0.0f
, 0.0f
, 1.0f
};
71 const GLfloat gui_grn
[4] = { 0.0f
, 1.0f
, 0.0f
, 1.0f
};
72 const GLfloat gui_blu
[4] = { 0.0f
, 0.0f
, 1.0f
, 1.0f
};
73 const GLfloat gui_blk
[4] = { 0.0f
, 0.0f
, 0.0f
, 1.0f
};
74 const GLfloat gui_gry
[4] = { 0.0f
, 0.0f
, 0.0f
, 0.5f
};
76 /*---------------------------------------------------------------------------*/
78 static struct widget widget
[MAXWIDGET
];
81 static TTF_Font
*font
[3] = { NULL
, NULL
, NULL
};
83 static GLuint digit_text
[3][11];
84 static GLuint digit_list
[3][11];
85 static int digit_w
[3][11];
86 static int digit_h
[3][11];
90 /*---------------------------------------------------------------------------*/
92 * Initialize a display list containing a rectangle (x, y, w, h) to
93 * which a rendered-font texture may be applied. Colors c0 and c1
94 * determine the top-to-bottom color gradiant of the text.
97 static GLuint
gui_list(int x
, int y
,
98 int w
, int h
, const float *c0
, const float *c1
)
100 GLuint list
= glGenLists(1);
105 int W
, H
, d
= h
/ 16;
107 /* Assume the applied texture size is rect size rounded to power-of-two. */
109 image_size(&W
, &H
, w
, h
);
111 s0
= 0.5f
* (W
- w
) / W
;
112 t0
= 0.5f
* (H
- h
) / H
;
116 glNewList(list
, GL_COMPILE
);
120 glColor4f(0.0f
, 0.0f
, 0.0f
, 0.5f
);
121 glTexCoord2f(s0
, t1
); glVertex2i(x
+ d
, y
- d
);
122 glTexCoord2f(s1
, t1
); glVertex2i(x
+ w
+ d
, y
- d
);
123 glTexCoord2f(s1
, t0
); glVertex2i(x
+ w
+ d
, y
+ h
- d
);
124 glTexCoord2f(s0
, t0
); glVertex2i(x
+ d
, y
+ h
- d
);
127 glTexCoord2f(s0
, t1
); glVertex2i(x
, y
);
128 glTexCoord2f(s1
, t1
); glVertex2i(x
+ w
, y
);
131 glTexCoord2f(s1
, t0
); glVertex2i(x
+ w
, y
+ h
);
132 glTexCoord2f(s0
, t0
); glVertex2i(x
, y
+ h
);
142 * Initialize a display list containing a rounded-corner rectangle (x,
143 * y, w, h). Generate texture coordinates to properly apply a texture
144 * map to the rectangle as though the corners were not rounded.
147 static GLuint
gui_rect(int x
, int y
, int w
, int h
, int f
, int r
)
149 GLuint list
= glGenLists(1);
154 glNewList(list
, GL_COMPILE
);
156 glBegin(GL_QUAD_STRIP
);
160 for (i
= 0; i
<= n
; i
++)
162 float a
= 0.5f
* V_PI
* (float) i
/ (float) n
;
163 float s
= r
* fsinf(a
);
164 float c
= r
* fcosf(a
);
167 float Ya
= y
+ h
+ ((f
& GUI_NW
) ? (s
- r
) : 0);
168 float Yb
= y
+ ((f
& GUI_SW
) ? (r
- s
) : 0);
170 glTexCoord2f((X
- x
) / w
, 1 - (Ya
- y
) / h
);
173 glTexCoord2f((X
- x
) / w
, 1 - (Yb
- y
) / h
);
177 /* ... Right side. */
179 for (i
= 0; i
<= n
; i
++)
181 float a
= 0.5f
* V_PI
* (float) i
/ (float) n
;
182 float s
= r
* fsinf(a
);
183 float c
= r
* fcosf(a
);
185 float X
= x
+ w
- r
+ s
;
186 float Ya
= y
+ h
+ ((f
& GUI_NE
) ? (c
- r
) : 0);
187 float Yb
= y
+ ((f
& GUI_SE
) ? (r
- c
) : 0);
189 glTexCoord2f((X
- x
) / w
, 1 - (Ya
- y
) / h
);
192 glTexCoord2f((X
- x
) / w
, 1 - (Yb
- y
) / h
);
203 /*---------------------------------------------------------------------------*/
207 const float *c0
= gui_yel
;
208 const float *c1
= gui_red
;
210 int i
, j
, h
= config_get(CONFIG_HEIGHT
);
212 /* Initialize font rendering. */
216 /* Load small, medium, and large typefaces. */
218 font
[GUI_SML
] = TTF_OpenFont(GUI_FACE
, h
/ 24);
219 font
[GUI_MED
] = TTF_OpenFont(GUI_FACE
, h
/ 12);
220 font
[GUI_LRG
] = TTF_OpenFont(GUI_FACE
, h
/ 6);
223 /* Initialize the global pause GUI. */
225 if ((pause_id
= gui_pause(0)))
226 gui_layout(pause_id
, 0, 0);
228 /* Initialize digit glyphs and lists for counters and clocks. */
230 for (i
= 0; i
< 3; i
++)
234 /* Draw digits 0 throught 9. */
236 for (j
= 0; j
< 10; j
++)
238 text
[0] = '0' + (char) j
;
241 digit_text
[i
][j
] = make_image_from_font(NULL
, NULL
,
245 digit_list
[i
][j
] = gui_list(-digit_w
[i
][j
] / 2,
248 +digit_h
[i
][j
], c0
, c1
);
251 /* Draw the colon for the clock. */
253 digit_text
[i
][j
] = make_image_from_font(NULL
, NULL
,
257 digit_list
[i
][j
] = gui_list(-digit_w
[i
][10] / 2,
260 +digit_h
[i
][10], c0
, c1
);
271 /* Release any remaining widget texture and display list indices. */
273 for (id
= 1; id
< MAXWIDGET
; id
++)
275 if (glIsTexture(widget
[id
].text_img
))
276 glDeleteTextures(1, &widget
[id
].text_img
);
278 if (glIsList(widget
[id
].text_obj
))
279 glDeleteLists(widget
[id
].text_obj
, 1);
280 if (glIsList(widget
[id
].rect_obj
))
281 glDeleteLists(widget
[id
].rect_obj
, 1);
283 widget
[id
].type
= GUI_FREE
;
284 widget
[id
].text_img
= 0;
285 widget
[id
].text_obj
= 0;
286 widget
[id
].rect_obj
= 0;
291 /* Release all digit textures and display lists. */
293 for (i
= 0; i
< 3; i
++)
294 for (j
= 0; j
< 11; j
++)
296 if (glIsTexture(digit_text
[i
][j
]))
297 glDeleteTextures(1, &digit_text
[i
][j
]);
299 if (glIsList(digit_list
[i
][j
]))
300 glDeleteLists(digit_list
[i
][j
], 1);
303 /* Release all loaded fonts and finalize font rendering. */
305 if (font
[GUI_LRG
]) TTF_CloseFont(font
[GUI_LRG
]);
306 if (font
[GUI_MED
]) TTF_CloseFont(font
[GUI_MED
]);
307 if (font
[GUI_SML
]) TTF_CloseFont(font
[GUI_SML
]);
312 /*---------------------------------------------------------------------------*/
314 static int gui_widget(int pd
, int type
)
318 /* Find an unused entry in the widget table. */
320 for (id
= 1; id
< MAXWIDGET
; id
++)
321 if (widget
[id
].type
== GUI_FREE
)
323 /* Set the type and default properties. */
325 widget
[id
].type
= type
;
326 widget
[id
].token
= 0;
327 widget
[id
].value
= 0;
329 widget
[id
].rect
= GUI_NW
| GUI_SW
| GUI_NE
| GUI_SE
;
332 widget
[id
].text_img
= 0;
333 widget
[id
].text_obj
= 0;
334 widget
[id
].rect_obj
= 0;
335 widget
[id
].color0
= gui_wht
;
336 widget
[id
].color1
= gui_wht
;
337 widget
[id
].scale
= 1.0f
;
339 /* Insert the new widget into the parents's widget list. */
344 widget
[id
].cdr
= widget
[pd
].car
;
359 int gui_harray(int pd
) { return gui_widget(pd
, GUI_HARRAY
); }
360 int gui_varray(int pd
) { return gui_widget(pd
, GUI_VARRAY
); }
361 int gui_hstack(int pd
) { return gui_widget(pd
, GUI_HSTACK
); }
362 int gui_vstack(int pd
) { return gui_widget(pd
, GUI_VSTACK
); }
363 int gui_filler(int pd
) { return gui_widget(pd
, GUI_FILLER
); }
365 /*---------------------------------------------------------------------------*/
367 void gui_set_image(int id
, const char *file
)
369 if (glIsTexture(widget
[id
].text_img
))
370 glDeleteTextures(1, &widget
[id
].text_img
);
372 widget
[id
].text_img
= make_image_from_file(NULL
, NULL
, NULL
, NULL
, file
);
375 void gui_set_label(int id
, const char *text
)
379 if (glIsTexture(widget
[id
].text_img
))
380 glDeleteTextures(1, &widget
[id
].text_img
);
381 if (glIsList(widget
[id
].text_obj
))
382 glDeleteLists(widget
[id
].text_obj
, 1);
384 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
, &w
, &h
,
385 text
, font
[widget
[id
].size
]);
386 widget
[id
].text_obj
= gui_list(-w
/ 2, -h
/ 2, w
, h
,
387 widget
[id
].color0
, widget
[id
].color1
);
390 void gui_set_count(int id
, int value
)
392 widget
[id
].value
= value
;
395 void gui_set_clock(int id
, int value
)
397 widget
[id
].value
= value
;
400 void gui_set_multi(int id
, const char *text
)
409 /* Copy each delimited string to a line buffer. */
411 for (p
= text
, j
= 0; *p
&& j
< 8; j
++)
413 strncpy(s
[j
], p
, (n
= strcspn(p
, "\\")));
416 if (*(p
+= n
) == '\\') p
++;
419 /* Set the label value for each line. */
421 for (i
= j
- 1, jd
= widget
[id
].car
; i
>= 0 && jd
; i
--, jd
= widget
[jd
].cdr
)
422 gui_set_label(jd
, s
[i
]);
425 /*---------------------------------------------------------------------------*/
427 int gui_image(int pd
, const char *file
, int w
, int h
)
431 if ((id
= gui_widget(pd
, GUI_IMAGE
)))
433 widget
[id
].text_img
= make_image_from_file(NULL
, NULL
,
441 int gui_start(int pd
, const char *text
, int size
, int token
, int value
)
445 if ((id
= gui_state(pd
, text
, size
, token
, value
)))
451 int gui_state(int pd
, const char *text
, int size
, int token
, int value
)
455 if ((id
= gui_widget(pd
, GUI_STATE
)))
457 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
,
461 widget
[id
].size
= size
;
462 widget
[id
].token
= token
;
463 widget
[id
].value
= value
;
468 int gui_label(int pd
, const char *text
, int size
, int rect
, const float *c0
,
473 if ((id
= gui_widget(pd
, GUI_LABEL
)))
475 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
,
479 widget
[id
].size
= size
;
480 widget
[id
].color0
= c0
? c0
: gui_yel
;
481 widget
[id
].color1
= c1
? c1
: gui_red
;
482 widget
[id
].rect
= rect
;
487 int gui_count(int pd
, int value
, int size
, int rect
)
491 if ((id
= gui_widget(pd
, GUI_COUNT
)))
493 for (i
= value
; i
; i
/= 10)
494 widget
[id
].w
+= digit_w
[size
][0];
496 widget
[id
].h
= digit_h
[size
][0];
497 widget
[id
].value
= value
;
498 widget
[id
].size
= size
;
499 widget
[id
].color0
= gui_yel
;
500 widget
[id
].color1
= gui_red
;
501 widget
[id
].rect
= rect
;
506 int gui_clock(int pd
, int value
, int size
, int rect
)
510 if ((id
= gui_widget(pd
, GUI_CLOCK
)))
512 widget
[id
].w
= digit_w
[size
][0] * 6;
513 widget
[id
].h
= digit_h
[size
][0];
514 widget
[id
].value
= value
;
515 widget
[id
].size
= size
;
516 widget
[id
].color0
= gui_yel
;
517 widget
[id
].color1
= gui_red
;
518 widget
[id
].rect
= rect
;
523 int gui_space(int pd
)
527 if ((id
= gui_widget(pd
, GUI_SPACE
)))
535 int gui_pause(int pd
)
537 const char *text
= "Paused";
540 if ((id
= gui_widget(pd
, GUI_PAUSE
)))
542 widget
[id
].text_img
= make_image_from_font(NULL
, NULL
,
545 text
, font
[GUI_LRG
]);
546 widget
[id
].color0
= gui_wht
;
547 widget
[id
].color1
= gui_wht
;
548 widget
[id
].value
= 0;
549 widget
[id
].size
= GUI_LRG
;
550 widget
[id
].rect
= GUI_ALL
;
555 /*---------------------------------------------------------------------------*/
557 * Create a multi-line text box using a vertical array of labels.
558 * Parse the text for '\' characters and treat them as line-breaks.
559 * Preserve the rect specifation across the entire array.
562 int gui_multi(int pd
, const char *text
, int size
, int rect
, const float *c0
,
567 if (text
&& (id
= gui_varray(pd
)))
577 /* Copy each delimited string to a line buffer. */
579 for (p
= text
, j
= 0; *p
&& j
< 8; j
++)
581 strncpy(s
[j
], p
, (n
= strcspn(p
, "\\")));
585 if (*(p
+= n
) == '\\') p
++;
588 /* Set the curves for the first and last lines. */
592 r
[0] |= rect
& (GUI_NW
| GUI_NE
);
593 r
[j
- 1] |= rect
& (GUI_SW
| GUI_SE
);
596 /* Create a label widget for each line. */
598 for (i
= 0; i
< j
; i
++)
599 gui_label(id
, s
[i
], size
, r
[i
], c0
, c1
);
604 /*---------------------------------------------------------------------------*/
606 * The bottom-up pass determines the area of all widgets. The minimum
607 * width and height of a leaf widget is given by the size of its
608 * contents. Array and stack widths and heights are computed
609 * recursively from these.
612 static void gui_widget_up(int id
);
614 static void gui_harray_up(int id
)
618 /* Find the widest child width and the highest child height. */
620 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
624 if (widget
[id
].h
< widget
[jd
].h
)
625 widget
[id
].h
= widget
[jd
].h
;
626 if (widget
[id
].w
< widget
[jd
].w
)
627 widget
[id
].w
= widget
[jd
].w
;
632 /* Total width is the widest child width times the child count. */
637 static void gui_varray_up(int id
)
641 /* Find the widest child width and the highest child height. */
643 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
647 if (widget
[id
].h
< widget
[jd
].h
)
648 widget
[id
].h
= widget
[jd
].h
;
649 if (widget
[id
].w
< widget
[jd
].w
)
650 widget
[id
].w
= widget
[jd
].w
;
655 /* Total height is the highest child height times the child count. */
660 static void gui_hstack_up(int id
)
664 /* Find the highest child height. Sum the child widths. */
666 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
670 if (widget
[id
].h
< widget
[jd
].h
)
671 widget
[id
].h
= widget
[jd
].h
;
673 widget
[id
].w
+= widget
[jd
].w
;
677 static void gui_vstack_up(int id
)
681 /* Find the widest child width. Sum the child heights. */
683 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
687 if (widget
[id
].w
< widget
[jd
].w
)
688 widget
[id
].w
= widget
[jd
].w
;
690 widget
[id
].h
+= widget
[jd
].h
;
694 static void gui_paused_up(int id
)
696 /* Store width and height for later use in text rendering. */
698 widget
[id
].x
= widget
[id
].w
;
699 widget
[id
].y
= widget
[id
].h
;
701 /* The pause widget fills the screen. */
703 widget
[id
].w
= config_get(CONFIG_WIDTH
);
704 widget
[id
].h
= config_get(CONFIG_HEIGHT
);
707 static void gui_button_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 /* Padded text elements look a little nicer. */
716 if (widget
[id
].w
< config_get(CONFIG_WIDTH
))
717 widget
[id
].w
+= radius
;
718 if (widget
[id
].h
< config_get(CONFIG_HEIGHT
))
719 widget
[id
].h
+= radius
;
722 static void gui_widget_up(int id
)
725 switch (widget
[id
].type
)
727 case GUI_HARRAY
: gui_harray_up(id
); break;
728 case GUI_VARRAY
: gui_varray_up(id
); break;
729 case GUI_HSTACK
: gui_hstack_up(id
); break;
730 case GUI_VSTACK
: gui_vstack_up(id
); break;
731 case GUI_PAUSE
: gui_paused_up(id
); break;
732 default: gui_button_up(id
); break;
736 /*---------------------------------------------------------------------------*/
738 * The top-down layout pass distributes available area as computed
739 * during the bottom-up pass. Widgets use their area and position to
740 * initialize rendering state.
743 static void gui_widget_dn(int id
, int x
, int y
, int w
, int h
);
745 static void gui_harray_dn(int id
, int x
, int y
, int w
, int h
)
747 int jd
, i
= 0, c
= 0;
754 /* Count children. */
756 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
759 /* Distribute horizontal space evenly to all children. */
761 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
, i
++)
763 int x0
= x
+ i
* w
/ c
;
764 int x1
= x
+ (i
+ 1) * w
/ c
;
766 gui_widget_dn(jd
, x0
, y
, x1
- x0
, h
);
770 static void gui_varray_dn(int id
, int x
, int y
, int w
, int h
)
772 int jd
, i
= 0, c
= 0;
779 /* Count children. */
781 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
784 /* Distribute vertical space evenly to all children. */
786 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
, i
++)
788 int y0
= y
+ i
* h
/ c
;
789 int y1
= y
+ (i
+ 1) * h
/ c
;
791 gui_widget_dn(jd
, x
, y0
, w
, y1
- y0
);
795 static void gui_hstack_dn(int id
, int x
, int y
, int w
, int h
)
797 int jd
, jx
= x
, jw
= 0, c
= 0;
804 /* Measure the total width requested by non-filler children. */
806 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
807 if (widget
[jd
].type
== GUI_FILLER
)
812 /* Give non-filler children their requested space. */
813 /* Distribute the rest evenly among filler children. */
815 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
817 if (widget
[jd
].type
== GUI_FILLER
)
818 gui_widget_dn(jd
, jx
, y
, (w
- jw
) / c
, h
);
820 gui_widget_dn(jd
, jx
, y
, widget
[jd
].w
, h
);
826 static void gui_vstack_dn(int id
, int x
, int y
, int w
, int h
)
828 int jd
, jy
= y
, jh
= 0, c
= 0;
835 /* Measure the total height requested by non-filler children. */
837 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
838 if (widget
[jd
].type
== GUI_FILLER
)
843 /* Give non-filler children their requested space. */
844 /* Distribute the rest evenly among filler children. */
846 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
848 if (widget
[jd
].type
== GUI_FILLER
)
849 gui_widget_dn(jd
, x
, jy
, w
, (h
- jh
) / c
);
851 gui_widget_dn(jd
, x
, jy
, w
, widget
[jd
].h
);
857 static void gui_filler_dn(int id
, int x
, int y
, int w
, int h
)
859 /* Filler expands to whatever size it is given. */
867 static void gui_button_dn(int id
, int x
, int y
, int w
, int h
)
869 /* Recall stored width and height for text rendering. */
871 int W
= widget
[id
].x
;
872 int H
= widget
[id
].y
;
873 int R
= widget
[id
].rect
;
874 int r
= (widget
[id
].type
== GUI_PAUSE
? radius
* 4 : radius
);
876 const float *c0
= widget
[id
].color0
;
877 const float *c1
= widget
[id
].color1
;
884 /* Create display lists for the text area and rounded rectangle. */
886 widget
[id
].text_obj
= gui_list(-W
/ 2, -H
/ 2, W
, H
, c0
, c1
);
887 widget
[id
].rect_obj
= gui_rect(-w
/ 2, -h
/ 2, w
, h
, R
, r
);
890 static void gui_widget_dn(int id
, int x
, int y
, int w
, int h
)
893 switch (widget
[id
].type
)
895 case GUI_HARRAY
: gui_harray_dn(id
, x
, y
, w
, h
); break;
896 case GUI_VARRAY
: gui_varray_dn(id
, x
, y
, w
, h
); break;
897 case GUI_HSTACK
: gui_hstack_dn(id
, x
, y
, w
, h
); break;
898 case GUI_VSTACK
: gui_vstack_dn(id
, x
, y
, w
, h
); break;
899 case GUI_FILLER
: gui_filler_dn(id
, x
, y
, w
, h
); break;
900 case GUI_SPACE
: gui_filler_dn(id
, x
, y
, w
, h
); break;
901 default: gui_button_dn(id
, x
, y
, w
, h
); break;
905 /*---------------------------------------------------------------------------*/
907 * During GUI layout, we make a bottom-up pass to determine total area
908 * requirements for the widget tree. We position this area to the
909 * sides or center of the screen. Finally, we make a top-down pass to
910 * distribute this area to each widget.
913 void gui_layout(int id
, int xd
, int yd
)
917 int w
, W
= config_get(CONFIG_WIDTH
);
918 int h
, H
= config_get(CONFIG_HEIGHT
);
926 else if (xd
> 0) x
= (W
- w
);
927 else x
= (W
- w
) / 2;
930 else if (yd
> 0) y
= (H
- h
);
931 else y
= (H
- h
) / 2;
933 gui_widget_dn(id
, x
, y
, w
, h
);
936 int gui_search(int id
, int x
, int y
)
940 /* Search the hierarchy for the widget containing the given point. */
942 if (id
&& (widget
[id
].x
<= x
&& x
< widget
[id
].x
+ widget
[id
].w
&&
943 widget
[id
].y
<= y
&& y
< widget
[id
].y
+ widget
[id
].h
))
945 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
946 if ((kd
= gui_search(jd
, x
, y
)))
949 if (widget
[id
].type
== GUI_STATE
)
955 int gui_delete(int id
)
959 /* Recursively delete all subwidgets. */
961 gui_delete(widget
[id
].cdr
);
962 gui_delete(widget
[id
].car
);
964 /* Release any GL resources held by this widget. */
966 if (glIsTexture(widget
[id
].text_img
))
967 glDeleteTextures(1, &widget
[id
].text_img
);
969 if (glIsList(widget
[id
].text_obj
))
970 glDeleteLists(widget
[id
].text_obj
, 1);
971 if (glIsList(widget
[id
].rect_obj
))
972 glDeleteLists(widget
[id
].rect_obj
, 1);
974 /* Mark this widget unused. */
976 widget
[id
].type
= GUI_FREE
;
977 widget
[id
].text_img
= 0;
978 widget
[id
].text_obj
= 0;
979 widget
[id
].rect_obj
= 0;
986 /*---------------------------------------------------------------------------*/
988 static void gui_paint_rect(int id
)
990 static const GLfloat back
[4][4] = {
991 { 0.1f
, 0.1f
, 0.1f
, 0.5f
}, /* off and inactive */
992 { 0.3f
, 0.3f
, 0.3f
, 0.5f
}, /* off and active */
993 { 0.7f
, 0.3f
, 0.0f
, 0.5f
}, /* on and inactive */
994 { 1.0f
, 0.7f
, 0.3f
, 0.5f
}, /* on and active */
999 switch (widget
[id
].type
)
1011 /* Recursively paint all subwidgets. */
1013 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1020 /* Draw a leaf's background, colored by widget state. */
1026 if (widget
[id
].type
== GUI_STATE
)
1027 i
= (((widget
[id
].value
) ? 2 : 0) +
1028 ((id
== active
) ? 1 : 0));
1030 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1031 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1033 glColor4fv(back
[i
]);
1034 glCallList(widget
[id
].rect_obj
);
1042 /*---------------------------------------------------------------------------*/
1044 static void gui_paint_text(int id
);
1046 static void gui_paint_array(int id
)
1050 /* Recursively paint all subwidgets. */
1052 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1056 static void gui_paint_image(int id
)
1058 /* Draw the widget rect, textured using the image. */
1062 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1063 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1065 glBindTexture(GL_TEXTURE_2D
, widget
[id
].text_img
);
1066 glColor4fv(gui_wht
);
1067 glCallList(widget
[id
].rect_obj
);
1072 static void gui_paint_count(int id
)
1074 int j
, i
= widget
[id
].size
;
1078 glColor4fv(gui_wht
);
1080 /* Translate to the widget center, and apply the pulse scale. */
1082 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1083 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1085 glScalef(widget
[id
].scale
,
1089 if (widget
[id
].value
)
1091 /* Translate left by half the total width of the rendered value. */
1093 for (j
= widget
[id
].value
; j
; j
/= 10)
1094 glTranslatef((GLfloat
) +digit_w
[i
][j
% 10] / 2.0f
, 0.0f
, 0.0f
);
1096 glTranslatef((GLfloat
) -digit_w
[i
][0] / 2.0f
, 0.0f
, 0.0f
);
1098 /* Render each digit, moving right after each. */
1100 for (j
= widget
[id
].value
; j
; j
/= 10)
1102 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][j
% 10]);
1103 glCallList(digit_list
[i
][j
% 10]);
1104 glTranslatef((GLfloat
) -digit_w
[i
][j
% 10], 0.0f
, 0.0f
);
1109 /* If the value is zero, just display a zero in place. */
1111 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][0]);
1112 glCallList(digit_list
[i
][0]);
1118 static void gui_paint_clock(int id
)
1120 int i
= widget
[id
].size
;
1121 int mt
= (widget
[id
].value
/ 6000) / 10;
1122 int mo
= (widget
[id
].value
/ 6000) % 10;
1123 int st
= ((widget
[id
].value
% 6000) / 100) / 10;
1124 int so
= ((widget
[id
].value
% 6000) / 100) % 10;
1125 int ht
= ((widget
[id
].value
% 6000) % 100) / 10;
1126 int ho
= ((widget
[id
].value
% 6000) % 100) % 10;
1128 GLfloat dx_large
= (GLfloat
) digit_w
[i
][0];
1129 GLfloat dx_small
= (GLfloat
) digit_w
[i
][0] * 0.75f
;
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 /* Translate left by half the total width of the rendered value. */
1147 glTranslatef(-2.25f
* dx_large
, 0.0f
, 0.0f
);
1149 glTranslatef(-1.75f
* dx_large
, 0.0f
, 0.0f
);
1151 /* Render the minutes counter. */
1155 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][mt
]);
1156 glCallList(digit_list
[i
][mt
]);
1157 glTranslatef(dx_large
, 0.0f
, 0.0f
);
1160 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][mo
]);
1161 glCallList(digit_list
[i
][mo
]);
1162 glTranslatef(dx_small
, 0.0f
, 0.0f
);
1164 /* Render the colon. */
1166 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][10]);
1167 glCallList(digit_list
[i
][10]);
1168 glTranslatef(dx_small
, 0.0f
, 0.0f
);
1170 /* Render the seconds counter. */
1172 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][st
]);
1173 glCallList(digit_list
[i
][st
]);
1174 glTranslatef(dx_large
, 0.0f
, 0.0f
);
1176 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][so
]);
1177 glCallList(digit_list
[i
][so
]);
1178 glTranslatef(dx_small
, 0.0f
, 0.0f
);
1180 /* Render hundredths counter half size. */
1182 glScalef(0.5f
, 0.5f
, 1.0f
);
1184 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][ht
]);
1185 glCallList(digit_list
[i
][ht
]);
1186 glTranslatef(dx_large
, 0.0f
, 0.0f
);
1188 glBindTexture(GL_TEXTURE_2D
, digit_text
[i
][ho
]);
1189 glCallList(digit_list
[i
][ho
]);
1194 static void gui_paint_label(int id
)
1196 /* Draw the widget text box, textured using the glyph. */
1200 glTranslatef((GLfloat
) (widget
[id
].x
+ widget
[id
].w
/ 2),
1201 (GLfloat
) (widget
[id
].y
+ widget
[id
].h
/ 2), 0.f
);
1203 glScalef(widget
[id
].scale
,
1207 glBindTexture(GL_TEXTURE_2D
, widget
[id
].text_img
);
1208 glCallList(widget
[id
].text_obj
);
1213 static void gui_paint_text(int id
)
1215 switch (widget
[id
].type
)
1217 case GUI_SPACE
: break;
1218 case GUI_FILLER
: break;
1219 case GUI_HARRAY
: gui_paint_array(id
); break;
1220 case GUI_VARRAY
: gui_paint_array(id
); break;
1221 case GUI_HSTACK
: gui_paint_array(id
); break;
1222 case GUI_VSTACK
: gui_paint_array(id
); break;
1223 case GUI_IMAGE
: gui_paint_image(id
); break;
1224 case GUI_COUNT
: gui_paint_count(id
); break;
1225 case GUI_CLOCK
: gui_paint_clock(id
); break;
1226 default: gui_paint_label(id
); break;
1230 void gui_paint(int id
)
1234 glPushAttrib(GL_LIGHTING_BIT
);
1235 glPushAttrib(GL_COLOR_BUFFER_BIT
);
1236 glPushAttrib(GL_DEPTH_BUFFER_BIT
);
1237 config_push_ortho();
1240 glEnable(GL_COLOR_MATERIAL
);
1241 glDisable(GL_LIGHTING
);
1242 glDisable(GL_DEPTH_TEST
);
1244 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1246 glPushAttrib(GL_TEXTURE_BIT
);
1248 glDisable(GL_TEXTURE_2D
);
1255 config_pop_matrix();
1262 void gui_blank(void)
1264 gui_paint(pause_id
);
1267 /*---------------------------------------------------------------------------*/
1269 void gui_dump(int id
, int d
)
1277 switch (widget
[id
].type
)
1279 case GUI_HARRAY
: type
= "harray"; break;
1280 case GUI_VARRAY
: type
= "varray"; break;
1281 case GUI_HSTACK
: type
= "hstack"; break;
1282 case GUI_VSTACK
: type
= "vstack"; break;
1283 case GUI_FILLER
: type
= "filler"; break;
1284 case GUI_STATE
: type
= "state"; break;
1285 case GUI_IMAGE
: type
= "image"; break;
1286 case GUI_LABEL
: type
= "label"; break;
1287 case GUI_COUNT
: type
= "count"; break;
1288 case GUI_CLOCK
: type
= "clock"; break;
1291 for (i
= 0; i
< d
; i
++)
1294 printf("%04d %s\n", id
, type
);
1296 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1297 gui_dump(jd
, d
+ 1);
1301 void gui_pulse(int id
, float k
)
1303 if (id
) widget
[id
].scale
= k
;
1306 void gui_timer(int id
, float dt
)
1312 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1315 if (widget
[id
].scale
- 1.0f
< dt
)
1316 widget
[id
].scale
= 1.0f
;
1318 widget
[id
].scale
-= dt
;
1322 int gui_point(int id
, int x
, int y
)
1324 /* Short-circuit check the current active widget. */
1326 int jd
= gui_search(active
, x
, y
);
1328 /* If not still active, search the hierarchy for a new active widget. */
1331 jd
= gui_search(id
, x
, y
);
1333 /* If the active widget has changed, return the new active id. */
1335 if (jd
== 0 || jd
== active
)
1346 int gui_token(int id
)
1348 return id
? widget
[id
].token
: 0;
1351 int gui_value(int id
)
1353 return id
? widget
[id
].value
: 0;
1356 void gui_toggle(int id
)
1358 widget
[id
].value
= widget
[id
].value
? 0 : 1;
1361 /*---------------------------------------------------------------------------*/
1363 static int gui_vert_test(int id
, int jd
)
1365 /* Determine whether widget id is in vertical contact with widget jd. */
1367 if (id
&& widget
[id
].type
== GUI_STATE
&&
1368 jd
&& widget
[jd
].type
== GUI_STATE
)
1370 int i0
= widget
[id
].x
;
1371 int i1
= widget
[id
].x
+ widget
[id
].w
;
1372 int j0
= widget
[jd
].x
;
1373 int j1
= widget
[jd
].x
+ widget
[jd
].w
;
1375 /* Is widget id's top edge is in contact with jd's bottom edge? */
1377 if (widget
[id
].y
+ widget
[id
].h
== widget
[jd
].y
)
1379 /* Do widgets id and jd overlap horizontally? */
1381 if (j0
<= i0
&& i0
< j1
) return 1;
1382 if (j0
< i1
&& i1
<= j1
) return 1;
1383 if (i0
<= j0
&& j0
< i1
) return 1;
1384 if (i0
< j1
&& j1
<= i1
) return 1;
1390 static int gui_horz_test(int id
, int jd
)
1392 /* Determine whether widget id is in horizontal contact with widget jd. */
1394 if (id
&& widget
[id
].type
== GUI_STATE
&&
1395 jd
&& widget
[jd
].type
== GUI_STATE
)
1397 int i0
= widget
[id
].y
;
1398 int i1
= widget
[id
].y
+ widget
[id
].h
;
1399 int j0
= widget
[jd
].y
;
1400 int j1
= widget
[jd
].y
+ widget
[jd
].h
;
1402 /* Is widget id's right edge in contact with jd's left edge? */
1404 if (widget
[id
].x
+ widget
[id
].w
== widget
[jd
].x
)
1406 /* Do widgets id and jd overlap vertically? */
1408 if (j0
<= i0
&& i0
< j1
) return 1;
1409 if (j0
< i1
&& i1
<= j1
) return 1;
1410 if (i0
<= j0
&& j0
< i1
) return 1;
1411 if (i0
< j1
&& j1
<= i1
) return 1;
1417 /*---------------------------------------------------------------------------*/
1419 static int gui_stick_L(int id
, int dd
)
1423 /* Find a widget to the left of widget dd. */
1425 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1426 if ((kd
= gui_stick_L(jd
, dd
)))
1429 return (gui_horz_test(id
, dd
)) ? id
: 0;
1432 static int gui_stick_R(int id
, int dd
)
1436 /* Find a widget to the right of widget dd. */
1438 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1439 if ((kd
= gui_stick_R(jd
, dd
)))
1442 return (gui_horz_test(dd
, id
)) ? id
: 0;
1445 static int gui_stick_D(int id
, int dd
)
1449 /* Find a widget below widget dd. */
1451 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1452 if ((kd
= gui_stick_D(jd
, dd
)))
1455 return (gui_vert_test(id
, dd
)) ? id
: 0;
1458 static int gui_stick_U(int id
, int dd
)
1462 /* Find a widget above widget dd. */
1464 for (jd
= widget
[id
].car
; jd
; jd
= widget
[jd
].cdr
)
1465 if ((kd
= gui_stick_U(jd
, dd
)))
1468 return (gui_vert_test(dd
, id
)) ? id
: 0;
1472 int gui_stick(int id
, int x
, int y
)
1474 /* Flag the axes to prevent uncontrolled scrolling. */
1476 static int xflag
= 1;
1477 static int yflag
= 1;
1481 /* Find a new active widget in the direction of joystick motion. */
1483 if (-JOY_MID
<= x
&& x
<= +JOY_MID
)
1485 else if (x
< -JOY_MID
&& xflag
&& (jd
= gui_stick_L(id
, active
)))
1487 else if (x
> +JOY_MID
&& xflag
&& (jd
= gui_stick_R(id
, active
)))
1490 if (-JOY_MID
<= y
&& y
<= +JOY_MID
)
1492 else if (y
< -JOY_MID
&& yflag
&& (jd
= gui_stick_U(id
, active
)))
1494 else if (y
> +JOY_MID
&& yflag
&& (jd
= gui_stick_D(id
, active
)))
1497 /* If the active widget has changed, return the new active id. */
1499 if (jd
== 0 || jd
== active
)
1505 /*---------------------------------------------------------------------------*/