aboutsummaryrefslogtreecommitdiffstats
path: root/pugl/detail
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-03-16 20:32:28 +0100
committerDavid Robillard <d@drobilla.net>2020-03-16 21:21:15 +0100
commita54361853bdfa08437c2858e603ce6202fb341b2 (patch)
tree5e86a841cc14dd4ca04d7b7b54b05f9c37e6feaf /pugl/detail
parent7de08cd2a57d26f546060183944632da71f643f2 (diff)
downloadpugl-a54361853bdfa08437c2858e603ce6202fb341b2.tar.gz
pugl-a54361853bdfa08437c2858e603ce6202fb341b2.tar.bz2
pugl-a54361853bdfa08437c2858e603ce6202fb341b2.zip
Add timer events
Diffstat (limited to 'pugl/detail')
-rw-r--r--pugl/detail/mac.h1
-rw-r--r--pugl/detail/mac.m44
-rw-r--r--pugl/detail/win.c22
-rw-r--r--pugl/detail/x11.c139
-rw-r--r--pugl/detail/x11.h11
5 files changed, 217 insertions, 0 deletions
diff --git a/pugl/detail/mac.h b/pugl/detail/mac.h
index adeebe9..d167f76 100644
--- a/pugl/detail/mac.h
+++ b/pugl/detail/mac.h
@@ -33,6 +33,7 @@
NSMutableAttributedString* markedText;
NSTimer* timer;
NSTimer* urgentTimer;
+ NSMutableDictionary* userTimers;
bool reshaped;
}
diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m
index 23fad7b..1c81abe 100644
--- a/pugl/detail/mac.m
+++ b/pugl/detail/mac.m
@@ -640,6 +640,14 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[puglview->world->impl->app requestUserAttention:NSInformationalRequest];
}
+- (void) timerTick:(NSTimer*)userTimer
+{
+ const NSNumber* userInfo = userTimer.userInfo;
+ const PuglEventTimer ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue};
+
+ puglDispatchEvent(puglview, (const PuglEvent*)&ev);
+}
+
- (void) viewDidEndLiveResize
{
[super viewDidEndLiveResize];
@@ -763,6 +771,7 @@ puglCreateWindow(PuglView* view, const char* title)
// Create wrapper view to handle input
impl->wrapperView = [PuglWrapperView alloc];
impl->wrapperView->puglview = view;
+ impl->wrapperView->userTimers = [[NSMutableDictionary alloc] init];
impl->wrapperView->markedText = [[NSMutableAttributedString alloc] init];
[impl->wrapperView setAutoresizesSubviews:YES];
[impl->wrapperView initWithFrame:
@@ -920,6 +929,41 @@ puglRequestAttention(PuglView* view)
return PUGL_SUCCESS;
}
+PuglStatus
+puglStartTimer(PuglView* view, uintptr_t id, double timeout)
+{
+ puglStopTimer(view, id);
+
+ NSNumber* idNumber = [NSNumber numberWithUnsignedLong:id];
+
+ NSTimer* timer = [NSTimer timerWithTimeInterval:timeout
+ target:view->impl->wrapperView
+ selector:@selector(timerTick:)
+ userInfo:idNumber
+ repeats:YES];
+
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
+
+ view->impl->wrapperView->userTimers[idNumber] = timer;
+
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id)
+{
+ NSNumber* idNumber = [NSNumber numberWithUnsignedLong:id];
+ NSTimer* timer = view->impl->wrapperView->userTimers[idNumber];
+
+ if (timer) {
+ [view->impl->wrapperView->userTimers removeObjectForKey:timer];
+ [timer invalidate];
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_UNKNOWN_ERROR;
+}
+
PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event)
{
if (event->type == PUGL_CLIENT) {
diff --git a/pugl/detail/win.c b/pugl/detail/win.c
index cb4dfad..971ecdd 100644
--- a/pugl/detail/win.c
+++ b/pugl/detail/win.c
@@ -51,6 +51,7 @@
#define PUGL_LOCAL_CLIENT_MSG (WM_USER + 52)
#define PUGL_RESIZE_TIMER_ID 9461
#define PUGL_URGENT_TIMER_ID 9462
+#define PUGL_USER_TIMER_MIN 9470
typedef BOOL (WINAPI *PFN_SetProcessDPIAware)(void);
@@ -589,6 +590,9 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT);
} else if (wParam == PUGL_URGENT_TIMER_ID) {
FlashWindow(view->impl->hwnd, TRUE);
+ } else if (wParam >= PUGL_USER_TIMER_MIN) {
+ const PuglEventTimer ev = {PUGL_TIMER, 0, wParam - PUGL_USER_TIMER_MIN};
+ puglDispatchEvent(view, (const PuglEvent*)&ev);
}
break;
case WM_EXITSIZEMOVE:
@@ -742,6 +746,24 @@ puglRequestAttention(PuglView* view)
}
PuglStatus
+puglStartTimer(PuglView* view, uintptr_t id, double timeout)
+{
+ const UINT msec = (UINT)floor(timeout * 1000.0);
+
+ return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL)
+ ? PUGL_SUCCESS
+ : PUGL_UNKNOWN_ERROR);
+}
+
+PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id)
+{
+ return (KillTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id)
+ ? PUGL_SUCCESS
+ : PUGL_UNKNOWN_ERROR);
+}
+
+PuglStatus
puglSendEvent(PuglView* view, const PuglEvent* event)
{
if (event->type == PUGL_CLIENT) {
diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c
index 3684e46..9613dfb 100644
--- a/pugl/detail/x11.c
+++ b/pugl/detail/x11.c
@@ -35,6 +35,10 @@
#include <X11/Xutil.h>
#include <X11/keysym.h>
+#ifdef HAVE_XSYNC
+# include <X11/extensions/sync.h>
+#endif
+
#include <sys/select.h>
#include <sys/time.h>
@@ -65,6 +69,37 @@ static const long eventMask =
EnterWindowMask | LeaveWindowMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask);
+static bool
+puglInitXSync(PuglWorldInternals* impl)
+{
+#ifdef HAVE_XSYNC
+ int syncMajor;
+ int syncMinor;
+ int errorBase;
+ XSyncSystemCounter* counters;
+ int numCounters;
+
+ if (XSyncQueryExtension(impl->display, &impl->syncEventBase, &errorBase) &&
+ XSyncInitialize(impl->display, &syncMajor, &syncMinor) &&
+ (counters = XSyncListSystemCounters(impl->display, &numCounters))) {
+
+ for (int n = 0; n < numCounters; ++n) {
+ if (!strcmp(counters[n].name, "SERVERTIME")) {
+ impl->serverTimeCounter = counters[n].counter;
+ impl->syncSupported = true;
+ break;
+ }
+ }
+
+ XSyncFreeSystemCounterList(counters);
+ }
+#else
+ (void)impl;
+#endif
+
+ return false;
+}
+
PuglWorldInternals*
puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags)
{
@@ -100,6 +135,7 @@ puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags)
impl->xim = XOpenIM(display, NULL, NULL, NULL);
}
+ puglInitXSync(impl);
XFlush(display);
return impl;
@@ -301,6 +337,7 @@ puglFreeWorldInternals(PuglWorld* world)
XCloseIM(world->impl->xim);
}
XCloseDisplay(world->impl->display);
+ free(world->impl->timers);
free(world->impl);
}
@@ -588,6 +625,80 @@ puglRequestAttention(PuglView* view)
return PUGL_SUCCESS;
}
+PuglStatus
+puglStartTimer(PuglView* view, uintptr_t id, double timeout)
+{
+#ifdef HAVE_XSYNC
+ if (view->world->impl->syncSupported) {
+ XSyncValue value;
+ XSyncIntToValue(&value, (int)floor(timeout * 1000.0));
+
+ PuglWorldInternals* w = view->world->impl;
+ Display* const display = w->display;
+ const XSyncCounter counter = w->serverTimeCounter;
+ const XSyncTrigger trigger = {counter, XSyncRelative, value, 0};
+ XSyncAlarmAttributes attr = {trigger, value, True, XSyncAlarmActive};
+ const XSyncAlarm alarm = XSyncCreateAlarm(display, 0x17, &attr);
+ const PuglTimer timer = {alarm, view, id};
+
+ if (alarm != None) {
+ for (size_t i = 0; i < w->numTimers; ++i) {
+ if (w->timers[i].view == view && w->timers[i].id == id) {
+ // Replace existing timer
+ XSyncDestroyAlarm(w->display, w->timers[i].alarm);
+ w->timers[i] = timer;
+ return PUGL_SUCCESS;
+ }
+ }
+
+ // Add new timer
+ const size_t size = ++w->numTimers * sizeof(timer);
+ w->timers = (PuglTimer*)realloc(w->timers, size);
+ w->timers[w->numTimers - 1] = timer;
+ return PUGL_SUCCESS;
+ }
+ }
+#else
+ (void)view;
+ (void)id;
+ (void)timeout;
+#endif
+
+ return PUGL_FAILURE;
+}
+
+PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id)
+{
+#ifdef HAVE_XSYNC
+ PuglWorldInternals* w = view->world->impl;
+
+ for (size_t i = 0; i < w->numTimers; ++i) {
+ if (w->timers[i].view == view && w->timers[i].id == id) {
+ XSyncDestroyAlarm(w->display, w->timers[i].alarm);
+
+ if (i == w->numTimers - 1) {
+ memset(&w->timers[i], 0, sizeof(PuglTimer));
+ } else {
+ memmove(w->timers + i,
+ w->timers + i + 1,
+ sizeof(PuglTimer) * (w->numTimers - i - 1));
+
+ memset(&w->timers[i], 0, sizeof(PuglTimer));
+ }
+
+ --w->numTimers;
+ return PUGL_SUCCESS;
+ }
+ }
+#else
+ (void)view;
+ (void)id;
+#endif
+
+ return PUGL_FAILURE;
+}
+
static XEvent
puglEventToX(PuglView* view, const PuglEvent* event)
{
@@ -765,6 +876,30 @@ flushExposures(PuglWorld* world)
}
}
+static bool
+handleTimerEvent(PuglWorld* world, XEvent xevent)
+{
+#ifdef HAVE_XSYNC
+ if (xevent.type == world->impl->syncEventBase + XSyncAlarmNotify) {
+ XSyncAlarmNotifyEvent* notify = ((XSyncAlarmNotifyEvent*)&xevent);
+
+ for (size_t i = 0; i < world->impl->numTimers; ++i) {
+ if (world->impl->timers[i].alarm == notify->alarm) {
+ const PuglEventTimer ev = {PUGL_TIMER, 0, world->impl->timers[i].id};
+ puglDispatchEvent(world->impl->timers[i].view, (const PuglEvent*)&ev);
+ }
+ }
+
+ return true;
+ }
+#else
+ (void)world;
+ (void)xevent;
+#endif
+
+ return false;
+}
+
static PuglStatus
puglDispatchX11Events(PuglWorld* world)
{
@@ -779,6 +914,10 @@ puglDispatchX11Events(PuglWorld* world)
XEvent xevent;
XNextEvent(display, &xevent);
+ if (handleTimerEvent(world, xevent)) {
+ continue;
+ }
+
PuglView* view = puglFindView(world, xevent.xany.window);
if (!view) {
continue;
diff --git a/pugl/detail/x11.h b/pugl/detail/x11.h
index fe8ce01..6f86a90 100644
--- a/pugl/detail/x11.h
+++ b/pugl/detail/x11.h
@@ -38,10 +38,21 @@ typedef struct {
Atom NET_WM_STATE_DEMANDS_ATTENTION;
} PuglX11Atoms;
+typedef struct {
+ XID alarm;
+ PuglView* view;
+ uint64_t id;
+} PuglTimer;
+
struct PuglWorldInternalsImpl {
Display* display;
PuglX11Atoms atoms;
XIM xim;
+ PuglTimer* timers;
+ size_t numTimers;
+ XID serverTimeCounter;
+ int syncEventBase;
+ bool syncSupported;
bool dispatchingEvents;
};