diff options
author | David Robillard <d@drobilla.net> | 2021-05-08 13:42:37 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2021-05-08 14:29:42 -0400 |
commit | fcce346e6787875e6526efea89e74055e447f889 (patch) | |
tree | 3fa0589c678e1142bfa9a42ef939ae43c92d0e78 /src/x11.c | |
parent | 17ad5fd72bd51c5324b8e52e1f55020d5a97944a (diff) | |
download | pugl-fcce346e6787875e6526efea89e74055e447f889.tar.gz pugl-fcce346e6787875e6526efea89e74055e447f889.tar.bz2 pugl-fcce346e6787875e6526efea89e74055e447f889.zip |
Send unmap/map events when the view is minimized/restored
X11 Window managers set WM_STATE to notify about minimization, often without
sending core X visibility events (which seems odd to me, but that's what Gnome
does anyway). So, implement this protocol and send map/unmap events to the
view, and adjust the Windows implementation to do the same for consistency
across all platforms.
Diffstat (limited to 'src/x11.c')
-rw-r--r-- | src/x11.c | 100 |
1 files changed, 92 insertions, 8 deletions
@@ -48,6 +48,7 @@ #include <sys/select.h> #include <sys/time.h> +#include <limits.h> #include <math.h> #include <stdbool.h> #include <stdint.h> @@ -126,6 +127,8 @@ puglInitWorldInternals(const PuglWorldType type, const PuglWorldFlags flags) impl->atoms.NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", 0); impl->atoms.NET_WM_STATE_DEMANDS_ATTENTION = XInternAtom(display, "_NET_WM_STATE_DEMANDS_ATTENTION", 0); + impl->atoms.NET_WM_STATE_HIDDEN = + XInternAtom(display, "_NET_WM_STATE_HIDDEN", 0); // Open input method XSetLocaleModifiers(""); @@ -327,6 +330,7 @@ puglRealize(PuglView* const view) attr.event_mask |= KeyReleaseMask; attr.event_mask |= LeaveWindowMask; attr.event_mask |= PointerMotionMask; + attr.event_mask |= PropertyChangeMask; attr.event_mask |= StructureNotifyMask; attr.event_mask |= VisibilityChangeMask; @@ -550,6 +554,33 @@ translateModifiers(const unsigned xstate) ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0u)); } +static PuglStatus +getAtomProperty(PuglView* const view, + const Window window, + const Atom property, + unsigned long* numValues, + Atom** values) +{ + Atom actualType = 0; + int actualFormat = 0; + unsigned long bytesAfter = 0; + + return (XGetWindowProperty(view->impl->display, + window, + property, + 0, + LONG_MAX, + False, + XA_ATOM, + &actualType, + &actualFormat, + numValues, + &bytesAfter, + (unsigned char**)values) == Success) + ? PUGL_SUCCESS + : PUGL_FAILURE; +} + static PuglEvent translateClientMessage(PuglView* const view, const XClientMessageEvent message) { @@ -571,6 +602,39 @@ translateClientMessage(PuglView* const view, const XClientMessageEvent message) } static PuglEvent +translatePropertyNotify(PuglView* const view, XPropertyEvent message) +{ + const PuglX11Atoms* const atoms = &view->world->impl->atoms; + + PuglEvent event = {{PUGL_NOTHING, 0}}; + bool hidden = false; + if (message.atom == atoms->NET_WM_STATE) { + unsigned long numHints = 0; + Atom* hints = NULL; + if (getAtomProperty( + view, view->impl->win, message.atom, &numHints, &hints)) { + return event; + } + + for (unsigned long i = 0; i < numHints; ++i) { + if (hints[i] == atoms->NET_WM_STATE_HIDDEN) { + hidden = true; + } + } + + if (hidden && view->visible) { + event.type = PUGL_UNMAP; + } else if (!hidden && !view->visible) { + event.type = PUGL_MAP; + } + + XFree(hints); + } + + return event; +} + +static PuglEvent translateEvent(PuglView* const view, XEvent xevent) { PuglEvent event = {{PUGL_NOTHING, 0}}; @@ -580,15 +644,21 @@ translateEvent(PuglView* const view, XEvent xevent) case ClientMessage: event = translateClientMessage(view, xevent.xclient); break; + case PropertyNotify: + event = translatePropertyNotify(view, xevent.xproperty); + break; case VisibilityNotify: - view->visible = xevent.xvisibility.state != VisibilityFullyObscured; + if (xevent.xvisibility.state == VisibilityFullyObscured) { + event.type = PUGL_UNMAP; + } else { + event.type = PUGL_MAP; + } break; case MapNotify: event.type = PUGL_MAP; break; case UnmapNotify: - event.type = PUGL_UNMAP; - view->visible = false; + event.type = PUGL_UNMAP; break; case ConfigureNotify: event.type = PUGL_CONFIGURE; @@ -986,21 +1056,35 @@ flushExposures(PuglWorld* const world) for (size_t i = 0; i < world->numViews; ++i) { PuglView* const view = world->views[i]; + // Send update event so the application can trigger redraws if (view->visible) { puglDispatchSimpleEvent(view, PUGL_UPDATE); } + // Copy and reset pending events (in case their handlers write new ones) const PuglEvent configure = view->impl->pendingConfigure; const PuglEvent expose = view->impl->pendingExpose; view->impl->pendingConfigure.type = PUGL_NOTHING; view->impl->pendingExpose.type = PUGL_NOTHING; - if (configure.type || expose.type) { - view->backend->enter(view, expose.type ? &expose.expose : NULL); - puglDispatchEventInContext(view, &configure); - puglDispatchEventInContext(view, &expose); - view->backend->leave(view, expose.type ? &expose.expose : NULL); + if (expose.type) { + view->backend->enter(view, &expose.expose); + + if (configure.type) { + puglConfigure(view, &configure); + } + + puglExpose(view, &expose); + view->backend->leave(view, &expose.expose); + } else if (configure.type) { + view->backend->enter(view, NULL); + + if (configure.type) { + puglConfigure(view, &configure); + } + + view->backend->leave(view, NULL); } } } |