1 /***************************************************************************
2                                gui.cpp -- gui
3                              -------------------
4     created              : Fri Aug 13 22:01:33 CEST 1999
5     copyright            : (C) 1999 by Eric Espie
6     email                : torcs@free.fr
7     version              : $Id: gui.cpp,v 1.7.2.8 2014/02/10 18:31:28 berniw Exp $
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 /** @file
20     		This API is used to manage all the menu screens.
21     @author	<a href=mailto:torcs@free.fr>Eric Espie</a>
22     @version	$Id: gui.cpp,v 1.7.2.8 2014/02/10 18:31:28 berniw Exp $
23     @ingroup	gui
24 */
25 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <time.h>
29 
30 #include <tgfclient.h>
31 #include "gui.h"
32 
33 #include <portability.h>
34 
35 tGfuiScreen	*GfuiScreen;	/* current screen */
36 static int	GfuiMouseVisible = 1;
37 tMouseInfo	GfuiMouse;
38 
39 int		GfuiMouseHW = 0;
40 
41 float		GfuiColor[GFUI_COLORNB][4];
42 
43 static int	ScrW, ScrH, ViewW, ViewH;
44 
45 static tdble DelayRepeat;
46 static double LastTimeClick;
47 #define REPEAT1	1.0
48 #define REPEAT2 0.2
49 
50 static void
gfuiColorInit(void)51 gfuiColorInit(void)
52 {
53 	const int BUFSIZE = 1024;
54 	char buf[BUFSIZE];
55 
56 	void *hdle;
57 	int  i, j;
58 	const char *rgba[4] = {GFSCR_ATTR_RED, GFSCR_ATTR_GREEN, GFSCR_ATTR_BLUE, GFSCR_ATTR_ALPHA};
59 	const char *clr[GFUI_COLORNB] = {
60 		GFSCR_ELT_BGCOLOR, GFSCR_ELT_TITLECOLOR, GFSCR_ELT_BGBTNFOCUS, GFSCR_ELT_BGBTNCLICK,
61 		GFSCR_ELT_BGBTNENABLED, GFSCR_ELT_BGBTNDISABLED, GFSCR_ELT_BTNFOCUS, GFSCR_ELT_BTNCLICK,
62 		GFSCR_ELT_BTNENABLED, GFSCR_ELT_BTNDISABLED, GFSCR_ELT_LABELCOLOR, GFSCR_ELT_TIPCOLOR,
63 		GFSCR_ELT_MOUSECOLOR1, GFSCR_ELT_MOUSECOLOR2, GFSCR_ELT_HELPCOLOR1, GFSCR_ELT_HELPCOLOR2,
64 		GFSCR_ELT_BGSCROLLIST, GFSCR_ELT_SCROLLIST, GFSCR_ELT_BGSELSCROLLIST, GFSCR_ELT_SELSCROLLIST,
65 		GFSCR_ELT_EDITCURSORCLR
66 	};
67 
68 	snprintf(buf, BUFSIZE, "%s%s", GetLocalDir(), GFSCR_CONF_FILE);
69 	hdle = GfParmReadFile(buf, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
70 
71 	for (i = 0; i < GFUI_COLORNB; i++) {
72 		for (j = 0; j < 4; j++) {
73 			snprintf(buf, BUFSIZE, "%s/%s/%s", GFSCR_SECT_MENUCOL, GFSCR_LIST_COLORS, clr[i]);
74 			GfuiColor[i][j] = GfParmGetNum(hdle, buf, rgba[j], (char*)NULL, 1.0);
75 		}
76 	}
77 
78 	GfParmReleaseHandle(hdle);
79 
80 	/* Remove the X11/Windows cursor  */
81 	if (!GfuiMouseHW) {
82 		glutSetCursor(GLUT_CURSOR_NONE);
83 	}
84 
85 	GfuiMouseVisible = 1;
86 }
87 
88 
89 void
gfuiInit(void)90 gfuiInit(void)
91 {
92 	gfuiButtonInit();
93 	gfuiHelpInit();
94 	gfuiLabelInit();
95 	gfuiObjectInit();
96 	gfuiColorInit();
97 	gfuiLoadFonts();
98 
99 /*     glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF); */
100 }
101 
102 
103 /** Dummy display function for glut.
104     Declare this function to glut if nothing is to be displayed by the redisplay mechanism.
105     @ingroup	gui
106  */
107 void
GfuiDisplayNothing(void)108 GfuiDisplayNothing(void)
109 {
110 }
111 
112 /** Idle function for the GUI to be called during Idle loop of glut.
113     @ingroup	gui
114  */
115 void
GfuiIdle(void)116 GfuiIdle(void)
117 {
118 	double		curtime = GfTimeClock();
119 
120 	if ((curtime - LastTimeClick) > DelayRepeat) {
121 		DelayRepeat = REPEAT2;
122 		LastTimeClick = curtime;
123 		if (GfuiScreen->mouse == 1) {
124 			/* button down */
125 			gfuiUpdateFocus();
126 			gfuiMouseAction((void*)0);
127 			glutPostRedisplay();
128 		}
129 	}
130 }
131 
132 /** Display function for the GUI to be called during redisplay of glut.
133     @ingroup	gui
134 */
135 void
GfuiDisplay(void)136 GfuiDisplay(void)
137 {
138 	tGfuiObject	*curObj;
139 
140 	glDisable(GL_DEPTH_TEST);
141 	glDisable(GL_LIGHTING);
142 	glDisable(GL_TEXTURE_2D);
143 	glDisable(GL_CULL_FACE);
144 	glDisable(GL_ALPHA_TEST);
145 	glEnable(GL_BLEND);
146 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
147 
148 	GfScrGetSize(&ScrW, &ScrH, &ViewW, &ViewH);
149 
150 	glViewport((ScrW-ViewW) / 2, (ScrH-ViewH) / 2, ViewW, ViewH);
151 	glMatrixMode(GL_PROJECTION);
152 	glLoadIdentity();
153 	gluOrtho2D(0, GfuiScreen->width, 0, GfuiScreen->height);
154 	glMatrixMode(GL_MODELVIEW);
155 	glLoadIdentity();
156 
157 	if (GfuiScreen->bgColor[3] != 0.0) {
158 		glClearColor(GfuiScreen->bgColor[0],
159 					GfuiScreen->bgColor[1],
160 					GfuiScreen->bgColor[2],
161 					GfuiScreen->bgColor[3]);
162 		glClear(GL_COLOR_BUFFER_BIT);
163 	}
164 
165 	if (glIsTexture(GfuiScreen->bgImage) == GL_TRUE) {
166 		GLfloat tx1 = 0.0f, tx2 = 1.0f, ty1 = 0.0f, ty2 = 1.0f;
167 
168 		// All background images are 16:10 images which are stored as quadratic images.
169 		// Compute texture coordinates to ensure proper unskewed/unstretched display of
170 		// image content.
171 		tdble rfactor = (16.0f*ViewH)/(10.0f*ViewW);
172 		if (rfactor >= 1.0f) {
173 			// Aspect ratio of view is smaller than 16:10, "cut off" sides
174 			tdble tdx = (1.0f-1.0f/rfactor)/2.0f;
175 			tx1 += tdx;
176 			tx2 -= tdx;
177 		} else {
178 			// Aspect ratio of view is larger than 16:10, "cut off" top and bottom
179 			tdble tdy = (1.0f-rfactor)/2.0f;
180 			ty1 += tdy;
181 			ty2 -= tdy;
182 		}
183 
184 		glDisable(GL_BLEND);
185 		glEnable(GL_TEXTURE_2D);
186 		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
187 		glColor3f(0.0, 0.0, 1.0);
188 		glBindTexture(GL_TEXTURE_2D, GfuiScreen->bgImage);
189 		glBegin(GL_QUADS);
190 		glTexCoord2f(tx1, ty1); glVertex3f(0.0, 0.0, 0.0);
191 		glTexCoord2f(tx1, ty2); glVertex3f(0.0, GfuiScreen->height, 0.0);
192 		glTexCoord2f(tx2, ty2); glVertex3f(GfuiScreen->width, GfuiScreen->height, 0.0);
193 		glTexCoord2f(tx2, ty1); glVertex3f(GfuiScreen->width, 0.0, 0.0);
194 		glEnd();
195 		glDisable(GL_TEXTURE_2D);
196 		glEnable(GL_BLEND);
197 	}
198 
199 	curObj = GfuiScreen->objects;
200 	if (curObj) {
201 		do {
202 			curObj = curObj->next;
203 			GfuiDraw(curObj);
204 		} while (curObj != GfuiScreen->objects);
205 	}
206 
207 	if (!GfuiMouseHW && GfuiMouseVisible && GfuiScreen->mouseAllowed) {
208 		GfuiDrawCursor();
209 	}
210 
211 	glDisable(GL_BLEND);
212 	glutSwapBuffers();
213 }
214 
215 /** Hide the mouse cursor
216     @ingroup	gui
217     @return	none
218 */
219 void
GfuiMouseHide(void)220 GfuiMouseHide(void)
221 {
222     GfuiScreen->mouseAllowed = 0;
223 }
224 
225 /** Show the mouse cursor
226     @ingroup	gui
227     @return	none
228 */
229 void
GfuiMouseShow(void)230 GfuiMouseShow(void)
231 {
232     GfuiScreen->mouseAllowed = 1;
233 }
234 
235 /** Force the hardware mouse pointer
236     @ingroup	ctrl
237     @return	<tt>0 ... </tt>Ok
238 		<br><tt>-1 .. </tt>Error
239 */
240 void
GfuiMouseSetHWPresent(void)241 GfuiMouseSetHWPresent(void)
242 {
243     GfuiMouseHW = 1;
244 }
245 
246 static void
gfuiKeyboard(unsigned char key,int,int)247 gfuiKeyboard(unsigned char key, int /* x */, int /* y */)
248 {
249 	tGfuiKey	*curKey;
250 	int		modifier;
251 	tGfuiObject	*obj;
252 
253 	modifier = glutGetModifiers();
254 
255 	/* user preempt key */
256 	if (GfuiScreen->onKeyAction && GfuiScreen->onKeyAction(key, modifier, GFUI_KEY_DOWN)) {
257 		return;
258 	}
259 
260 	/* now see the user's defined keys */
261 	if (GfuiScreen->userKeys != NULL) {
262 		curKey = GfuiScreen->userKeys;
263 		do {
264 			curKey = curKey->next;
265 			if ((curKey->key == key) && ((curKey->modifier == 0) || (curKey->modifier & modifier) != 0)) {
266 				if (curKey->onPress) curKey->onPress(curKey->userData);
267 				break;
268 			}
269 		} while (curKey != GfuiScreen->userKeys);
270 	}
271 
272 	obj = GfuiScreen->hasFocus;
273 	if (obj != NULL) {
274 		switch (obj->widget) {
275 			case GFUI_EDITBOX:
276 				gfuiEditboxKey(obj, (int)key, modifier);
277 				break;
278 		}
279 	}
280 	glutPostRedisplay();
281 }
282 
283 static void
gfuiSpecial(int key,int,int)284 gfuiSpecial(int key, int /* x */, int /* y */)
285 {
286 	tGfuiKey	*curKey;
287 	int		modifier;
288 	tGfuiObject	*obj;
289 
290 	modifier = glutGetModifiers();
291 
292 	/* user preempt key */
293 	if (GfuiScreen->onSKeyAction && GfuiScreen->onSKeyAction(key, modifier, GFUI_KEY_DOWN)) {
294 		return;
295 	}
296 
297 	/* now see the user's defined keys */
298 	if (GfuiScreen->userSpecKeys != NULL) {
299 		curKey = GfuiScreen->userSpecKeys;
300 		do {
301 			curKey = curKey->next;
302 			if ((curKey->specialkey == key) && ((curKey->modifier == 0) || (curKey->modifier & modifier) != 0)) {
303 				if (curKey->onPress) curKey->onPress(curKey->userData);
304 				break;
305 			}
306 		} while (curKey != GfuiScreen->userSpecKeys);
307 	}
308 
309 	obj = GfuiScreen->hasFocus;
310 	if (obj != NULL) {
311 		switch (obj->widget) {
312 		case GFUI_EDITBOX:
313 			gfuiEditboxKey(obj, key + 256, modifier);
314 			break;
315 		}
316 	}
317 	glutPostRedisplay();
318 }
319 
320 static void
gfuiKeyboardUp(unsigned char key,int,int)321 gfuiKeyboardUp(unsigned char key, int /* x */, int /* y */)
322 {
323 	tGfuiKey	*curKey;
324 	int		modifier;
325 
326 	modifier = glutGetModifiers();
327 
328 	/* user preempt key */
329 	if (GfuiScreen->onKeyAction && GfuiScreen->onKeyAction(key, modifier, GFUI_KEY_UP)) {
330 		return;
331 	}
332 
333 	/* now see the user's defined keys */
334 	if (GfuiScreen->userKeys != NULL) {
335 		curKey = GfuiScreen->userKeys;
336 		do {
337 			curKey = curKey->next;
338 			if ((curKey->key == key) && ((curKey->modifier == 0) || (curKey->modifier & modifier) != 0)) {
339 				if (curKey->onRelease) curKey->onRelease(curKey->userData);
340 				break;
341 			}
342 		} while (curKey != GfuiScreen->userKeys);
343 	}
344 
345 	glutPostRedisplay();
346 }
347 
348 static void
gfuiSpecialUp(int key,int,int)349 gfuiSpecialUp(int key, int /* x */, int /* y */)
350 {
351 	tGfuiKey	*curKey;
352 	int		modifier;
353 
354 	modifier = glutGetModifiers();
355 
356 	/* user preempt key */
357 	if (GfuiScreen->onSKeyAction && GfuiScreen->onSKeyAction(key, modifier, GFUI_KEY_UP)) {
358 		return;
359 	}
360 
361 	/* now see the user's defined keys */
362 	if (GfuiScreen->userSpecKeys != NULL) {
363 		curKey = GfuiScreen->userSpecKeys;
364 		do {
365 			curKey = curKey->next;
366 			if ((curKey->specialkey == key) && ((curKey->modifier == 0) || (curKey->modifier & modifier) != 0)) {
367 				if (curKey->onRelease) curKey->onRelease(curKey->userData);
368 				break;
369 			}
370 		} while (curKey != GfuiScreen->userSpecKeys);
371 	}
372 
373 	glutPostRedisplay();
374 }
375 
376 /** Get the mouse information (position and buttons)
377     @ingroup	gui
378     @return	mouse information
379 */
GfuiMouseInfo(void)380 tMouseInfo *GfuiMouseInfo(void)
381 {
382 	return &GfuiMouse;
383 }
384 
385 /** Set the mouse position
386     @ingroup	gui
387     @param	x	mouse x pos
388     @param	y	mouse y pos
389     @return	none
390 */
GfuiMouseSetPos(int x,int y)391 void GfuiMouseSetPos(int x, int y)
392 {
393 	glutWarpPointer(x, y);
394 }
395 
396 
397 static void
gfuiMouse(int button,int state,int x,int y)398 gfuiMouse(int button, int state, int x, int y)
399 {
400 	// Check array range of button array!
401 	if (button > -1 && button < 3) {
402 		GfuiMouse.X = (x - (ScrW - ViewW)/2) * (int)GfuiScreen->width / ViewW;
403 		GfuiMouse.Y = (ViewH - y + (ScrH - ViewH)/2) * (int)GfuiScreen->height / ViewH;
404 
405 		GfuiMouse.button[button] = 1 - state;
406 
407 		DelayRepeat = REPEAT1;
408 		LastTimeClick = GfTimeClock();
409 		if (state == GLUT_DOWN) {
410 			if (button == GLUT_RIGHT_BUTTON) {
411 				GfuiScreen->mouse = 0;
412 				/* GfuiMouseVisible = 1 - GfuiMouseVisible; */
413 				gfuiUpdateFocus();
414 			} else {
415 				GfuiScreen->mouse = 1;
416 				gfuiUpdateFocus();
417 				gfuiMouseAction((void*)0);
418 			}
419 		} else {
420 			GfuiScreen->mouse = 0;
421 			gfuiUpdateFocus();
422 			if (button != GLUT_RIGHT_BUTTON) {
423 				gfuiMouseAction((void*)1);
424 			}
425 		}
426 		glutPostRedisplay();
427 	}
428 }
429 
430 static void
gfuiMotion(int x,int y)431 gfuiMotion(int x, int y)
432 {
433 	GfuiMouse.X = (x - (ScrW - ViewW)/2) * (int)GfuiScreen->width / ViewW;
434 	GfuiMouse.Y = (ViewH - y + (ScrH - ViewH)/2) * (int)GfuiScreen->height / ViewH;
435 	gfuiUpdateFocus();
436 	gfuiMouseAction((void*)(1 - GfuiScreen->mouse));
437 	glutPostRedisplay();
438 	DelayRepeat = REPEAT1;
439 }
440 
441 static void
gfuiPassiveMotion(int x,int y)442 gfuiPassiveMotion(int x, int y)
443 {
444 	GfuiMouse.X = (x - (ScrW - ViewW)/2) * (int)GfuiScreen->width / ViewW;
445 	GfuiMouse.Y = (ViewH - y + (ScrH - ViewH)/2) * (int)GfuiScreen->height / ViewH;
446 	gfuiUpdateFocus();
447 	glutPostRedisplay();
448 }
449 
450 /** Tell if the screen is active or not.
451     @ingroup	gui
452     @param	screen	Screen to activate
453     @return	1 if active and 0 if not.
454  */
455 int
GfuiScreenIsActive(void * screen)456 GfuiScreenIsActive(void *screen)
457 {
458 	return (GfuiScreen == screen);
459 }
460 
461 /** Activate a screen and make it current.
462     @ingroup	gui
463     @param	screen	Screen to activate
464     @warning	The current screen at the call time is deactivated.
465  */
466 void
GfuiScreenActivate(void * screen)467 GfuiScreenActivate(void *screen)
468 {
469 	if ((GfuiScreen) && (GfuiScreen->onDeactivate)) GfuiScreen->onDeactivate(GfuiScreen->userDeactData);
470 
471 	GfuiScreen = (tGfuiScreen*)screen;
472 
473 	glutKeyboardFunc(gfuiKeyboard);
474 	glutSpecialFunc(gfuiSpecial);
475 	glutKeyboardUpFunc(gfuiKeyboardUp);
476 	glutSpecialUpFunc(gfuiSpecialUp);
477 	glutMouseFunc(gfuiMouse);
478 	glutMotionFunc(gfuiMotion);
479 	glutPassiveMotionFunc(gfuiPassiveMotion);
480 	glutIdleFunc((void(*)(void))NULL);
481 
482 	if (GfuiScreen->onlyCallback == 0) {
483 		if (GfuiScreen->hasFocus == NULL) {
484 			gfuiSelectNext(NULL);
485 		}
486 		glutDisplayFunc(GfuiDisplay);
487 	} else {
488 		glutDisplayFunc(GfuiDisplayNothing);
489 	}
490 
491 	if (GfuiScreen->onActivate) GfuiScreen->onActivate(GfuiScreen->userActData);
492 
493 	if (GfuiScreen->onlyCallback == 0) {
494 		GfuiDisplay();
495 		glutPostRedisplay();
496 	}
497 }
498 
499 
500 /** Activate a screen and make it current plus release the current screen.
501     @ingroup	gui
502     @param	screen	Screen to activate
503     @warning	The current screen at the call time is deactivated.
504  */
505 void
GfuiScreenReplace(void * screen)506 GfuiScreenReplace(void *screen)
507 {
508 	tGfuiScreen	*oldScreen = GfuiScreen;
509 
510 	if (oldScreen) {
511 		GfuiScreenRelease(oldScreen);
512 	}
513 	GfuiScreenActivate(screen);
514 }
515 
516 /** Deactivate the current screen.
517     @ingroup	gui
518  */
519 void
GfuiScreenDeactivate(void)520 GfuiScreenDeactivate(void)
521 {
522 	if (GfuiScreen->onDeactivate) GfuiScreen->onDeactivate(GfuiScreen->userDeactData);
523 
524 	GfuiScreen = (tGfuiScreen*)NULL;
525 
526 	glutKeyboardFunc((void(*)(unsigned char,int,int))NULL);
527 	glutSpecialFunc((void(*)(int,int,int))NULL);
528 	glutKeyboardUpFunc((void(*)(unsigned char,int,int))NULL);
529 	glutSpecialUpFunc((void(*)(int,int,int))NULL);
530 	glutMouseFunc((void(*)(int,int,int,int))NULL);
531 	glutMotionFunc((void(*)(int,int))NULL);
532 	glutPassiveMotionFunc((void(*)(int,int))NULL);
533 	glutIdleFunc((void(*)(void))NULL);
534 	glutDisplayFunc(GfuiDisplayNothing);
535 }
536 
537 /** Create a new screen.
538     @ingroup	gui
539     @return	New screen instance
540 		<br>NULL if Error
541  */
542 void *
GfuiScreenCreate(void)543 GfuiScreenCreate(void)
544 {
545 	tGfuiScreen	*screen;
546 
547 	screen = (tGfuiScreen*)calloc(1, sizeof(tGfuiScreen));
548 
549 	screen->width = 640.0;
550 	screen->height = 480.0;
551 
552 	screen->bgColor = (float*)calloc(4, sizeof(float));
553 	int i;
554 	for(i = 0; i < 4; i++) {
555 		screen->bgColor[i] = GfuiColor[GFUI_BGCOLOR][i];
556 	}
557 
558 	screen->mouseColor[0] = &(GfuiColor[GFUI_MOUSECOLOR1][0]);
559 	screen->mouseColor[1] = &(GfuiColor[GFUI_MOUSECOLOR2][0]);
560 	screen->mouseAllowed = 1;
561 
562 	return (void*)screen;
563 }
564 
565 /** Create a screen.
566     @ingroup	gui
567     @param	bgColor			pointer on color array (RGBA) (if NULL default color is used)
568     @param	userDataOnActivate	Parameter to the activate function
569     @param	onActivate		Function called when the screen is activated
570     @param	userDataOnDeactivate	Parameter to the deactivate function
571     @param	onDeactivate		Function called when the screen is deactivated
572     @param	mouseAllowed		Flag to tell if the mouse cursor can be displayed
573     @return	New screen instance
574 		<br>NULL if Error
575     @bug	Only black background work well
576  */
577 void *
GfuiScreenCreateEx(float * bgColor,void * userDataOnActivate,tfuiCallback onActivate,void * userDataOnDeactivate,tfuiCallback onDeactivate,int mouseAllowed)578 GfuiScreenCreateEx(float *bgColor,
579 		   void *userDataOnActivate, tfuiCallback onActivate,
580 		   void *userDataOnDeactivate, tfuiCallback onDeactivate,
581 		   int mouseAllowed)
582 {
583 	int		i;
584 	tGfuiScreen	*screen;
585 
586 	screen = (tGfuiScreen*)calloc(1, sizeof(tGfuiScreen));
587 
588 	screen->width = 640.0;
589 	screen->height = 480.0;
590 
591 	screen->bgColor = (float*)calloc(4, sizeof(float));
592 	for(i = 0; i < 4; i++) {
593 		if (bgColor != NULL) {
594 			screen->bgColor[i] = bgColor[i];
595 		} else {
596 			screen->bgColor[i] = GfuiColor[GFUI_BGCOLOR][i];
597 		}
598 	}
599 
600 	screen->mouseColor[0] = &(GfuiColor[GFUI_MOUSECOLOR1][0]);
601 	screen->mouseColor[1] = &(GfuiColor[GFUI_MOUSECOLOR2][0]);
602 	screen->onActivate = onActivate;
603 	screen->userActData = userDataOnActivate;
604 	screen->onDeactivate = onDeactivate;
605 	screen->userDeactData = userDataOnDeactivate;
606 
607 	screen->mouseAllowed = mouseAllowed;
608 
609 	return (void*)screen;
610 }
611 
612 /** Release the given screen.
613     @ingroup	gui
614     @param	scr	Screen to release
615     @warning	If the screen was activated, it is deactivated.
616  */
617 void
GfuiScreenRelease(void * scr)618 GfuiScreenRelease(void *scr)
619 {
620 	tGfuiObject *curObject;
621 	tGfuiObject *nextObject;
622 	tGfuiKey *curKey;
623 	tGfuiKey *nextKey;
624 	tGfuiScreen *screen = (tGfuiScreen*)scr;
625 
626 	if (GfuiScreen == screen) {
627 		GfuiScreenDeactivate();
628 	}
629 
630 	if (glIsTexture(screen->bgImage) == GL_TRUE) {
631 		glDeleteTextures(1, &screen->bgImage);
632 	}
633 
634 	if (screen->bgColor != NULL) {
635 		free(screen->bgColor);
636 		screen->bgColor = NULL;
637 	}
638 
639 	curObject = screen->objects;
640 	if (curObject != NULL) {
641 		do {
642 			nextObject = curObject->next;
643 			gfuiReleaseObject(curObject);
644 			curObject = nextObject;
645 		} while (curObject != screen->objects);
646 	}
647 
648 	curKey = screen->userKeys;
649 	if (curKey != NULL) {
650 		do {
651 			nextKey = curKey->next;
652 			free(curKey->name);
653 			free(curKey->descr);
654 			free(curKey);
655 			curKey = nextKey;
656 		} while (curKey != screen->userKeys);
657 	}
658 	curKey = screen->userSpecKeys;
659 	if (curKey != NULL) {
660 		do {
661 			nextKey = curKey->next;
662 			free(curKey->name);
663 			free(curKey->descr);
664 			free(curKey);
665 			curKey = nextKey;
666 		} while (curKey != screen->userSpecKeys);
667 	}
668 	free(screen);
669 }
670 
671 /** Create a callback hook.
672     @ingroup	gui
673     @param	userDataOnActivate	Parameter to the activate function
674     @param	onActivate		Function called when the screen is activated
675     @return	New hook instance
676 		<br>NULL if Error
677  */
678 void *
GfuiHookCreate(void * userDataOnActivate,tfuiCallback onActivate)679 GfuiHookCreate(void *userDataOnActivate, tfuiCallback onActivate)
680 {
681 	tGfuiScreen	*screen;
682 
683 	screen = (tGfuiScreen*)calloc(1, sizeof(tGfuiScreen));
684 	screen->onActivate = onActivate;
685 	screen->userActData = userDataOnActivate;
686 	screen->onlyCallback = 1;
687 
688 	return (void*)screen;
689 }
690 
691 /** Release the given hook.
692     @ingroup	gui
693     @param	hook	Hook to release
694  */
695 void
GfuiHookRelease(void * hook)696 GfuiHookRelease(void *hook)
697 {
698 	free(hook);
699 }
700 
701 void
GfuiKeyEventRegister(void * scr,tfuiKeyCallback onKeyAction)702 GfuiKeyEventRegister(void *scr, tfuiKeyCallback onKeyAction)
703 {
704 	tGfuiScreen	*screen = (tGfuiScreen*)scr;
705 
706 	screen->onKeyAction = onKeyAction;
707 }
708 
709 
710 void
GfuiSKeyEventRegister(void * scr,tfuiSKeyCallback onSKeyAction)711 GfuiSKeyEventRegister(void *scr, tfuiSKeyCallback onSKeyAction)
712 {
713 	tGfuiScreen	*screen = (tGfuiScreen*)scr;
714 
715 	screen->onSKeyAction = onSKeyAction;
716 }
717 
718 void
GfuiKeyEventRegisterCurrent(tfuiKeyCallback onKeyAction)719 GfuiKeyEventRegisterCurrent(tfuiKeyCallback onKeyAction)
720 {
721 	GfuiScreen->onKeyAction = onKeyAction;
722 }
723 
724 
725 void
GfuiSKeyEventRegisterCurrent(tfuiSKeyCallback onSKeyAction)726 GfuiSKeyEventRegisterCurrent(tfuiSKeyCallback onSKeyAction)
727 {
728 	GfuiScreen->onSKeyAction = onSKeyAction;
729 }
730 
731 
732 /** Add a Keyboard callback to a screen.
733     @ingroup	gui
734     @param	scr		Screen
735     @param	key		Key code (glut value)
736     @param	descr		Description for help screen
737     @param	userData	Parameter to the callback function
738     @param	onKeyPressed	Callback function
739     @param	onKeyReleased	Callback function
740  */
741 void
GfuiAddKey(void * scr,unsigned char key,const char * descr,void * userData,tfuiCallback onKeyPressed,tfuiCallback onKeyReleased)742 GfuiAddKey(void *scr, unsigned char key, const char *descr, void *userData, tfuiCallback onKeyPressed, tfuiCallback onKeyReleased)
743 {
744 	tGfuiKey	*curKey;
745 	tGfuiScreen	*screen = (tGfuiScreen*)scr;
746 	const int BUFSIZE = 16;
747 	char buf[BUFSIZE];
748 
749 	curKey = (tGfuiKey*)calloc(1, sizeof(tGfuiKey));
750 	curKey->key = key;
751 	curKey->userData = userData;
752 	curKey->onPress = onKeyPressed;
753 
754 	if (descr == NULL) {
755 		curKey->descr = strdup("");
756 	} else {
757 		curKey->descr = strdup(descr);
758 	}
759 
760 	switch(key){
761 		case 8:
762 			curKey->name = strdup("backspace");
763 			break;
764 		case 9:
765 			curKey->name = strdup("tab");
766 			break;
767 		case 13:
768 			curKey->name = strdup("enter");
769 			break;
770 		case 27:
771 			curKey->name = strdup("esc");
772 			break;
773 		case ' ':
774 			curKey->name = strdup("space");
775 			break;
776 		default:
777 			snprintf(buf, BUFSIZE, "%c", key);
778 			curKey->name = strdup(buf);
779 			break;
780 	}
781 
782 	if (screen->userKeys == NULL) {
783 		screen->userKeys = curKey;
784 		curKey->next = curKey;
785 	} else {
786 		curKey->next = screen->userKeys->next;
787 		screen->userKeys->next = curKey;
788 	}
789 }
790 
791 /** Add a Keyboard callback to the current screen.
792     @ingroup	gui
793     @param	key		Key code (glut value)
794     @param	descr		Description for help screen
795     @param	userData	Parameter to the callback function
796     @param	onKeyPressed	Callback function called when the specified key is pressed
797     @param	onKeyReleased	Callback function
798  */
799 void
GfuiRegisterKey(unsigned char key,char * descr,void * userData,tfuiCallback onKeyPressed,tfuiCallback onKeyReleased)800 GfuiRegisterKey(unsigned char key, char *descr, void *userData, tfuiCallback onKeyPressed, tfuiCallback onKeyReleased)
801 {
802 	GfuiAddKey(GfuiScreen, key, descr, userData, onKeyPressed, onKeyReleased);
803 }
804 
805 /** Add a Special Keyboard shortcut to the screen.
806     (see glut for normal and special keys)
807     @ingroup	gui
808     @param	scr		Screen
809     @param	key		Key code (glut value)
810     @param	descr		Description for help screen
811     @param	userData	Parameter to the callback function
812     @param	onKeyPressed	Callback function
813     @param	onKeyReleased	Callback function
814  */
815 void
GfuiAddSKey(void * scr,int key,const char * descr,void * userData,tfuiCallback onKeyPressed,tfuiCallback onKeyReleased)816 GfuiAddSKey(void *scr, int key, const char *descr, void *userData, tfuiCallback onKeyPressed, tfuiCallback onKeyReleased)
817 {
818 	tGfuiKey	*curKey;
819 	tGfuiScreen	*screen = (tGfuiScreen*)scr;
820 
821 	curKey = (tGfuiKey*)calloc(1, sizeof(tGfuiKey));
822 	curKey->specialkey = key;
823 	curKey->userData = userData;
824 	curKey->onPress = onKeyPressed;
825 
826 	if (descr == NULL) {
827 		curKey->descr = strdup("");
828 	} else {
829 		curKey->descr = strdup(descr);
830 	}
831 
832 	switch(key) {
833 		case GLUT_KEY_F1:
834 			curKey->name = strdup("F1");
835 			break;
836 		case GLUT_KEY_F2:
837 			curKey->name = strdup("F2");
838 			break;
839 		case GLUT_KEY_F3:
840 			curKey->name = strdup("F3");
841 			break;
842 		case GLUT_KEY_F4:
843 			curKey->name = strdup("F4");
844 			break;
845 		case GLUT_KEY_F5:
846 			curKey->name = strdup("F5");
847 			break;
848 		case GLUT_KEY_F6:
849 			curKey->name = strdup("F6");
850 			break;
851 		case GLUT_KEY_F7:
852 			curKey->name = strdup("F7");
853 			break;
854 		case GLUT_KEY_F8:
855 			curKey->name = strdup("F8");
856 			break;
857 		case GLUT_KEY_F9:
858 			curKey->name = strdup("F9");
859 			break;
860 		case GLUT_KEY_F10:
861 			curKey->name = strdup("F10");
862 			break;
863 		case GLUT_KEY_F11:
864 			curKey->name = strdup("F11");
865 			break;
866 		case GLUT_KEY_F12:
867 			curKey->name = strdup("F12");
868 			break;
869 		case GLUT_KEY_LEFT:
870 			curKey->name = strdup("Left Arrow");
871 			break;
872 		case GLUT_KEY_UP:
873 			curKey->name = strdup("Up Arrow");
874 			break;
875 		case GLUT_KEY_RIGHT:
876 			curKey->name = strdup("Right Arrow");
877 			break;
878 		case GLUT_KEY_DOWN:
879 			curKey->name = strdup("Down Arrow");
880 			break;
881 		case GLUT_KEY_PAGE_UP:
882 			curKey->name = strdup("Page Up");
883 			break;
884 		case GLUT_KEY_PAGE_DOWN:
885 			curKey->name = strdup("Page Down");
886 			break;
887 		case GLUT_KEY_HOME:
888 			curKey->name = strdup("Home");
889 			break;
890 		case GLUT_KEY_END:
891 			curKey->name = strdup("End");
892 			break;
893 		case GLUT_KEY_INSERT:
894 			curKey->name = strdup("Insert");
895 			break;
896 	}
897 
898 
899 	if (screen->userSpecKeys == NULL) {
900 		screen->userSpecKeys = curKey;
901 		curKey->next = curKey;
902 	} else {
903 		curKey->next = screen->userSpecKeys->next;
904 		screen->userSpecKeys->next = curKey;
905 		screen->userSpecKeys = curKey;
906 	}
907 }
908 
909 /** Save a screen shot in png format.
910     @ingroup	screen
911  */
912 void
GfuiScreenShot(void *)913 GfuiScreenShot(void * /* notused */)
914 {
915 	unsigned char *img;
916 	const int BUFSIZE = 1024;
917 	char buf[BUFSIZE];
918 	struct tm *stm;
919 	time_t t;
920 	int sw, sh, vw, vh;
921 	char path[BUFSIZE];
922 
923 	snprintf(path, BUFSIZE, "%sscreenshots", GetLocalDir());
924 	// Ensure that screenshot directory exists.
925 	if (GfCreateDir(path) == GF_DIR_CREATED) {
926 
927 		GfScrGetSize(&sw, &sh, &vw, &vh);
928 		img = (unsigned char*)malloc(vw * vh * 3);
929 		if (img == NULL) {
930 			return;
931 		}
932 
933 		glPixelStorei(GL_PACK_ROW_LENGTH, 0);
934 		glPixelStorei(GL_PACK_ALIGNMENT, 1);
935 		glReadBuffer(GL_FRONT);
936 		glReadPixels((sw-vw)/2, (sh-vh)/2, vw, vh, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img);
937 
938 		t = time(NULL);
939 		stm = localtime(&t);
940 		snprintf(buf, BUFSIZE, "%s/torcs-%4d%02d%02d%02d%02d%02d.png",
941 			path,
942 			stm->tm_year+1900,
943 			stm->tm_mon+1,
944 			stm->tm_mday,
945 			stm->tm_hour,
946 			stm->tm_min,
947 			stm->tm_sec);
948 		GfImgWritePng(img, buf, vw, vh);
949 
950 		free(img);
951 	}
952 }
953 
954 /** Add an image background to a screen.
955     @ingroup	gui
956     @param	scr		Screen
957     @param	filename	file name of the bg image
958     @return	None.
959  */
960 void
GfuiScreenAddBgImg(void * scr,const char * filename)961 GfuiScreenAddBgImg(void *scr, const char *filename)
962 {
963 	tGfuiScreen	*screen = (tGfuiScreen*)scr;
964 	void *handle;
965 	float screen_gamma;
966 	GLbyte *tex;
967 	int w,h;
968 	const int BUFSIZE = 1024;
969 	char buf[BUFSIZE];
970 
971 	if (glIsTexture(screen->bgImage) == GL_TRUE) {
972 		glDeleteTextures(1, &screen->bgImage);
973 	}
974 
975 	snprintf(buf, BUFSIZE, "%s%s", GetLocalDir(), GFSCR_CONF_FILE);
976 	handle = GfParmReadFile(buf, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
977 	screen_gamma = (float)GfParmGetNum(handle, GFSCR_SECT_PROP, GFSCR_ATT_GAMMA, (char*)NULL, 2.0);
978 	tex = (GLbyte*)GfImgReadPng(filename, &w, &h, screen_gamma);
979 	if (!tex) {
980 		GfParmReleaseHandle(handle);
981 		return;
982 	}
983 
984 	glGenTextures(1, &screen->bgImage);
985 	glBindTexture(GL_TEXTURE_2D, screen->bgImage);
986 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
987 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
988 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)(tex));
989 	free(tex);
990 	GfParmReleaseHandle(handle);
991 }
992 
993 
994