/* * Copyright (c) 2022-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. */ /* * Multi-window Driver for OpenGL graphics via SDL2. * One GL context is created for each Agar window. */ #include #include #include #include #include #include #include #include #include #if defined(AG_WIDGETS) && defined(AG_DEBUG) #include #include #include #include #endif /* #define DEBUG_DISPLAY */ static int nDrivers = 0; /* Drivers open */ static int initedSDL = 0; /* Inited TIMERS and EVENTS */ AG_EventSink *_Nullable sdl2mwEventEpilogue = NULL; /* Event sink epilogue */ AG_EventSink *_Nullable sdl2mwEventSpinner = NULL; /* For agTimeOps_renderer */ enum ag_sdl2mw_out { AG_SDL2MW_OUT_NONE, /* No capture */ AG_SDL2MW_OUT_JPEG, /* Output JPEG files */ AG_SDL2MW_OUT_PNG /* Output PNG files */ }; typedef struct ag_sdl2mw_driver { struct ag_driver_mw _inherit; /* AG_Driver -> AG_DriverMw */ SDL_Window *_Nullable window; /* SDL window */ AG_GL_Context gl; /* Agar OpenGL context data */ SDL_GLContext glCtx; /* SDL per-window OpenGL context */ Uint8 *_Nullable outBuf; /* Output capture buffer */ char *_Nullable outPath; /* Output capture path (%N = window name) */ enum ag_sdl2mw_out outMode; /* Output capture mode */ Uint outFrame; /* Capture frame# counter */ Uint outLast; /* Terminate after this many frames */ Uint outJpegQual; /* Quality (%) for jpeg output */ Uint outJpegFlags;/* DCT options */ Uint32 _pad; } AG_DriverSDL2MW; AG_DriverMwClass agDriverSDL2MW; static int SDL2MW_ProcessEvent(void *, AG_DriverEvent *); static void Init(void *_Nonnull obj) { AG_DriverMw *dmw = obj; AG_DriverSDL2MW *smw = obj; /* * Advertise this driver's capability of creating windows at * undefined positions. */ dmw->flags |= AG_DRIVER_MW_ANYPOS_AVAIL; smw->window = NULL; /* Remains NULL for the root driver instance */ smw->outBuf = NULL; smw->outPath = NULL; smw->outMode = AG_SDL2MW_OUT_NONE; smw->outFrame = 0; smw->outLast = 0; smw->outJpegQual = 100; smw->outJpegFlags = 0; } static int SDL2MW_EventSink(AG_EventSink *es, AG_Event *event) { AG_DriverEvent dev; AG_Driver *drv = AG_DRIVER_PTR(1); if (SDL_PollEvent(NULL) != 0) { while (AG_SDL2_GetNextEvent(drv, &dev) == 1) { if (dev.type != AG_DRIVER_UNKNOWN) (void)SDL2MW_ProcessEvent(drv, &dev); } } else { AG_Delay(1); } return (0); } static int SDL2MW_Open(void *_Nonnull obj, const char *_Nullable spec) { AG_Driver *drv = obj; AG_DriverSDL2MW *smw = obj; if (!initedSDL) { if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) == -1) { AG_SetError(_("SDL_Init() failed: %s"), SDL_GetError()); return (-1); } initedSDL = 1; } #if 0 /* Use SDL's time interface. */ AG_SetTimeOps(&agTimeOps_SDL); AG_DestroyEventSubsystem(); AG_InitEventSubsystem(AG_SOFT_TIMERS); #endif if ((drv->mouse = AG_MouseNew(drv, "SDL2 Mouse")) == NULL || (drv->kbd = AG_KeyboardNew(drv, "SDL2 Keyboard")) == NULL) goto fail; /* Driver manages rendering of window background. */ drv->flags |= AG_DRIVER_WINDOW_BG; if (nDrivers == 0) { /* Root driver instance */ #ifdef SDL_HINT_MOUSE_AUTO_CAPTURE if (AG_Defined(drv, "noAutoCapture") && AG_GetInt(drv, "noAutoCapture")) SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); #endif /* Enable the joystick subsystem if requested. */ if (AG_Defined(drv, "ctrl")) { Uint32 sdlFlags = SDL_INIT_GAMECONTROLLER; Debug(drv, "Enabling controller subsystem\n"); if (AG_Defined(drv, "haptic")) { sdlFlags |= SDL_INIT_HAPTIC; } if (SDL_InitSubSystem(sdlFlags) < 0) AG_Verbose("SDL_INIT_GAMECONTROLLER: %s\n", SDL_GetError()); } else if (AG_Defined(drv, "joy")) { Uint32 sdlFlags = SDL_INIT_JOYSTICK; Debug(drv, "Enabling joystick subsystem\n"); if (AG_Defined(drv, "haptic")) { sdlFlags |= SDL_INIT_HAPTIC; } if (SDL_InitSubSystem(sdlFlags) < 0) AG_Verbose("SDL_INIT_JOYSTICK: %s\n", SDL_GetError()); } /* * Parse global attributes for capturing to image files * ("out", "outFirst", "outLast", "jpegQual" and "jpegDCT"). */ if (AG_Defined(drv, "out")) { char buf[256]; char *ext; AG_GetString(drv, "out", buf, sizeof(buf)); Debug(drv, "Enabled capture (%s)\n", buf); if ((ext = strrchr(buf, '.')) != NULL && ext[1] != '\0') { if (Strcasecmp(&ext[1], "jpeg") == 0 || Strcasecmp(&ext[1], "jpg") == 0) { smw->outMode = AG_SDL2MW_OUT_JPEG; if ((smw->outPath = TryStrdup(buf)) == NULL) return (-1); } else if (Strcasecmp(&ext[1], "png") == 0) { smw->outMode = AG_SDL2MW_OUT_PNG; if ((smw->outPath = TryStrdup(buf)) == NULL) return (-1); } else { AG_SetError(_("Invalid out= argument: `%s'"), buf); return (-1); } if (AG_Defined(drv, "outFirst")) { AG_GetString(drv, "outFirst", buf, sizeof(buf)); smw->outFrame = atoi(buf); } else { smw->outFrame = 0; } if (AG_Defined(drv, "outLast")) { AG_GetString(drv, "outLast", buf, sizeof(buf)); smw->outLast = atoi(buf); } } if (AG_Defined(drv, "jpegQual")) { AG_GetString(drv, "jpegQual", buf, sizeof(buf)); smw->outJpegQual = atoi(buf); } if (AG_Defined(drv, "jpegDCT")) { AG_GetString(drv, "jpegDCT", buf, sizeof(buf)); if (Strcasecmp(buf, "islow")) { smw->outJpegFlags = AG_EXPORT_JPEG_JDCT_ISLOW; } else if (Strcasecmp(buf, "ifast")) { smw->outJpegFlags = AG_EXPORT_JPEG_JDCT_IFAST; } else if (Strcasecmp(buf, "float")) { smw->outJpegFlags = AG_EXPORT_JPEG_JDCT_FLOAT; } } } /* * Register event sink and epilogue routines for use by the * standard AG_EventLoop() routine. Custom event loops may * use alternate routines. */ if ((sdl2mwEventSpinner = AG_AddEventSpinner( SDL2MW_EventSink,"%p",drv)) == NULL) { goto fail; } if ((sdl2mwEventEpilogue = AG_AddEventEpilogue( AG_SDL2_EventEpilogue, NULL)) == NULL) { AG_DelEventSink(sdl2mwEventSpinner); sdl2mwEventSpinner = NULL; goto fail; } } nDrivers++; return (0); fail: if (drv->kbd != NULL) { AG_ObjectDelete(drv->kbd); drv->kbd = NULL; } if (drv->mouse != NULL) { AG_ObjectDelete(drv->mouse); drv->mouse = NULL; } return (-1); } static void SDL2MW_Close(void *_Nonnull obj) { AG_Driver *drv = obj; #ifdef AG_DEBUG if (nDrivers == 0) { AG_FatalError("Driver close without open"); } #endif if (drv->kbd) { AG_ObjectDetach(drv->kbd); AG_ObjectDestroy(drv->kbd); drv->kbd = NULL; } if (drv->mouse) { AG_ObjectDetach(drv->mouse); AG_ObjectDestroy(drv->mouse); drv->mouse = NULL; } if (--nDrivers == 0) { if (sdl2mwEventSpinner != NULL) { AG_DelEventSink(sdl2mwEventSpinner); sdl2mwEventSpinner = NULL; } if (AG_Defined(drv, "ctrl")) { Uint32 sdlFlags = SDL_INIT_GAMECONTROLLER; if (AG_Defined(drv, "haptic")) { sdlFlags |= SDL_INIT_HAPTIC; } SDL_QuitSubSystem(sdlFlags); } else if (AG_Defined(drv, "joy")) { Uint32 sdlFlags = SDL_INIT_JOYSTICK; if (AG_Defined(drv, "haptic")) { sdlFlags |= SDL_INIT_HAPTIC; } SDL_QuitSubSystem(sdlFlags); } } } static void SDL2MW_PostMoveCallback(AG_Window *_Nonnull win, AG_SizeAlloc *_Nonnull a) { AG_Driver *drv = WIDGET(win)->drv; AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)drv; AG_SizeAlloc wa; const int useText = (win->flags & AG_WINDOW_USE_TEXT); SDL_GL_MakeCurrent(smw->window, smw->glCtx); if (useText) { AG_PushTextState(); AG_TextFont(WIDGET(win)->font); AG_TextColor(&WIDGET(win)->pal.c[WIDGET(win)->state] [AG_TEXT_COLOR]); } wa.x = 0; wa.y = 0; wa.w = a->w; wa.h = a->h; AG_WidgetSizeAlloc(win, &wa); AG_WidgetUpdateCoords(win, 0,0); WIDGET(win)->x = a->x; WIDGET(win)->y = a->y; if (useText) AG_PopTextState(); win->dirty = 1; if (agWindowPinnedCount > 0) AG_WindowMovePinned(win, a->x - WIDGET(win)->x, a->y - WIDGET(win)->y); } static void SDL2MW_PostResizeCallback(AG_Window *_Nonnull win, AG_SizeAlloc *_Nonnull a) { AG_Driver *drv = WIDGET(win)->drv; AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)drv; AG_SizeAlloc wa; AG_Rect rVP; const int useText = (win->flags & AG_WINDOW_USE_TEXT); SDL_GL_MakeCurrent(smw->window, smw->glCtx); if (useText) { AG_PushTextState(); AG_TextFont(WIDGET(win)->font); AG_TextColor(&WIDGET(win)->pal.c[WIDGET(win)->state] [AG_TEXT_COLOR]); } wa.x = 0; wa.y = 0; wa.w = a->w; wa.h = a->h; AG_WidgetSizeAlloc(win, &wa); AG_WidgetUpdateCoords(win, 0,0); SDL_GetWindowPosition(smw->window, &WIDGET(win)->x, &WIDGET(win)->y); win->dirty = 1; if (useText) AG_PopTextState(); rVP.x = 0; rVP.y = 0; rVP.w = WIDTH(win); rVP.h = HEIGHT(win); AG_GL_SetViewport(&smw->gl, &rVP); } static int SDL2MW_ProcessEvent(void *obj, AG_DriverEvent *dev) { int rv = 0; AG_LockVFS(&agDrivers); if (AG_SDL2_ProcessEvent_MW(obj, dev) == 0) { AG_SizeAlloc a; AG_Window *win; if ((win = dev->win) == NULL) { rv = 0; goto out; } switch (dev->type) { case AG_DRIVER_EXPOSE: win->dirty = 1; break; case AG_DRIVER_MOVED: a.x = dev->moved.x; a.y = dev->moved.y; if (a.x != WIDGET(win)->x || a.y != WIDGET(win)->y) { a.w = WIDGET(win)->w; a.h = WIDGET(win)->h; SDL2MW_PostMoveCallback(win, &a); } rv = 1; break; case AG_DRIVER_VIDEORESIZE: a.x = dev->videoresize.x; a.y = dev->videoresize.y; a.w = dev->videoresize.w; a.h = dev->videoresize.h; if (a.w != WIDTH(win) || a.h != HEIGHT(win)) { SDL2MW_PostResizeCallback(win, &a); } rv = 1; break; default: break; } } out: AG_UnlockVFS(&agDrivers); return (rv); } static void SDL2MW_BeginRendering(void *_Nonnull obj) { AG_DriverSDL2MW *smw = obj; SDL_GL_MakeCurrent(smw->window, smw->glCtx); } static void SDL2MW_RenderWindow(AG_Window *_Nonnull win) { const AG_Color *cBg = &WCOLOR(win, BG_COLOR); AG_PushClipRect(win, &WIDGET(win)->r); glClearColor((float)cBg->r / AG_COLOR_LASTF, (float)cBg->g / AG_COLOR_LASTF, (float)cBg->b / AG_COLOR_LASTF, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); AG_WidgetDraw(win); AG_PopClipRect(win); } static void SDL2MW_EndRendering(void *_Nonnull obj) { AG_DriverSDL2MW *smw = obj; AG_GL_Context *gl = &smw->gl; Uint i; #if 0 if (smw->outMode != AG_SDL2GL_OUT_NONE) /* Capture output */ SDL2MW_CaptureOutput(smw); #endif SDL_GL_SwapWindow(smw->window); /* Remove textures and display lists queued for deletion. */ glDeleteTextures(gl->nTextureGC, (const GLuint *)gl->textureGC); for (i = 0; i < gl->nListGC; i++) { glDeleteLists(gl->listGC[i], 1); } gl->nTextureGC = 0; gl->nListGC = 0; } /* * Window operations */ static int SDL2MW_OpenWindow(AG_Window *_Nonnull win, const AG_Rect *_Nonnull r, int depthReq, Uint mwFlags) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; AG_Driver *drv = WIDGET(win)->drv; Uint32 swFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; SDL_Surface *Swin; AG_Rect rVP; if (win->flags & AG_WINDOW_NOBORDERS) { swFlags |= SDL_WINDOW_BORDERLESS; } if (win->flags & AG_WINDOW_NORESIZE) { swFlags &= ~(SDL_WINDOW_RESIZABLE); } if (win->flags & AG_WINDOW_MINIMIZED) { swFlags |= SDL_WINDOW_MINIMIZED; } if (win->flags & AG_WINDOW_MAXIMIZED) { swFlags |= SDL_WINDOW_MAXIMIZED; } if (win->flags & AG_WINDOW_KEEPABOVE) { swFlags |= SDL_WINDOW_ALWAYS_ON_TOP; } switch (win->wmType) { case AG_WINDOW_WM_MENU: case AG_WINDOW_WM_POPUP_MENU: case AG_WINDOW_WM_DROPDOWN_MENU: case AG_WINDOW_WM_COMBO: swFlags |= SDL_WINDOW_POPUP_MENU; swFlags |= SDL_WINDOW_SKIP_TASKBAR; break; case AG_WINDOW_WM_UTILITY: swFlags |= SDL_WINDOW_UTILITY; swFlags |= SDL_WINDOW_SKIP_TASKBAR; break; case AG_WINDOW_WM_DOCK: case AG_WINDOW_WM_TOOLBAR: case AG_WINDOW_WM_TOOLTIP: case AG_WINDOW_WM_NOTIFICATION: case AG_WINDOW_WM_DND: swFlags |= SDL_WINDOW_SKIP_TASKBAR; break; default: break; } if (mwFlags & AG_DRIVER_MW_ANYPOS) { smw->window = SDL_CreateWindow(win->caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, r->w, r->h, swFlags); } else { smw->window = SDL_CreateWindow(win->caption, r->x, r->y, r->w, r->h, swFlags); } if (smw->window == NULL) { AG_SetError("SDL_CreateWindow: %s", SDL_GetError()); return (-1); } /* For AG_SDL_GetWindowFromID(). */ AGDRIVERMW(drv)->windowID = SDL_GetWindowID(smw->window); /* Get the preferred pixel format for the window. */ Swin = SDL_GetWindowSurface(smw->window); drv->videoFmt = Malloc(sizeof(AG_PixelFormat)); AG_PixelFormatRGBA(drv->videoFmt, Swin->format->BitsPerPixel, Swin->format->Rmask, Swin->format->Gmask, Swin->format->Bmask, Swin->format->Amask); if ((smw->glCtx = SDL_GL_CreateContext(smw->window)) == NULL) { AG_SetError("SDL_GL_CreateContext: %s", SDL_GetError()); SDL_DestroyWindow(smw->window); smw->window = NULL; return (-1); } #ifdef DEBUG_DISPLAY Debug(smw, "New display (%d x %d x %d bpp)\n", Swin->w, Swin->h, Swin->format->BitsPerPixel); #endif /* Create the cursors. */ AG_SDL2_InitDefaultCursor(smw); AG_InitStockCursors(drv); /* Initialize our OpenGL context and viewport. */ AG_GL_InitContext(smw, &smw->gl); rVP.x = 0; rVP.y = 0; rVP.w = Swin->w; rVP.h = Swin->h; AG_GL_SetViewport(&smw->gl, &rVP); AG_InitStockCursors(drv); return (0); } static int SDL2MW_MapWindow(AG_Window *_Nonnull win) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; if (win->wMin > 0 && win->hMin > 0) { SDL_SetWindowMinimumSize(smw->window, win->wMin, win->hMin); } SDL_ShowWindow(smw->window); return (0); } static void SDL2MW_CloseWindow(AG_Window *_Nonnull win) { AG_Driver *drv = WIDGET(win)->drv; AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)drv; SDL_GL_MakeCurrent(smw->window, smw->glCtx); AG_FreeCursors(drv); AG_GL_DestroyContext(smw); SDL_GL_DeleteContext(smw->glCtx); SDL_DestroyWindow(smw->window); smw->window = NULL; if (drv->videoFmt) { AG_PixelFormatFree(drv->videoFmt); free(drv->videoFmt); drv->videoFmt = NULL; } } static int SDL2MW_UnmapWindow(AG_Window *_Nonnull win) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_HideWindow(smw->window); return (0); } static int SDL2MW_RaiseWindow(AG_Window *_Nonnull win) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_RaiseWindow(smw->window); return (0); } static int SDL2MW_LowerWindow(AG_Window *_Nonnull win) { AG_SetErrorS(_("Not implemented")); return (-1); } static int SDL2MW_ReparentWindow(AG_Window *_Nonnull win, AG_Window *_Nonnull winParent, int x, int y) { AG_SetErrorS(_("Not implemented")); return (-1); } static int SDL2MW_GetInputFocus(AG_Window *_Nonnull *_Nonnull rv) { AG_SetErrorS(_("Not implemented")); return (-1); } static int SDL2MW_SetInputFocus(AG_Window *_Nonnull win) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; if (SDL_SetWindowInputFocus(smw->window) != 0) { AG_SetError("SDL_SetWindowInputFocus: %s", SDL_GetError()); return (-1); } return (0); } #if 0 static void SDL2MW_FreeWidgetResources(AG_Widget *_Nonnull wid) { AG_Widget *chld; OBJECT_FOREACH_CHILD(chld, wid, ag_widget) { SDL2MW_FreeWidgetResources(chld); } AG_WidgetFreeResourcesGL(wid); } static void RegenWidgetResources(AG_Widget *_Nonnull wid) { AG_Widget *chld; OBJECT_FOREACH_CHILD(chld, wid, ag_widget) { RegenWidgetResources(chld); } AG_WidgetRegenResourcesGL(wid); } #endif static void SDL2MW_PreResizeCallback(AG_Window *_Nonnull win) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_GL_MakeCurrent(smw->window, smw->glCtx); } static int SDL2MW_MoveWindow(AG_Window *_Nonnull win, int dx, int dy) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_SetWindowPosition(smw->window, dx, dy); return (0); } static int SDL2MW_ResizeWindow(AG_Window *_Nonnull win, Uint w, Uint h) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_SetWindowSize(smw->window, (int)w, (int)h); return (0); } static int SDL2MW_MoveResizeWindow(AG_Window *_Nonnull win, AG_SizeAlloc *_Nonnull a) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; /* int w, h; */ SDL_SetWindowSize(smw->window, a->w, a->h); #if 0 SDL_GetWindowSize(smw->window, &w, &h); if (w != a->w || h != a->h) { SDL_SetErrorS(_("SDL_SetWindowSize failed")); return (-1); } #endif SDL_SetWindowPosition(smw->window, a->x, a->y); return (0); } static int SDL2MW_SetBorderWidth(AG_Window *_Nonnull win, Uint width) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_SetWindowBordered(smw->window, (width > 0) ? SDL_TRUE : SDL_FALSE); return (0); } static int SDL2MW_SetWindowCaption(AG_Window *_Nonnull win, const char *_Nonnull s) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_SetWindowTitle(smw->window, s); return (0); } static void SDL2MW_SetWindowMinSize(AG_Window *_Nonnull win, int w, int h) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_SetWindowMinimumSize(smw->window, w,h); } static void SDL2MW_SetWindowMaxSize(AG_Window *_Nonnull win, int w, int h) { AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)WIDGET(win)->drv; SDL_SetWindowMaximumSize(smw->window, w,h); } static void SDL2MW_SetMouseAutoCapture(void *_Nonnull obj, int state) { #ifdef SDL_HINT_MOUSE_AUTO_CAPTURE if (state == 0) { SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); } else if (state == -1) { SDL_ResetHint(SDL_HINT_MOUSE_AUTO_CAPTURE); } else { SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "1"); } #endif } #if defined(AG_WIDGETS) && defined(AG_DEBUG) static void PollGLContext(AG_Event *_Nonnull event) { AG_Tlist *tl = AG_TLIST_SELF(); AG_DriverSDL2MW *smw = (AG_DriverSDL2MW *)AG_PTR(1); const AG_GL_Context *ctx = &smw->gl; AG_TlistItem *it; Uint i; if (!AG_OBJECT_VALID(smw) || OBJECT(smw)->cid != AGC_DRIVER_SDL2MW) { if (WIDGET(tl)->window != NULL) { AG_ObjectDetach(WIDGET(tl)->window); return; } return; } AG_TlistBegin(tl); for (i = 0; i < ctx->nClipRects; i++) { AG_ClipRect *cr = &ctx->clipRects[i]; it = AG_TlistAdd(tl, NULL, _("Clipping Rectangle #" AGSI_BOLD "%d" AGSI_RST " (%dx%d) at [" AGSI_BOLD "%d,%d" AGSI_BOLD "]"), i, cr->r.x, cr->r.y, cr->r.w, cr->r.h); it->p1 = cr; } for (i = 0; i < ctx->nBlendStates; i++) { AG_GL_BlendState *bs = &ctx->blendStates[i]; it = AG_TlistAdd(tl, NULL, _("Blending State #" AGSI_BOLD "%d" AGSI_RST " (" AGSI_BOLD "%s" AGSI_RST ", SrcFac=%d, DstFac=%d)"), i, bs->enabled ? _("Enabled") : _("Disabled"), bs->srcFactor, bs->dstFactor); it->p1 = bs; } for (i = 0; i < ctx->nTextureGC; i++) { it = AG_TlistAdd(tl, NULL, _("Texture Delete #" AGSI_BOLD "%d" AGSI_RST " (Texture=%u)"), i, ctx->textureGC[i]); it->p1 = &ctx->textureGC[i]; } for (i = 0; i < ctx->nListGC; i++) { it = AG_TlistAdd(tl, NULL, _("List Delete #" AGSI_BOLD "%d" AGSI_RST " (List=%u)"), i, ctx->listGC[i]); it->p1 = &ctx->listGC[i]; } AG_TlistEnd(tl); } static void *_Nullable Edit(void *_Nonnull obj) { AG_DriverSDL2MW *smw = obj; AG_Window *win; AG_Label *lbl; AG_Tlist *tl; AG_Keyboard *kbd = AGDRIVER(smw)->kbd; AG_Mouse *mouse = AGDRIVER(smw)->mouse; AG_Notebook *nb; AG_NotebookTab *nt; if ((win = AG_WindowNew(0)) == NULL) { return (NULL); } AG_WindowSetPosition(win, AG_WINDOW_BL, 0); lbl = AG_LabelNew(win, 0, _("SDL2MW Driver: %s"), OBJECT(smw)->name); AG_SetFontFamily(lbl, "league-spartan"); AG_SetFontSize(lbl, "150%"); nb = AG_NotebookNew(win, AG_NOTEBOOK_EXPAND); if (OBJECT_CLASS(kbd)->edit != NULL) { nt = AG_NotebookAdd(nb, _("Keyboard"), AG_BOX_VERT); AG_ObjectAttach(nt, OBJECT_CLASS(kbd)->edit(kbd)); } if (OBJECT_CLASS(mouse)->edit != NULL) { nt = AG_NotebookAdd(nb, _("Mouse"), AG_BOX_VERT); AG_ObjectAttach(nt, OBJECT_CLASS(mouse)->edit(mouse)); } nt = AG_NotebookAdd(nb, _("OpenGL"), AG_BOX_VERT); { AG_LabelNewS(nt, 0, _("Pushed GL States:")); tl = AG_TlistNewPolled(nt, AG_TLIST_EXPAND, PollGLContext,"%p",smw); AG_SetFontSize(tl, "80%"); AG_TlistSizeHint(tl, "", 4); } #if 0 nt = AG_NotebookAdd(nb, _("WM States"), AG_BOX_VERT); { AG_PushDisabledState(nt); AG_CheckboxNewInt(nt, 0, _("Pointer Is Grabbed"), &smw->ptrIsGrabbed); AG_CheckboxNewInt(nt, 0, _("WM Hints Are Set"), &smw->wmHintsSet); AG_CheckboxNewInt(nt, 0, _("Clipboard Selection Waiting"), &agClipboardSelectionWaiting); AG_PopDisabledState(nt); } #endif return (win); } #endif /* AG_WIDGETS and AG_DEBUG */ AG_DriverMwClass agDriverSDL2MW = { { { "AG_Driver:AG_DriverMw:AG_DriverSDL2MW", sizeof(AG_DriverSDL2MW), { 1,7, AGC_DRIVER_SDL2MW, 0xE097 }, Init, NULL, /* reset */ NULL, /* destroy */ NULL, /* load */ NULL, /* save */ #if defined(AG_WIDGETS) && defined(AG_DEBUG) Edit, #else NULL /* edit */ #endif }, "sdl2mw", AG_VECTOR, AG_WM_MULTIPLE, AG_DRIVER_OPENGL | AG_DRIVER_TEXTURES | AG_DRIVER_SDL2, SDL2MW_Open, SDL2MW_Close, AG_SDL2_GetDisplaySize, AG_SDL2_BeginEventProcessing, AG_SDL2_PendingEvents, AG_SDL2_GetNextEvent, SDL2MW_ProcessEvent, NULL, /* genericEventLoop */ NULL, /* endEventProcessing */ NULL, /* terminate */ SDL2MW_BeginRendering, SDL2MW_RenderWindow, SDL2MW_EndRendering, AG_GL_FillRect, NULL, /* updateRegion */ AG_GL_StdUploadTexture, AG_GL_StdUpdateTexture, AG_GL_StdDeleteTexture, NULL, /* setRefreshRate */ AG_GL_StdPushClipRect, AG_GL_StdPopClipRect, AG_GL_StdPushBlendingMode, AG_GL_StdPopBlendingMode, AG_SDL2_CreateCursor, AG_SDL2_FreeCursor, AG_SDL2_SetCursor, AG_SDL2_UnsetCursor, AG_SDL2_GetCursorVisibility, AG_SDL2_SetCursorVisibility, AG_GL_BlitSurface, AG_GL_BlitSurfaceFrom, #ifdef HAVE_OPENGL AG_GL_BlitSurfaceGL, AG_GL_BlitSurfaceFromGL, AG_GL_BlitSurfaceFlippedGL, #endif AG_GL_BackupSurfaces, AG_GL_RestoreSurfaces, AG_GL_RenderToSurface, AG_GL_PutPixel, AG_GL_PutPixel32, AG_GL_PutPixelRGB8, #if AG_MODEL == AG_LARGE AG_GL_PutPixel64, AG_GL_PutPixelRGB16, #endif AG_GL_BlendPixel, AG_GL_DrawLine, AG_GL_DrawLineH, AG_GL_DrawLineV, AG_GL_DrawLineBlended, AG_GL_DrawLineW, AG_GL_DrawLineW_Sti16, AG_GL_DrawTriangle, AG_GL_DrawPolygon, AG_GL_DrawPolygon_Sti32, AG_GL_DrawArrow, AG_GL_DrawBoxRounded, AG_GL_DrawBoxRoundedTop, AG_GL_DrawCircle, AG_GL_DrawCircleFilled, AG_GL_DrawRectFilled, AG_GL_DrawRectBlended, AG_GL_DrawRectDithered, AG_GL_UpdateGlyph, AG_GL_DrawGlyph, AG_GL_StdDeleteList, NULL, /* getClipboardText */ NULL, /* setClipboardText */ SDL2MW_SetMouseAutoCapture }, SDL2MW_OpenWindow, SDL2MW_CloseWindow, SDL2MW_MapWindow, SDL2MW_UnmapWindow, SDL2MW_RaiseWindow, SDL2MW_LowerWindow, SDL2MW_ReparentWindow, SDL2MW_GetInputFocus, SDL2MW_SetInputFocus, SDL2MW_MoveWindow, SDL2MW_ResizeWindow, SDL2MW_MoveResizeWindow, SDL2MW_PreResizeCallback, SDL2MW_PostResizeCallback, SDL2MW_SetBorderWidth, SDL2MW_SetWindowCaption, NULL, /* setTransientFor */ NULL, /* setOpacity */ NULL, /* tweakAlignment */ SDL2MW_SetWindowMinSize, SDL2MW_SetWindowMaxSize };