From 294c0f7d89b206612634978e3df166ce532a1118 Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Sun, 5 Jul 2020 11:53:19 +0200
Subject: Replace grab flag in PuglEventFocus with crossing mode

---
 pugl/detail/mac.m |  4 ++--
 pugl/detail/x11.c |  7 ++++++-
 pugl/pugl.h       |  6 +++---
 test/test_utils.h | 23 +++++++++++++++++++----
 4 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m
index 1139bc8..3e5a018 100644
--- a/pugl/detail/mac.m
+++ b/pugl/detail/mac.m
@@ -778,7 +778,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
 	(void)notification;
 
 	PuglEvent ev = {{PUGL_FOCUS_IN, 0}};
-	ev.focus.grab = false;
+	ev.focus.mode = PUGL_CROSSING_NORMAL;
 	puglDispatchEvent(window->puglview, &ev);
 }
 
@@ -787,7 +787,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
 	(void)notification;
 
 	PuglEvent ev = {{PUGL_FOCUS_OUT, 0}};
-	ev.focus.grab = false;
+	ev.focus.mode = PUGL_CROSSING_NORMAL;
 	puglDispatchEvent(window->puglview, &ev);
 }
 
diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c
index 12efc43..1cd76f7 100644
--- a/pugl/detail/x11.c
+++ b/pugl/detail/x11.c
@@ -644,7 +644,12 @@ translateEvent(PuglView* view, XEvent xevent)
 	case FocusIn:
 	case FocusOut:
 		event.type = (xevent.type == FocusIn) ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT;
-		event.focus.grab = (xevent.xfocus.mode != NotifyNormal);
+		event.focus.mode = PUGL_CROSSING_NORMAL;
+		if (xevent.xfocus.mode == NotifyGrab) {
+			event.focus.mode = PUGL_CROSSING_GRAB;
+		} else if (xevent.xfocus.mode == NotifyUngrab) {
+			event.focus.mode = PUGL_CROSSING_UNGRAB;
+		}
 		break;
 
 	default:
diff --git a/pugl/pugl.h b/pugl/pugl.h
index 13d8801..137e6d1 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -352,9 +352,9 @@ typedef PuglEventAny PuglEventClose;
    view with the keyboard focus will receive any key press or release events.
 */
 typedef struct {
-	PuglEventType  type;  ///< #PUGL_FOCUS_IN or #PUGL_FOCUS_OUT
-	PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
-	bool           grab;  ///< True iff this is a grab/ungrab event
+	PuglEventType    type;  ///< #PUGL_FOCUS_IN or #PUGL_FOCUS_OUT
+	PuglEventFlags   flags; ///< Bitwise OR of #PuglEventFlag values
+	PuglCrossingMode mode;  ///< Reason for focus change
 } PuglEventFocus;
 
 /**
diff --git a/test/test_utils.h b/test/test_utils.h
index 3cbed63..232537e 100644
--- a/test/test_utils.h
+++ b/test/test_utils.h
@@ -67,6 +67,21 @@ printModifiers(const uint32_t mods)
 	               (mods & PUGL_MOD_SUPER) ? " Super" : "");
 }
 
+static inline const char*
+crossingModeString(const PuglCrossingMode mode)
+{
+	switch (mode) {
+	case PUGL_CROSSING_NORMAL:
+		return "normal";
+	case PUGL_CROSSING_GRAB:
+		return "grab";
+	case PUGL_CROSSING_UNGRAB:
+		return "ungrab";
+	}
+
+	return "unknown";
+}
+
 static inline int
 printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
 {
@@ -121,13 +136,13 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
 		             event->crossing.x,
 		             event->crossing.y);
 	case PUGL_FOCUS_IN:
-		return PRINT("%sFocus in%s\n",
+		return PRINT("%sFocus in (%s)\n",
 		             prefix,
-		             event->focus.grab ? " (grab)" : "");
+		             crossingModeString(event->crossing.mode));
 	case PUGL_FOCUS_OUT:
-		return PRINT("%sFocus out%s\n",
+		return PRINT("%sFocus out (%s)\n",
 		             prefix,
-		             event->focus.grab ? " (ungrab)" : "");
+		             crossingModeString(event->crossing.mode));
 	case PUGL_CLIENT:
 		return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
 		             prefix,
-- 
cgit v1.2.1