From fcce346e6787875e6526efea89e74055e447f889 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 8 May 2021 13:42:37 -0400 Subject: 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. --- src/implementation.c | 48 ++++++++++++++++--------- src/implementation.h | 8 +++-- src/win.c | 15 ++++---- src/x11.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++----- src/x11.h | 1 + 5 files changed, 139 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/implementation.c b/src/implementation.c index ad0de63..d7763a7 100644 --- a/src/implementation.c +++ b/src/implementation.c @@ -373,23 +373,25 @@ puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) } void -puglDispatchEventInContext(PuglView* view, const PuglEvent* event) +puglConfigure(PuglView* view, const PuglEvent* event) { - if (event->type == PUGL_CONFIGURE) { - view->frame.x = event->configure.x; - view->frame.y = event->configure.y; - view->frame.width = event->configure.width; - view->frame.height = event->configure.height; + assert(event->type == PUGL_CONFIGURE); - if (puglMustConfigure(view, &event->configure)) { - view->eventFunc(view, event); - view->lastConfigure = event->configure; - } - } else if (event->type == PUGL_EXPOSE) { - if (event->expose.width > 0 && event->expose.height > 0) { - view->eventFunc(view, event); - } - } else { + view->frame.x = event->configure.x; + view->frame.y = event->configure.y; + view->frame.width = event->configure.width; + view->frame.height = event->configure.height; + + if (puglMustConfigure(view, &event->configure)) { + view->eventFunc(view, event); + view->lastConfigure = event->configure; + } +} + +void +puglExpose(PuglView* view, const PuglEvent* event) +{ + if (event->expose.width > 0.0 && event->expose.height > 0.0) { view->eventFunc(view, event); } } @@ -409,13 +411,25 @@ puglDispatchEvent(PuglView* view, const PuglEvent* event) case PUGL_CONFIGURE: if (puglMustConfigure(view, &event->configure)) { view->backend->enter(view, NULL); - puglDispatchEventInContext(view, event); + puglConfigure(view, event); view->backend->leave(view, NULL); } break; + case PUGL_MAP: + if (!view->visible) { + view->visible = true; + view->eventFunc(view, event); + } + break; + case PUGL_UNMAP: + if (view->visible) { + view->visible = false; + view->eventFunc(view, event); + } + break; case PUGL_EXPOSE: view->backend->enter(view, &event->expose); - puglDispatchEventInContext(view, event); + puglExpose(view, event); view->backend->leave(view, &event->expose); break; default: diff --git a/src/implementation.h b/src/implementation.h index 8c5398c..936322d 100644 --- a/src/implementation.h +++ b/src/implementation.h @@ -58,9 +58,13 @@ puglDecodeUTF8(const uint8_t* buf); void puglDispatchSimpleEvent(PuglView* view, PuglEventType type); -/// Dispatch `event` to `view` while already in the graphics context +/// Process configure event while already in the graphics context void -puglDispatchEventInContext(PuglView* view, const PuglEvent* event); +puglConfigure(PuglView* view, const PuglEvent* event); + +/// Process expose event while already in the graphics context +void +puglExpose(PuglView* view, const PuglEvent* event); /// Dispatch `event` to `view`, entering graphics context if necessary void diff --git a/src/win.c b/src/win.c index 448e053..d1a66cd 100644 --- a/src/win.c +++ b/src/win.c @@ -579,14 +579,17 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_INTERNALPAINT); } - if ((bool)wParam != view->visible) { - view->visible = wParam; - event.any.type = wParam ? PUGL_MAP : PUGL_UNMAP; - } + event.any.type = wParam ? PUGL_MAP : PUGL_UNMAP; break; case WM_SIZE: - handleConfigure(view, &event); - InvalidateRect(view->impl->hwnd, NULL, false); + if (wParam == SIZE_MINIMIZED) { + event.type = PUGL_UNMAP; + } else if (!view->visible) { + event.type = PUGL_MAP; + } else { + handleConfigure(view, &event); + InvalidateRect(view->impl->hwnd, NULL, false); + } break; case WM_SIZING: if (view->minAspectX) { diff --git a/src/x11.c b/src/x11.c index 559c05b..50695ff 100644 --- a/src/x11.c +++ b/src/x11.c @@ -48,6 +48,7 @@ #include #include +#include #include #include #include @@ -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) { @@ -570,6 +601,39 @@ translateClientMessage(PuglView* const view, const XClientMessageEvent message) return event; } +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) { @@ -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); } } } diff --git a/src/x11.h b/src/x11.h index 313b4f5..daf177a 100644 --- a/src/x11.h +++ b/src/x11.h @@ -38,6 +38,7 @@ typedef struct { Atom NET_WM_NAME; Atom NET_WM_STATE; Atom NET_WM_STATE_DEMANDS_ATTENTION; + Atom NET_WM_STATE_HIDDEN; } PuglX11Atoms; typedef struct { -- cgit v1.2.1