1 /*
2  * Copyright (c) 2009-2015 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 /*
27  * Main Agar-GUI initialization.
28  */
29 
30 #include <agar/core/core.h>
31 #include <agar/core/config.h>
32 
33 #include <agar/config/have_opengl.h>
34 #include <agar/config/ag_debug_gui.h>
35 
36 #include <agar/gui/gui.h>
37 #include <agar/gui/box.h>
38 #include <agar/gui/button.h>
39 #include <agar/gui/checkbox.h>
40 #include <agar/gui/combo.h>
41 #include <agar/gui/console.h>
42 #include <agar/gui/dir_dlg.h>
43 #include <agar/gui/editable.h>
44 #include <agar/gui/file_dlg.h>
45 #include <agar/gui/file_selector.h>
46 #include <agar/gui/fixed.h>
47 #include <agar/gui/fspinbutton.h>
48 #include <agar/gui/fixed_plotter.h>
49 #include <agar/gui/font_selector.h>
50 #include <agar/gui/glview.h>
51 #include <agar/gui/graph.h>
52 #include <agar/gui/hsvpal.h>
53 #include <agar/gui/icon.h>
54 #include <agar/gui/label.h>
55 #include <agar/gui/menu.h>
56 #include <agar/gui/mfspinbutton.h>
57 #include <agar/gui/mpane.h>
58 #include <agar/gui/mspinbutton.h>
59 #include <agar/gui/notebook.h>
60 #include <agar/gui/numerical.h>
61 #include <agar/gui/objsel.h>
62 #include <agar/gui/pane.h>
63 #include <agar/gui/pixmap.h>
64 #include <agar/gui/progress_bar.h>
65 #include <agar/gui/radio.h>
66 #include <agar/gui/scrollbar.h>
67 #include <agar/gui/scrollview.h>
68 #include <agar/gui/separator.h>
69 #include <agar/gui/slider.h>
70 #include <agar/gui/socket.h>
71 #include <agar/gui/spinbutton.h>
72 #include <agar/gui/statusbar.h>
73 #include <agar/gui/table.h>
74 #include <agar/gui/treetbl.h>
75 #include <agar/gui/textbox.h>
76 #include <agar/gui/titlebar.h>
77 #include <agar/gui/tlist.h>
78 #include <agar/gui/toolbar.h>
79 #include <agar/gui/ucombo.h>
80 
81 #include <agar/gui/colors.h>
82 #include <agar/gui/cursors.h>
83 #include <agar/gui/primitive.h>
84 #include <agar/gui/icons.h>
85 #include <agar/gui/icons_data.h>
86 #include <agar/gui/text.h>
87 
88 static struct {
89 	const char *key;
90 	int *p;
91 } agGUIOptions[] = {
92 	{ "ag_kbd_delay",		&agKbdDelay		},
93 	{ "ag_kbd_repeat",		&agKbdRepeat		},
94 	{ "ag_mouse_dblclick_delay",	&agMouseDblclickDelay	},
95 	{ "ag_mouse_spin_delay",	&agMouseSpinDelay	},
96 	{ "ag_mouse_spin_interval",	&agMouseSpinIval	},
97 	{ "ag_text_composition",	&agTextComposition	},
98 	{ "ag_text_bidi",		&agTextBidi		},
99 	{ "ag_text_cache",		&agTextCache		},
100 	{ "ag_text_tab_width",		&agTextTabWidth		},
101 	{ "ag_text_blink_rate",		&agTextBlinkRate	},
102 	{ "ag_text_symbols",		&agTextSymbols		},
103 	{ "ag_page_increment",		&agPageIncrement	},
104 	{ "ag_idle_threshold",		&agIdleThresh		},
105 	{ "ag_screenshot_quality",	&agScreenshotQuality	},
106 	{ "ag_msg_delay",		&agMsgDelay		}
107 };
108 const Uint agGUIOptionCount = sizeof(agGUIOptions) / sizeof(agGUIOptions[0]);
109 
110 void *agStdClasses[] = {
111 	&agDriverClass,
112 	&agDriverSwClass,
113 	&agDriverMwClass,
114 	&agInputDeviceClass,
115 	&agMouseClass,
116 	&agKeyboardClass,
117 	NULL
118 };
119 void *agStdWidgets[] = {
120 	&agWidgetClass,
121 	&agWindowClass,
122 	&agFontClass,
123 	&agBoxClass,
124 	&agButtonClass,
125 	&agCheckboxClass,
126 	&agComboClass,
127 	&agConsoleClass,
128 	&agDirDlgClass,
129 	&agEditableClass,
130 	&agFontSelectorClass,
131 	&agFileDlgClass,
132 	&agFileSelectorClass,
133 	&agFixedClass,
134 	&agFSpinbuttonClass,
135 	&agFixedPlotterClass,
136 	&agGraphClass,
137 #ifdef HAVE_OPENGL
138 	&agGLViewClass,
139 #endif
140 	&agHSVPalClass,
141 	&agIconClass,
142 	&agLabelClass,
143 	&agMenuClass,
144 	&agMenuViewClass,
145 	&agMFSpinbuttonClass,
146 	&agMPaneClass,
147 	&agMSpinbuttonClass,
148 	&agNotebookClass,
149 	&agNotebookTabClass,
150 	&agNumericalClass,
151 	&agObjectSelectorClass,
152 	&agPaneClass,
153 	&agPixmapClass,
154 	&agProgressBarClass,
155 	&agRadioClass,
156 	&agScrollbarClass,
157 	&agScrollviewClass,
158 	&agSeparatorClass,
159 	&agSliderClass,
160 	&agSocketClass,
161 	&agSpinbuttonClass,
162 	&agStatusbarClass,
163 	&agTitlebarClass,
164 	&agTableClass,
165 	&agTreetblClass,
166 	&agTextboxClass,
167 	&agTlistClass,
168 	&agToolbarClass,
169 	&agUComboClass,
170 	NULL
171 };
172 
173 static int initedGlobals = 0;		/* GUI globals are initialized */
174 
175 int agGUI = 0;				/* GUI is initialized */
176 int agRenderingContext = 0;		/* In rendering context */
177 int agStereo = 0;			/* Stereoscopic display */
178 int agKbdDelay = 250;			/* Key repeat delay */
179 int agKbdRepeat = 35;			/* Key repeat interval */
180 int agMouseDblclickDelay = 250;		/* Mouse double-click delay */
181 int agMouseSpinDelay = 250;		/* Spinbutton repeat delay */
182 int agMouseSpinIval = 50;		/* Spinbutton repeat interval */
183 int agMouseScrollDelay = 100;		/* Scrollbar increment delay */
184 int agMouseScrollIval = 50;		/* Scrollbar increment interval */
185 int agTextComposition = 1;		/* Built-in input composition */
186 int agTextBidi = 0;			/* Bidirectionnal text display */
187 int agTextCache = 1;			/* Dynamic text caching */
188 int agTextTabWidth = 40;		/* Tab width (px) */
189 int agTextBlinkRate = 500;		/* Cursor blink rate (ms) */
190 int agTextSymbols = 1;			/* Process special symbols in text */
191 int agPageIncrement = 4;		/* Pgup/Pgdn scrolling increment */
192 int agIdleThresh = 20;			/* Idling threshold */
193 int agScreenshotQuality = 100;		/* JPEG quality in % */
194 int agMsgDelay = 500;			/* Display duration of infoboxes (ms) */
195 double agZoomValues[AG_ZOOM_RANGE] = {	/* Scale values for zoom */
196 	30.00, 50.00, 67.00, 80.00, 90.00,
197 	100.00,
198 	110.00, 120.00, 133.00, 150.00, 170.00, 200.00, 240.00, 300.00
199 };
200 
201 /*
202  * Initialize the Agar-GUI globals and built-in classes. This function
203  * is invoked internally prior to graphics initialization.
204  */
205 int
AG_InitGUIGlobals(void)206 AG_InitGUIGlobals(void)
207 {
208 	AG_Config *cfg;
209 	void **cl;
210 	Uint i;
211 
212 	if (initedGlobals++ > 0) {
213 		return (0);
214 	}
215 	agGUI = 1;
216 
217 	for (cl = &agStdClasses[0]; *cl != NULL; cl++)
218 		AG_RegisterClass(*cl);
219 	for (i = 0; i < agDriverListSize; i++)
220 		AG_RegisterClass(agDriverList[i]);
221 
222 	AG_InitGlobalKeys();
223 	AG_EditableInitClipboards();
224 
225 	agSurfaceFmt = AG_PixelFormatRGBA(32,
226 #if AG_BYTEORDER == AG_BIG_ENDIAN
227 	    0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
228 #else
229 	    0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
230 #endif
231 	);
232 
233 	agRenderingContext = 0;
234 	AG_ObjectInitStatic(&agDrivers, &agObjectClass);
235 	AG_ObjectSetName(&agDrivers, "agDrivers");
236 	AG_ObjectInitStatic(&agInputDevices, &agObjectClass);
237 	AG_ObjectSetName(&agInputDevices, "agInputDevices");
238 
239 	cfg = AG_ConfigObject();
240 	for (i = 0; i < agGUIOptionCount; i++)
241 		AG_BindInt(cfg, agGUIOptions[i].key, agGUIOptions[i].p);
242 
243 	AG_LoadStyleSheet(NULL, "_agStyleDefault");
244 	return (0);
245 }
246 
247 /*
248  * Destroy the Agar-GUI globals and built-in classes. This function
249  * is invoked internally after the graphics subsystem is destroyed.
250  */
251 void
AG_DestroyGUIGlobals(void)252 AG_DestroyGUIGlobals(void)
253 {
254 	AG_Config *cfg;
255 	void **cl;
256 	Uint i;
257 
258 	if (--initedGlobals > 0)
259 		return;
260 
261 	AG_DestroyStyleSheet(&agDefaultCSS);
262 
263 	cfg = AG_ConfigObject();
264 	for (i = 0; i < agGUIOptionCount; i++)
265 		AG_Unset(cfg, agGUIOptions[i].key);
266 
267 	AG_ObjectDestroy(&agInputDevices);
268 #ifndef __APPLE__ /* XXX mutex issue */
269 	AG_ObjectDestroy(&agDrivers);
270 #endif
271 
272 	AG_PixelFormatFree(agSurfaceFmt); agSurfaceFmt = NULL;
273 	AG_EditableDestroyClipboards();
274 	AG_DestroyGlobalKeys();
275 
276 	for (i = 0; i < agDriverListSize; i++)
277 		AG_UnregisterClass(agDriverList[i]);
278 	for (cl = &agStdClasses[0]; *cl != NULL; cl++)
279 		AG_UnregisterClass(*cl);
280 
281 	agRenderingContext = 0;
282 	agGUI = 0;
283 }
284 
285 /*
286  * Initialize the Agar-GUI library. This is called internally by
287  * AG_InitGraphics().
288  *
289  * As an alternative to AG_InitGraphics(), applications may also invoke
290  * AG_InitGUI() directly (following one or more AG_DriverOpen() calls).
291  */
292 int
AG_InitGUI(Uint flags)293 AG_InitGUI(Uint flags)
294 {
295 	void **ops;
296 
297 	for (ops = &agStdWidgets[0]; *ops != NULL; ops++) {
298 		AG_RegisterClass(*ops);
299 	}
300 	agIcon_Init();
301 
302 	if (AG_InitTextSubsystem() == -1) {
303 		return (-1);
304 	}
305 	AG_InitWindowSystem();
306 	AG_InitAppMenu();
307 	return (0);
308 }
309 
310 /*
311  * Release all resources allocated by the Agar-GUI library. This is called
312  * directly from the application, or from AG_Destroy().
313  */
314 void
AG_DestroyGUI(void)315 AG_DestroyGUI(void)
316 {
317 	void **ops;
318 	AG_Object *drv, *drvNext;
319 	AG_Window *win;
320 
321 	AG_LockVFS(&agDrivers);
322 
323 	/* Destroy all windows */
324 #ifdef AG_DEBUG_GUI
325 	Debug(NULL, "AG_DestroyGUI()\n");
326 #endif
327 	OBJECT_FOREACH_CHILD(drv, &agDrivers, ag_object) {
328 		OBJECT_FOREACH_CHILD(win, drv, ag_window) {
329 #ifdef AG_DEBUG_GUI
330 			Debug(drv, "Freeing Window %s (\"%s\")\n", OBJECT(win)->name, win->caption);
331 #endif
332 			AG_ObjectDetach(win);
333 		}
334 	}
335 	while (!TAILQ_EMPTY(&agWindowDetachQ)) {
336 		AG_WindowProcessDetachQueue();
337 	}
338 
339 	for (drv = TAILQ_FIRST(&agDrivers.children);
340 	     drv != TAILQ_END(&agDrivers.children);
341 	     drv = drvNext) {
342 		drvNext = TAILQ_NEXT(drv, cobjs);
343 #ifdef AG_DEBUG_GUI
344 		Debug(drv, "Freeing Driver %s\n", OBJECT(drv)->name);
345 #endif
346 		TAILQ_INIT(&drv->children);
347 		AG_DriverClose((AG_Driver *)drv);
348 	}
349 
350 	agDriverSw = NULL;
351 	agDriverMw = NULL;
352 	agDriverOps = NULL;
353 	AG_UnlockVFS(&agDrivers);
354 
355 	AG_DestroyAppMenu();
356 	AG_DestroyWindowSystem();
357 	AG_DestroyTextSubsystem();
358 	agIcon_Destroy();
359 
360 	for (ops = &agStdWidgets[0]; *ops != NULL; ops++)
361 		AG_UnregisterClass(*ops);
362 
363 	AG_DestroyGUIGlobals();
364 }
365 
366 /* Break out of the event loop. */
367 void
AG_QuitGUI(void)368 AG_QuitGUI(void)
369 {
370 	AG_Terminate(0);
371 }
372 
373 /*
374  * Initialize the Agar-GUI library. If spec is non-NULL, select the driver
375  * by priority from a comma-separated list. If spec is NULL, try to match
376  * the "best" driver for the current platform.
377  *
378  * Note: Instead of using AG_InitGraphics(), applications may also use the
379  * low-level AG_DriverOpen(), AG_InitGUI() and AG_DestroyGUI() interface
380  * (as is needed when creating multiple driver instances).
381  */
382 int
AG_InitGraphics(const char * spec)383 AG_InitGraphics(const char *spec)
384 {
385 	char specBuf[128], *s, *sOpts = "", *tok;
386 	AG_Driver *drv = NULL;
387 	AG_DriverClass *dc = NULL;
388 	int i;
389 	size_t len;
390 
391 	if (AG_InitGUIGlobals() == -1)
392 		return (-1);
393 
394 	if (agDriverMw != NULL || agDriverSw != NULL) {
395 		AG_SetError(_("Root driver already initialized"));
396 		goto fail;
397 	}
398 	if (spec != NULL && spec[0] != '\0') {
399 		Strlcpy(specBuf, spec, sizeof(specBuf));
400 		s = &specBuf[0];
401 
402 		if (strncmp(s, "<OpenGL>", 8) == 0) {
403 			/*
404 			 * Select preferred OpenGL-compatible driver.
405 			 */
406 			sOpts = &s[8];
407 			for (i = 0; i < agDriverListSize; i++) {
408 				dc = agDriverList[i];
409 				if (dc->flags & AG_DRIVER_OPENGL &&
410 				   (drv = AG_DriverOpen(dc)) != NULL)
411 					break;
412 			}
413 			if (i == agDriverListSize) {
414 				AG_SetError(_("No OpenGL drivers are available"));
415 				goto fail;
416 			}
417 		} else if (strncmp(s, "<SDL>", 5) == 0) {
418 			/*
419 			 * Select preferred SDL-compatible driver.
420 			 */
421 			sOpts = &s[5];
422 			for (i = 0; i < agDriverListSize; i++) {
423 				dc = agDriverList[i];
424 				if (dc->flags & AG_DRIVER_SDL &&
425 				   (drv = AG_DriverOpen(dc)) != NULL)
426 					break;
427 			}
428 			if (i == agDriverListSize) {
429 				AG_SetError(_("No SDL drivers are available"));
430 				goto fail;
431 			}
432 		} else {
433 			/*
434 			 * Try explicit list of preferred drivers.
435 			 */
436 			while ((tok = AG_Strsep(&s, ",;")) != NULL) {
437 				for (i = 0; i < agDriverListSize; i++) {
438 					dc = agDriverList[i];
439 					len = strlen(dc->name);
440 					if (strncmp(dc->name, tok, len) == 0 &&
441 					    (tok[len] == '\0' || tok[len] == '(') &&
442 					    (drv = AG_DriverOpen(dc)) != NULL) {
443 						sOpts = &tok[len];
444 						break;
445 					}
446 				}
447 				if (i < agDriverListSize)
448 					break;
449 			}
450 			if (tok == NULL) {
451 				char availDrvs[256];
452 
453 				for (availDrvs[0] = '\0', i = 0;
454 				     i < agDriverListSize; i++) {
455 					dc = agDriverList[i];
456 					Strlcat(availDrvs, " ", sizeof(availDrvs));
457 					Strlcat(availDrvs, dc->name, sizeof(availDrvs));
458 				}
459 				AG_SetError(_("Agar driver is not available: \"%s\"\n"
460 				              "(compiled-in drivers: <%s >)"),
461 					      specBuf, availDrvs);
462 				goto fail;
463 			}
464 		}
465 	} else {
466 		/*
467 		 * Auto-select best available driver.
468 		 */
469 		for (i = 0; i < agDriverListSize; i++) {
470 			dc = agDriverList[i];
471 			if ((drv = AG_DriverOpen(dc)) != NULL)
472 				break;
473 		}
474 		if (i == agDriverListSize) {
475 			AG_SetError(_("No graphics drivers are available"));
476 			goto fail;
477 		}
478 	}
479 
480 	/* Process driver options */
481 	if (sOpts[0] == '(' && sOpts[1] != '\0') {
482 		char *key, *val, *ep;
483 
484 		sOpts++;
485 		if ((ep = strrchr(sOpts, ')')) != NULL) {
486 			*ep = '\0';
487 		} else {
488 			Verbose(_("Syntax error in driver options: %s\n"), sOpts);
489 		}
490 		while ((tok = AG_Strsep(&sOpts, ":")) != NULL) {
491 			if ((key = AG_Strsep(&tok, "=")) != NULL) {
492 				if (Strcasecmp(key, "stereo") == 0) {
493 					agStereo = 1;
494 					continue;
495 				}
496 				if ((val = AG_Strsep(&tok, "=")) != NULL) {
497 					AG_SetString(drv, key, val);
498 				} else {
499 					AG_SetString(drv, key, "");
500 				}
501 			}
502 		}
503 	}
504 
505 	switch (dc->wm) {
506 	case AG_WM_MULTIPLE:
507 		agDriverMw = AGDRIVER_MW(drv);
508 		break;
509 	case AG_WM_SINGLE:
510 		if (AGDRIVER_SW_CLASS(drv)->openVideo(drv, 0,0,0,
511 		    AG_VIDEO_RESIZABLE) == -1) {
512 			AG_SetError("%s: %s", OBJECT(drv)->name,
513 			    AG_GetError());
514 			goto fail_close;
515 		}
516 		agDriverSw = AGDRIVER_SW(drv);
517 		break;
518 	}
519 	agDriverOps = dc;
520 
521 	if (AG_InitGUI(0) == -1) {
522 		goto fail_close;
523 	}
524 	return (0);
525 fail_close:
526 	if (drv != NULL) { AG_DriverClose(drv); }
527 	agDriverOps = NULL;
528 	agDriverSw = NULL;
529 	agDriverMw = NULL;
530 fail:
531 	AG_DestroyGUIGlobals();
532 	return (-1);
533 }
534 
535 /* Provided for symmetry. */
536 void
AG_DestroyGraphics(void)537 AG_DestroyGraphics(void)
538 {
539 	AG_DestroyGUI();
540 }
541 
542 /*
543  * Zoom in/out the GUI elements of the active window, changing
544  * the default font size accordingly. Depending on style settings,
545  * the default font size of a window may affect that of its logical
546  * child windows as well.
547  *
548  * It is customary to assign AG_GlobalKeys(3) shortcuts to those
549  * routines. Most users will expect Ctrl+{Plus,Minus,0} to work.
550  */
551 void
AG_ZoomIn(void)552 AG_ZoomIn(void)
553 {
554 	AG_Window *win;
555 
556 	AG_LockVFS(&agDrivers);
557 	if ((win = agWindowFocused) != NULL) {
558 		AG_WindowSetZoom(win, win->zoom+1);
559 	} else {
560 		Verbose("No window is focused for zoom\n");
561 	}
562 	AG_UnlockVFS(&agDrivers);
563 }
564 void
AG_ZoomOut(void)565 AG_ZoomOut(void)
566 {
567 	AG_Window *win;
568 
569 	AG_LockVFS(&agDrivers);
570 	if ((win = agWindowFocused) != NULL) {
571 		AG_WindowSetZoom(win, win->zoom-1);
572 	} else {
573 		Verbose("No window is focused for zoom-out\n");
574 	}
575 	AG_UnlockVFS(&agDrivers);
576 }
577 void
AG_ZoomReset(void)578 AG_ZoomReset(void)
579 {
580 	AG_Window *win;
581 
582 	AG_LockVFS(&agDrivers);
583 	if ((win = agWindowFocused) != NULL) {
584 		AG_WindowSetZoom(win, AG_ZOOM_DEFAULT);
585 	} else {
586 		Verbose("No window is focused for zoom-in\n");
587 	}
588 	AG_UnlockVFS(&agDrivers);
589 }
590 
591 #ifdef AG_LEGACY
592 /*
593  * Initialize Agar with a single-window driver of specified resolution.
594  * As of Agar-1.4, this interface obsolete but kept for backward compat.
595  */
596 int
AG_InitVideo(int w,int h,int depth,Uint flags)597 AG_InitVideo(int w, int h, int depth, Uint flags)
598 {
599 	AG_Driver *drv = NULL;
600 	AG_DriverClass *dc = NULL;
601 	int i;
602 
603 	if (AG_InitGUIGlobals() == -1) {
604 		return (-1);
605 	}
606 	if (agDriverMw != NULL || agDriverSw != NULL) {
607 		AG_SetError("Root driver already initialized");
608 		goto fail;
609 	}
610 	if (depth < 1 || w < 16 || h < 16) {
611 		AG_SetError("Resolution too small");
612 		goto fail;
613 	}
614 	if (flags & (AG_VIDEO_OPENGL|AG_VIDEO_OPENGL_OR_SDL)) {
615 		for (i = 0; i < agDriverListSize; i++) {
616 			dc = agDriverList[i];
617 			if (dc->wm == AG_WM_SINGLE &&
618 			    (dc->flags & AG_DRIVER_OPENGL) &&
619 			    (drv = AG_DriverOpen(dc)) != NULL)
620 				break;
621 		}
622 		if (i == agDriverListSize) {
623 			if (flags & AG_VIDEO_OPENGL_OR_SDL) {
624 				for (i = 0; i < agDriverListSize; i++) {
625 					dc = agDriverList[i];
626 					if (dc->wm == AG_WM_SINGLE &&
627 					    (dc->flags & AG_DRIVER_SDL) &&
628 					    (drv = AG_DriverOpen(dc)) != NULL)
629 						break;
630 				}
631 				if (i == agDriverListSize) {
632 					AG_SetError("SDL/GL not available");
633 					goto fail;
634 				}
635 			} else {
636 				AG_SetError("GL not available");
637 				goto fail;
638 			}
639 		}
640 	} else if (flags & AG_VIDEO_SDL) {
641 		for (i = 0; i < agDriverListSize; i++) {
642 			dc = agDriverList[i];
643 			if (dc->wm == AG_WM_SINGLE &&
644 			    (dc->flags & AG_DRIVER_SDL) &&
645 			    (drv = AG_DriverOpen(dc)) != NULL)
646 				break;
647 		}
648 		if (i == agDriverListSize) {
649 			AG_SetError("SDL not available");
650 			goto fail;
651 		}
652 	} else {
653 		for (i = 0; i < agDriverListSize; i++) {
654 			dc = agDriverList[i];
655 			if (dc->wm == AG_WM_SINGLE &&
656 			    (drv = AG_DriverOpen(dc)) != NULL)
657 				break;
658 		}
659 		if (i == agDriverListSize) {
660 			AG_SetError("No graphics drivers are available");
661 			goto fail;
662 		}
663 	}
664 	if (AGDRIVER_SW_CLASS(drv)->openVideo(drv, w,h, depth, flags) == -1) {
665 		AG_DriverClose(drv);
666 		goto fail;
667 	}
668 	if (AG_InitGUI(0) == -1) {
669 		AG_DriverClose(drv);
670 		goto fail;
671 	}
672 	agDriverOps = dc;
673 	agDriverSw = AGDRIVER_SW(drv);
674 	return (0);
675 fail:
676 	AG_DestroyGUIGlobals();
677 	return (-1);
678 }
679 void
AG_DestroyVideo(void)680 AG_DestroyVideo(void)
681 {
682 	AG_DestroyGUI();
683 }
684 #endif /* AG_LEGACY */
685