/* * Copyright (c) 2009-2023 Julien Nadeau Carriere * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Main Agar-GUI initialization. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Import icon bitmap data */ #include static struct { const char *_Nonnull key; int *_Nonnull p; } agGUIOptions[] = { { "ClipboardIntegration", &agClipboardIntegration }, { "KbdDelay", &agKbdDelay }, { "KbdRepeat", &agKbdRepeat }, { "MouseDblclickDelay", &agMouseDblclickDelay }, { "MouseSpinDelay", &agMouseSpinDelay }, { "MouseSpinIval", &agMouseSpinIval }, { "MouseScrollIval", &agMouseScrollIval }, { "ScrollButtonIval", &agScrollButtonIval }, { "PageIncrement", &agPageIncrement }, { "AutocompleteDelay", &agAutocompleteDelay }, { "AutocompleteRate", &agAutocompleteRate }, { "LatinInput", &agLatinInput }, { "LatinComposition", &agLatinComposition }, { "ScreenshotQuality", &agScreenshotQuality }, { "TextTabWidth", &agTextTabWidth }, { "TextBlinkRate", &agTextBlinkRate }, { "GLdebugOutput", &agGLdebugOutput }, { "GLuseNPOT", &agGLuseNPOT }, { "CtrlMouseWheelAction", &agCtrlMouseWheelAction }, }; const Uint agGUIOptionCount = sizeof(agGUIOptions) / sizeof(agGUIOptions[0]); void *agStdClasses[] = { &agDriverClass, &agDriverSwClass, &agDriverMwClass, &agInputDeviceClass, &agMouseClass, &agKeyboardClass, &agJoystickClass, &agControllerClass, &agFontClass, &agFontBfClass, #ifdef HAVE_FREETYPE &agFontFtClass, #endif NULL }; void *agStdWidgets[] = { &agWidgetClass, &agWindowClass, #ifdef AG_WIDGETS &agBoxClass, &agButtonClass, &agCheckboxClass, &agComboClass, &agConsoleClass, &agEditableClass, &agDirDlgClass, &agFontSelectorClass, &agFileDlgClass, &agFixedClass, &agFixedPlotterClass, &agGraphClass, # ifdef HAVE_OPENGL &agGLViewClass, # endif &agHSVPalClass, &agIconClass, &agLabelClass, &agMenuClass, &agMenuViewClass, &agMFSpinbuttonClass, &agMPaneClass, &agMSpinbuttonClass, &agNotebookClass, &agNotebookTabClass, &agNumericalClass, &agObjectSelectorClass, &agPaneClass, &agPixmapClass, &agProgressBarClass, &agRadioClass, &agScrollbarClass, &agScrollviewClass, &agSeparatorClass, &agSliderClass, &agSocketClass, &agStatusbarClass, &agTitlebarClass, &agTableClass, &agTreetblClass, &agTextboxClass, &agTlistClass, &agToolbarClass, &agUComboClass, #endif /* AG_WIDGETS */ NULL }; static int initedGlobals = 0; /* GUI globals are initialized */ int agGUI = 0; /* GUI is initialized */ int agRenderingContext = 0; /* In rendering context */ int agStereo = 0; /* Stereoscopic display */ int agXsync = 0; /* Synchronous X events */ int agClipboardIntegration = 1; /* Native clipboard integration */ int agKbdDelay = 250; /* Key repeat delay */ int agKbdRepeat = 30; /* Key repeat interval */ int agMouseDblclickDelay = 250; /* Mouse double-click delay */ int agMouseSpinDelay = 350; /* Spinbutton repeat delay */ int agMouseSpinIval = 30; /* Spinbutton repeat interval */ int agMouseScrollIval = 1; /* Scrollbar increment interval */ int agScrollButtonIval = 100; /* Scrollbar button interval */ int agPageIncrement = 4; /* Pgup/Pgdn scrolling increment */ int agAutocompleteDelay = 1; /* Delay before autocomplete (ms) */ int agAutocompleteRate = 80; /* Autocomplete refresh rate (ms) */ int agScreenshotQuality = 100; /* JPEG quality in % */ int agLatinInput = 1; /* Latin character input with Alt/Shift */ int agLatinComposition = 0; /* Latin character composition */ int agTextTabWidth = 40; /* Tab width (px) */ int agTextBlinkRate = 500; /* Cursor blink rate (ms) */ int agGLdebugOutput = 0; /* Enable GL_DEBUG_OUTPUT */ int agGLuseNPOT = 1; /* Use non-power-of-two textures */ int agCtrlMouseWheelAction = 1; /* Ctrl + Mouse Wheel action (1=zoom) */ /* GUI zoom levels */ double agZoomValues[AG_ZOOM_MAX] = { 25.0, 31.25, 37.5, 43.75, 46.875, 50.0, 53.125, 56.25, 59.375, 62.5, 65.625, 68.75, 71.875, 75.0, 78.125, 81.25, 87.5, 93.75, 100.0, 106.25, 112.5, 118.75, 125.0, 137.5, 150.0, 162.5, 175.0, 187.5, 212.5, 237.5, 262.5, 312.5 }; /* * Initialize the Agar-GUI globals and built-in classes. This function * is invoked internally prior to graphics initialization. */ int AG_InitGUIGlobals(void) { void **cl; AG_DriverClass **pd; int i; if (initedGlobals++ > 0) { return (0); } for (cl = &agStdClasses[0]; *cl != NULL; cl++) AG_RegisterClass(*cl); for (pd = &agDriverList[0]; *pd != NULL; pd++) AG_RegisterClass(*pd); AG_InitGlobalKeys(); AG_EditableInitClipboards(); /* Set a default recommended surface format. */ if ((agSurfaceFmt = TryMalloc(sizeof(AG_PixelFormat))) == NULL) { return (-1); } AG_PixelFormatRGBA(agSurfaceFmt, 32, #if AG_BYTEORDER == AG_BIG_ENDIAN 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff #else 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 #endif ); /* Initialize the blitter tables. */ /* TODO mprotect() */ for (i = 0; i < AG_SURFACE_MODE_LAST; i++) { const int count = agLowerBlits_Std_Count[i]; const AG_Size size = (count * sizeof(AG_LowerBlit)); agLowerBlits[i] = Malloc(size); memcpy(agLowerBlits[i], agLowerBlits_Std[i], size); agLowerBlits_Count[i] = count; } agGUI = 1; agRenderingContext = 0; AG_ObjectInit(&agDrivers, &agObjectClass); AG_ObjectSetName(&agDrivers, "agDrivers"); agDrivers.flags |= AG_OBJECT_STATIC; AG_ObjectInit(&agInputDevices, &agObjectClass); AG_ObjectSetName(&agInputDevices, "agInputDevices"); agInputDevices.flags |= AG_OBJECT_STATIC; { #ifdef AG_DEBUG const int dbgLvlSave = agDebugLvl; /* Mute */ agDebugLvl = 0; #endif for (i = 0; i < agGUIOptionCount; i++) { AG_BindInt(agConfig, agGUIOptions[i].key, agGUIOptions[i].p); } #ifdef AG_DEBUG agDebugLvl = dbgLvlSave; /* Unmute */ #endif if (AG_LoadStyleSheet(NULL, "_agStyleDefault") == NULL) AG_Verbose("Error loading stylesheet: %s\n", AG_GetError()); } return (0); } /* * Destroy the Agar-GUI globals and built-in classes. This function * is invoked internally after the graphics subsystem is destroyed. */ void AG_DestroyGUIGlobals(void) { AG_DriverClass **pd; void **pcl; Uint i; if (--initedGlobals > 0) return; AG_DestroyStyleSheet(&agDefaultCSS); { #ifdef AG_DEBUG int debugLvlSave; #endif Debug_Mute(debugLvlSave); for (i = 0; i < agGUIOptionCount; i++) { AG_Unset(agConfig, agGUIOptions[i].key); } Debug_Unmute(debugLvlSave); } AG_ObjectDestroy(&agInputDevices); #ifndef __APPLE__ /* XXX mutex issue */ AG_ObjectDestroy(&agDrivers); #endif AG_PixelFormatFree(agSurfaceFmt); free(agSurfaceFmt); agSurfaceFmt = NULL; for (i = 0; i < AG_SURFACE_MODE_LAST; i++) free(agLowerBlits[i]); AG_EditableDestroyClipboards(); AG_DestroyGlobalKeys(); for (pd = &agDriverList[0]; *pd != NULL; pd++) AG_UnregisterClass(*pd); for (pcl = &agStdClasses[0]; *pcl != NULL; pcl++) AG_UnregisterClass(*pcl); agRenderingContext = 0; agGUI = 0; } /* * Initialize the Agar-GUI library. This is called internally by * AG_InitGraphics(). * * As an alternative to AG_InitGraphics(), applications may also invoke * AG_InitGUI() directly (following one or more AG_DriverOpen() calls). */ int AG_InitGUI(Uint flags) { void **ops; /* Register standard GUI widget classes. */ for (ops = &agStdWidgets[0]; *ops != NULL; ops++) AG_RegisterClass(*ops); /* Initialize the statically-compiled icon data. */ agIcon_Init(); /* Start the font engine. */ if (AG_InitTextSubsystem() == -1) return (-1); /* Initialize global Window lists and pointers. */ TAILQ_INIT(&agWindowDetachQ); TAILQ_INIT(&agWindowShowQ); TAILQ_INIT(&agWindowHideQ); agWindowToFocus = NULL; agWindowFocused = NULL; return (0); } /* * Release all resources allocated by the Agar-GUI library. This is called * directly from the application, or from AG_Destroy(). */ void AG_DestroyGUI(void) { void **ops; AG_Object *drv, *drvNext; AG_Window *win; AG_LockVFS(&agDrivers); /* Destroy all windows */ OBJECT_FOREACH_CHILD(drv, &agDrivers, ag_object) { OBJECT_FOREACH_CHILD(win, drv, ag_window) { AG_ObjectDetach(win); } } while (!TAILQ_EMPTY(&agWindowDetachQ)) { AG_WindowProcessDetachQueue(); } for (drv = TAILQ_FIRST(&agDrivers.children); drv != TAILQ_END(&agDrivers.children); drv = drvNext) { drvNext = TAILQ_NEXT(drv, cobjs); TAILQ_INIT(&drv->children); AG_DriverClose((AG_Driver *)drv); } agDriverSw = NULL; agDriverMw = NULL; agDriverOps = NULL; AG_UnlockVFS(&agDrivers); AG_DestroyTextSubsystem(); agIcon_Destroy(); for (ops = &agStdWidgets[0]; *ops != NULL; ops++) AG_UnregisterClass(*ops); AG_DestroyGUIGlobals(); } #ifdef AG_EVENT_LOOP /* * Break out of the event loop. */ void AG_QuitGUI(void) { AG_Terminate(0); } #endif /* * Initialize the Agar-GUI library. If spec is non-NULL, select the driver * by priority from a comma-separated list. If spec is NULL, try to match * the "best" driver for the current platform. * * Note: Instead of using AG_InitGraphics(), applications may also use the * low-level AG_DriverOpen(), AG_InitGUI() and AG_DestroyGUI() interface * (as is needed when creating multiple driver instances). */ int AG_InitGraphics(const char *spec) { char specBuf[128], *s, *tok; AG_Driver *drv = NULL; AG_DriverClass *dc = NULL, **pd; if (AG_InitGUIGlobals() == -1) return (-1); if (agDriverMw != NULL || agDriverSw != NULL) { AG_SetErrorS("agDriver is already set"); goto fail; } if (spec != NULL && spec[0] != '\0') { Strlcpy(specBuf, spec, sizeof(specBuf)); s = &specBuf[0]; if (Strncasecmp(s, "", 8) == 0) { /* Any GL driver */ for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->flags & AG_DRIVER_OPENGL && (drv = AG_DriverOpen(*pd, spec)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetErrorS(_("No OpenGL drivers are available")); goto fail; } } else if (Strncasecmp(s, "", 5) == 0) { /* SDL 1 or 2 */ for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->flags & AG_DRIVER_SDL && (drv = AG_DriverOpen(*pd, spec)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetErrorS(_("No SDL drivers are available")); goto fail; } } else if (Strncasecmp(s, "", 5) == 0) { /* SDL 1.2 */ for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->flags & AG_DRIVER_SDL1 && (drv = AG_DriverOpen(*pd, spec)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetErrorS(_("No SDL1 drivers are available")); goto fail; } } else if (Strncasecmp(s, "", 5) == 0) { /* SDL 2.0 */ for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->flags & AG_DRIVER_SDL2 && (drv = AG_DriverOpen(*pd, spec)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetErrorS(_("No SDL2 drivers are available")); goto fail; } } else if (Strncasecmp(s, "", 4) == 0) { /* Any framebuffer driver */ for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->type == AG_FRAMEBUFFER && (drv = AG_DriverOpen(*pd, spec)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetErrorS(_("No framebuffer drivers are available")); goto fail; } } else { /* * Try explicit list of preferred drivers. */ while ((tok = AG_Strsep(&s, ",;")) != NULL) { for (pd = &agDriverList[0]; *pd != NULL; pd++) { size_t len = strlen((*pd)->name); if (strncmp((*pd)->name, tok, len) == 0 && (tok[len] == '\0' || tok[len] == '(') && (drv = AG_DriverOpen(*pd, spec)) != NULL) { dc = *pd; break; } } if (dc != NULL) break; } if (tok == NULL) { AG_SetError(_("No such Agar driver: \"%s\""), specBuf); AG_SetErrorCode(AG_ENOENT); goto fail; } } } else { /* * Auto-select best available driver. */ for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((drv = AG_DriverOpen(*pd, NULL)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetError(_("No Agar drivers are available")); goto fail; } } switch (dc->wm) { case AG_WM_MULTIPLE: agDriverMw = AGDRIVERMW(drv); break; case AG_WM_SINGLE: if (AGDRIVER_SW_CLASS(drv)->openVideo(drv, 0,0, 0, AG_VIDEO_RESIZABLE) == -1) { AG_SetError("%s: openVideo: %s", OBJECT(drv)->name, AG_GetError()); goto fail_close; } agDriverSw = AGDRIVERSW(drv); break; } agDriverOps = dc; if (AG_InitGUI(0) == -1) { goto fail_close; } return (0); fail_close: if (drv != NULL) { AG_DriverClose(drv); } agDriverOps = NULL; agDriverSw = NULL; agDriverMw = NULL; fail: AG_DestroyGUIGlobals(); return (-1); } /* Provided for symmetry. */ void AG_DestroyGraphics(void) { AG_DestroyGUI(); } /* Close any expanded Menu or Combo expansion window prior to zooming. */ static void CloseMenuWindows(void) { AG_Driver *drv; AGOBJECT_FOREACH_CHILD(drv, &agDrivers, ag_driver) { AG_Window *win; AG_FOREACH_WINDOW(win, drv) { if (win->visible && (win->wmType == AG_WINDOW_WM_DROPDOWN_MENU || win->wmType == AG_WINDOW_WM_POPUP_MENU || win->wmType == AG_WINDOW_WM_COMBO)) { AG_PostEvent(win, "window-close", NULL); } } } } /* * Zoom in/out the GUI elements of the active window, changing * the default font size accordingly. Depending on style settings, * the default font size of a window may affect that of its logical * child windows as well. * * It is customary to assign AG_GlobalKeys(3) shortcuts to those * routines. Most users will expect Ctrl+{Plus,Minus,0} to work. */ void AG_ZoomIn(void) { AG_Window *win; AG_LockVFS(&agDrivers); CloseMenuWindows(); if ((win = agWindowFocused) != NULL) { AG_WindowSetZoom(win, win->zoom+1); } AG_UnlockVFS(&agDrivers); } void AG_ZoomOut(void) { AG_Window *win; AG_LockVFS(&agDrivers); CloseMenuWindows(); if ((win = agWindowFocused) != NULL) { AG_WindowSetZoom(win, win->zoom-1); } AG_UnlockVFS(&agDrivers); } void AG_ZoomReset(void) { AG_Window *win; AG_LockVFS(&agDrivers); CloseMenuWindows(); if ((win = agWindowFocused) != NULL) { AG_WindowSetZoom(win, AG_ZOOM_DEFAULT); } AG_UnlockVFS(&agDrivers); } /* Generate "About Agar" dialog window. */ void AG_About(AG_Event *event) { char path[AG_PATHNAME_MAX]; AG_Window *win; AG_Box *box; AG_Textbox *tb; FILE *f; if ((win = AG_WindowNewNamedS(0, "_agAbout")) == NULL) { return; } AG_WindowSetCaption(win, _("About Agar GUI")); AG_WindowSetCloseAction(win, AG_WINDOW_DETACH); box = AG_BoxNewHoriz(win, AG_BOX_HFILL); { AG_AgarVersion av; AG_Box *vbox; if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "sq-agar.bmp", path, sizeof(path)) == 0) { AG_Pixmap *px; px = AG_PixmapFromFile(box, 0, path); AG_SetPadding(px, "5"); } AG_GetVersion(&av); vbox = AG_BoxNewVert(box, AG_BOX_HFILL); { AG_CPUInfo cpu; AG_Label *lbl; #if AG_MODEL == AG_LARGE const char *memModel = _("Large"); #else const char *memModel = _("Medium"); #endif AG_GetCPUInfo(&cpu); lbl = AG_LabelNew(vbox, 0, AGSI_BOLD "Agar %d.%d.%d" AGSI_RST " (\"" AGSI_CYAN "%s" AGSI_RST "\")\n" "on " AGSI_YEL AGSI_LEAGUE_SPARTAN "%s" AGSI_RST " (" AGSI_ITALIC "%s" AGSI_RST ")", av.major, av.minor, av.patch, (av.release) ? av.release : "beta", cpu.arch, memModel); AG_SetFontSize(lbl, "140%"); lbl = AG_LabelNewS(vbox, AG_LABEL_HFILL, AGSI_BR_CYAN "https://www.libAgar.org/\n" AGSI_RST AGSI_BR_YEL "https://patreon.com/libAgar/"); AG_SetFontFamily(lbl, "monoalgue"); AG_SetColor(lbl, "darkblue"); AG_SetPadding(lbl, "3 5 3 0"); AG_LabelJustify(lbl, AG_TEXT_RIGHT); } } tb = AG_TextboxNewS(win, AG_TEXTBOX_MULTILINE | AG_TEXTBOX_EXPAND | AG_TEXTBOX_READONLY | AG_TEXTBOX_WORDWRAP, NULL); AG_SetFontFamily(tb, "monoalgue"); AG_TextboxSizeHintPixels(tb, 550, 0); AG_TextboxSizeHintLines(tb, 22); if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "license.txt", path, sizeof(path)) == 0 && (f = fopen(path, "r")) != NULL) { char *s; long size; size_t ret; fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); if (size > 0) { s = Malloc((AG_Size)size + 1); ret = fread(s, (size_t)size, 1, f); } else { s = NULL; ret = 0; } fclose(f); if (size > 0 && ret == 1) { s[size] = '\0'; AG_TextboxBindASCII(tb, s, size); } else { AG_TextboxPrintf(tb, _("Failed to read license.txt")); } } else { AG_TextboxPrintf(tb, _("Failed to open license.txt")); } AG_ButtonNewFn(win, AG_BUTTON_HFILL, _("Close"), AGWINCLOSE(win)); AG_WindowShow(win); } #ifdef AG_LEGACY /* * Initialize Agar with a single-window driver of specified resolution. * Kept for backward compatibility with Agar < 1.4. */ int AG_InitVideo(int w, int h, int depth, Uint flags) { AG_Driver *drv = NULL; AG_DriverClass *dc = NULL, **pd; if (AG_InitGUIGlobals() == -1) { return (-1); } if (agDriverMw != NULL || agDriverSw != NULL) { AG_SetErrorS("agDriver is already set"); goto fail; } if (depth < 1 || w < 16 || h < 16) { AG_SetError("Resolution too small"); goto fail; } if (flags & (AG_VIDEO_OPENGL | AG_VIDEO_OPENGL_OR_SDL)) { for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->wm == AG_WM_SINGLE && ((*pd)->flags & AG_DRIVER_OPENGL) && (drv = AG_DriverOpen(*pd, NULL)) != NULL) { dc = *pd; break; } } if (dc == NULL) { if (flags & AG_VIDEO_OPENGL_OR_SDL) { for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->wm == AG_WM_SINGLE && ((*pd)->flags & AG_DRIVER_SDL) && (drv = AG_DriverOpen(*pd, NULL)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetError("SDL/GL not available"); goto fail; } } else { AG_SetError("GL not available"); goto fail; } } } else if (flags & AG_VIDEO_SDL) { for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->wm == AG_WM_SINGLE && ((*pd)->flags & AG_DRIVER_SDL) && (drv = AG_DriverOpen(*pd, NULL)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetError("SDL not available"); goto fail; } } else { for (pd = &agDriverList[0]; *pd != NULL; pd++) { if ((*pd)->wm == AG_WM_SINGLE && (drv = AG_DriverOpen(*pd, NULL)) != NULL) { dc = *pd; break; } } if (dc == NULL) { AG_SetError("No graphics drivers are available"); goto fail; } } if (AGDRIVER_SW_CLASS(drv)->openVideo(drv, w,h, depth, flags) == -1) { AG_DriverClose(drv); goto fail; } if (AG_InitGUI(0) == -1) { AG_DriverClose(drv); goto fail; } agDriverOps = dc; agDriverSw = AGDRIVERSW(drv); return (0); fail: AG_DestroyGUIGlobals(); return (-1); } void AG_DestroyVideo(void) { AG_DestroyGUI(); } #endif /* AG_LEGACY */