aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-08-27 23:32:26 +0000
committerDavid Robillard <d@drobilla.net>2014-08-27 23:32:26 +0000
commitd0c878fe0e496083a1e892795c23a1ac939714a9 (patch)
tree6a70e0fbd68726357a8b008ecd2bba1d3062e6b1
parentb4dbb3616865cd236cda4f2e4ac806b3b4840b29 (diff)
Event-based dispatch.
-rw-r--r--pugl/common.h124
-rw-r--r--pugl/event.h223
-rw-r--r--pugl/pugl.h98
-rw-r--r--pugl/pugl_internal.h115
-rw-r--r--pugl/pugl_osx.m6
-rw-r--r--pugl/pugl_win.cpp6
-rw-r--r--pugl/pugl_x11.c438
-rw-r--r--pugl_cairo_test.c49
8 files changed, 731 insertions, 328 deletions
diff --git a/pugl/common.h b/pugl/common.h
new file mode 100644
index 0000000..4a92403
--- /dev/null
+++ b/pugl/common.h
@@ -0,0 +1,124 @@
+/*
+ Copyright 2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file common.h Common definitions.
+
+ @ingroup pugl
+ @{
+*/
+
+#ifndef PUGL_COMMON_H_INCLUDED
+#define PUGL_COMMON_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ A Pugl view.
+*/
+typedef struct PuglViewImpl PuglView;
+
+/**
+ A native window handle.
+
+ On X11, this is a Window.
+ On OSX, this is an NSView*.
+ On Windows, this is a HWND.
+*/
+typedef intptr_t PuglNativeWindow;
+
+/**
+ Handle for opaque user data.
+*/
+typedef void* PuglHandle;
+
+/**
+ Return status code.
+*/
+typedef enum {
+ PUGL_SUCCESS = 0
+} PuglStatus;
+
+/**
+ Drawing context type.
+*/
+typedef enum {
+ PUGL_GL,
+ PUGL_CAIRO
+} PuglContextType;
+
+/**
+ Convenience symbols for ASCII control characters.
+*/
+typedef enum {
+ PUGL_CHAR_BACKSPACE = 0x08,
+ PUGL_CHAR_ESCAPE = 0x1B,
+ PUGL_CHAR_DELETE = 0x7F
+} PuglChar;
+
+/**
+ Keyboard modifier flags.
+*/
+typedef enum {
+ PUGL_MOD_SHIFT = 1, /**< Shift key */
+ PUGL_MOD_CTRL = 1 << 1, /**< Control key */
+ PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
+ PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
+} PuglMod;
+
+/**
+ Special (non-Unicode) keyboard keys.
+*/
+typedef enum {
+ PUGL_KEY_F1 = 1,
+ PUGL_KEY_F2,
+ PUGL_KEY_F3,
+ PUGL_KEY_F4,
+ PUGL_KEY_F5,
+ PUGL_KEY_F6,
+ PUGL_KEY_F7,
+ PUGL_KEY_F8,
+ PUGL_KEY_F9,
+ PUGL_KEY_F10,
+ PUGL_KEY_F11,
+ PUGL_KEY_F12,
+ PUGL_KEY_LEFT,
+ PUGL_KEY_UP,
+ PUGL_KEY_RIGHT,
+ PUGL_KEY_DOWN,
+ PUGL_KEY_PAGE_UP,
+ PUGL_KEY_PAGE_DOWN,
+ PUGL_KEY_HOME,
+ PUGL_KEY_END,
+ PUGL_KEY_INSERT,
+ PUGL_KEY_SHIFT,
+ PUGL_KEY_CTRL,
+ PUGL_KEY_ALT,
+ PUGL_KEY_SUPER
+} PuglKey;
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PUGL_COMMON_H_INCLUDED */
diff --git a/pugl/event.h b/pugl/event.h
new file mode 100644
index 0000000..211916d
--- /dev/null
+++ b/pugl/event.h
@@ -0,0 +1,223 @@
+/*
+ Copyright 2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file event.h PuglEvent struct definition.
+
+ @ingroup pugl
+ @{
+
+ @name Event
+ @{
+*/
+
+#ifndef PUGL_EVENT_H_INCLUDED
+#define PUGL_EVENT_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+#include "pugl/common.h"
+
+/**
+ The type of a PuglEvent.
+*/
+typedef enum {
+ PUGL_BUTTON_PRESS,
+ PUGL_BUTTON_RELEASE,
+ PUGL_CONFIGURE,
+ PUGL_EXPOSE,
+ PUGL_KEY_PRESS,
+ PUGL_KEY_RELEASE,
+ PUGL_ENTER_NOTIFY,
+ PUGL_LEAVE_NOTIFY,
+ PUGL_MOTION_NOTIFY,
+ PUGL_NOTHING,
+ PUGL_SCROLL
+} PuglEventType;
+
+/**
+ Reason for a PuglEventCrossing.
+*/
+typedef enum {
+ PUGL_CROSSING_NORMAL, /**< Crossing due to pointer motion. */
+ PUGL_CROSSING_GRAB, /**< Crossing due to a grab. */
+ PUGL_CROSSING_UNGRAB /**< Crossing due to a grab release. */
+} PuglCrossingMode;
+
+/**
+ Common header for all event structs.
+*/
+typedef struct {
+ PuglEventType type; /**< Event type. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+} PuglEventAny;
+
+/**
+ Button press or release event.
+
+ For event types PUGL_BUTTON_PRESS and PUGL_BUTTON_RELEASE.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_BUTTON_PRESS or PUGL_BUTTON_RELEASE. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ uint32_t time; /**< Time in milliseconds. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double x_root; /**< Root-relative X coordinate. */
+ double y_root; /**< Root-relative Y coordinate. */
+ unsigned state; /**< Bitwise OR of PuglMod flags. */
+ unsigned button; /**< 1-relative button number. */
+} PuglEventButton;
+
+/**
+ Configure event for when window size or position has changed.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_CONFIGURE. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ double x; /**< New parent-relative X coordinate. */
+ double y; /**< New parent-relative Y coordinate. */
+ double width; /**< New width. */
+ double height; /**< New height. */
+} PuglEventConfigure;
+
+/**
+ Expose event for when a region must be redrawn.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_EXPOSE. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double width; /**< Width of exposed region. */
+ double height; /**< Height of exposed region. */
+ int count; /**< Number of expose events to follow. */
+} PuglEventExpose;
+
+/**
+ Key press event.
+
+ Keys that correspond to a Unicode character are expressed as a character
+ code. For other keys, `character` will be 0 and `special` indicates the key
+ pressed.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_KEY_PRESS or PUGL_KEY_RELEASE. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ uint32_t time; /**< Time in milliseconds. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double x_root; /**< Root-relative X coordinate. */
+ double y_root; /**< Root-relative Y coordinate. */
+ unsigned state; /**< Bitwise OR of PuglMod flags. */
+ uint32_t character; /**< Unicode character code, or 0. */
+ PuglKey special; /**< Special key, if character is 0. */
+} PuglEventKey;
+
+/**
+ Pointer crossing event (enter and leave).
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_ENTER_NOTIFY or PUGL_LEAVE_NOTIFY. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ uint32_t time; /**< Time in milliseconds. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double x_root; /**< Root-relative X coordinate. */
+ double y_root; /**< Root-relative Y coordinate. */
+ unsigned state; /**< Bitwise OR of PuglMod flags. */
+ PuglCrossingMode mode; /**< Reason for crossing. */
+} PuglEventCrossing;
+
+/**
+ Pointer motion event.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_MOTION_NOTIFY. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ uint32_t time; /**< Time in milliseconds. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double x_root; /**< Root-relative X coordinate. */
+ double y_root; /**< Root-relative Y coordinate. */
+ unsigned state; /**< Bitwise OR of PuglMod flags. */
+ bool is_hint; /**< True iff this event is a motion hint. */
+ bool focus; /**< True iff this is the focused window. */
+} PuglEventMotion;
+
+/**
+ Scroll event.
+
+ The scroll distance is expressed in "lines", an arbitrary unit that
+ corresponds to a single tick of a detented mouse wheel. For example, `dy` =
+ 1.0 scrolls 1 line up. Some systems and devices support finer resolution
+ and/or higher values for fast scrolls, so programs should handle any value
+ gracefully.
+ */
+typedef struct {
+ PuglEventType type; /**< PUGL_SCROLL. */
+ PuglView* view; /**< View that received this event. */
+ bool send_event; /**< True iff event was sent explicitly. */
+ uint32_t time; /**< Time in milliseconds. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double x_root; /**< Root-relative X coordinate. */
+ double y_root; /**< Root-relative Y coordinate. */
+ unsigned state; /**< Bitwise OR of PuglMod flags. */
+ double dx; /**< Scroll X distance in lines. */
+ double dy; /**< Scroll Y distance in lines. */
+} PuglEventScroll;
+
+/**
+ Interface event.
+
+ This is a union of all event structs. The `type` must be checked to
+ determine which fields are safe to access. A pointer to PuglEvent can
+ either be cast to the appropriate type, or the union members used.
+*/
+typedef union {
+ PuglEventType type; /**< Event type. */
+ PuglEventAny any; /**< Valid for all event types. */
+ PuglEventButton button; /**< PUGL_BUTTON_PRESS, PUGL_BUTTON_RELEASE. */
+ PuglEventConfigure configure; /**< PUGL_CONFIGURE. */
+ PuglEventCrossing crossing; /**< PUGL_ENTER_NOTIFY, PUGL_LEAVE_NOTIFY. */
+ PuglEventExpose expose; /**< PUGL_EXPOSE. */
+ PuglEventKey key; /**< PUGL_KEY_PRESS, PUGL_KEY_RELEASE. */
+ PuglEventMotion motion; /**< PUGL_MOTION_NOTIFY. */
+ PuglEventScroll scroll; /**< PUGL_SCROLL. */
+} PuglEvent;
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PUGL_EVENT_H_INCLUDED */
diff --git a/pugl/pugl.h b/pugl/pugl.h
index 8cd8afb..d13ce12 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -23,6 +23,9 @@
#include <stdint.h>
+#include "pugl/common.h"
+#include "pugl/event.h"
+
#ifdef PUGL_SHARED
# ifdef _WIN32
# define PUGL_LIB_IMPORT __declspec(dllimport)
@@ -53,88 +56,9 @@ extern "C" {
*/
/**
- An OpenGL view.
-*/
-typedef struct PuglViewImpl PuglView;
-
-/**
- A native window handle.
-
- On X11, this is a Window.
- On OSX, this is an NSView*.
- On Windows, this is a HWND.
-*/
-typedef intptr_t PuglNativeWindow;
-
-/**
- Return status code.
+ A function called when an event occurs.
*/
-typedef enum {
- PUGL_SUCCESS = 0
-} PuglStatus;
-
-/**
- Drawing context type.
-*/
-typedef enum {
- PUGL_GL,
- PUGL_CAIRO
-} PuglContextType;
-
-/**
- Convenience symbols for ASCII control characters.
-*/
-typedef enum {
- PUGL_CHAR_BACKSPACE = 0x08,
- PUGL_CHAR_ESCAPE = 0x1B,
- PUGL_CHAR_DELETE = 0x7F
-} PuglChar;
-
-/**
- Special (non-Unicode) keyboard keys.
-*/
-typedef enum {
- PUGL_KEY_F1 = 1,
- PUGL_KEY_F2,
- PUGL_KEY_F3,
- PUGL_KEY_F4,
- PUGL_KEY_F5,
- PUGL_KEY_F6,
- PUGL_KEY_F7,
- PUGL_KEY_F8,
- PUGL_KEY_F9,
- PUGL_KEY_F10,
- PUGL_KEY_F11,
- PUGL_KEY_F12,
- PUGL_KEY_LEFT,
- PUGL_KEY_UP,
- PUGL_KEY_RIGHT,
- PUGL_KEY_DOWN,
- PUGL_KEY_PAGE_UP,
- PUGL_KEY_PAGE_DOWN,
- PUGL_KEY_HOME,
- PUGL_KEY_END,
- PUGL_KEY_INSERT,
- PUGL_KEY_SHIFT,
- PUGL_KEY_CTRL,
- PUGL_KEY_ALT,
- PUGL_KEY_SUPER
-} PuglKey;
-
-/**
- Keyboard modifier flags.
-*/
-typedef enum {
- PUGL_MOD_SHIFT = 1, /**< Shift key */
- PUGL_MOD_CTRL = 1 << 1, /**< Control key */
- PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
- PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
-} PuglMod;
-
-/**
- Handle for opaque user data.
-*/
-typedef void* PuglHandle;
+typedef void (*PuglEventFunc)(PuglView* view, const PuglEvent* event);
/**
A function called when the window is closed.
@@ -314,6 +238,12 @@ PUGL_API void
puglIgnoreKeyRepeat(PuglView* view, bool ignore);
/**
+ Set the function to call when an event occurs.
+*/
+PUGL_API void
+puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc);
+
+/**
Set the function to call when the window is closed.
*/
PUGL_API void
@@ -368,6 +298,12 @@ PUGL_API PuglNativeWindow
puglGetNativeWindow(PuglView* view);
/**
+ Grab the input focus.
+*/
+PUGL_API void
+puglGrabFocus(PuglView* view);
+
+/**
Process all pending window events.
This handles input events as well as rendering, so it should be called
diff --git a/pugl/pugl_internal.h b/pugl/pugl_internal.h
index 3db08f7..32d31bf 100644
--- a/pugl/pugl_internal.h
+++ b/pugl/pugl_internal.h
@@ -24,27 +24,18 @@
If you are copying the pugl code into your source tree, the following
symbols can be defined to tweak pugl behaviour:
- PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus.
- PUGL_VERBOSE: Print graphics information to console.
PUGL_HAVE_CAIRO: Include Cairo support code.
PUGL_HAVE_GL: Include OpenGL support code.
*/
-#include "pugl.h"
-
-#ifdef PUGL_VERBOSE
-# include <stdio.h>
-# define PUGL_LOG(str) fprintf(stderr, "pugl: " str)
-# define PUGL_LOGF(fmt, ...) fprintf(stderr, "pugl: " fmt, __VA_ARGS__)
-#else
-# define PUGL_LOG(str)
-# define PUGL_LOGF(fmt, ...)
-#endif
+#include "pugl/pugl.h"
+#include "pugl/event.h"
typedef struct PuglInternalsImpl PuglInternals;
struct PuglViewImpl {
PuglHandle handle;
+ PuglEventFunc eventFunc;
PuglCloseFunc closeFunc;
PuglDisplayFunc displayFunc;
PuglKeyboardFunc keyboardFunc;
@@ -71,8 +62,6 @@ struct PuglViewImpl {
PuglInternals* puglInitInternals();
-void puglDefaultReshape(PuglView* view, int width, int height);
-
PuglView*
puglInit(int* pargc, char** argv)
{
@@ -143,27 +132,15 @@ puglGetModifiers(PuglView* view)
}
void
-puglDefaultReshape(PuglView* view, int width, int height)
+puglIgnoreKeyRepeat(PuglView* view, bool ignore)
{
-#ifdef PUGL_HAVE_GL
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, width, height, 0, 0, 1);
- glViewport(0, 0, width, height);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-#endif
- return;
-
- // unused
- (void)view;
+ view->ignoreKeyRepeat = ignore;
}
void
-puglIgnoreKeyRepeat(PuglView* view, bool ignore)
+puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
{
- view->ignoreKeyRepeat = ignore;
+ view->eventFunc = eventFunc;
}
void
@@ -213,3 +190,81 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc)
{
view->specialFunc = specialFunc;
}
+
+void
+puglEnterContext(PuglView* view);
+
+void
+puglLeaveContext(PuglView* view, bool flush);
+
+static void
+puglDispatchEvent(PuglView* view, const PuglEvent* event)
+{
+ if (view->eventFunc) {
+ view->eventFunc(view, event);
+ }
+
+ switch (event->type) {
+ case PUGL_CONFIGURE:
+ puglEnterContext(view);
+ view->width = event->configure.width;
+ view->height = event->configure.height;
+ if (view->reshapeFunc) {
+ view->reshapeFunc(view, view->width, view->height);
+ }
+ puglLeaveContext(view, false);
+ break;
+ case PUGL_EXPOSE:
+ if (event->expose.count == 0) {
+ puglEnterContext(view);
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+ view->redisplay = false;
+ puglLeaveContext(view, true);
+ }
+ break;
+ case PUGL_MOTION_NOTIFY:
+ view->event_timestamp_ms = event->motion.time;
+ view->mods = event->motion.state;
+ if (view->motionFunc) {
+ view->motionFunc(view, event->motion.x, event->motion.y);
+ }
+ break;
+ case PUGL_SCROLL:
+ if (view->scrollFunc) {
+ view->scrollFunc(view,
+ event->scroll.x, event->scroll.y,
+ event->scroll.dx, event->scroll.dy);
+ }
+ break;
+ case PUGL_BUTTON_PRESS:
+ case PUGL_BUTTON_RELEASE:
+ view->event_timestamp_ms = event->button.time;
+ view->mods = event->button.state;
+ if (view->mouseFunc) {
+ view->mouseFunc(view,
+ event->button.button,
+ event->type == PUGL_BUTTON_PRESS,
+ event->button.x,
+ event->button.y);
+ }
+ break;
+ case PUGL_KEY_PRESS:
+ case PUGL_KEY_RELEASE:
+ view->event_timestamp_ms = event->key.time;
+ view->mods = event->key.state;
+ if (event->key.special && view->specialFunc) {
+ view->specialFunc(view,
+ event->type == PUGL_KEY_PRESS,
+ event->key.special);
+ } else if (event->key.character && view->keyboardFunc) {
+ view->keyboardFunc(view,
+ event->type == PUGL_KEY_PRESS,
+ event->key.character);
+ }
+ break;
+ default:
+ break;
+ }
+}
diff --git a/pugl/pugl_osx.m b/pugl/pugl_osx.m
index 86c3c34..d502002 100644
--- a/pugl/pugl_osx.m
+++ b/pugl/pugl_osx.m
@@ -419,6 +419,12 @@ puglDestroy(PuglView* view)
free(view);
}
+void
+puglGrabFocus(PuglView* view)
+{
+ // TODO
+}
+
PuglStatus
puglProcessEvents(PuglView* view)
{
diff --git a/pugl/pugl_win.cpp b/pugl/pugl_win.cpp
index a458bee..9dfa70c 100644
--- a/pugl/pugl_win.cpp
+++ b/pugl/pugl_win.cpp
@@ -358,6 +358,12 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
return 0;
}
+void
+puglGrabFocus(PuglView* view)
+{
+ // TODO
+}
+
PuglStatus
puglProcessEvents(PuglView* view)
{
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;
diff --git a/pugl_cairo_test.c b/pugl_cairo_test.c
index 6ff5bd5..bd97668 100644
--- a/pugl_cairo_test.c
+++ b/pugl_cairo_test.c
@@ -63,7 +63,7 @@ roundedBox(cairo_t* cr, double x, double y, double w, double h)
}
static void
-drawButton(cairo_t* cr, const Button* but)
+buttonDraw(cairo_t* cr, const Button* but)
{
// Draw base
if (but->pressed) {
@@ -90,39 +90,47 @@ drawButton(cairo_t* cr, const Button* but)
cairo_show_text(cr, but->label);
}
+static bool
+buttonTouches(const Button* but, double x, double y)
+{
+ return (x >= toggle_button.x && x <= toggle_button.x + toggle_button.w &&
+ y >= toggle_button.y && y <= toggle_button.y + toggle_button.h);
+}
+
static void
onDisplay(PuglView* view)
{
cairo_t* cr = puglGetContext(view);
- drawButton(cr, &toggle_button);
+ buttonDraw(cr, &toggle_button);
}
static void
-onKeyboard(PuglView* view, bool press, uint32_t key)
+onClose(PuglView* view)
{
- if (key == 'q' || key == 'Q' || key == PUGL_CHAR_ESCAPE) {
- quit = 1;
- }
+ quit = 1;
}
static void
-onMouse(PuglView* view, int button, bool press, int x, int y)
+onEvent(PuglView* view, const PuglEvent* event)
{
- if (press &&
- x >= toggle_button.x && x <= toggle_button.x + toggle_button.w &&
- y >= toggle_button.y && y <= toggle_button.y + toggle_button.h) {
- toggle_button.pressed = !toggle_button.pressed;
- puglPostRedisplay(view);
+ switch (event->type) {
+ case PUGL_KEY_PRESS:
+ if (event->key.character == 'q' ||
+ event->key.character == 'Q' ||
+ event->key.character == PUGL_CHAR_ESCAPE) {
+ quit = 1;
+ }
+ break;
+ case PUGL_BUTTON_PRESS:
+ if (buttonTouches(&toggle_button, event->button.x, event->button.y)) {
+ toggle_button.pressed = !toggle_button.pressed;
+ puglPostRedisplay(view);
+ }
+ default: break;
}
}
-static void
-onClose(PuglView* view)
-{
- quit = 1;
-}
-
int
main(int argc, char** argv)
{
@@ -148,10 +156,9 @@ main(int argc, char** argv)
puglInitWindowSize(view, 512, 512);
puglInitResizable(view, resizable);
puglInitContextType(view, PUGL_CAIRO);
-
+
puglIgnoreKeyRepeat(view, ignoreKeyRepeat);
- puglSetKeyboardFunc(view, onKeyboard);
- puglSetMouseFunc(view, onMouse);
+ puglSetEventFunc(view, onEvent);
puglSetDisplayFunc(view, onDisplay);
puglSetCloseFunc(view, onClose);