aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-07-21 19:42:50 +0200
committerDavid Robillard <d@drobilla.net>2019-07-21 21:32:55 +0200
commit80191fb070d60e7bffd78c2ef9e43b2610f2b8ff (patch)
tree195be0ccf9b018da9f00e49185290f65a3bb5187
parent081490898ed788a184eb74074130634ce9067174 (diff)
Add puglRequestAttention()
-rw-r--r--pugl/pugl.h10
-rw-r--r--pugl/pugl.hpp1
-rw-r--r--pugl/pugl_osx.m26
-rw-r--r--pugl/pugl_win.c21
-rw-r--r--pugl/pugl_x11.c33
-rw-r--r--pugl/pugl_x11.h2
-rw-r--r--pugl_test.c8
7 files changed, 100 insertions, 1 deletions
diff --git a/pugl/pugl.h b/pugl/pugl.h
index c8e62d4..d744477 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -599,6 +599,16 @@ PUGL_API void
puglGrabFocus(PuglView* view);
/**
+ Request user attention.
+
+ This hints to the system that the window or application requires attention
+ from the user. The exact effect depends on the platform, but is usually
+ something like flashing a task bar entry.
+*/
+PUGL_API void
+puglRequestAttention(PuglView* view);
+
+/**
Block and wait for an event to be ready.
This can be used in a loop to only process events via puglProcessEvents when
diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp
index 8232887..36e7e13 100644
--- a/pugl/pugl.hpp
+++ b/pugl/pugl.hpp
@@ -83,6 +83,7 @@ public:
virtual void* getContext() { return puglGetContext(_view); }
virtual void ignoreKeyRepeat(bool ignore) { puglIgnoreKeyRepeat(_view, ignore); }
virtual void grabFocus() { puglGrabFocus(_view); }
+ virtual void requestAttention() { puglRequestAttention(_view); }
virtual PuglStatus waitForEvent() { return puglWaitForEvent(_view); }
virtual PuglStatus processEvents() { return puglProcessEvents(_view); }
virtual void postRedisplay() { puglPostRedisplay(_view); }
diff --git a/pugl/pugl_osx.m b/pugl/pugl_osx.m
index 73fe654..7a804b7 100644
--- a/pugl/pugl_osx.m
+++ b/pugl/pugl_osx.m
@@ -101,6 +101,7 @@ struct PuglInternalsImpl {
PuglView* puglview;
NSTrackingArea* trackingArea;
NSTimer* timer;
+ NSTimer* urgentTimer;
}
@end
@@ -544,6 +545,11 @@ handleCrossing(PuglOpenGLView* view, NSEvent* event, const PuglEventType type)
puglPostRedisplay(puglview);
}
+- (void) urgentTick
+{
+ [NSApp requestUserAttention:NSInformationalRequest];
+}
+
- (void) viewDidEndLiveResize
{
[super viewDidEndLiveResize];
@@ -582,6 +588,12 @@ handleCrossing(PuglOpenGLView* view, NSEvent* event, const PuglEventType type)
- (void) windowDidBecomeKey:(NSNotification*)notification
{
+ PuglOpenGLView* glview = window->puglview->impl->glview;
+ if (window->puglview->impl->glview->urgentTimer) {
+ [glview->urgentTimer invalidate];
+ glview->urgentTimer = NULL;
+ }
+
const PuglEventFocus ev = { PUGL_FOCUS_IN, window->puglview, 0, false };
puglDispatchEvent(window->puglview, (const PuglEvent*)&ev);
}
@@ -740,6 +752,20 @@ puglGrabFocus(PuglView* view)
[view->impl->window makeKeyWindow];
}
+void
+puglRequestAttention(PuglView* view)
+{
+ if (![view->impl->window isKeyWindow]) {
+ [NSApp requestUserAttention:NSInformationalRequest];
+ view->impl->glview->urgentTimer =
+ [NSTimer scheduledTimerWithTimeInterval:2.0
+ target:view->impl->glview
+ selector:@selector(urgentTick)
+ userInfo:nil
+ repeats:YES];
+ }
+}
+
PuglStatus
puglWaitForEvent(PuglView* view)
{
diff --git a/pugl/pugl_win.c b/pugl/pugl_win.c
index 2e05c71..c91d383 100644
--- a/pugl/pugl_win.c
+++ b/pugl/pugl_win.c
@@ -45,6 +45,7 @@
#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
#define PUGL_RESIZE_TIMER_ID 9461
+#define PUGL_URGENT_TIMER_ID 9462
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
@@ -631,6 +632,13 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos)
puglDispatchEvent(view, (const PuglEvent*)&ev);
}
+static void
+stopFlashing(PuglView* view)
+{
+ KillTimer(view->impl->hwnd, PUGL_URGENT_TIMER_ID);
+ FlashWindow(view->impl->hwnd, FALSE);
+}
+
static LRESULT
handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -671,6 +679,8 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
case WM_TIMER:
if (wParam == PUGL_RESIZE_TIMER_ID) {
puglPostRedisplay(view);
+ } else if (wParam == PUGL_URGENT_TIMER_ID) {
+ FlashWindow(view->impl->hwnd, TRUE);
}
break;
case WM_EXITSIZEMOVE:
@@ -704,6 +714,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
tme.hwndTrack = view->impl->hwnd;
TrackMouseEvent(&tme);
+ stopFlashing(view);
handleCrossing(view, PUGL_ENTER_NOTIFY, pt);
view->impl->mouseTracked = true;
}
@@ -778,6 +789,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
}
break;
case WM_SETFOCUS:
+ stopFlashing(view);
event.type = PUGL_FOCUS_IN;
break;
case WM_KILLFOCUS:
@@ -802,6 +814,15 @@ puglGrabFocus(PuglView* view)
SetFocus(view->impl->hwnd);
}
+void
+puglRequestAttention(PuglView* view)
+{
+ if (!view->impl->mouseTracked || GetFocus() != view->impl->hwnd) {
+ FlashWindow(view->impl->hwnd, TRUE);
+ SetTimer(view->impl->hwnd, PUGL_URGENT_TIMER_ID, 500, NULL);
+ }
+}
+
PuglStatus
puglWaitForEvent(PuglView* view)
{
diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c
index 8bfcf17..49a1a9c 100644
--- a/pugl/pugl_x11.c
+++ b/pugl/pugl_x11.c
@@ -50,6 +50,12 @@
# define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
+enum WmClientStateMessageAction {
+ WM_STATE_REMOVE,
+ WM_STATE_ADD,
+ WM_STATE_TOGGLE
+};
+
PuglInternals*
puglInitInternals(void)
{
@@ -80,6 +86,9 @@ puglCreateWindow(PuglView* view, const char* title)
// Intern the various atoms we will need
impl->atoms.WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0);
impl->atoms.WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+ 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);
if (view->ctx_type == PUGL_GL) {
#ifdef PUGL_HAVE_GL
@@ -435,6 +444,30 @@ puglGrabFocus(PuglView* view)
view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime);
}
+void
+puglRequestAttention(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ XEvent event = {0};
+
+ event.type = ClientMessage;
+ event.xclient.window = impl->win;
+ event.xclient.format = 32;
+ event.xclient.message_type = impl->atoms.NET_WM_STATE;
+ event.xclient.data.l[0] = WM_STATE_ADD;
+ event.xclient.data.l[1] = impl->atoms.NET_WM_STATE_DEMANDS_ATTENTION;
+ event.xclient.data.l[2] = 0;
+ event.xclient.data.l[3] = 1;
+ event.xclient.data.l[4] = 0;
+
+ const Window root = RootWindow(impl->display, impl->screen);
+ XSendEvent(impl->display,
+ root,
+ False,
+ SubstructureNotifyMask | SubstructureRedirectMask,
+ (XEvent*)&event);
+}
+
PuglStatus
puglWaitForEvent(PuglView* view)
{
diff --git a/pugl/pugl_x11.h b/pugl/pugl_x11.h
index f33d8de..6a38c62 100644
--- a/pugl/pugl_x11.h
+++ b/pugl/pugl_x11.h
@@ -33,5 +33,7 @@ struct PuglInternalsImpl {
struct {
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
+ Atom NET_WM_STATE;
+ Atom NET_WM_STATE_DEMANDS_ATTENTION;
} atoms;
};
diff --git a/pugl_test.c b/pugl_test.c
index 8425611..6520b07 100644
--- a/pugl_test.c
+++ b/pugl_test.c
@@ -297,7 +297,8 @@ main(int argc, char** argv)
puglShowWindow(view);
- float lastReportTime = (float)puglGetTime(view);
+ float lastReportTime = (float)puglGetTime(view);
+ bool requestedAttention = false;
while (!quit) {
const float thisTime = (float)puglGetTime(view);
@@ -309,6 +310,11 @@ main(int argc, char** argv)
puglProcessEvents(view);
+ if (!requestedAttention && thisTime > 5) {
+ puglRequestAttention(view);
+ requestedAttention = true;
+ }
+
if (continuous && thisTime > lastReportTime + 5) {
const double fps = framesDrawn / (thisTime - lastReportTime);
fprintf(stderr,