aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-03-09 21:49:58 +0100
committerDavid Robillard <d@drobilla.net>2020-03-09 22:17:44 +0100
commit9be7bab7cf3c63cafa07e5a70d6c59559ffdc1de (patch)
treedb882f9d08f5b91b31271cd2844cae11f64bf1dd
parenta303b9374dbb9eeef1a31dcad3cb0a4e9b7fd5a3 (diff)
Add PuglEventClient and puglSendEvent()
This event makes it possible to send an arbitrary event to a view, which is useful for many things. In particular, this method of communication with views will wake up the event loop, unlike hacks in applications that share data in some other way.
-rw-r--r--pugl/detail/mac.m48
-rw-r--r--pugl/detail/win.c29
-rw-r--r--pugl/detail/x11.c19
-rw-r--r--pugl/detail/x11.h1
-rw-r--r--pugl/pugl.h29
-rw-r--r--test/test_utils.h8
6 files changed, 128 insertions, 6 deletions
diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m
index c083919..837d3cc 100644
--- a/pugl/detail/mac.m
+++ b/pugl/detail/mac.m
@@ -947,6 +947,32 @@ puglPollEvents(PuglWorld* world, const double timeout)
return PUGL_SUCCESS;
}
+PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event)
+{
+ if (event->type == PUGL_CLIENT) {
+ PuglWrapperView* wrapper = view->impl->wrapperView;
+ const NSWindow* window = [wrapper window];
+ const NSRect rect = [wrapper frame];
+ const NSPoint center = {NSMidX(rect), NSMidY(rect)};
+
+ NSEvent* nsevent = [NSEvent
+ otherEventWithType:NSApplicationDefined
+ location:center
+ modifierFlags:0
+ timestamp:[[NSProcessInfo processInfo] systemUptime]
+ windowNumber:window.windowNumber
+ context:nil
+ subtype:PUGL_CLIENT
+ data1:event->client.data1
+ data2:event->client.data2];
+
+ [view->world->impl->app postEvent:nsevent atStart:false];
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_UNSUPPORTED_TYPE;
+}
+
#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglWaitForEvent(PuglView* view)
@@ -955,6 +981,25 @@ puglWaitForEvent(PuglView* view)
}
#endif
+static void
+dispatchClientEvent(PuglWorld* world, NSEvent* ev)
+{
+ NSWindow* win = [ev window];
+ NSPoint loc = [ev locationInWindow];
+ for (size_t i = 0; i < world->numViews; ++i) {
+ PuglView* view = world->views[i];
+ PuglWrapperView* wrapper = view->impl->wrapperView;
+ if ([wrapper window] == win && NSPointInRect(loc, [wrapper frame])) {
+ const PuglEventClient event = {PUGL_CLIENT,
+ 0,
+ [ev data1],
+ [ev data2]};
+
+ view->eventFunc(view, (const PuglEvent*)&event);
+ }
+ }
+}
+
PUGL_API PuglStatus
puglDispatchEvents(PuglWorld* world)
{
@@ -970,6 +1015,9 @@ puglDispatchEvents(PuglWorld* world)
// Event is later, put it back for the next iteration and return
[world->impl->app postEvent:ev atStart:true];
break;
+ } else if ([ev type] == NSApplicationDefined &&
+ [ev subtype] == PUGL_CLIENT) {
+ dispatchClientEvent(world, ev);
}
[world->impl->app sendEvent: ev];
diff --git a/pugl/detail/win.c b/pugl/detail/win.c
index 2e0cd96..bf60ddb 100644
--- a/pugl/detail/win.c
+++ b/pugl/detail/win.c
@@ -47,10 +47,11 @@
# define GWLP_USERDATA (-21)
#endif
-#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
-#define PUGL_LOCAL_MARK_MSG (WM_USER + 51)
-#define PUGL_RESIZE_TIMER_ID 9461
-#define PUGL_URGENT_TIMER_ID 9462
+#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
+#define PUGL_LOCAL_MARK_MSG (WM_USER + 51)
+#define PUGL_LOCAL_CLIENT_MSG (WM_USER + 52)
+#define PUGL_RESIZE_TIMER_ID 9461
+#define PUGL_URGENT_TIMER_ID 9462
typedef BOOL (WINAPI *PFN_SetProcessDPIAware)(void);
@@ -700,6 +701,11 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
break;
case WM_SYSCHAR:
return TRUE;
+ case PUGL_LOCAL_CLIENT_MSG:
+ event.client.type = PUGL_CLIENT;
+ event.client.data1 = (uintptr_t)wParam;
+ event.client.data2 = (uintptr_t)lParam;
+ break;
case WM_QUIT:
case PUGL_LOCAL_CLOSE_MSG:
event.any.type = PUGL_CLOSE;
@@ -738,6 +744,21 @@ puglRequestAttention(PuglView* view)
return PUGL_SUCCESS;
}
+PuglStatus
+puglSendEvent(PuglView* view, const PuglEvent* event)
+{
+ if (event->type == PUGL_CLIENT) {
+ PostMessage(view->impl->hwnd,
+ PUGL_LOCAL_CLIENT_MSG,
+ (WPARAM)event->client.data1,
+ (LPARAM)event->client.data2);
+
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_UNSUPPORTED_TYPE;
+}
+
#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglWaitForEvent(PuglView* PUGL_UNUSED(view))
diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c
index 9c9fdfa..ae00f73 100644
--- a/pugl/detail/x11.c
+++ b/pugl/detail/x11.c
@@ -84,6 +84,7 @@ puglInitWorldInternals(void)
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_WM_NAME = XInternAtom(display, "_NET_WM_NAME", 0);
impl->atoms.NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", 0);
impl->atoms.NET_WM_STATE_DEMANDS_ATTENTION =
@@ -429,6 +430,10 @@ translateEvent(PuglView* view, XEvent xevent)
if (protocol == atoms->WM_DELETE_WINDOW) {
event.type = PUGL_CLOSE;
}
+ } else if (xevent.xclient.message_type == atoms->PUGL_CLIENT_MSG) {
+ event.type = PUGL_CLIENT;
+ event.client.data1 = xevent.xclient.data.l[0];
+ event.client.data2 = xevent.xclient.data.l[1];
}
break;
case VisibilityNotify:
@@ -613,6 +618,18 @@ puglEventToX(PuglView* view, const PuglEvent* event)
break;
}
+ case PUGL_CLIENT:
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.display = view->impl->display;
+ xev.xclient.window = view->impl->win;
+ xev.xclient.message_type = view->world->impl->atoms.PUGL_CLIENT_MSG;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = event->client.data1;
+ xev.xclient.data.l[1] = event->client.data2;
+ break;
+
default:
break;
}
@@ -620,7 +637,7 @@ puglEventToX(PuglView* view, const PuglEvent* event)
return xev;
}
-static PuglStatus
+PuglStatus
puglSendEvent(PuglView* view, const PuglEvent* event)
{
XEvent xev = puglEventToX(view, event);
diff --git a/pugl/detail/x11.h b/pugl/detail/x11.h
index 220f3c8..fe8ce01 100644
--- a/pugl/detail/x11.h
+++ b/pugl/detail/x11.h
@@ -32,6 +32,7 @@ typedef struct {
Atom UTF8_STRING;
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
+ Atom PUGL_CLIENT_MSG;
Atom NET_WM_NAME;
Atom NET_WM_STATE;
Atom NET_WM_STATE_DEMANDS_ATTENTION;
diff --git a/pugl/pugl.h b/pugl/pugl.h
index 04f5343..53327c8 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -183,7 +183,8 @@ typedef enum {
PUGL_MOTION_NOTIFY, ///< Pointer moved, a #PuglEventMotion
PUGL_SCROLL, ///< Scrolled, a #PuglEventScroll
PUGL_FOCUS_IN, ///< Keyboard focus entered view, a #PuglEventFocus
- PUGL_FOCUS_OUT ///< Keyboard focus left view, a #PuglEventFocus
+ PUGL_FOCUS_OUT, ///< Keyboard focus left view, a #PuglEventFocus
+ PUGL_CLIENT ///< Custom client message, a #PuglEventClient
} PuglEventType;
/**
@@ -389,6 +390,20 @@ typedef struct {
} PuglEventFocus;
/**
+ Custom client message event.
+
+ This can be used to send a custom message to a view, which is delivered via
+ the window system and processed in the event loop as usual. Among other
+ things, this makes it possible to wake up the event loop for any reason.
+*/
+typedef struct {
+ PuglEventType type; ///< PUGL_CLIENT
+ PuglEventFlags flags; ///< Bitwise OR of PuglEventFlag values
+ uintptr_t data1; ///< Client-specific data
+ uintptr_t data2; ///< Client-specific data
+} PuglEventClient;
+
+/**
View event.
This is a union of all event types. The #type must be checked to determine
@@ -411,6 +426,7 @@ typedef union {
PuglEventMotion motion; ///< #PUGL_MOTION_NOTIFY
PuglEventScroll scroll; ///< #PUGL_SCROLL
PuglEventFocus focus; ///< #PUGL_FOCUS_IN, #PUGL_FOCUS_OUT
+ PuglEventClient client; ///< #PUGL_CLIENT
} PuglEvent;
/**
@@ -984,6 +1000,17 @@ PUGL_API PuglStatus
puglRequestAttention(PuglView* view);
/**
+ Send an event to a view via the window system.
+
+ If supported, the event will be delivered to the view via the event loop
+ like other events. Note that this function only works for certain event
+ types, and will return PUGL_UNSUPPORTED_TYPE for events that are not
+ supported.
+*/
+PUGL_API PuglStatus
+puglSendEvent(PuglView* view, const PuglEvent* event);
+
+/**
@}
*/
diff --git a/test/test_utils.h b/test/test_utils.h
index aca3376..7dc6e6e 100644
--- a/test/test_utils.h
+++ b/test/test_utils.h
@@ -14,8 +14,11 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define __STDC_FORMAT_MACROS 1
+
#include "pugl/pugl.h"
+#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
@@ -118,6 +121,11 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
return PRINT("%sFocus out%s\n",
prefix,
event->focus.grab ? " (ungrab)" : "");
+ case PUGL_CLIENT:
+ return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
+ prefix,
+ event->client.data1,
+ event->client.data2);
default:
break;
}