From 2881f3defa1d89b2c6808db2344ea6e525aefd28 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 8 Jan 2023 09:37:26 -0500 Subject: X11: Add support for _NET_WM_PING protocol This is mainly used to allow the window manager to close locked or otherwise misbehaving windows. The PID and hostname properties are both required to properly support this, but may also be used for other things. --- src/x11.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++------------- src/x11.h | 3 +++ 2 files changed, 56 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/x11.c b/src/x11.c index 4e8d591..ce9387b 100644 --- a/src/x11.c +++ b/src/x11.c @@ -41,6 +41,7 @@ #include #include #include +#include #ifndef MIN # define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -157,14 +158,17 @@ puglInitWorldInternals(const PuglWorldType type, const PuglWorldFlags flags) // Intern the various atoms we'll need - impl->atoms.CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0); - impl->atoms.UTF8_STRING = XInternAtom(display, "UTF8_STRING", 0); - impl->atoms.WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0); - impl->atoms.WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", 0); - impl->atoms.PUGL_CLIENT_MSG = XInternAtom(display, "_PUGL_CLIENT_MSG", 0); - impl->atoms.NET_CLOSE_WINDOW = XInternAtom(display, "_NET_CLOSE_WINDOW", 0); - impl->atoms.NET_WM_NAME = XInternAtom(display, "_NET_WM_NAME", 0); - impl->atoms.NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", 0); + impl->atoms.CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0); + impl->atoms.UTF8_STRING = XInternAtom(display, "UTF8_STRING", 0); + impl->atoms.WM_CLIENT_MACHINE = XInternAtom(display, "WM_CLIENT_MACHINE", 0); + impl->atoms.WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0); + impl->atoms.WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", 0); + impl->atoms.PUGL_CLIENT_MSG = XInternAtom(display, "_PUGL_CLIENT_MSG", 0); + impl->atoms.NET_CLOSE_WINDOW = XInternAtom(display, "_NET_CLOSE_WINDOW", 0); + impl->atoms.NET_WM_NAME = XInternAtom(display, "_NET_WM_NAME", 0); + impl->atoms.NET_WM_PID = XInternAtom(display, "_NET_WM_PID", 0); + impl->atoms.NET_WM_PING = XInternAtom(display, "_NET_WM_PING", 0); + impl->atoms.NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", 0); impl->atoms.NET_WM_STATE_ABOVE = XInternAtom(display, "_NET_WM_STATE_ABOVE", 0); @@ -583,14 +587,38 @@ puglRealize(PuglView* const view) puglSetWindowTitle(view, view->title); } - if (parent == root) { - XSetWMProtocols(display, impl->win, &atoms->WM_DELETE_WINDOW, 1); - } - if (view->transientParent) { puglSetTransientParent(view, view->transientParent); } + // Set PID and hostname so the window manager can access our process + char hostname[256] = {0}; + const long pid = (long)getpid(); + if (pid > 0 && !gethostname(hostname, sizeof(hostname))) { + hostname[sizeof(hostname) - 1] = '\0'; + XChangeProperty(display, + impl->win, + atoms->WM_CLIENT_MACHINE, + XA_STRING, + 8, + PropModeReplace, + (const uint8_t*)hostname, + (int)strlen(hostname)); + + XChangeProperty(display, + impl->win, + atoms->NET_WM_PID, + XA_CARDINAL, + 32, + PropModeReplace, + (const uint8_t*)&pid, + 1); + } + + // Set supported WM protocols + Atom protocols[] = {atoms->NET_WM_PING, atoms->WM_DELETE_WINDOW}; + XSetWMProtocols(display, impl->win, protocols, (parent == root) ? 2 : 1); + // Create input context if (world->impl->xim) { impl->xic = XCreateIC(world->impl->xim, @@ -913,13 +941,24 @@ setClipboardFormats(PuglView* const view, static PuglEvent translateClientMessage(PuglView* const view, XClientMessageEvent message) { - const PuglX11Atoms* const atoms = &view->world->impl->atoms; - PuglEvent event = {{PUGL_NOTHING, 0}}; + Display* const display = view->world->impl->display; + const PuglX11Atoms* const atoms = &view->world->impl->atoms; + PuglEvent event = {{PUGL_NOTHING, 0}}; if (message.message_type == atoms->WM_PROTOCOLS) { const Atom protocol = (Atom)message.data.l[0]; if (protocol == atoms->WM_DELETE_WINDOW) { event.type = PUGL_CLOSE; + } else if (protocol == atoms->NET_WM_PING) { + const Window root = RootWindow(display, view->impl->screen); + XEvent reply = {ClientMessage}; + reply.xclient = message; + reply.xclient.window = root; + XSendEvent(display, + root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &reply); } } else if (message.message_type == atoms->PUGL_CLIENT_MSG) { event.type = PUGL_CLIENT; diff --git a/src/x11.h b/src/x11.h index e90694a..b2c0e54 100644 --- a/src/x11.h +++ b/src/x11.h @@ -21,11 +21,14 @@ typedef struct { Atom CLIPBOARD; Atom UTF8_STRING; + Atom WM_CLIENT_MACHINE; Atom WM_PROTOCOLS; Atom WM_DELETE_WINDOW; Atom PUGL_CLIENT_MSG; Atom NET_CLOSE_WINDOW; Atom NET_WM_NAME; + Atom NET_WM_PID; + Atom NET_WM_PING; Atom NET_WM_STATE; Atom NET_WM_STATE_ABOVE; Atom NET_WM_STATE_BELOW; -- cgit v1.2.1