/* * widgets_x11.c — X11-specific WINGs platform init. * * Owns every Xlib call that used to live in widgets.c. The shared * widgets.c is now backend-neutral and dispatches to this file (via * direct function calls — ZZ TODO: vtable when adding more backends) * for X11-specific setup. * * This file is the X11 counterpart to wclient_wl.c (Wayland client) * and to the compositor-internal wl_backend.c paths. */ #include "WINGsP.h" #include "WINGs/WINGsX11.h" #include "wconfig.h" #include #include #include #include #include #include #include /* These match the static stipple data in widgets.c (kept as fallback). */ #define STIPPLE_WIDTH 8 #define STIPPLE_HEIGHT 8 static char STIPPLE_BITS[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }; /* * W_X11PlatformInit — the GCs/cursors/atoms/IM init that the X11 view * backend hooks via W_ViewBackendX11.platform_init. Moved from * widgets.c so the shared file stops linking against Xlib. */ void W_X11PlatformInit(W_Screen *scrPtr) { Display *display = scrPtr->display; XGCValues gcv; Pixmap stipple; int i; static char *atomNames[] = { "_GNUSTEP_WM_ATTR", "WM_DELETE_WINDOW", "WM_PROTOCOLS", "CLIPBOARD", "XdndAware", "XdndSelection", "XdndEnter", "XdndLeave", "XdndPosition", "XdndDrop", "XdndFinished", "XdndTypeList", "XdndActionList", "XdndActionDescription", "XdndStatus", "XdndActionCopy", "XdndActionMove", "XdndActionLink", "XdndActionAsk", "XdndActionPrivate", "_WINGS_DND_MOUSE_OFFSET", "WM_STATE", "UTF8_STRING", "_NET_WM_NAME", "_NET_WM_ICON_NAME", "_NET_WM_ICON", }; Atom atoms[wlengthof(atomNames)]; /* Create missing CUT_BUFFERs */ { Atom *rootWinProps; int exists[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int count; rootWinProps = XListProperties(display, scrPtr->rootWin, &count); for (i = 0; i < count; i++) { switch (rootWinProps[i]) { case XA_CUT_BUFFER0: exists[0] = 1; break; case XA_CUT_BUFFER1: exists[1] = 1; break; case XA_CUT_BUFFER2: exists[2] = 1; break; case XA_CUT_BUFFER3: exists[3] = 1; break; case XA_CUT_BUFFER4: exists[4] = 1; break; case XA_CUT_BUFFER5: exists[5] = 1; break; case XA_CUT_BUFFER6: exists[6] = 1; break; case XA_CUT_BUFFER7: exists[7] = 1; break; default: break; } } if (rootWinProps) XFree(rootWinProps); for (i = 0; i < 8; i++) { if (!exists[i]) XStoreBuffer(display, "", 0, i); } } /* Modifier mask detection */ scrPtr->ignoredModifierMask = 0; { XModifierKeymap *modmap; KeyCode nlock, slock; static int mask_table[8] = { WM_MOD_SHIFT, WM_MOD_LOCK, WM_MOD_CONTROL, WM_MOD_MOD1, WM_MOD_MOD2, WM_MOD_MOD3, WM_MOD_MOD4, WM_MOD_MOD5 }; unsigned int numLockMask = 0, scrollLockMask = 0; nlock = XKeysymToKeycode(display, XK_Num_Lock); slock = XKeysymToKeycode(display, XK_Scroll_Lock); modmap = XGetModifierMapping(display); if (modmap != NULL && modmap->max_keypermod > 0) { for (i = 0; i < 8 * modmap->max_keypermod; i++) { if (modmap->modifiermap[i] == nlock && nlock != 0) numLockMask = mask_table[i / modmap->max_keypermod]; else if (modmap->modifiermap[i] == slock && slock != 0) scrollLockMask = mask_table[i / modmap->max_keypermod]; } } if (modmap) XFreeModifiermap(modmap); scrPtr->ignoredModifierMask = numLockMask | scrollLockMask | WM_MOD_LOCK; } /* GCs */ gcv.graphics_exposures = False; gcv.function = GXxor; gcv.foreground = W_PIXEL(scrPtr->white); if (gcv.foreground == 0) gcv.foreground = 1; scrPtr->xorGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction | GCGraphicsExposures | GCForeground, &gcv); gcv.function = GXxor; gcv.foreground = W_PIXEL(scrPtr->gray); gcv.subwindow_mode = IncludeInferiors; scrPtr->ixorGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction | GCGraphicsExposures | GCForeground | GCSubwindowMode, &gcv); gcv.function = GXcopy; scrPtr->copyGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction | GCGraphicsExposures, &gcv); scrPtr->clipGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCFunction | GCGraphicsExposures, &gcv); stipple = XCreateBitmapFromData(display, W_DRAWABLE(scrPtr), STIPPLE_BITS, STIPPLE_WIDTH, STIPPLE_HEIGHT); gcv.foreground = W_PIXEL(scrPtr->darkGray); gcv.background = W_PIXEL(scrPtr->gray); gcv.fill_style = FillStippled; gcv.stipple = stipple; scrPtr->stippleGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCForeground | GCBackground | GCStipple | GCFillStyle | GCGraphicsExposures, &gcv); scrPtr->drawStringGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCGraphicsExposures, &gcv); scrPtr->drawImStringGC = XCreateGC(display, W_DRAWABLE(scrPtr), GCGraphicsExposures, &gcv); scrPtr->monoGC = XCreateGC(display, stipple, 0, NULL); scrPtr->stipple = stipple; /* Input method */ W_InitIM(scrPtr); /* Cursors */ scrPtr->defaultCursor = XCreateFontCursor(display, XC_left_ptr); scrPtr->textCursor = XCreateFontCursor(display, XC_xterm); { XColor bla; Pixmap blank; blank = XCreatePixmap(display, scrPtr->stipple, 1, 1, 1); XSetForeground(display, scrPtr->monoGC, 0); XFillRectangle(display, blank, scrPtr->monoGC, 0, 0, 1, 1); scrPtr->invisibleCursor = XCreatePixmapCursor(display, blank, blank, &bla, &bla, 0, 0); XFreePixmap(display, blank); } /* Atoms */ #ifdef HAVE_XINTERNATOMS XInternAtoms(display, atomNames, wlengthof(atomNames), False, atoms); #else for (i = 0; i < wlengthof(atomNames); i++) atoms[i] = XInternAtom(display, atomNames[i], False); #endif i = 0; scrPtr->attribsAtom = atoms[i++]; scrPtr->deleteWindowAtom = atoms[i++]; scrPtr->protocolsAtom = atoms[i++]; scrPtr->clipboardAtom = atoms[i++]; scrPtr->xdndAwareAtom = atoms[i++]; scrPtr->xdndSelectionAtom = atoms[i++]; scrPtr->xdndEnterAtom = atoms[i++]; scrPtr->xdndLeaveAtom = atoms[i++]; scrPtr->xdndPositionAtom = atoms[i++]; scrPtr->xdndDropAtom = atoms[i++]; scrPtr->xdndFinishedAtom = atoms[i++]; scrPtr->xdndTypeListAtom = atoms[i++]; scrPtr->xdndActionListAtom = atoms[i++]; scrPtr->xdndActionDescriptionAtom = atoms[i++]; scrPtr->xdndStatusAtom = atoms[i++]; scrPtr->xdndActionCopy = atoms[i++]; scrPtr->xdndActionMove = atoms[i++]; scrPtr->xdndActionLink = atoms[i++]; scrPtr->xdndActionAsk = atoms[i++]; scrPtr->xdndActionPrivate = atoms[i++]; scrPtr->wmIconDragOffsetAtom = atoms[i++]; scrPtr->wmStateAtom = atoms[i++]; scrPtr->utf8String = atoms[i++]; scrPtr->netwmName = atoms[i++]; scrPtr->netwmIconName = atoms[i++]; scrPtr->netwmIcon = atoms[i++]; /* Application group leader */ W_InitApplication(scrPtr); } /* * WMCreateScreen — public API: open a WINGs screen on an existing X11 * Display. Lives in the X11 backend file because it directly creates * an X11 RContext via RCreateContext(). Wayland callers should use * WMOpenScreen() instead. */ WMScreen *WMCreateScreen(Display * display, int screen) { return WMCreateScreenWithRContext(display, screen, RCreateContext(display, screen, NULL)); } /* * WMCreateSimpleApplicationScreen — public API: like WMCreateScreen * but defaults to the X server's default screen and marks the screen * as a simple (no app-icon) application. X11-only. */ WMScreen *WMCreateSimpleApplicationScreen(Display * display) { WMScreen *scr = WMCreateScreen(display, DefaultScreen(display)); if (!scr) return NULL; scr->aflags.hasAppIcon = 0; scr->aflags.simpleApplication = 1; return scr; } /* * W_X11OpenDisplay — open the X11 display and return a populated WMScreen. * * Called by WMOpenScreen() from widgets.c when the autodetect logic * decides this should be an X11 connection (no WAYLAND_DISPLAY env, no * "wayland" prefix in the display string, etc.). Extracting this here * lets widgets.c be free of XOpenDisplay. */ WMScreen *W_X11OpenDisplay(const char *display) { Display *dpy = XOpenDisplay(display && display[0] ? display : NULL); if (!dpy) { wwarning(_("WINGs: could not open display %s"), XDisplayName(display)); return NULL; } return WMCreateSimpleApplicationScreen(dpy); } /* * W_PlatformBackendX11.detect — return 1 if this is an X11 connection. * * Rules: * - explicit display string that does NOT start with "wayland" → X11. * - display==NULL but DISPLAY env set → X11. * - everything else → 0 (let the next backend try). * * The Wayland-client backend's detect() is consulted FIRST (registered * earlier in W_PlatformBackends), so the typical "WAYLAND_DISPLAY set, * DISPLAY unset" case picks Wayland. X11 is the fallback. */ static int x11_detect(const char *display) { if (display && strncmp(display, "wayland", 7) == 0) return 0; if (display && display[0]) return 1; /* explicit X display */ if (getenv("DISPLAY")) return 1; /* legacy: DISPLAY set */ return 0; /* no display info; not us */ } const W_PlatformBackend W_PlatformBackendX11 = { .name = "x11", .detect = x11_detect, .open = W_X11OpenDisplay, }; /* * W_NativeInternAtom — atom-intern wrapper used by shared code that * needs an Atom value at run-time (e.g. wtextfield.c's selection * request handler). In X11 mode delegates to XInternAtom. In Wayland * mode the stub in widgets_wl.c returns WM_NATIVE_NONE so the calling * code falls into a "no match" branch and selection no-ops. Either * way the shared file does not directly link against XInternAtom. */ WMNativeAtom W_NativeInternAtom(W_Screen *scr, const char *name) { if (!scr || !scr->display) return WM_NATIVE_NONE; return XInternAtom(scr->display, name, False); }