aboutsummaryrefslogtreecommitdiff
path: root/pugl/pugl/detail/x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'pugl/pugl/detail/x11.c')
-rw-r--r--pugl/pugl/detail/x11.c237
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)
{