diff options
Diffstat (limited to 'pugl/pugl/detail/x11.c')
-rw-r--r-- | pugl/pugl/detail/x11.c | 237 |
1 files changed, 181 insertions, 56 deletions
diff --git a/pugl/pugl/detail/x11.c b/pugl/pugl/detail/x11.c index e3fb264..7b8daf2 100644 --- a/pugl/pugl/detail/x11.c +++ b/pugl/pugl/detail/x11.c @@ -1,5 +1,5 @@ /* - Copyright 2012-2020 David Robillard <http://drobilla.net> + Copyright 2012-2020 David Robillard <d@drobilla.net> Copyright 2013 Robin Gareus <robin@gareus.org> Copyright 2011-2012 Ben Loftis, Harrison Consoles @@ -17,7 +17,8 @@ */ /** - @file x11.c X11 implementation. + @file x11.c + @brief X11 implementation. */ #define _POSIX_C_SOURCE 199309L @@ -25,6 +26,7 @@ #include "pugl/detail/x11.h" #include "pugl/detail/implementation.h" +#include "pugl/detail/stub.h" #include "pugl/detail/types.h" #include "pugl/pugl.h" #include "pugl/pugl_stub.h" @@ -40,6 +42,11 @@ # include <X11/extensions/syncconst.h> #endif +#ifdef HAVE_XCURSOR +# include <X11/Xcursor/Xcursor.h> +# include <X11/cursorfont.h> +#endif + #include <sys/select.h> #include <sys/time.h> @@ -74,11 +81,11 @@ static bool puglInitXSync(PuglWorldInternals* impl) { #ifdef HAVE_XSYNC - int syncMajor; - int syncMinor; - int errorBase; - XSyncSystemCounter* counters; - int numCounters; + int syncMajor = 0; + int syncMinor = 0; + int errorBase = 0; + XSyncSystemCounter* counters = NULL; + int numCounters = 0; if (XSyncQueryExtension(impl->display, &impl->syncEventBase, &errorBase) && XSyncInitialize(impl->display, &syncMajor, &syncMinor) && @@ -151,7 +158,13 @@ puglGetNativeWorld(PuglWorld* world) PuglInternals* puglInitViewInternals(void) { - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + +#ifdef HAVE_XCURSOR + impl->cursorShape = XC_arrow; +#endif + + return impl; } static PuglStatus @@ -192,23 +205,40 @@ puglFindView(PuglWorld* world, const Window window) return NULL; } -static XSizeHints -getSizeHints(const PuglView* view) +static PuglStatus +updateSizeHints(const PuglView* view) { + if (!view->impl->win) { + return PUGL_SUCCESS; + } + + Display* display = view->world->impl->display; XSizeHints sizeHints = {0}; if (!view->hints[PUGL_RESIZABLE]) { - sizeHints.flags = PMinSize|PMaxSize; - sizeHints.min_width = (int)view->frame.width; - sizeHints.min_height = (int)view->frame.height; - sizeHints.max_width = (int)view->frame.width; - sizeHints.max_height = (int)view->frame.height; + sizeHints.flags = PBaseSize | PMinSize | PMaxSize; + sizeHints.base_width = (int)view->frame.width; + sizeHints.base_height = (int)view->frame.height; + sizeHints.min_width = (int)view->frame.width; + sizeHints.min_height = (int)view->frame.height; + sizeHints.max_width = (int)view->frame.width; + sizeHints.max_height = (int)view->frame.height; } else { + if (view->defaultWidth || view->defaultHeight) { + sizeHints.flags = PBaseSize; + sizeHints.base_width = view->defaultWidth; + sizeHints.base_height = view->defaultHeight; + } if (view->minWidth || view->minHeight) { sizeHints.flags = PMinSize; sizeHints.min_width = view->minWidth; sizeHints.min_height = view->minHeight; } + if (view->maxWidth || view->maxHeight) { + sizeHints.flags = PMaxSize; + sizeHints.max_width = view->maxWidth; + sizeHints.max_height = view->maxHeight; + } if (view->minAspectX) { sizeHints.flags |= PAspect; sizeHints.min_aspect.x = view->minAspectX; @@ -218,8 +248,28 @@ getSizeHints(const PuglView* view) } } - return sizeHints; + XSetNormalHints(display, view->impl->win, &sizeHints); + return PUGL_SUCCESS; +} + +#ifdef HAVE_XCURSOR +static PuglStatus +puglDefineCursorShape(PuglView* view, unsigned shape) +{ + PuglInternals* const impl = view->impl; + PuglWorld* const world = view->world; + Display* const display = world->impl->display; + const Cursor cur = XcursorShapeLoadCursor(display, shape); + + if (cur) { + XDefineCursor(display, impl->win, cur); + XFreeCursor(display, cur); + return PUGL_SUCCESS; + } + + return PUGL_FAILURE; } +#endif PuglStatus puglRealize(PuglView* view) @@ -234,6 +284,18 @@ puglRealize(PuglView* view) if (!view->backend || !view->backend->configure) { return PUGL_BAD_BACKEND; + } else if (view->frame.width == 0.0 && view->frame.height == 0.0) { + if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) { + return PUGL_BAD_CONFIGURATION; + } + + const int screenWidth = DisplayWidth(display, impl->screen); + const int screenHeight = DisplayHeight(display, impl->screen); + + view->frame.width = view->defaultWidth; + view->frame.height = view->defaultHeight; + view->frame.x = screenWidth / 2.0 - view->frame.width / 2.0; + view->frame.y = screenHeight / 2.0 - view->frame.height / 2.0; } PuglStatus st = view->backend->configure(view); @@ -263,8 +325,7 @@ puglRealize(PuglView* view) return st; } - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, win, &sizeHints); + updateSizeHints(view); XClassHint classHint = { world->className, world->className }; XSetClassHint(display, win, &classHint); @@ -293,6 +354,10 @@ puglRealize(PuglView* view) "XCreateID failed\n"); } +#ifdef HAVE_XCURSOR + puglDefineCursorShape(view, impl->cursorShape); +#endif + puglDispatchSimpleEvent(view, PUGL_CREATE); return PUGL_SUCCESS; @@ -450,10 +515,10 @@ translateKey(PuglView* view, XEvent* xevent, PuglEvent* event) static uint32_t translateModifiers(const unsigned xstate) { - return (((xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0) | - ((xstate & ControlMask) ? PUGL_MOD_CTRL : 0) | - ((xstate & Mod1Mask) ? PUGL_MOD_ALT : 0) | - ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0)); + return (((xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0u) | + ((xstate & ControlMask) ? PUGL_MOD_CTRL : 0u) | + ((xstate & Mod1Mask) ? PUGL_MOD_ALT : 0u) | + ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0u)); } static PuglEvent @@ -500,22 +565,23 @@ translateEvent(PuglView* view, XEvent xevent) 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; - event.motion.time = xevent.xmotion.time / 1e3; + event.motion.time = (double)xevent.xmotion.time / 1e3; event.motion.x = xevent.xmotion.x; event.motion.y = xevent.xmotion.y; event.motion.xRoot = xevent.xmotion.x_root; event.motion.yRoot = xevent.xmotion.y_root; event.motion.state = translateModifiers(xevent.xmotion.state); - event.motion.isHint = (xevent.xmotion.is_hint == NotifyHint); + if (xevent.xmotion.is_hint == NotifyHint) { + event.motion.flags |= PUGL_IS_HINT; + } break; case ButtonPress: if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) { event.type = PUGL_SCROLL; - event.scroll.time = xevent.xbutton.time / 1e3; + event.scroll.time = (double)xevent.xbutton.time / 1e3; event.scroll.x = xevent.xbutton.x; event.scroll.y = xevent.xbutton.y; event.scroll.xRoot = xevent.xbutton.x_root; @@ -524,10 +590,22 @@ translateEvent(PuglView* view, XEvent xevent) event.scroll.dx = 0.0; event.scroll.dy = 0.0; switch (xevent.xbutton.button) { - case 4: event.scroll.dy = 1.0; break; - case 5: event.scroll.dy = -1.0; break; - case 6: event.scroll.dx = -1.0; break; - case 7: event.scroll.dx = 1.0; break; + case 4: + event.scroll.dy = 1.0; + event.scroll.direction = PUGL_SCROLL_UP; + break; + case 5: + event.scroll.dy = -1.0; + event.scroll.direction = PUGL_SCROLL_DOWN; + break; + case 6: + event.scroll.dx = -1.0; + event.scroll.direction = PUGL_SCROLL_LEFT; + break; + case 7: + event.scroll.dx = 1.0; + event.scroll.direction = PUGL_SCROLL_RIGHT; + break; } // fallthru } @@ -537,7 +615,7 @@ translateEvent(PuglView* view, XEvent xevent) event.button.type = ((xevent.type == ButtonPress) ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE); - event.button.time = xevent.xbutton.time / 1e3; + event.button.time = (double)xevent.xbutton.time / 1e3; event.button.x = xevent.xbutton.x; event.button.y = xevent.xbutton.y; event.button.xRoot = xevent.xbutton.x_root; @@ -551,7 +629,7 @@ translateEvent(PuglView* view, XEvent xevent) event.type = ((xevent.type == KeyPress) ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE); - event.key.time = xevent.xkey.time / 1e3; + event.key.time = (double)xevent.xkey.time / 1e3; event.key.x = xevent.xkey.x; event.key.y = xevent.xkey.y; event.key.xRoot = xevent.xkey.x_root; @@ -564,7 +642,7 @@ translateEvent(PuglView* view, XEvent xevent) event.type = ((xevent.type == EnterNotify) ? PUGL_POINTER_IN : PUGL_POINTER_OUT); - event.crossing.time = xevent.xcrossing.time / 1e3; + event.crossing.time = (double)xevent.xcrossing.time / 1e3; event.crossing.x = xevent.xcrossing.x; event.crossing.y = xevent.xcrossing.y; event.crossing.xRoot = xevent.xcrossing.x_root; @@ -581,7 +659,12 @@ translateEvent(PuglView* view, XEvent xevent) case FocusIn: case FocusOut: event.type = (xevent.type == FocusIn) ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT; - event.focus.grab = (xevent.xfocus.mode != NotifyNormal); + event.focus.mode = PUGL_CROSSING_NORMAL; + if (xevent.xfocus.mode == NotifyGrab) { + event.focus.mode = PUGL_CROSSING_GRAB; + } else if (xevent.xfocus.mode == NotifyUngrab) { + event.focus.mode = PUGL_CROSSING_UNGRAB; + } break; default: @@ -646,7 +729,8 @@ puglStartTimer(PuglView* view, uintptr_t id, double timeout) PuglWorldInternals* w = view->world->impl; Display* const display = w->display; const XSyncCounter counter = w->serverTimeCounter; - const XSyncTrigger trigger = {counter, XSyncRelative, value, 0}; + const XSyncTestType type = XSyncPositiveTransition; + const XSyncTrigger trigger = {counter, XSyncRelative, value, type}; XSyncAlarmAttributes attr = {trigger, value, True, XSyncAlarmActive}; const XSyncAlarm alarm = XSyncCreateAlarm(display, 0x17, &attr); const PuglTimer timer = {alarm, view, id}; @@ -791,7 +875,6 @@ mergeExposeEvents(PuglEvent* dst, const PuglEvent* src) dst->expose.y = MIN(dst->expose.y, src->expose.y); dst->expose.width = max_x - dst->expose.x; dst->expose.height = max_y - dst->expose.y; - dst->expose.count = MIN(dst->expose.count, src->expose.count); } } @@ -896,8 +979,10 @@ handleTimerEvent(PuglWorld* world, XEvent 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); + PuglEvent event = {{PUGL_TIMER, 0}}; + event.timer.id = world->impl->timers[i].id; + puglDispatchEvent(world->impl->timers[i].view, + (const PuglEvent*)&event); } } @@ -1033,7 +1118,8 @@ puglGetTime(const PuglWorld* world) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); - return ((double)ts.tv_sec + ts.tv_nsec / 1000000000.0) - world->startTime; + return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - + world->startTime; } PuglStatus @@ -1048,7 +1134,7 @@ PuglStatus puglPostRedisplayRect(PuglView* view, PuglRect rect) { const PuglEventExpose event = { - PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height, 0 + PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height }; if (view->world->impl->dispatchingEvents) { @@ -1102,19 +1188,27 @@ puglSetFrame(PuglView* view, const PuglRect frame) } PuglStatus -puglSetMinSize(PuglView* const view, const int width, const int height) +puglSetDefaultSize(PuglView* const view, const int width, const int height) { - Display* display = view->world->impl->display; + view->defaultWidth = width; + view->defaultHeight = height; + return updateSizeHints(view); +} +PuglStatus +puglSetMinSize(PuglView* const view, const int width, const int height) +{ view->minWidth = width; view->minHeight = height; + return updateSizeHints(view); +} - if (view->impl->win) { - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, view->impl->win, &sizeHints); - } - - return PUGL_SUCCESS; +PuglStatus +puglSetMaxSize(PuglView* const view, const int width, const int height) +{ + view->minWidth = width; + view->minHeight = height; + return updateSizeHints(view); } PuglStatus @@ -1124,19 +1218,12 @@ puglSetAspectRatio(PuglView* const view, const int maxX, const int maxY) { - Display* display = view->world->impl->display; - view->minAspectX = minX; view->minAspectY = minY; view->maxAspectX = maxX; view->maxAspectY = maxY; - if (view->impl->win) { - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, view->impl->win, &sizeHints); - } - - return PUGL_SUCCESS; + return updateSizeHints(view); } PuglStatus @@ -1202,6 +1289,44 @@ puglSetClipboard(PuglView* const view, return PUGL_SUCCESS; } +#ifdef HAVE_XCURSOR +static const unsigned cursor_nums[] = { + XC_arrow, // ARROW + XC_xterm, // CARET + XC_crosshair, // CROSSHAIR + XC_hand2, // HAND + XC_pirate, // NO + XC_sb_h_double_arrow, // LEFT_RIGHT + XC_sb_v_double_arrow, // UP_DOWN +}; +#endif + +PuglStatus +puglSetCursor(PuglView* view, PuglCursor cursor) +{ +#ifdef HAVE_XCURSOR + PuglInternals* const impl = view->impl; + const unsigned index = (unsigned)cursor; + const unsigned count = sizeof(cursor_nums) / sizeof(cursor_nums[0]); + if (index >= count) { + return PUGL_BAD_PARAMETER; + } + + const unsigned shape = cursor_nums[index]; + if (!impl->win || impl->cursorShape == shape) { + return PUGL_SUCCESS; + } + + impl->cursorShape = cursor_nums[index]; + + return puglDefineCursorShape(view, impl->cursorShape); +#else + (void)view; + (void)cursor; + return PUGL_FAILURE; +#endif +} + const PuglBackend* puglStubBackend(void) { |