aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-05-08 13:42:37 -0400
committerDavid Robillard <d@drobilla.net>2021-05-08 14:29:42 -0400
commitfcce346e6787875e6526efea89e74055e447f889 (patch)
tree3fa0589c678e1142bfa9a42ef939ae43c92d0e78 /src
parent17ad5fd72bd51c5324b8e52e1f55020d5a97944a (diff)
downloadpugl-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')
-rw-r--r--src/implementation.c48
-rw-r--r--src/implementation.h8
-rw-r--r--src/win.c15
-rw-r--r--src/x11.c100
-rw-r--r--src/x11.h1
5 files changed, 139 insertions, 33 deletions
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 <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);
}
}
}
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 {