From a54361853bdfa08437c2858e603ce6202fb341b2 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 16 Mar 2020 20:32:28 +0100 Subject: Add timer events --- pugl/detail/mac.h | 1 + pugl/detail/mac.m | 44 +++++++++++++++++ pugl/detail/win.c | 22 +++++++++ pugl/detail/x11.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pugl/detail/x11.h | 11 +++++ 5 files changed, 217 insertions(+) (limited to 'pugl/detail') 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: @@ -741,6 +745,24 @@ puglRequestAttention(PuglView* view) return PUGL_SUCCESS; } +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) { 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 #include +#ifdef HAVE_XSYNC +# include +#endif + #include #include @@ -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; }; -- cgit v1.2.1