/* * Copyright (c) 2002-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. */ /* * This tool allows a developer to inspect a live VFS of Agar widgets * and manipulate both generic and class-specific attributes of them. */ #include #if defined(AG_WIDGETS) && defined(AG_DEBUG) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static AG_Window *_Nullable agDebuggerWindow = NULL; static AG_Tlist *_Nullable agDebuggerTlist = NULL; static AG_Label *_Nullable agDebuggerLabel = NULL; static AG_Box *_Nullable agDebuggerBox = NULL; static void FindWidgets(AG_Widget *_Nonnull wid, AG_Tlist *_Nonnull tl, int depth, Uint *_Nonnull nContainers, Uint *_Nonnull nLeaves) { char text[AG_TLIST_LABEL_MAX]; AG_TlistItem *it; AG_Widget *widChld; Strlcpy(text, OBJECT(wid)->name, sizeof(text)); if (AG_WINDOW_ISA(wid)) { AG_Window *win = (AG_Window *)wid; Strlcat(text, " (\"", sizeof(text)); Strlcat(text, win->caption, sizeof(text)); Strlcat(text, "\")", sizeof(text)); } it = AG_TlistAddPtr(tl, NULL, text, wid); it->depth = depth; it->cat = "widget"; it->flags |= AG_TLIST_ITEM_EXPANDED; if (!TAILQ_EMPTY(&OBJECT(wid)->children)) { it->flags |= AG_TLIST_HAS_CHILDREN; (*nContainers)++; } else { (*nLeaves)++; } if (AG_TlistVisibleChildren(tl, it)) { OBJECT_FOREACH_CHILD(widChld, wid, ag_widget) FindWidgets(widChld, tl, depth+1, nContainers, nLeaves); } } static void FindWindows(AG_Tlist *_Nonnull tl, const AG_Window *_Nonnull win, int depth, Uint *_Nonnull nWindows, Uint *_Nonnull nContainers, Uint *_Nonnull nLeaves) { const char *name = OBJECT(win)->name; /* AG_Window *wSub; */ AG_Widget *wChild; AG_TlistItem *it; if (strcmp(name, "_agDbgr") == 0) /* Unsafe */ return; if (strncmp(name, "win", 3) == 0 && isdigit((int)name[4])) { it = AG_TlistAddS(tl, NULL, win->caption[0] !='\0' ? win->caption : _("Untitled")); } else { it = AG_TlistAdd(tl, NULL, "<%s> (\"%s\")", name, win->caption[0] != '\0' ? win->caption : _("Untitled")); } it->p1 = (AG_Window *)win; it->depth = depth; it->cat = "window"; it->flags |= AG_TLIST_ITEM_EXPANDED; (*nWindows)++; if (!TAILQ_EMPTY(&OBJECT(win)->children) || !TAILQ_EMPTY(&win->pvt.subwins)) { it->flags |= AG_TLIST_HAS_CHILDREN; } if (AG_TlistVisibleChildren(tl, it)) { // TAILQ_FOREACH(wSub, &win->pvt.subwins, pvt.swins) // FindWindows(tl, wSub, depth+1, nWindows, nContainers, nLeaves); OBJECT_FOREACH_CHILD(wChild, win, ag_widget) FindWidgets(wChild, tl, depth+1, nContainers, nLeaves); } } static void TargetRoot(void) { agDebuggerTgt = NULL; if (agDebuggerWindow) { AG_WindowSetCaptionS(agDebuggerWindow, _("Agar GUI Debugger: / (root)")); if (agDebuggerTlist) AG_TlistDeselectAll(agDebuggerTlist); if (agDebuggerLabel) AG_LabelText(agDebuggerLabel, _("Target: " AGSI_PATH AGSI_YEL "/" AGSI_RST)); } } static void PollWidgets(AG_Event *_Nonnull event) { AG_Tlist *tl = AG_TLIST_SELF(); AG_Label *lblStats = AG_LABEL_PTR(1); const AG_Window *tgt = agDebuggerTgtWindow; AG_Driver *drv; Uint nWindows=0, nContainers=0, nLeaves=0; AG_TlistBegin(tl); if (tgt != NULL && AG_OBJECT_VALID(tgt)) { FindWindows(tl, tgt, 0, &nWindows, &nContainers, &nLeaves); } else { /* Retarget root */ TargetRoot(); AG_LockVFS(&agDrivers); AGOBJECT_FOREACH_CHILD(drv, &agDrivers, ag_driver) { AG_FOREACH_WINDOW(tgt, drv) { if (tgt == agDebuggerWindow) { continue; } FindWindows(tl, tgt, 0, &nWindows, &nContainers, &nLeaves); } } AG_UnlockVFS(&agDrivers); } AG_TlistEnd(tl); AG_LabelText(lblStats, _("%u windows, %u containers & %u leaves (t = %ums)"), nWindows, nContainers, nLeaves, (Uint)AG_GetTicks()); } static void ShowWindow(AG_Event *_Nonnull event) { AG_Window *win = AG_WINDOW_PTR(1); AG_WindowShow(win); } static void HideWindow(AG_Event *_Nonnull event) { AG_Window *win = AG_WINDOW_PTR(1); AG_WindowHide(win); } static void SelectedSurface(AG_Event *_Nonnull event) { AG_Pixmap *px = AG_PIXMAP_PTR(1); AG_Label *lblInfo = AG_LABEL_PTR(2); AG_TlistItem *it = AG_TLISTITEM_PTR(3); AG_Surface *S = it->p1; const char *grayscaleModeNames[] = { /* Sync with AG_GrayscaleMode */ "BT.709", "R-Y", "Y" }; if (S == NULL || strcmp(it->cat, "surface") != 0) return; if (S->w < 1 || S->w > 2560 || S->h < 1 || S->h > 1440) return; switch (S->format.mode) { case AG_SURFACE_PACKED: AG_LabelText(lblInfo, _("Surface Mode: Packed.\n" "Surface Flags: " AGSI_CODE "0x%04x" AGSI_RST ".\n" "Surface Size: %u x %u.\n" "Pitch: %u, Paddings: %u,%u.\n" "Guides: %u %u %u %u.\n" "Bits Per Pixel: %d.\n" "RGBA Masks: " AGSI_CODE AGSI_RED "0x%08lx" AGSI_RST "," AGSI_CODE AGSI_GRN "0x%08lx" AGSI_RST "," AGSI_CODE AGSI_BLU "0x%08lx" AGSI_RST "," AGSI_CODE "0x%08lx" AGSI_RST "\n"), S->flags, S->w, S->h, S->pitch, S->Lpadding, S->padding, S->guides[0], S->guides[1], S->guides[2], S->guides[3], S->format.BitsPerPixel, (Ulong)S->format.Rmask, (Ulong)S->format.Gmask, (Ulong)S->format.Bmask, (Ulong)S->format.Amask); break; case AG_SURFACE_INDEXED: AG_LabelText(lblInfo, _("Surface Mode: Indexed.\n" "Surface Flags: " AGSI_CODE "0x%x" AGSI_RST ".\n" "Surface Size: %d x %d.\n" "Pitch: %u, Paddings: %u,%u.\n" "Bits Per Pixel: %d.\n" "Palette Entries: " AGSI_BOLD "%d" AGSI_RST ".\n"), S->flags, S->w, S->h, S->pitch, S->Lpadding, S->padding, S->format.BitsPerPixel, S->format.palette->nColors); break; case AG_SURFACE_GRAYSCALE: AG_LabelText(lblInfo, _("Surface Mode: Grayscale.\n" "Surface Flags: " AGSI_CODE "0x%x" AGSI_RST ".\n" "Surface Size: %d x %d.\n" "Pitch: %u, Paddings: %u,%u.\n" "Bits Per Pixel: %d.\n" "Grayscale Mode: " AGSI_BOLD "%s" AGSI_RST ".\n"), S->flags, S->w, S->h, S->pitch, S->Lpadding, S->padding, S->format.BitsPerPixel, grayscaleModeNames[S->format.graymode]); break; default: break; } AG_PixmapSetSurface(px, AG_PixmapAddSurfaceScaled(px, S, S->w, S->h)); AG_WindowUpdate(WIDGET(px)->window); AG_Redraw(px); } static void ExportSurface(AG_Event *_Nonnull event) { const AG_Pixmap *px = AG_cPIXMAP_PTR(1); const char *path = AG_STRING(2); AG_Surface *S; if (px->n == -1) return; S = AG_PixmapGetSurface(px, px->n); if (AG_SurfaceExportFile(S, path) == 0) { AG_TextTmsg(AG_MSG_INFO, 2000, _("Exported %u x %u x %ubpp surface to:\n%s"), S->w, S->h, S->format.BitsPerPixel, path); } else { AG_TextMsgFromError(); } AG_SurfaceFree(S); } static void ExportSurfaceDlg(AG_Event *_Nonnull event) { const AG_Pixmap *px = AG_cPIXMAP_PTR(1); AG_Window *win; AG_FileDlg *fd; if ((win = AG_WindowNew(0)) == NULL) { return; } AG_WindowSetCaptionS(win, _("Export to image file...")); fd = AG_FileDlgNewMRU(win, "debugger-imgs", AG_FILEDLG_SAVE | AG_FILEDLG_CLOSEWIN | AG_FILEDLG_MASK_EXT | AG_FILEDLG_EXPAND); AG_FileDlgAddImageTypes(fd, ExportSurface, "%Cp", px); AG_WindowShow(win); } static void PollSurfaces(AG_Event *_Nonnull event) { AG_Tlist *tl = AG_TLIST_SELF(); AG_Widget *wid = agDebuggerTgt; AG_TlistItem *it; Uint i; if (wid == NULL || !AG_OBJECT_VALID(wid) || !AG_WIDGET_ISA(wid)) { TargetRoot(); return; } AG_ObjectLock(wid); AG_TlistBegin(tl); for (i = 0; i < wid->nSurfaces; i++) { const AG_Surface *S = WSURFACE(wid,i); /* Sometimes WSURFACE returns NULL. This may be a bug * elsewhere, maybe in the glxdriver, causing wid->nSurfaces * to be inconsistent with the number of non-null surfaces. * Without this check, S will get DE referenced and cause * a segfault. */ if (S == NULL) { it = AG_TlistAdd(tl, NULL, "Surface%u = NULL", i); it->cat = "null-surface"; } else { if (wid->textures[i] != -1) { it = AG_TlistAdd(tl, NULL, "Surface#%u (%ux%u, %ubpp, texture #%d%s%s)", i, S->w, S->h, S->format.BitsPerPixel, wid->textures[i], (wid->surfaceFlags[i] & AG_WIDGET_SURFACE_NODUP) ? ", " : "", (wid->surfaceFlags[i] & AG_WIDGET_SURFACE_REGEN) ? ", " : ""); } else { it = AG_TlistAdd(tl, NULL, "Surface#%u (%ux%u, %ubpp%s%s)", i, S->w, S->h, S->format.BitsPerPixel, (wid->surfaceFlags[i] & AG_WIDGET_SURFACE_NODUP) ? ", " : "", (wid->surfaceFlags[i] & AG_WIDGET_SURFACE_REGEN) ? ", " : ""); } it->cat = "surface"; } it->p1 = (AG_Surface *)S; } AG_TlistEnd(tl); AG_ObjectUnlock(wid); } static void PollVariables(AG_Event *_Nonnull event) { char val[AG_LABEL_MAX]; AG_Tlist *tl = AG_TLIST_SELF(); AG_Object *obj = AGOBJECT(agDebuggerTgt); AG_Variable *V; if (obj == NULL || !AG_OBJECT_VALID(obj)) { TargetRoot(); return; } val[0] = '\0'; AG_ObjectLock(obj); AG_TlistBegin(tl); TAILQ_FOREACH(V, &obj->vars, vars) { AG_TlistItem *it; if ((V->type == AG_VARIABLE_P_UINT || V->type == AG_VARIABLE_P_INT) && strcmp(V->name, "flags") == 0) { Snprintf(val, sizeof(val), "0x%08x", *(Uint *)V->data.p); } else { AG_PrintVariable(val, sizeof(val), V); } switch (V->type) { case AG_VARIABLE_P_FLAG: case AG_VARIABLE_P_FLAG8: case AG_VARIABLE_P_FLAG16: case AG_VARIABLE_P_FLAG32: it = AG_TlistAdd(tl, NULL, "%s %s [mask 0x%x] = %s", agVariableTypes[V->type].name, V->name, V->info.bitmask.u, val); it->p1 = V; break; default: it = AG_TlistAdd(tl, NULL, "%s %s = %s", agVariableTypes[V->type].name, V->name, val); it->p1 = V; break; } } AG_TlistEnd(tl); AG_ObjectUnlock(obj); } static void FlagChanged(AG_Event *_Nonnull event) { AG_Widget *tgt = AG_WIDGET_PTR(1); Debug(tgt, "Flags changed in debugger (0x%x)\n", WIDGET(tgt)->flags); AG_Redraw(tgt); } static void InputVariable(AG_Event *_Nonnull event) { AG_Textbox *tb = AG_TEXTBOX_PTR(1); AG_Widget *tgt = (AG_Widget *)AG_PTR(2); char *s = AG_TextboxDupString(tb), *ps = s; const char *key = Strsep(&ps, ":="); const char *val = Strsep(&ps, ":="); const char *c; int floatChars; if (!AG_OBJECT_VALID(tgt) || !AG_WIDGET_ISA(tgt)) { Debug(NULL, "Invalid target %p\n", tgt); return; } if (key == NULL || val == NULL) return; while (isspace((int)*key)) { key++; } while (isspace((int)*val)) { val++; } /* * Try to infer type from the value. */ for (c=val, floatChars=0; *c != '\0'; c++) { if (*c == '.' || *c == 'e') { floatChars++; continue; } if (!isdigit((int)*c) && *c != '-') break; } if (*c == '\0') { if (floatChars) { AG_SetFloat(tgt, key, atof(val)); } else { AG_SetInt(tgt, key, atoi(val)); } } else { if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X') && val[2] != '\0') { AG_SetUint(tgt, key, (Uint)strtol(val,NULL,16)); } else { if (Strcasecmp(val, "TRUE") == 0) { AG_SetBool(tgt, key, 1); } else if (Strcasecmp(val, "FALSE") == 0) { AG_SetBool(tgt, key, 0); } else { AG_SetString(tgt, key, val); } } } AG_WindowUpdate(AG_ParentWindow(tgt)); AG_TextboxClearString(tb); free(s); } /* Select a widget for inspection in Debugger. */ static void TargetWidget(AG_Event *_Nonnull event) { AG_Box *box = agDebuggerBox; AG_TlistItem *ti = AG_TLISTITEM_PTR(1); AG_Widget *tgt = ti->p1; AG_Notebook *nb; AG_NotebookTab *nt; AG_Textbox *tb; int savedTabID; agDebuggerTgt = tgt; if ((nb = AG_ObjectFindChild(box, "notebook0")) != NULL) { savedTabID = nb->selTabID; } else { savedTabID = -1; } AG_ObjectFreeChildren(box); nb = AG_NotebookNew(box, AG_NOTEBOOK_EXPAND); nt = AG_NotebookAdd(nb, AGSI_IDEOGRAM AGSI_SMALL_WINDOW AGSI_RST " AG_Widget", AG_BOX_VERT); AG_SetPadding(nt, "3 4 3 4"); nt->id = 1; { static const AG_FlagDescr flagDescr[] = { { AG_WIDGET_HIDE, "HIDE", 1 }, { AG_WIDGET_DISABLED, "DISABLED", 1 }, { AG_WIDGET_FOCUSABLE, "FOCUSABLE", 1 }, { AG_WIDGET_VISIBLE, "VISIBLE", 0 }, { AG_WIDGET_FOCUSED, "FOCUSED", 0 }, { AG_WIDGET_UNDERSIZE, "UNDERSIZE", 0 }, { AG_WIDGET_HFILL, "HFILL", 0 }, { AG_WIDGET_VFILL, "VFILL", 0 }, { AG_WIDGET_USE_MOUSEOVER, "USE_MOUSEOVER", 1 }, { AG_WIDGET_MOUSEOVER, "MOUSEOVER", 0 }, { AG_WIDGET_USE_TEXT, "USE_TEXT", 0 }, { AG_WIDGET_USE_OPENGL, "USE_OPENGL", 0 }, { 0, NULL, 0 } }; AG_Box *boxWide, *boxVert; AG_MSpinbutton *msb; AG_Label *lbl; lbl = AG_LabelNew(nt, AG_LABEL_HFILL, _("Widget Structure of " AGSI_BOLD "%s" AGSI_RST " " "(" AGSI_CYAN "%s" AGSI_RST ")"), OBJECT(tgt)->name, OBJECT(tgt)->cls->hier); AG_LabelJustify(lbl, AG_TEXT_CENTER); AG_SetFontFamily(lbl, "league-spartan"); AG_SetFontSize(lbl, "130%"); AG_SeparatorNewHoriz(nt); AG_LabelNewPolledMT(nt, AG_LABEL_SLOW | AG_LABEL_HFILL, &OBJECT(tgt)->lock, _("Parent Window: %[objName]" " @ (" AGSI_CYAN AGSI_CODE "AG_Window" AGSI_RST " *)%p\n" "Parent Driver: %[objName]" " @ (" AGSI_CYAN AGSI_CODE "AG_Driver" AGSI_RST " *)%p\n"), &tgt->window, &tgt->window, &tgt->drv, &tgt->drv); boxWide = AG_BoxNewHoriz(nt, AG_BOX_EXPAND); boxVert = AG_BoxNewVert(boxWide, AG_BOX_HFILL); AG_SetFontSize(boxVert, "110%"); AG_SetPadding(boxVert, "4"); tb = AG_TextboxNewS(boxVert, AG_TEXTBOX_HFILL, _("Name: ")); #ifdef AG_UNICODE AG_TextboxBindUTF8(tb, OBJECT(tgt)->name, sizeof(OBJECT(tgt)->name)); #else AG_TextboxBindASCII(tb, OBJECT(tgt)->name, sizeof(OBJECT(tgt)->name)); #endif msb = AG_MSpinbuttonNew(boxVert, AG_MSPINBUTTON_HFILL, "x", _("Size: ")); AG_BindInt(msb, "xvalue", &tgt->w); AG_BindInt(msb, "yvalue", &tgt->h); msb = AG_MSpinbuttonNew(boxVert, AG_MSPINBUTTON_HFILL, ",", _("Position: ")); AG_BindInt(msb, "xvalue", &tgt->x); AG_BindInt(msb, "yvalue", &tgt->y); msb = AG_MSpinbuttonNew(boxVert, AG_MSPINBUTTON_HFILL, ",", _("View UL: ")); AG_BindInt(msb, "xvalue", &tgt->rView.x1); AG_BindInt(msb, "yvalue", &tgt->rView.y1); msb = AG_MSpinbuttonNew(boxVert, AG_MSPINBUTTON_HFILL, ",", _("View LR: ")); AG_BindInt(msb, "xvalue", &tgt->rView.x2); AG_BindInt(msb, "yvalue", &tgt->rView.y2); boxVert = AG_BoxNewVert(boxWide, AG_BOX_VFILL); AG_CheckboxSetFromFlagsFn(boxVert, 0, &tgt->flags, flagDescr, FlagChanged,"%p",tgt); } if (OBJECT_CLASS(tgt)->edit != NULL) { AG_Widget *editRv; char label[64]; if (AG_SCROLLBAR_ISA(tgt)) { Strlcpy(label, AGSI_IDEOGRAM AGSI_HORIZ_SCROLLBAR AGSI_RST " ", sizeof(label)); } else if (AG_BOX_ISA(tgt)) { Strlcpy(label, AGSI_IDEOGRAM AGSI_BOX_VERT AGSI_RST " ", sizeof(label)); } else if (AG_EDITABLE_ISA(tgt) || AG_TEXTBOX_ISA(tgt) || AG_LABEL_ISA(tgt) || AG_NUMERICAL_ISA(tgt)) { Strlcpy(label, AGSI_IDEOGRAM AGSI_TEXTBOX AGSI_RST " ", sizeof(label)); } else { Strlcpy(label, AGSI_IDEOGRAM AGSI_SMALL_SPHERE AGSI_RST " ", sizeof(label)); } Strlcat(label, OBJECT(tgt)->cls->name, sizeof(label)); nt = AG_NotebookAdd(nb, label, AG_BOX_VERT); nt->id = 2; AG_SetPadding(nt, "3"); editRv = OBJECT_CLASS(tgt)->edit(tgt); AG_ObjectAttach(nt, editRv); AG_WidgetCompileStyle(editRv); AG_Redraw(editRv); } nt = AG_NotebookAdd(nb, _(AGSI_IDEOGRAM AGSI_MATH_X_EQUALS AGSI_RST " Variables"), AG_BOX_VERT); nt->id = 3; { AG_Textbox *tb; tb = AG_TextboxNewS(nt, AG_TEXTBOX_HFILL | AG_TEXTBOX_RETURN_BUTTON, "+ "); AG_SetEvent(tb, "textbox-return", InputVariable, "%p,%p", tb,tgt); AG_TlistNewPolledMs(nt, AG_TLIST_EXPAND, 333, PollVariables, NULL); } nt = AG_NotebookAdd(nb, _(AGSI_IDEOGRAM AGSI_LOAD_IMAGE AGSI_RST " Surfaces"), AG_BOX_VERT); nt->id = 4; { AG_Pane *pane; AG_Pixmap *px; AG_Tlist *tl; AG_MenuItem *mi; AG_Label *lblInfo; AG_Box *paneTop, *paneBottom; pane = AG_PaneNewVert(nt, AG_PANE_EXPAND); AG_PaneResizeAction(pane, AG_PANE_DIVIDE_EVEN); paneTop = pane->div[0]; paneBottom = pane->div[1]; px = AG_PixmapNew(paneTop, AG_PIXMAP_EXPAND, 320, 240); lblInfo = AG_LabelNew(paneTop, AG_LABEL_HFILL, _("(No data)")); AG_LabelSizeHint(lblInfo, 5, ""); AG_SetFontSize(lblInfo, "90%"); tl = AG_TlistNewPolled(paneBottom, AG_TLIST_EXPAND, PollSurfaces,NULL); AG_SetEvent(tl, "tlist-selected", SelectedSurface,"%p,%p",px,lblInfo); mi = AG_TlistSetPopup(tl, "surface"); AG_MenuAction(mi, _("Export to image file..."), agIconSave.s, ExportSurfaceDlg,"%Cp",px); AG_PaneMoveDividerPct(pane, 50); } AG_NotebookSelectByID(nb, savedTabID); /* Restore active tab */ AG_WidgetCompileStyle(box); AG_WidgetShowAll(box); AG_WidgetUpdate(box); } static void ContextualMenu(AG_Event *_Nonnull event) { AG_Tlist *tl = AG_TLIST_PTR(1); AG_MenuItem *mi = AG_MENUITEM_PTR(2); AG_TlistItem *ti = AG_TlistSelectedItem(tl); AG_Window *win; if (ti == NULL) return; if ((win = ti->p1) != NULL && AG_WINDOW_ISA(win)) { if (win->visible) { AG_MenuAction(mi, _("Hide window"), NULL, HideWindow,"%p",win); } else { AG_MenuAction(mi, _("Show window"), NULL, ShowWindow,"%p",win); } } } static void CloseDebuggerWindow(AG_Event *_Nonnull event) { agDebuggerWindow = NULL; agDebuggerTlist = NULL; agDebuggerBox = NULL; agDebuggerTgtWindow = NULL; } void AG_GuiDebuggerDetachTarget(void) { if (agDebuggerBox) { AG_ObjectFreeChildren(agDebuggerBox); } agDebuggerTgt = NULL; } void AG_GuiDebuggerDetachWindow(void) { if (agDebuggerBox) { AG_ObjectFreeChildren(agDebuggerBox); } agDebuggerTgtWindow = NULL; TargetRoot(); } static void SetAutorefresh(AG_Event *_Nonnull event) { AG_Tlist *tl = AG_TLIST_PTR(1); const int enable = AG_INT(2); AG_TlistSetRefresh(tl, enable ? 250 : -1); } /* * Open the GUI debugger window (with tgt at the root). */ AG_Window *_Nullable AG_GuiDebugger(AG_Window *_Nonnull tgt) { char path[AG_OBJECT_PATH_MAX]; AG_Window *win; AG_Pane *pane; AG_Tlist *tl; AG_MenuItem *mi; AG_Button *buRefresh; AG_Box *div, *toolbar; AG_Label *lblStats; if (tgt == NULL) { AG_TextError(_("No window is focused.\n" "Focus on a window to target it in Debugger.")); return (NULL); } if (tgt == agDebuggerWindow) { /* Unsafe */ return (NULL); } if ((win = agDebuggerWindow) != NULL) { AG_WindowFocus(win); if (agDebuggerTgtWindow != tgt) { agDebuggerTgtWindow = tgt; AG_WindowSetCaption(win, _("Agar GUI Debugger: <%s> (\"%s\")"), OBJECT(tgt)->name, AGWINDOW(tgt)->caption); if (agDebuggerBox) AG_ObjectFreeChildren(agDebuggerBox); AG_ObjectCopyName(tgt, path, sizeof(path)); AG_LabelText(agDebuggerLabel, _("Target: " AGSI_PATH AGSI_YEL "%s" AGSI_RST), path); } return (win); } if ((win = agDebuggerWindow = AG_WindowNewNamedS(0, "_agDbgr")) == NULL) return (NULL); agDebuggerTgtWindow = tgt; AG_WindowSetCaption(win, _("Agar GUI Debugger: <%s> (\"%s\")"), OBJECT(tgt)->name, AGWINDOW(tgt)->caption); toolbar = AG_BoxNewHoriz(win, AG_BOX_HFILL); AG_SetFontSize(toolbar, "150%"); { #if 0 /* Set pick mode */ btn = AG_ButtonNewFn(toolbar, AG_BUTTON_STICKY, AGSI_ALGUE AGSI_NW_ARROW_TO_CORNER, SetPickStatus, "%p,%p", win, tl); AG_SetPadding(btn, "0 5 3 5"); #endif /* Toggle VFS autorefresh */ buRefresh = AG_ButtonNewS(toolbar, AG_BUTTON_STICKY | AG_BUTTON_SET, AGSI_BR_GRN AGSI_ALGUE AGSI_CCW_CLOSED_CIRCLE_ARROW); AG_SetPadding(buRefresh, "0 10 3 5"); } pane = AG_PaneNewHoriz(win, AG_PANE_EXPAND); div = pane->div[0]; agDebuggerBox = pane->div[1]; AG_ObjectCopyName(tgt, path, sizeof(path)); agDebuggerLabel = AG_LabelNew(div, AG_LABEL_HFILL, _("Target: " AGSI_PATH AGSI_YEL "%s" AGSI_RST), path); lblStats = AG_LabelNewS(div, AG_LABEL_HFILL, _("XX windows, XX containers & XX leaves (t = XXXXXms)")); AG_SetFontSize(lblStats, "80%"); tl = agDebuggerTlist = AG_TlistNewPolledMs(div, AG_TLIST_EXPAND, 125, PollWidgets, "%p", lblStats); AG_TlistSizeHint(tl, "", 15); AG_SetEvent(tl, "tlist-selected", TargetWidget, NULL); AG_SetEvent(buRefresh, "button-pushed", SetAutorefresh, "%p", tl); mi = AG_TlistSetPopup(tl, "window"); AG_MenuSetPollFn(mi, ContextualMenu, "%p", tl); AG_AddEvent(win, "window-close", CloseDebuggerWindow, NULL); AG_WindowSetGeometryAlignedPct(win, AG_WINDOW_BR, 40, 40); AG_WindowSetCloseAction(win, AG_WINDOW_DETACH); AG_WidgetFocus(tl); return (win); } #endif /* AG_DEBUG */