windowmaker-wl/WINGs/widgets_x11.c

303 lines
10 KiB
C

/*
* 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 <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Xlocale.h>
#include <stdlib.h>
#include <string.h>
/* 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);
}