summaryrefslogtreecommitdiff
path: root/pugl/pugl_x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'pugl/pugl_x11.c')
-rw-r--r--pugl/pugl_x11.c438
1 files changed, 242 insertions, 196 deletions
diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c
index 509e527..65eaab8 100644
--- a/pugl/pugl_x11.c
+++ b/pugl/pugl_x11.c
@@ -40,6 +40,7 @@
#endif
#include "pugl_internal.h"
+#include "pugl/event.h"
struct PuglInternalsImpl {
Display* display;
@@ -60,15 +61,11 @@ puglInitInternals()
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
-int
-puglCreateWindow(PuglView* view, const char* title)
+static XVisualInfo*
+getVisual(PuglView* view)
{
- PuglInternals* impl = view->impl;
-
- impl->display = XOpenDisplay(0);
- impl->screen = DefaultScreen(impl->display);
-
- XVisualInfo* vi = NULL;
+ PuglInternals* const impl = view->impl;
+ XVisualInfo* vi = NULL;
#ifdef PUGL_HAVE_GL
if (view->ctx_type == PUGL_GL) {
@@ -93,14 +90,6 @@ puglCreateWindow(PuglView* view, const char* title)
} else {
impl->doubleBuffered = True;
}
-
- impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
- PUGL_LOGF("GLX depth %d, %s-buffered, %s\n",
- vi->depth,
- impl->doubleBuffered ? "double" : "single",
- (glXIsDirect(impl->display, impl->ctx)
- ? "direct (set LIBGL_ALWAYS_INDIRECT=1 to disable)"
- : "indirect"));
}
#endif
#ifdef PUGL_HAVE_CAIRO
@@ -112,6 +101,81 @@ puglCreateWindow(PuglView* view, const char* title)
}
#endif
+ return vi;
+}
+
+static void
+createContext(PuglView* view, XVisualInfo* vi)
+{
+ PuglInternals* const impl = view->impl;
+
+#ifdef PUGL_HAVE_GL
+ if (view->ctx_type == PUGL_GL) {
+ impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
+ }
+#endif
+#ifdef PUGL_HAVE_CAIRO
+ if (view->ctx_type == PUGL_CAIRO) {
+ cairo_surface_t* surface = cairo_xlib_surface_create(
+ impl->display, impl->win, vi->visual, view->width, view->height);
+ if (!(impl->cr = cairo_create(surface))) {
+ fprintf(stderr, "failed to create cairo context\n");
+ }
+ }
+#endif
+}
+
+static void
+destroyContext(PuglView* view)
+{
+#ifdef PUGL_HAVE_GL
+ if (view->ctx_type == PUGL_GL) {
+ glXDestroyContext(view->impl->display, view->impl->ctx);
+ }
+#endif
+#ifdef PUGL_HAVE_CAIRO
+ if (view->ctx_type == PUGL_CAIRO) {
+ glXDestroyContext(view->impl->display, view->impl->ctx);
+ }
+#endif
+}
+
+void
+puglEnterContext(PuglView* view)
+{
+#ifdef PUGL_HAVE_GL
+ if (view->ctx_type == PUGL_GL) {
+ glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
+ }
+#endif
+}
+
+void
+puglLeaveContext(PuglView* view, bool flush)
+{
+#ifdef PUGL_HAVE_GL
+ if (view->ctx_type == PUGL_GL && flush) {
+ glFlush();
+ if (view->impl->doubleBuffered) {
+ glXSwapBuffers(view->impl->display, view->impl->win);
+ }
+ }
+#endif
+}
+
+int
+puglCreateWindow(PuglView* view, const char* title)
+{
+ PuglInternals* const impl = view->impl;
+
+ impl->display = XOpenDisplay(0);
+ impl->screen = DefaultScreen(impl->display);
+
+ XVisualInfo* const vi = getVisual(view);
+ if (!vi) {
+ return 1;
+ }
+
Window xParent = view->parent
? (Window)view->parent
: RootWindow(impl->display, impl->screen);
@@ -124,27 +188,18 @@ puglCreateWindow(PuglView* view, const char* title)
attr.background_pixel = BlackPixel(impl->display, impl->screen);
attr.border_pixel = BlackPixel(impl->display, impl->screen);
attr.colormap = cmap;
- attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask
- | ButtonPressMask | ButtonReleaseMask
-#ifdef PUGL_GRAB_FOCUS
- | EnterWindowMask
-#endif
- | PointerMotionMask | StructureNotifyMask;
+ attr.event_mask = (ExposureMask | StructureNotifyMask |
+ EnterWindowMask | LeaveWindowMask |
+ KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask);
impl->win = XCreateWindow(
impl->display, xParent,
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual,
CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr);
-#ifdef PUGL_HAVE_CAIRO
- if (view->ctx_type == PUGL_CAIRO) {
- cairo_surface_t* surface = cairo_xlib_surface_create(
- impl->display, impl->win, vi->visual, view->width, view->height);
- if (!(impl->cr = cairo_create(surface))) {
- fprintf(stderr, "failed to create cairo context\n");
- }
- }
-#endif
+ createContext(view, vi);
XSizeHints sizeHints;
memset(&sizeHints, 0, sizeof(sizeHints));
@@ -174,17 +229,13 @@ puglCreateWindow(PuglView* view, const char* title)
void
puglShowWindow(PuglView* view)
{
- PuglInternals* impl = view->impl;
-
- XMapRaised(impl->display, impl->win);
+ XMapRaised(view->impl->display, view->impl->win);
}
void
puglHideWindow(PuglView* view)
{
- PuglInternals* impl = view->impl;
-
- XUnmapWindow(impl->display, impl->win);
+ XUnmapWindow(view->impl->display, view->impl->win);
}
void
@@ -194,63 +245,13 @@ puglDestroy(PuglView* view)
return;
}
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- glXDestroyContext(view->impl->display, view->impl->ctx);
- }
-#endif
-
+ destroyContext(view);
XDestroyWindow(view->impl->display, view->impl->win);
XCloseDisplay(view->impl->display);
free(view->impl);
free(view);
}
-static void
-puglReshape(PuglView* view, int width, int height)
-{
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
- }
-#endif
-
- if (view->reshapeFunc) {
- view->reshapeFunc(view, width, height);
- } else {
- puglDefaultReshape(view, width, height);
- }
-
- view->width = width;
- view->height = height;
-}
-
-static void
-puglDisplay(PuglView* view)
-{
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glLoadIdentity();
- }
-#endif
-
- if (view->displayFunc) {
- view->displayFunc(view);
- }
- view->redisplay = false;
-
-#ifdef PUGL_HAVE_GL
- if (view->ctx_type == PUGL_GL) {
- glFlush();
- if (view->impl->doubleBuffered) {
- glXSwapBuffers(view->impl->display, view->impl->win);
- }
- }
-#endif
-}
-
static PuglKey
keySymToSpecial(KeySym sym)
{
@@ -289,139 +290,184 @@ keySymToSpecial(KeySym sym)
}
static void
-setModifiers(PuglView* view, unsigned xstate, unsigned xtime)
+translateKey(PuglView* view, XEvent* xevent, PuglEvent* event)
{
- view->event_timestamp_ms = xtime;
+ KeySym sym;
+ char str[5];
+ const int n = XLookupString(&xevent->xkey, str, 4, &sym, NULL);
+ if (n == 1) {
+ event->key.character = str[0]; // TODO: multi-byte support
+ }
+ event->key.special = keySymToSpecial(sym);
+}
- view->mods = 0;
- view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0;
- view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0;
- view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0;
- view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0;
+static unsigned
+translateModifiers(unsigned xstate)
+{
+ unsigned state = 0;
+ state |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0;
+ state |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0;
+ state |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0;
+ state |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0;
+ return state;
}
-static void
-dispatchKey(PuglView* view, XEvent* event, bool press)
+static PuglEvent
+translateEvent(PuglView* view, XEvent xevent)
{
- KeySym sym;
- char str[5];
- const int n = XLookupString(&event->xkey, str, 4, &sym, NULL);
- if (n == 0) {
- return;
- } else if (n > 1) {
- fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym);
- return;
+ PuglEvent event;
+ memset(&event, 0, sizeof(event));
+
+ event.any.view = view;
+ event.any.send_event = xevent.xany.send_event;
+
+ switch (xevent.type) {
+ case ConfigureNotify:
+ event.type = PUGL_CONFIGURE;
+ event.configure.x = xevent.xconfigure.x;
+ event.configure.y = xevent.xconfigure.y;
+ event.configure.width = xevent.xconfigure.width;
+ event.configure.height = xevent.xconfigure.height;
+ break;
+ case Expose:
+ event.type = PUGL_EXPOSE;
+ event.expose.x = xevent.xexpose.x;
+ event.expose.y = xevent.xexpose.y;
+ event.expose.width = xevent.xexpose.width;
+ event.expose.height = xevent.xexpose.height;
+ event.expose.count = xevent.xexpose.count;
+ break;
+ case MotionNotify:
+ event.type = PUGL_MOTION_NOTIFY;
+ event.motion.time = xevent.xmotion.time;
+ event.motion.x = xevent.xmotion.x;
+ event.motion.y = xevent.xmotion.y;
+ event.motion.x_root = xevent.xmotion.x_root;
+ event.motion.y_root = xevent.xmotion.y_root;
+ event.motion.state = translateModifiers(xevent.xmotion.state);
+ event.motion.is_hint = (xevent.xmotion.is_hint == NotifyHint);
+ break;
+ case ButtonPress:
+ if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) {
+ event.type = PUGL_SCROLL;
+ event.scroll.time = xevent.xbutton.time;
+ event.scroll.x = xevent.xbutton.x;
+ event.scroll.y = xevent.xbutton.y;
+ event.scroll.x_root = xevent.xbutton.x_root;
+ event.scroll.y_root = xevent.xbutton.y_root;
+ event.scroll.state = translateModifiers(xevent.xbutton.state);
+ event.scroll.dx = 0.0;
+ event.scroll.dy = 0.0;
+ switch (xevent.xbutton.button) {
+ case 4: event.scroll.dy = 1.0f; break;
+ case 5: event.scroll.dy = -1.0f; break;
+ case 6: event.scroll.dx = -1.0f; break;
+ case 7: event.scroll.dx = 1.0f; break;
+ }
+ }
+ // nobreak
+ case ButtonRelease:
+ if (xevent.xbutton.button < 4 || xevent.xbutton.button > 7) {
+ event.button.type = ((xevent.type == ButtonPress)
+ ? PUGL_BUTTON_PRESS
+ : PUGL_BUTTON_RELEASE);
+ event.button.time = xevent.xbutton.time;
+ event.button.x = xevent.xbutton.x;
+ event.button.y = xevent.xbutton.y;
+ event.button.x_root = xevent.xbutton.x_root;
+ event.button.y_root = xevent.xbutton.y_root;
+ event.button.state = translateModifiers(xevent.xbutton.state);
+ event.button.button = xevent.xbutton.button;
+ }
+ break;
+ case KeyPress:
+ case KeyRelease:
+ event.type = ((xevent.type == KeyPress)
+ ? PUGL_KEY_PRESS
+ : PUGL_KEY_RELEASE);
+ event.key.time = xevent.xbutton.time;
+ event.key.x = xevent.xbutton.x;
+ event.key.y = xevent.xbutton.y;
+ event.key.x_root = xevent.xbutton.x_root;
+ event.key.y_root = xevent.xbutton.y_root;
+ event.key.state = translateModifiers(xevent.xbutton.state);
+ translateKey(view, &xevent, &event);
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ event.type = ((xevent.type == EnterNotify)
+ ? PUGL_ENTER_NOTIFY
+ : PUGL_LEAVE_NOTIFY);
+ event.crossing.time = xevent.xcrossing.time;
+ event.crossing.x = xevent.xcrossing.x;
+ event.crossing.y = xevent.xcrossing.y;
+ event.crossing.x_root = xevent.xcrossing.x_root;
+ event.crossing.y_root = xevent.xcrossing.y_root;
+ event.crossing.state = translateModifiers(xevent.xcrossing.state);
+ event.crossing.mode = PUGL_CROSSING_NORMAL;
+ if (xevent.xcrossing.mode == NotifyGrab) {
+ event.crossing.mode = PUGL_CROSSING_GRAB;
+ } else if (xevent.xcrossing.mode == NotifyUngrab) {
+ event.crossing.mode = PUGL_CROSSING_UNGRAB;
+ }
+ break;
+ default:
+ break;
}
- const PuglKey special = keySymToSpecial(sym);
- if (special && view->specialFunc) {
- view->specialFunc(view, press, special);
- } else if (!special && view->keyboardFunc) {
- view->keyboardFunc(view, press, str[0]);
- }
+ return event;
+}
+
+void
+puglGrabFocus(PuglView* view)
+{
+ XSetInputFocus(
+ view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime);
}
PuglStatus
puglProcessEvents(PuglView* view)
{
- XEvent event;
+ XEvent xevent;
while (XPending(view->impl->display) > 0) {
- XNextEvent(view->impl->display, &event);
- switch (event.type) {
- case MapNotify:
- puglReshape(view, view->width, view->height);
- break;
- case ConfigureNotify:
- if ((event.xconfigure.width != view->width) ||
- (event.xconfigure.height != view->height)) {
- puglReshape(view,
- event.xconfigure.width,
- event.xconfigure.height);
- }
- break;
- case Expose:
- if (event.xexpose.count != 0) {
- break;
- }
- puglDisplay(view);
- break;
- case MotionNotify:
- setModifiers(view, event.xmotion.state, event.xmotion.time);
- if (view->motionFunc) {
- view->motionFunc(view, event.xmotion.x, event.xmotion.y);
- }
- break;
- case ButtonPress:
- setModifiers(view, event.xbutton.state, event.xbutton.time);
- if (event.xbutton.button >= 4 && event.xbutton.button <= 7) {
- if (view->scrollFunc) {
- float dx = 0, dy = 0;
- switch (event.xbutton.button) {
- case 4: dy = 1.0f; break;
- case 5: dy = -1.0f; break;
- case 6: dx = -1.0f; break;
- case 7: dx = 1.0f; break;
- }
- view->scrollFunc(view,
- event.xbutton.x, event.xbutton.y,
- dx, dy);
- }
- break;
- }
- // nobreak
- case ButtonRelease:
- setModifiers(view, event.xbutton.state, event.xbutton.time);
- if (view->mouseFunc &&
- (event.xbutton.button < 4 || event.xbutton.button > 7)) {
- view->mouseFunc(view,
- event.xbutton.button, event.type == ButtonPress,
- event.xbutton.x, event.xbutton.y);
+ XNextEvent(view->impl->display, &xevent);
+ bool ignore = false;
+ if (xevent.type == ClientMessage) {
+ // Handle close message
+ char* type = XGetAtomName(view->impl->display,
+ xevent.xclient.message_type);
+ if (!strcmp(type, "WM_PROTOCOLS") && view->closeFunc) {
+ view->closeFunc(view);
}
- break;
- case KeyPress:
- setModifiers(view, event.xkey.state, event.xkey.time);
- dispatchKey(view, &event, true);
- break;
- case KeyRelease:
- setModifiers(view, event.xkey.state, event.xkey.time);
+ XFree(type);
+ continue;
+ } else if (xevent.type == KeyRelease) {
+ // Ignore key repeat if necessary
if (view->ignoreKeyRepeat &&
XEventsQueued(view->impl->display, QueuedAfterReading)) {
XEvent next;
XPeekEvent(view->impl->display, &next);
if (next.type == KeyPress &&
- next.xkey.time == event.xkey.time &&
- next.xkey.keycode == event.xkey.keycode) {
- XNextEvent(view->impl->display, &event);
- break;
- }
- }
- dispatchKey(view, &event, false);
- break;
- case ClientMessage: {
- char* type = XGetAtomName(view->impl->display,
- event.xclient.message_type);
- if (!strcmp(type, "WM_PROTOCOLS")) {
- if (view->closeFunc) {
- view->closeFunc(view);
+ next.xkey.time == xevent.xkey.time &&
+ next.xkey.keycode == xevent.xkey.keycode) {
+ XNextEvent(view->impl->display, &xevent);
+ ignore = true;
}
}
- XFree(type);
- } break;
-#ifdef PUGL_GRAB_FOCUS
- case EnterNotify:
- XSetInputFocus(view->impl->display,
- view->impl->win,
- RevertToPointerRoot,
- CurrentTime);
- break;
-#endif
- default:
- break;
+ }
+
+ if (!ignore) {
+ // Translate and dispatch event
+ const PuglEvent event = translateEvent(view, xevent);
+ puglDispatchEvent(view, &event);
}
}
if (view->redisplay) {
- puglDisplay(view);
+ const PuglEventExpose expose = {
+ PUGL_EXPOSE, view, true, 0, 0, view->width, view->height, 0
+ };
+ puglDispatchEvent(view, (const PuglEvent*)&expose);
}
return PUGL_SUCCESS;