aboutsummaryrefslogtreecommitdiffstats
path: root/pugl
diff options
context:
space:
mode:
Diffstat (limited to 'pugl')
-rw-r--r--pugl/gl.h32
-rw-r--r--pugl/glew.h32
-rw-r--r--pugl/glu.h32
-rw-r--r--pugl/pugl.h663
-rw-r--r--pugl/pugl.hpp106
-rw-r--r--pugl/pugl_internal.h269
-rw-r--r--pugl/pugl_internal_types.h100
-rw-r--r--pugl/pugl_osx.m743
-rw-r--r--pugl/pugl_win.cpp650
-rw-r--r--pugl/pugl_x11.c535
-rw-r--r--pugl/pugl_x11.h32
-rw-r--r--pugl/pugl_x11_cairo.c140
-rw-r--r--pugl/pugl_x11_cairo.h22
-rw-r--r--pugl/pugl_x11_gl.c208
-rw-r--r--pugl/pugl_x11_gl.h22
15 files changed, 3586 insertions, 0 deletions
diff --git a/pugl/gl.h b/pugl/gl.h
new file mode 100644
index 0000000..9a6aeef
--- /dev/null
+++ b/pugl/gl.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 2012-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 gl.h Portable header wrapper for gl.h.
+
+ Unfortunately, GL includes vary across platforms so this header allows for
+ pure portable programs.
+*/
+
+#ifdef __APPLE__
+# include "OpenGL/gl.h"
+#else
+# ifdef _WIN32
+# include <windows.h> /* Broken Windows GL headers require this */
+# endif
+# include "GL/gl.h"
+#endif
+
diff --git a/pugl/glew.h b/pugl/glew.h
new file mode 100644
index 0000000..5374406
--- /dev/null
+++ b/pugl/glew.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 2016 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 gl.h Portable header wrapper for glew.h.
+
+ Unfortunately, GL includes vary across platforms so this header allows for
+ pure portable programs.
+*/
+
+#ifdef __APPLE__
+# include "OpenGL/glew.h"
+#else
+# ifdef _WIN32
+# include <windows.h> /* Broken Windows GL headers require this */
+# endif
+# include "GL/glew.h"
+#endif
+
diff --git a/pugl/glu.h b/pugl/glu.h
new file mode 100644
index 0000000..0d3e8e1
--- /dev/null
+++ b/pugl/glu.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 2012-2015 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 gl.h Portable header wrapper for glu.h.
+
+ Unfortunately, GL includes vary across platforms so this header allows for
+ pure portable programs.
+*/
+
+#ifdef __APPLE__
+# include "OpenGL/glu.h"
+#else
+# ifdef _WIN32
+# include <windows.h> /* Broken Windows GL headers require this */
+# endif
+# include "GL/glu.h"
+#endif
+
diff --git a/pugl/pugl.h b/pugl/pugl.h
new file mode 100644
index 0000000..9e70100
--- /dev/null
+++ b/pugl/pugl.h
@@ -0,0 +1,663 @@
+/*
+ Copyright 2012-2016 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 pugl.h API for Pugl, a minimal portable API for OpenGL.
+*/
+
+#ifndef PUGL_H_INCLUDED
+#define PUGL_H_INCLUDED
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef PUGL_SHARED
+# ifdef _WIN32
+# define PUGL_LIB_IMPORT __declspec(dllimport)
+# define PUGL_LIB_EXPORT __declspec(dllexport)
+# else
+# define PUGL_LIB_IMPORT __attribute__((visibility("default")))
+# define PUGL_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef PUGL_INTERNAL
+# define PUGL_API PUGL_LIB_EXPORT
+# else
+# define PUGL_API PUGL_LIB_IMPORT
+# endif
+#else
+# define PUGL_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ @defgroup pugl Pugl
+ A minimal portable API for OpenGL.
+ @{
+*/
+
+/**
+ 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 = 1 << 0, /**< OpenGL (3D) */
+ PUGL_CAIRO = 1 << 1 /**< Cairo (2D) */
+} PuglContextType;
+
+/**
+ Window hint.
+*/
+typedef enum {
+ PUGL_USE_COMPAT_PROFILE, /**< Use compatible (not core) OpenGL profile */
+ PUGL_CONTEXT_VERSION_MAJOR, /**< OpenGL context major version */
+ PUGL_CONTEXT_VERSION_MINOR, /**< OpenGL context minor version */
+ PUGL_RED_BITS, /**< Number of bits for red channel */
+ PUGL_GREEN_BITS, /**< Number of bits for green channel */
+ PUGL_BLUE_BITS, /**< Number of bits for blue channel */
+ PUGL_ALPHA_BITS, /**< Number of bits for alpha channel */
+ PUGL_DEPTH_BITS, /**< Number of bits for depth buffer */
+ PUGL_STENCIL_BITS, /**< Number of bits for stencil buffer */
+ PUGL_SAMPLES, /**< Number of samples per pixel (AA) */
+ PUGL_DOUBLE_BUFFER, /**< True if double buffering should be used */
+ PUGL_RESIZABLE, /**< True if window should be resizable */
+} PuglWindowHint;
+
+/**
+ Special window hint value.
+*/
+typedef enum {
+ PUGL_DONT_CARE = -1, /**< Use best available value */
+ PUGL_FALSE = 0, /**< Explicitly false */
+ PUGL_TRUE = 1 /**< Explicitly true */
+} PuglWindowHintValue;
+
+/**
+ 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.
+
+ The numerical values of these symbols occupy a reserved range of Unicode
+ points, so it is possible to express either a PuglKey value or a Unicode
+ character in the same variable. This is sometimes useful for interfacing
+ with APIs that do not make this distinction.
+*/
+typedef enum {
+ PUGL_KEY_F1 = 0xE000,
+ 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;
+
+/**
+ The type of a PuglEvent.
+*/
+typedef enum {
+ PUGL_NOTHING, /**< No event */
+ PUGL_BUTTON_PRESS, /**< Mouse button press */
+ PUGL_BUTTON_RELEASE, /**< Mouse button release */
+ PUGL_CONFIGURE, /**< View moved and/or resized */
+ PUGL_EXPOSE, /**< View exposed, redraw required */
+ PUGL_CLOSE, /**< Close view */
+ PUGL_KEY_PRESS, /**< Key press */
+ PUGL_KEY_RELEASE, /**< Key release */
+ PUGL_ENTER_NOTIFY, /**< Pointer entered view */
+ PUGL_LEAVE_NOTIFY, /**< Pointer left view */
+ PUGL_MOTION_NOTIFY, /**< Pointer motion */
+ PUGL_SCROLL, /**< Scroll */
+ PUGL_FOCUS_IN, /**< Keyboard focus entered view */
+ PUGL_FOCUS_OUT /**< Keyboard focus left view */
+} PuglEventType;
+
+typedef enum {
+ PUGL_IS_SEND_EVENT = 1
+} PuglEventFlag;
+
+/**
+ 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+} 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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;
+
+/**
+ Window close event.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_CLOSE. */
+ PuglView* view; /**< View that received this event. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+} PuglEventClose;
+
+/**
+ Key press/release event.
+
+ Keys that correspond to a Unicode character have `character` and `utf8` set.
+ Other keys will have `character` 0, but `special` may be set if this is a
+ known special key.
+
+ A key press may be part of a multi-key sequence to generate a wide
+ character. If `filter` is set, this event is part of a multi-key sequence
+ and should be ignored if the application is reading textual input.
+ Following the series of filtered press events, a press event with
+ `character` and `utf8` (but `keycode` 0) will be sent. This event will have
+ no corresponding release event.
+
+ Generally, an application should either work with raw keyboard press/release
+ events based on `keycode` (ignoring events with `keycode` 0), or
+ read textual input based on `character` or `utf8` (ignoring releases and
+ events with `filter` 1). Note that blindly appending `utf8` will yield
+ incorrect text, since press events are sent for both individually composed
+ keys and the resulting synthetic multi-byte press.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_KEY_PRESS or PUGL_KEY_RELEASE. */
+ PuglView* view; /**< View that received this event. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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 keycode; /**< Raw key code. */
+ uint32_t character; /**< Unicode character code, or 0. */
+ PuglKey special; /**< Special key, or 0. */
+ uint8_t utf8[8]; /**< UTF-8 string. */
+ bool filter; /**< True if part of a multi-key sequence. */
+} 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ 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;
+
+/**
+ Keyboard focus event.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_FOCUS_IN or PUGL_FOCUS_OUT. */
+ PuglView* view; /**< View that received this event. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ bool grab; /**< True iff this is a grab/ungrab event. */
+} PuglEventFocus;
+
+/**
+ 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. */
+ PuglEventExpose expose; /**< PUGL_EXPOSE. */
+ PuglEventClose close; /**< PUGL_CLOSE. */
+ PuglEventKey key; /**< PUGL_KEY_PRESS, PUGL_KEY_RELEASE. */
+ PuglEventCrossing crossing; /**< PUGL_ENTER_NOTIFY, PUGL_LEAVE_NOTIFY. */
+ PuglEventMotion motion; /**< PUGL_MOTION_NOTIFY. */
+ PuglEventScroll scroll; /**< PUGL_SCROLL. */
+ PuglEventFocus focus; /**< PUGL_FOCUS_IN, PUGL_FOCUS_OUT. */
+} PuglEvent;
+
+/**
+ @name Initialization
+ Configuration functions which must be called before creating a window.
+ @{
+*/
+
+/**
+ Create a Pugl view.
+
+ To create a window, call the various puglInit* functions as necessary, then
+ call puglCreateWindow().
+
+ @param pargc Pointer to argument count (currently unused).
+ @param argv Arguments (currently unused).
+ @return A newly created view.
+*/
+PUGL_API PuglView*
+puglInit(int* pargc, char** argv);
+
+/**
+ Set a hint before creating a window.
+*/
+PUGL_API void
+puglInitWindowHint(PuglView* view, PuglWindowHint hint, int value);
+
+/**
+ Set the window class name before creating a window.
+*/
+PUGL_API void
+puglInitWindowClass(PuglView* view, const char* name);
+
+/**
+ Set the parent window before creating a window (for embedding).
+*/
+PUGL_API void
+puglInitWindowParent(PuglView* view, PuglNativeWindow parent);
+
+/**
+ Set the window size before creating a window.
+*/
+PUGL_API void
+puglInitWindowSize(PuglView* view, int width, int height);
+
+/**
+ Set the minimum window size before creating a window.
+*/
+PUGL_API void
+puglInitWindowMinSize(PuglView* view, int width, int height);
+
+/**
+ Set the window aspect ratio range before creating a window.
+
+ The x and y values here represent a ratio of width to height. To set a
+ fixed aspect ratio, set the minimum and maximum values to the same ratio.
+*/
+PUGL_API void
+puglInitWindowAspectRatio(PuglView* view,
+ int min_x,
+ int min_y,
+ int max_x,
+ int max_y);
+
+/**
+ Enable or disable resizing before creating a window.
+*/
+PUGL_API void
+puglInitResizable(PuglView* view, bool resizable);
+
+/**
+ Set transient parent before creating a window.
+
+ On X11, parent must be a Window.
+ On OSX, parent must be an NSView*.
+*/
+PUGL_API void
+puglInitTransientFor(PuglView* view, uintptr_t parent);
+
+/**
+ Set the context type before creating a window.
+*/
+PUGL_API void
+puglInitContextType(PuglView* view, PuglContextType type);
+
+/**
+ @}
+*/
+
+/**
+ @name Windows
+ Functions for creating and managing a visible window for a view.
+ @{
+*/
+
+/**
+ Create a window with the settings given by the various puglInit functions.
+
+ @return 1 (pugl does not currently support multiple windows).
+*/
+PUGL_API int
+puglCreateWindow(PuglView* view, const char* title);
+
+/**
+ Show the current window.
+*/
+PUGL_API void
+puglShowWindow(PuglView* view);
+
+/**
+ Hide the current window.
+*/
+PUGL_API void
+puglHideWindow(PuglView* view);
+
+/**
+ Return the native window handle.
+*/
+PUGL_API PuglNativeWindow
+puglGetNativeWindow(PuglView* view);
+
+/**
+ @}
+*/
+
+/**
+ Set the handle to be passed to all callbacks.
+
+ This is generally a pointer to a struct which contains all necessary state.
+ Everything needed in callbacks should be here, not in static variables.
+*/
+PUGL_API void
+puglSetHandle(PuglView* view, PuglHandle handle);
+
+/**
+ Get the handle to be passed to all callbacks.
+*/
+PUGL_API PuglHandle
+puglGetHandle(PuglView* view);
+
+/**
+ Return true iff the view is currently visible.
+*/
+PUGL_API bool
+puglGetVisible(PuglView* view);
+
+/**
+ Get the current size of the view.
+*/
+PUGL_API void
+puglGetSize(PuglView* view, int* width, int* height);
+
+/**
+ @name Context
+ Functions for accessing the drawing context.
+ @{
+*/
+
+/**
+ Get the drawing context.
+
+ For PUGL_GL contexts, this is unused and returns NULL.
+ For PUGL_CAIRO contexts, this returns a pointer to a cairo_t.
+*/
+PUGL_API void*
+puglGetContext(PuglView* view);
+
+/**
+ Enter the drawing context.
+
+ This must be called before any code that accesses the drawing context,
+ including any GL functions. This is only necessary for code that does so
+ outside the usual draw callback or handling of an expose event.
+*/
+PUGL_API void
+puglEnterContext(PuglView* view);
+
+/**
+ Leave the drawing context.
+
+ This must be called after puglEnterContext and applies the results of the
+ drawing code (for example, by swapping buffers).
+*/
+PUGL_API void
+puglLeaveContext(PuglView* view, bool flush);
+
+/**
+ @}
+*/
+
+/**
+ @name Event Handling
+ @{
+*/
+
+/**
+ A function called when an event occurs.
+*/
+typedef void (*PuglEventFunc)(PuglView* view, const PuglEvent* event);
+
+/**
+ Set the function to call when an event occurs.
+*/
+PUGL_API void
+puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc);
+
+/**
+ Ignore synthetic repeated key events.
+*/
+PUGL_API void
+puglIgnoreKeyRepeat(PuglView* view, bool ignore);
+
+/**
+ Grab the input focus.
+*/
+PUGL_API void
+puglGrabFocus(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
+ necessary. This function will block indefinitely if no events are
+ available, so is not appropriate for use in programs that need to perform
+ regular updates (e.g. animation).
+*/
+PUGL_API PuglStatus
+puglWaitForEvent(PuglView* view);
+
+/**
+ Process all pending window events.
+
+ This handles input events as well as rendering, so it should be called
+ regularly and rapidly enough to keep the UI responsive. This function does
+ not block if no events are pending.
+*/
+PUGL_API PuglStatus
+puglProcessEvents(PuglView* view);
+
+/**
+ @}
+*/
+
+/**
+ OpenGL extension function.
+*/
+typedef void (*PuglGlFunc)();
+
+/**
+ Return the address of an OpenGL extension function.
+*/
+PUGL_API PuglGlFunc
+puglGetProcAddress(const char* name);
+
+/**
+ Request a redisplay on the next call to puglProcessEvents().
+*/
+PUGL_API void
+puglPostRedisplay(PuglView* view);
+
+/**
+ Destroy a GL window.
+*/
+PUGL_API void
+puglDestroy(PuglView* view);
+
+/**
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PUGL_H_INCLUDED */
diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp
new file mode 100644
index 0000000..8232887
--- /dev/null
+++ b/pugl/pugl.hpp
@@ -0,0 +1,106 @@
+/*
+ Copyright 2012-2015 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 pugl.hpp C++ API for Pugl, a minimal portable API for OpenGL.
+*/
+
+#ifndef PUGL_HPP_INCLUDED
+#define PUGL_HPP_INCLUDED
+
+#include "pugl/pugl.h"
+
+/**
+ @defgroup puglmm Puglmm
+ C++ API wrapper for Pugl.
+ @{
+*/
+
+namespace pugl {
+
+class View {
+public:
+ View(int* pargc, char** argv)
+ : _view(puglInit(pargc, argv))
+ {
+ puglSetHandle(_view, this);
+ puglSetEventFunc(_view, _onEvent);
+ }
+
+ virtual ~View() { puglDestroy(_view); }
+
+ virtual void initWindowParent(PuglNativeWindow parent) {
+ puglInitWindowParent(_view, parent);
+ }
+
+ virtual void initWindowSize(int width, int height) {
+ puglInitWindowSize(_view, width, height);
+ }
+
+ virtual void initWindowMinSize(int width, int height) {
+ puglInitWindowMinSize(_view, width, height);
+ }
+
+ virtual void initWindowAspectRatio(int min_x, int min_y, int max_x, int max_y) {
+ puglInitWindowAspectRatio(_view, min_x, min_y, max_x, max_y);
+ }
+
+ virtual void initResizable(bool resizable) {
+ puglInitResizable(_view, resizable);
+ }
+
+ virtual void initTransientFor(uintptr_t parent) {
+ puglInitTransientFor(_view, parent);
+ }
+
+ virtual void initContextType(PuglContextType type) {
+ puglInitContextType(_view, type);
+ }
+
+ virtual void createWindow(const char* title) {
+ puglCreateWindow(_view, title);
+ }
+
+ virtual void showWindow() { puglShowWindow(_view); }
+ virtual void hideWindow() { puglHideWindow(_view); }
+ virtual PuglNativeWindow getNativeWindow() { return puglGetNativeWindow(_view); }
+
+ virtual void onEvent(const PuglEvent* event) = 0;
+
+ virtual void* getContext() { return puglGetContext(_view); }
+ virtual void ignoreKeyRepeat(bool ignore) { puglIgnoreKeyRepeat(_view, ignore); }
+ virtual void grabFocus() { puglGrabFocus(_view); }
+ virtual PuglStatus waitForEvent() { return puglWaitForEvent(_view); }
+ virtual PuglStatus processEvents() { return puglProcessEvents(_view); }
+ virtual void postRedisplay() { puglPostRedisplay(_view); }
+
+ PuglView* cobj() { return _view; }
+
+private:
+ static void _onEvent(PuglView* view, const PuglEvent* event) {
+ ((View*)puglGetHandle(view))->onEvent(event);
+ }
+
+ PuglView* _view;
+};
+
+} // namespace pugl
+
+/**
+ @}
+*/
+
+#endif /* PUGL_HPP_INCLUDED */
diff --git a/pugl/pugl_internal.h b/pugl/pugl_internal.h
new file mode 100644
index 0000000..2b54de4
--- /dev/null
+++ b/pugl/pugl_internal.h
@@ -0,0 +1,269 @@
+/*
+ Copyright 2012-2016 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 pugl_internal.h Private platform-independent definitions.
+
+ Note this file contains function definitions, so it must be compiled into
+ the final binary exactly once. Each platform specific implementation file
+ including it once should achieve this.
+
+ If you are copying the pugl code into your source tree, the following
+ symbols can be defined to tweak pugl behaviour:
+
+ PUGL_HAVE_CAIRO: Include Cairo support code.
+ PUGL_HAVE_GL: Include OpenGL support code.
+*/
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_internal_types.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+PuglInternals* puglInitInternals(void);
+
+static PuglHints
+puglDefaultHints()
+{
+ static const PuglHints hints = {
+ 2, 0, 4, 4, 4, 4, 24, 8, 0, true, true, false
+ };
+ return hints;
+}
+
+PuglView*
+puglInit(int* pargc, char** argv)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ if (!view) {
+ return NULL;
+ }
+
+ view->hints = puglDefaultHints();
+
+ PuglInternals* impl = puglInitInternals();
+ if (!impl) {
+ return NULL;
+ }
+
+ view->ctx_type = PUGL_GL;
+ view->impl = impl;
+ view->width = 640;
+ view->height = 480;
+
+ return view;
+}
+
+void
+puglInitWindowHint(PuglView* view, PuglWindowHint hint, int value)
+{
+ switch (hint) {
+ case PUGL_USE_COMPAT_PROFILE:
+ view->hints.use_compat_profile = value;
+ break;
+ case PUGL_CONTEXT_VERSION_MAJOR:
+ view->hints.context_version_major = value;
+ break;
+ case PUGL_CONTEXT_VERSION_MINOR:
+ view->hints.context_version_minor = value;
+ break;
+ case PUGL_RED_BITS:
+ view->hints.red_bits = value;
+ break;
+ case PUGL_GREEN_BITS:
+ view->hints.green_bits = value;
+ break;
+ case PUGL_BLUE_BITS:
+ view->hints.blue_bits = value;
+ break;
+ case PUGL_ALPHA_BITS:
+ view->hints.alpha_bits = value;
+ break;
+ case PUGL_DEPTH_BITS:
+ view->hints.depth_bits = value;
+ break;
+ case PUGL_STENCIL_BITS:
+ view->hints.stencil_bits = value;
+ break;
+ case PUGL_SAMPLES:
+ view->hints.samples = value;
+ break;
+ case PUGL_DOUBLE_BUFFER:
+ view->hints.double_buffer = value;
+ break;
+ case PUGL_RESIZABLE:
+ view->hints.resizable = value;
+ break;
+ }
+}
+
+void
+puglInitWindowSize(PuglView* view, int width, int height)
+{
+ view->width = width;
+ view->height = height;
+}
+
+void
+puglInitWindowMinSize(PuglView* view, int width, int height)
+{
+ view->min_width = width;
+ view->min_height = height;
+}
+
+void
+puglInitWindowAspectRatio(PuglView* view,
+ int min_x,
+ int min_y,
+ int max_x,
+ int max_y)
+{
+ view->min_aspect_x = min_x;
+ view->min_aspect_y = min_y;
+ view->max_aspect_x = max_x;
+ view->max_aspect_y = max_y;
+}
+
+void
+puglInitWindowClass(PuglView* view, const char* name)
+{
+ const size_t len = strlen(name);
+
+ free(view->windowClass);
+ view->windowClass = (char*)calloc(1, len + 1);
+ memcpy(view->windowClass, name, len);
+}
+
+void
+puglInitWindowParent(PuglView* view, PuglNativeWindow parent)
+{
+ view->parent = parent;
+}
+
+void
+puglInitResizable(PuglView* view, bool resizable)
+{
+ view->hints.resizable = resizable;
+}
+
+void
+puglInitTransientFor(PuglView* view, uintptr_t parent)
+{
+ view->transient_parent = parent;
+}
+
+void
+puglInitContextType(PuglView* view, PuglContextType type)
+{
+ view->ctx_type = type;
+}
+
+void
+puglSetHandle(PuglView* view, PuglHandle handle)
+{
+ view->handle = handle;
+}
+
+PuglHandle
+puglGetHandle(PuglView* view)
+{
+ return view->handle;
+}
+
+bool
+puglGetVisible(PuglView* view)
+{
+ return view->visible;
+}
+
+void
+puglGetSize(PuglView* view, int* width, int* height)
+{
+ *width = view->width;
+ *height = view->height;
+}
+
+void
+puglIgnoreKeyRepeat(PuglView* view, bool ignore)
+{
+ view->ignoreKeyRepeat = ignore;
+}
+
+void
+puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
+{
+ view->eventFunc = eventFunc;
+}
+
+/** Return the code point for buf, or the replacement character on error. */
+static uint32_t
+puglDecodeUTF8(const uint8_t* buf)
+{
+#define FAIL_IF(cond) { if (cond) return 0xFFFD; }
+
+ // http://en.wikipedia.org/wiki/UTF-8
+
+ if (buf[0] < 0x80) {
+ return buf[0];
+ } else if (buf[0] < 0xC2) {
+ return 0xFFFD;
+ } else if (buf[0] < 0xE0) {
+ FAIL_IF((buf[1] & 0xC0) != 0x80);
+ return (buf[0] << 6) + buf[1] - 0x3080;
+ } else if (buf[0] < 0xF0) {
+ FAIL_IF((buf[1] & 0xC0) != 0x80);
+ FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
+ FAIL_IF((buf[2] & 0xC0) != 0x80);
+ return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080;
+ } else if (buf[0] < 0xF5) {
+ FAIL_IF((buf[1] & 0xC0) != 0x80);
+ FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
+ FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
+ FAIL_IF((buf[2] & 0xC0) != 0x80);
+ FAIL_IF((buf[3] & 0xC0) != 0x80);
+ return ((buf[0] << 18) +
+ (buf[1] << 12) +
+ (buf[2] << 6) +
+ buf[3] - 0x3C82080);
+ }
+ return 0xFFFD;
+}
+
+static void
+puglDispatchEvent(PuglView* view, const PuglEvent* event)
+{
+ switch (event->type) {
+ case PUGL_NOTHING:
+ break;
+ case PUGL_CONFIGURE:
+ view->width = (int)event->configure.width;
+ view->height = (int)event->configure.height;
+ puglEnterContext(view);
+ view->eventFunc(view, event);
+ puglLeaveContext(view, false);
+ break;
+ case PUGL_EXPOSE:
+ if (event->expose.count == 0) {
+ puglEnterContext(view);
+ view->eventFunc(view, event);
+ puglLeaveContext(view, true);
+ }
+ break;
+ default:
+ view->eventFunc(view, event);
+ }
+}
diff --git a/pugl/pugl_internal_types.h b/pugl/pugl_internal_types.h
new file mode 100644
index 0000000..bbc39c0
--- /dev/null
+++ b/pugl/pugl_internal_types.h
@@ -0,0 +1,100 @@
+/*
+ Copyright 2012-2019 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 pugl_internal_types.h Private platform-independent type definitions.
+*/
+
+#ifndef PUGL_INTERNAL_TYPES_H
+#define PUGL_INTERNAL_TYPES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "pugl/pugl.h"
+
+/** Platform-specific internals. */
+typedef struct PuglInternalsImpl PuglInternals;
+
+typedef struct {
+ int context_version_major;
+ int context_version_minor;
+ int red_bits;
+ int green_bits;
+ int blue_bits;
+ int alpha_bits;
+ int depth_bits;
+ int stencil_bits;
+ int samples;
+ bool use_compat_profile;
+ bool double_buffer;
+ bool resizable;
+} PuglHints;
+
+/** Cross-platform view definition. */
+struct PuglViewImpl {
+ PuglHandle handle;
+ PuglEventFunc eventFunc;
+
+ PuglInternals* impl;
+
+ char* windowClass;
+ PuglNativeWindow parent;
+ PuglContextType ctx_type;
+ uintptr_t transient_parent;
+
+ PuglHints hints;
+ int width;
+ int height;
+ int min_width;
+ int min_height;
+ int min_aspect_x;
+ int min_aspect_y;
+ int max_aspect_x;
+ int max_aspect_y;
+ bool ignoreKeyRepeat;
+ bool redisplay;
+ bool visible;
+};
+
+/** Opaque surface used by draw context. */
+typedef void PuglSurface;
+
+/** Drawing context interface. */
+typedef struct {
+ /** Get visual information from display and setup view as necessary. */
+ int (*configure)(PuglView*);
+
+ /** Create surface and drawing context. */
+ int (*create)(PuglView*);
+
+ /** Destroy surface and drawing context. */
+ int (*destroy)(PuglView*);
+
+ /** Enter drawing context. */
+ int (*enter)(PuglView*);
+
+ /** Leave drawing context, explicitly flushing if parameter is true. */
+ int (*leave)(PuglView*, bool);
+
+ /** Resize drawing context to the given width and height. */
+ int (*resize)(PuglView*, int, int);
+
+ /** Return the puglGetContext() handle for the application, if any. */
+ void* (*getHandle)(PuglView*);
+} PuglDrawContext;
+
+#endif // PUGL_INTERNAL_TYPES_H
diff --git a/pugl/pugl_osx.m b/pugl/pugl_osx.m
new file mode 100644
index 0000000..e56a1a7
--- /dev/null
+++ b/pugl/pugl_osx.m
@@ -0,0 +1,743 @@
+/*
+ Copyright 2012-2017 David Robillard <http://drobilla.net>
+ Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
+
+ 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 pugl_osx.m OSX/Cocoa Pugl Implementation.
+*/
+
+#include "pugl/cairo_gl.h"
+#include "pugl/gl.h"
+#include "pugl/pugl_internal.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include <stdlib.h>
+
+@class PuglOpenGLView;
+
+struct PuglInternalsImpl {
+ NSApplication* app;
+ PuglOpenGLView* glview;
+ id window;
+ NSEvent* nextEvent;
+ unsigned mods;
+#ifdef PUGL_HAVE_CAIRO
+ cairo_surface_t* surface;
+ cairo_t* cr;
+ PuglCairoGL cairo_gl;
+#endif
+};
+
+@interface PuglWindow : NSWindow
+{
+@public
+ PuglView* puglview;
+}
+
+- (id) initWithContentRect:(NSRect)contentRect
+ styleMask:(unsigned int)aStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)flag;
+- (void) setPuglview:(PuglView*)view;
+- (BOOL) windowShouldClose:(id)sender;
+- (BOOL) canBecomeKeyWindow:(id)sender;
+@end
+
+@implementation PuglWindow
+
+- (id)initWithContentRect:(NSRect)contentRect
+ styleMask:(unsigned int)aStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)flag
+{
+ NSWindow* result = [super initWithContentRect:contentRect
+ styleMask:aStyle
+ backing:bufferingType
+ defer:NO];
+
+ [result setAcceptsMouseMovedEvents:YES];
+ return (PuglWindow*)result;
+}
+
+- (void)setPuglview:(PuglView*)view
+{
+ puglview = view;
+ [self setContentSize:NSMakeSize(view->width, view->height)];
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ const PuglEventClose ev = {
+ PUGL_CLOSE,
+ puglview,
+ 0
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+
+ return YES;
+}
+
+- (BOOL) canBecomeKeyWindow
+{
+ return YES;
+}
+
+- (BOOL) canBecomeMainWindow
+{
+ return YES;
+}
+
+- (BOOL) canBecomeKeyWindow:(id)sender
+{
+ return NO;
+}
+
+@end
+
+@interface PuglOpenGLView : NSOpenGLView
+{
+@public
+ PuglView* puglview;
+
+ NSTrackingArea* trackingArea;
+}
+
+- (id) initWithFrame:(NSRect)frame;
+- (void) reshape;
+- (void) drawRect:(NSRect)rect;
+- (NSPoint) eventLocation:(NSEvent*)event;
+- (void) mouseEntered:(NSEvent*)event;
+- (void) mouseExited:(NSEvent*)event;
+- (void) mouseMoved:(NSEvent*)event;
+- (void) mouseDragged:(NSEvent*)event;
+- (void) rightMouseDragged:(NSEvent*)event;
+- (void) mouseDown:(NSEvent*)event;
+- (void) mouseUp:(NSEvent*)event;
+- (void) rightMouseDown:(NSEvent*)event;
+- (void) rightMouseUp:(NSEvent*)event;
+- (void) otherMouseDragged:(NSEvent*)event;
+- (void) otherMouseDown:(NSEvent*)event;
+- (void) otherMouseUp:(NSEvent*)event;
+- (void) scrollWheel:(NSEvent*)event;
+- (void) keyDown:(NSEvent*)event;
+- (void) keyUp:(NSEvent*)event;
+- (void) flagsChanged:(NSEvent*)event;
+
+@end
+
+@implementation PuglOpenGLView
+
+- (id) initWithFrame:(NSRect)frame
+{
+ NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFAColorSize, 32,
+ NSOpenGLPFADepthSize, 32,
+ 0
+ };
+
+ NSOpenGLPixelFormat* pixelFormat = [
+ [NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
+
+ if (pixelFormat) {
+ self = [super initWithFrame:frame pixelFormat:pixelFormat];
+ [pixelFormat release];
+ } else {
+ self = [super initWithFrame:frame];
+ }
+
+ if (self) {
+ [[self openGLContext] makeCurrentContext];
+ [self reshape];
+ }
+ return self;
+}
+
+- (void) reshape
+{
+ [[self openGLContext] update];
+
+ if (!puglview) {
+ return;
+ }
+
+ const NSRect bounds = [self bounds];
+ const PuglEventConfigure ev = {
+ PUGL_CONFIGURE,
+ puglview,
+ 0,
+ bounds.origin.x,
+ bounds.origin.y,
+ bounds.size.width,
+ bounds.size.height,
+ };
+
+#ifdef PUGL_HAVE_CAIRO
+ PuglInternals* impl = puglview->impl;
+ if (puglview->ctx_type & PUGL_CAIRO) {
+ cairo_surface_destroy(impl->surface);
+ cairo_destroy(impl->cr);
+ impl->surface = pugl_cairo_gl_create(
+ &impl->cairo_gl, ev.width, ev.height, 4);
+ impl->cr = cairo_create(impl->surface);
+ pugl_cairo_gl_configure(&impl->cairo_gl, ev.width, ev.height);
+ }
+#endif
+
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+}
+
+- (void) drawRect:(NSRect)rect
+{
+ const PuglEventExpose ev = {
+ PUGL_EXPOSE,
+ puglview,
+ 0,
+ rect.origin.x,
+ rect.origin.y,
+ rect.size.width,
+ rect.size.height,
+ 0
+ };
+
+ puglDispatchEvent(puglview, (const PuglEvent*)&ev);
+
+#ifdef PUGL_HAVE_CAIRO
+ if (puglview->ctx_type & PUGL_CAIRO) {
+ pugl_cairo_gl_draw(
+ &puglview->impl->cairo_gl, puglview->width, puglview->height);
+ }
+#endif
+}
+
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+static unsigned
+getModifiers(PuglView* view, NSEvent* ev)
+{
+ const unsigned modifierFlags = [ev modifierFlags];
+
+ unsigned mods = 0;
+ mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
+ mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
+ mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0;
+ mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0;
+ return mods;
+}
+
+static PuglKey
+keySymToSpecial(PuglView* view, NSEvent* ev)
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if ([chars length] == 1) {
+ switch ([chars characterAtIndex:0]) {
+ case NSF1FunctionKey: return PUGL_KEY_F1;
+ case NSF2FunctionKey: return PUGL_KEY_F2;
+ case NSF3FunctionKey: return PUGL_KEY_F3;
+ case NSF4FunctionKey: return PUGL_KEY_F4;
+ case NSF5FunctionKey: return PUGL_KEY_F5;
+ case NSF6FunctionKey: return PUGL_KEY_F6;
+ case NSF7FunctionKey: return PUGL_KEY_F7;
+ case NSF8FunctionKey: return PUGL_KEY_F8;
+ case NSF9FunctionKey: return PUGL_KEY_F9;
+ case NSF10FunctionKey: return PUGL_KEY_F10;
+ case NSF11FunctionKey: return PUGL_KEY_F11;
+ case NSF12FunctionKey: return PUGL_KEY_F12;
+ case NSLeftArrowFunctionKey: return PUGL_KEY_LEFT;
+ case NSUpArrowFunctionKey: return PUGL_KEY_UP;
+ case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT;
+ case NSDownArrowFunctionKey: return PUGL_KEY_DOWN;
+ case NSPageUpFunctionKey: return PUGL_KEY_PAGE_UP;
+ case NSPageDownFunctionKey: return PUGL_KEY_PAGE_DOWN;
+ case NSHomeFunctionKey: return PUGL_KEY_HOME;
+ case NSEndFunctionKey: return PUGL_KEY_END;
+ case NSInsertFunctionKey: return PUGL_KEY_INSERT;
+ }
+ // SHIFT, CTRL, ALT, and SUPER are handled in [flagsChanged]
+ }
+ return (PuglKey)0;
+}
+
+-(void)updateTrackingAreas
+{
+ if (trackingArea != nil) {
+ [self removeTrackingArea:trackingArea];
+ [trackingArea release];
+ }
+
+ const int opts = (NSTrackingMouseEnteredAndExited |
+ NSTrackingMouseMoved |
+ NSTrackingActiveAlways);
+ trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
+ options:opts
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:trackingArea];
+}
+
+- (NSPoint) eventLocation:(NSEvent*)event
+{
+ return [self convertPoint:[event locationInWindow] fromView:nil];
+}
+
+- (void)mouseEntered:(NSEvent*)theEvent
+{
+ [self updateTrackingAreas];
+}
+
+- (void)mouseExited:(NSEvent*)theEvent
+{
+}
+
+- (void) mouseMoved:(NSEvent*)event
+{
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const PuglEventMotion ev = {
+ PUGL_MOTION_NOTIFY,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ 0,
+ 1
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+}
+
+- (void) mouseDragged:(NSEvent*)event
+{
+ [self mouseMoved: event];
+}
+
+- (void) rightMouseDragged:(NSEvent*)event
+{
+ [self mouseMoved: event];
+}
+
+- (void) otherMouseDragged:(NSEvent*)event
+{
+ [self mouseMoved: event];
+}
+
+- (void) mouseDown:(NSEvent*)event
+{
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const PuglEventButton ev = {
+ PUGL_BUTTON_PRESS,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ (unsigned)[event buttonNumber] + 1
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+}
+
+- (void) mouseUp:(NSEvent*)event
+{
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const PuglEventButton ev = {
+ PUGL_BUTTON_RELEASE,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ (unsigned)[event buttonNumber] + 1
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+ [self updateTrackingAreas];
+}
+
+- (void) rightMouseDown:(NSEvent*)event
+{
+ [self mouseDown: event];
+}
+
+- (void) rightMouseUp:(NSEvent*)event
+{
+ [self mouseUp: event];
+}
+
+- (void) otherMouseDown:(NSEvent*)event
+{
+ [self mouseDown: event];
+}
+
+- (void) otherMouseUp:(NSEvent*)event
+{
+ [self mouseUp: event];
+}
+
+- (void) scrollWheel:(NSEvent*)event
+{
+ [self updateTrackingAreas];
+
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const PuglEventScroll ev = {
+ PUGL_SCROLL,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ [event deltaX],
+ [event deltaY]
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+ [self updateTrackingAreas];
+}
+
+- (void) keyDown:(NSEvent*)event
+{
+ if (puglview->ignoreKeyRepeat && [event isARepeat]) {
+ return;
+ }
+
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const NSString* chars = [event characters];
+ const char* str = [chars UTF8String];
+ const uint32_t code = puglDecodeUTF8((const uint8_t*)str);
+ PuglEventKey ev = {
+ PUGL_KEY_PRESS,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ [event keyCode],
+ (code != 0xFFFD) ? code : 0,
+ keySymToSpecial(puglview, event),
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ false
+ };
+ strncpy((char*)ev.utf8, str, 8);
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+}
+
+- (void) keyUp:(NSEvent*)event
+{
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const NSString* chars = [event characters];
+ const char* str = [chars UTF8String];
+ const PuglEventKey ev = {
+ PUGL_KEY_RELEASE,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ [event keyCode],
+ puglDecodeUTF8((const uint8_t*)str),
+ keySymToSpecial(puglview, event),
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ false,
+ };
+ strncpy((char*)ev.utf8, str, 8);
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+}
+
+- (void) flagsChanged:(NSEvent*)event
+{
+ const unsigned mods = getModifiers(puglview, event);
+ PuglEventType type = PUGL_NOTHING;
+ PuglKey special = 0;
+
+ if ((mods & PUGL_MOD_SHIFT) != (puglview->impl->mods & PUGL_MOD_SHIFT)) {
+ type = mods & PUGL_MOD_SHIFT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_SHIFT;
+ } else if ((mods & PUGL_MOD_CTRL) != (puglview->impl->mods & PUGL_MOD_CTRL)) {
+ type = mods & PUGL_MOD_CTRL ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_CTRL;
+ } else if ((mods & PUGL_MOD_ALT) != (puglview->impl->mods & PUGL_MOD_ALT)) {
+ type = mods & PUGL_MOD_ALT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_ALT;
+ } else if ((mods & PUGL_MOD_SUPER) != (puglview->impl->mods & PUGL_MOD_SUPER)) {
+ type = mods & PUGL_MOD_SUPER ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ special = PUGL_KEY_SUPER;
+ }
+
+ if (special != 0) {
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ PuglEventKey ev = {
+ type,
+ puglview,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ mods,
+ [event keyCode],
+ 0,
+ special,
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ false
+ };
+ puglDispatchEvent(puglview, (PuglEvent*)&ev);
+ }
+
+ puglview->impl->mods = mods;
+}
+
+@end
+
+PuglInternals*
+puglInitInternals(void)
+{
+ return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+}
+
+void
+puglEnterContext(PuglView* view)
+{
+ [[view->impl->glview openGLContext] makeCurrentContext];
+}
+
+void
+puglLeaveContext(PuglView* view, bool flush)
+{
+#ifdef PUGL_HAVE_CAIRO
+ if (view->ctx_type & PUGL_CAIRO) {
+ pugl_cairo_gl_draw(&view->impl->cairo_gl, view->width, view->height);
+ }
+#endif
+
+ if (flush) {
+ [[view->impl->glview openGLContext] flushBuffer];
+ }
+}
+
+static NSLayoutConstraint*
+puglConstraint(id item, NSLayoutAttribute attribute, float constant)
+{
+ return [NSLayoutConstraint
+ constraintWithItem: item
+ attribute: attribute
+ relatedBy: NSLayoutRelationGreaterThanOrEqual
+ toItem: nil
+ attribute: NSLayoutAttributeNotAnAttribute
+ multiplier: 1.0
+ constant: constant];
+}
+
+int
+puglCreateWindow(PuglView* view, const char* title)
+{
+ PuglInternals* impl = view->impl;
+
+ [NSAutoreleasePool new];
+ impl->app = [NSApplication sharedApplication];
+
+ impl->glview = [PuglOpenGLView new];
+ impl->glview->puglview = view;
+
+ [impl->glview setFrameSize:NSMakeSize(view->width, view->height)];
+ [impl->glview addConstraint:
+ puglConstraint(impl->glview, NSLayoutAttributeWidth, view->min_width)];
+ [impl->glview addConstraint:
+ puglConstraint(impl->glview, NSLayoutAttributeHeight, view->min_height)];
+ if (!view->hints.resizable) {
+ [impl->glview setAutoresizingMask:NSViewNotSizable];
+ }
+
+ if (view->transient_parent) {
+ NSView* pview = (NSView*)view->transient_parent;
+ [pview addSubview:impl->glview];
+ [impl->glview setHidden:NO];
+ } else {
+ NSString* titleString = [[NSString alloc]
+ initWithBytes:title
+ length:strlen(title)
+ encoding:NSUTF8StringEncoding];
+ NSRect frame = NSMakeRect(0, 0, view->min_width, view->min_height);
+ unsigned style = NSClosableWindowMask | NSTitledWindowMask;
+ if (view->hints.resizable) {
+ style |= NSResizableWindowMask;
+ }
+
+ id window = [[[PuglWindow alloc]
+ initWithContentRect:frame
+ styleMask:style
+ backing:NSBackingStoreBuffered
+ defer:NO
+ ] retain];
+ [window setPuglview:view];
+ [window setTitle:titleString];
+ if (view->min_width || view->min_height) {
+ [window setContentMinSize:NSMakeSize(view->min_width,
+ view->min_height)];
+ }
+ impl->window = window;
+
+ [window setContentView:impl->glview];
+ [impl->app activateIgnoringOtherApps:YES];
+ [window makeFirstResponder:impl->glview];
+ [window makeKeyAndOrderFront:window];
+ }
+
+ return 0;
+}
+
+void
+puglShowWindow(PuglView* view)
+{
+ [view->impl->window setIsVisible:YES];
+ view->visible = true;
+}
+
+void
+puglHideWindow(PuglView* view)
+{
+ [view->impl->window setIsVisible:NO];
+ view->visible = false;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+#ifdef PUGL_HAVE_CAIRO
+ pugl_cairo_gl_free(&view->impl->cairo_gl);
+#endif
+ view->impl->glview->puglview = NULL;
+ [view->impl->glview removeFromSuperview];
+ if (view->impl->window) {
+ [view->impl->window close];
+ }
+ [view->impl->glview release];
+ if (view->impl->window) {
+ [view->impl->window release];
+ }
+ free(view->windowClass);
+ free(view->impl);
+ free(view);
+}
+
+void
+puglGrabFocus(PuglView* view)
+{
+ // TODO
+}
+
+PuglStatus
+puglWaitForEvent(PuglView* view)
+{
+ /* OSX supposedly has queue: and untilDate: selectors that can be used for
+ a blocking non-queueing event check, but if used here cause an
+ unsupported selector error at runtime. I have no idea why, so just get
+ the event and keep it around until the call to puglProcessEvents. */
+ if (!view->impl->nextEvent) {
+ view->impl->nextEvent = [view->impl->window
+ nextEventMatchingMask: NSAnyEventMask];
+ }
+
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ while (true) {
+ // Get the next event, or use the cached one from puglWaitForEvent
+ if (!view->impl->nextEvent) {
+ view->impl->nextEvent = [view->impl->window
+ nextEventMatchingMask: NSAnyEventMask];
+ }
+
+ if (!view->impl->nextEvent) {
+ break; // No events to process, done
+ }
+
+ // Dispatch event
+ [view->impl->app sendEvent: view->impl->nextEvent];
+ view->impl->nextEvent = NULL;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+PuglGlFunc
+puglGetProcAddress(const char *name)
+{
+ CFBundleRef framework =
+ CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+
+ CFStringRef symbol = CFStringCreateWithCString(
+ kCFAllocatorDefault, name, kCFStringEncodingASCII);
+
+ PuglGlFunc func = CFBundleGetFunctionPointerForName(framework, symbol);
+
+ CFRelease(symbol);
+
+ return func;
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ //view->redisplay = true; // unused
+ [view->impl->glview setNeedsDisplay: YES];
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return (PuglNativeWindow)view->impl->glview;
+}
+
+void*
+puglGetContext(PuglView* view)
+{
+#ifdef PUGL_HAVE_CAIRO
+ if (view->ctx_type & PUGL_CAIRO) {
+ return view->impl->cr;
+ }
+#endif
+ return NULL;
+}
diff --git a/pugl/pugl_win.cpp b/pugl/pugl_win.cpp
new file mode 100644
index 0000000..048c0c3
--- /dev/null
+++ b/pugl/pugl_win.cpp
@@ -0,0 +1,650 @@
+/*
+ Copyright 2012-2015 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 pugl_win.cpp Windows/WGL Pugl Implementation.
+*/
+
+#include "pugl/pugl_internal.h"
+
+#include <windows.h>
+#include <windowsx.h>
+#include <GL/gl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wctype.h>
+
+#ifndef WM_MOUSEWHEEL
+# define WM_MOUSEWHEEL 0x020A
+#endif
+#ifndef WM_MOUSEHWHEEL
+# define WM_MOUSEHWHEEL 0x020E
+#endif
+#ifndef WHEEL_DELTA
+# define WHEEL_DELTA 120
+#endif
+#ifdef _WIN64
+# ifndef GWLP_USERDATA
+# define GWLP_USERDATA (-21)
+# endif
+#else
+# ifndef GWL_USERDATA
+# define GWL_USERDATA (-21)
+# endif
+#endif
+
+#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
+
+struct PuglInternalsImpl {
+ HWND hwnd;
+ HDC hdc;
+ HGLRC hglrc;
+ WNDCLASS wc;
+};
+
+LRESULT CALLBACK
+wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+PuglView*
+puglInit()
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = 640;
+ view->height = 480;
+
+ return view;
+}
+
+PuglInternals*
+puglInitInternals(void)
+{
+ return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+}
+
+void
+puglEnterContext(PuglView* view)
+{
+ PAINTSTRUCT ps;
+ BeginPaint(view->impl->hwnd, &ps);
+
+#ifdef PUGL_HAVE_GL
+ if (view->ctx_type == PUGL_GL) {
+ wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
+ }
+#endif
+}
+
+void
+puglLeaveContext(PuglView* view, bool flush)
+{
+#ifdef PUGL_HAVE_GL
+ if (view->ctx_type == PUGL_GL && flush) {
+ glFlush();
+ SwapBuffers(view->impl->hdc);
+ }
+#endif
+
+ PAINTSTRUCT ps;
+ EndPaint(view->impl->hwnd, &ps);
+}
+
+int
+puglCreateWindow(PuglView* view, const char* title)
+{
+ static const TCHAR* DEFAULT_CLASSNAME = "Pugl";
+
+ PuglInternals* impl = view->impl;
+
+ if (!title) {
+ title = "Window";
+ }
+
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.style = CS_OWNDC;
+ wc.lpfnWndProc = wndProc;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // TODO: user-specified icon
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ wc.lpszClassName = view->windowClass ? view->windowClass : DEFAULT_CLASSNAME;
+ if (!RegisterClassEx(&wc)) {
+ free((void*)impl->wc.lpszClassName);
+ free(impl);
+ free(view);
+ return NULL;
+ }
+
+ int winFlags = WS_POPUPWINDOW | WS_CAPTION;
+ if (view->hints.resizable) {
+ winFlags |= WS_SIZEBOX;
+ if (view->min_width || view->min_height) {
+ // Adjust the minimum window size to accomodate requested view size
+ RECT mr = { 0, 0, view->min_width, view->min_height };
+ AdjustWindowRectEx(&mr, winFlags, FALSE, WS_EX_TOPMOST);
+ view->min_width = mr.right - mr.left;
+ view->min_height = mr.bottom - mr.top;
+ }
+ }
+
+ // Adjust the window size to accomodate requested view size
+ RECT wr = { 0, 0, view->width, view->height };
+ AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
+
+ impl->hwnd = CreateWindowEx(
+ WS_EX_TOPMOST,
+ wc.lpszClassName, title,
+ (view->parent ? WS_CHILD : winFlags),
+ CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top,
+ (HWND)view->parent, NULL, NULL, NULL);
+
+ if (!impl->hwnd) {
+ free((void*)impl->wc.lpszClassName);
+ free(impl);
+ free(view);
+ return 1;
+ }
+
+#ifdef _WIN64
+ SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
+#else
+ SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view);
+#endif
+
+ impl->hdc = GetDC(impl->hwnd);
+
+ PIXELFORMATDESCRIPTOR pfd;
+ ZeroMemory(&pfd, sizeof(pfd));
+ pfd.nSize = sizeof(pfd);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.cDepthBits = 16;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+
+ int format = ChoosePixelFormat(impl->hdc, &pfd);
+ SetPixelFormat(impl->hdc, format, &pfd);
+
+ impl->hglrc = wglCreateContext(impl->hdc);
+ if (!impl->hglrc) {
+ ReleaseDC(impl->hwnd, impl->hdc);
+ DestroyWindow(impl->hwnd);
+ UnregisterClass(impl->wc.lpszClassName, NULL);
+ free((void*)impl->wc.lpszClassName);
+ free(impl);
+ free(view);
+ return NULL;
+ }
+ wglMakeCurrent(impl->hdc, impl->hglrc);
+
+ return 0;
+}
+
+void
+puglShowWindow(PuglView* view)
+{
+ PuglInternals* impl = view->impl;
+
+ ShowWindow(impl->hwnd, SW_SHOWNORMAL);
+ view->visible = true;
+}
+
+void
+puglHideWindow(PuglView* view)
+{
+ PuglInternals* impl = view->impl;
+
+ ShowWindow(impl->hwnd, SW_HIDE);
+ view->visible = false;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(view->impl->hglrc);
+ ReleaseDC(view->impl->hwnd, view->impl->hdc);
+ DestroyWindow(view->impl->hwnd);
+ UnregisterClass(view->impl->wc.lpszClassName, NULL);
+ free(view->windowClass);
+ free(view->impl);
+ free(view);
+}
+
+static PuglKey
+keySymToSpecial(int sym)
+{
+ switch (sym) {
+ case VK_F1: return PUGL_KEY_F1;
+ case VK_F2: return PUGL_KEY_F2;
+ case VK_F3: return PUGL_KEY_F3;
+ case VK_F4: return PUGL_KEY_F4;
+ case VK_F5: return PUGL_KEY_F5;
+ case VK_F6: return PUGL_KEY_F6;
+ case VK_F7: return PUGL_KEY_F7;
+ case VK_F8: return PUGL_KEY_F8;
+ case VK_F9: return PUGL_KEY_F9;
+ case VK_F10: return PUGL_KEY_F10;
+ case VK_F11: return PUGL_KEY_F11;
+ case VK_F12: return PUGL_KEY_F12;
+ case VK_LEFT: return PUGL_KEY_LEFT;
+ case VK_UP: return PUGL_KEY_UP;
+ case VK_RIGHT: return PUGL_KEY_RIGHT;
+ case VK_DOWN: return PUGL_KEY_DOWN;
+ case VK_PRIOR: return PUGL_KEY_PAGE_UP;
+ case VK_NEXT: return PUGL_KEY_PAGE_DOWN;
+ case VK_HOME: return PUGL_KEY_HOME;
+ case VK_END: return PUGL_KEY_END;
+ case VK_INSERT: return PUGL_KEY_INSERT;
+ case VK_SHIFT: return PUGL_KEY_SHIFT;
+ case VK_CONTROL: return PUGL_KEY_CTRL;
+ case VK_MENU: return PUGL_KEY_ALT;
+ case VK_LWIN: return PUGL_KEY_SUPER;
+ case VK_RWIN: return PUGL_KEY_SUPER;
+ }
+ return (PuglKey)0;
+}
+
+static unsigned int
+getModifiers()
+{
+ unsigned int mods = 0;
+ mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0;
+ mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0;
+ mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0;
+ mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0;
+ mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0;
+ return mods;
+}
+
+static void
+initMouseEvent(PuglEvent* event,
+ PuglView* view,
+ int button,
+ bool press,
+ LPARAM lParam)
+{
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ ClientToScreen(view->impl->hwnd, &pt);
+
+ if (press) {
+ SetCapture(view->impl->hwnd);
+ } else {
+ ReleaseCapture();
+ }
+
+ event->button.time = GetMessageTime();
+ event->button.type = press ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE;
+ event->button.x = GET_X_LPARAM(lParam);
+ event->button.y = GET_Y_LPARAM(lParam);
+ event->button.x_root = pt.x;
+ event->button.y_root = pt.y;
+ event->button.state = getModifiers();
+ event->button.button = button;
+}
+
+static void
+initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam, WPARAM wParam)
+{
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ ScreenToClient(view->impl->hwnd, &pt);
+
+ event->scroll.time = GetMessageTime();
+ event->scroll.type = PUGL_SCROLL;
+ event->scroll.x = pt.x;
+ event->scroll.y = pt.y;
+ event->scroll.x_root = GET_X_LPARAM(lParam);
+ event->scroll.y_root = GET_Y_LPARAM(lParam);
+ event->scroll.state = getModifiers();
+ event->scroll.dx = 0;
+ event->scroll.dy = 0;
+}
+
+static unsigned int
+utf16_to_code_point(const wchar_t* input, size_t input_size)
+{
+ unsigned int code_unit = *input;
+ // Equiv. range check between 0xD800 to 0xDBFF inclusive
+ if ((code_unit & 0xFC00) == 0xD800) {
+ if (input_size < 2) {
+ // "Error: is surrogate but input_size too small"
+ return 0xFFFD; // replacement character
+ }
+
+ unsigned int code_unit_2 = *++input;
+ // Equiv. range check between 0xDC00 to 0xDFFF inclusive
+ if ((code_unit_2 & 0xFC00) == 0xDC00) {
+ return (code_unit << 10) + code_unit_2 - 0x35FDC00;
+ }
+
+ // TODO: push_back(code_unit_2);
+ // "Error: Unpaired surrogates."
+ return 0xFFFD; // replacement character
+ }
+ return code_unit;
+}
+
+static void
+initKeyEvent(PuglEvent* event, PuglView* view, bool press, LPARAM lParam)
+{
+ POINT rpos = { 0, 0 };
+ GetCursorPos(&rpos);
+
+ POINT cpos = { rpos.x, rpos.y };
+ ScreenToClient(view->impl->hwnd, &rpos);
+
+ event->key.type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ event->key.time = GetMessageTime();
+ event->key.state = getModifiers();
+ event->key.x_root = rpos.x;
+ event->key.y_root = rpos.y;
+ event->key.x = cpos.x;
+ event->key.y = cpos.y;
+ event->key.keycode = (lParam & 0xFF0000) >> 16;
+ event->key.character = 0;
+ event->key.special = static_cast<PuglKey>(0);
+ event->key.filter = 0;
+}
+
+static void
+wcharBufToEvent(wchar_t* buf, int n, PuglEvent* event)
+{
+ if (n > 0) {
+ char* charp = reinterpret_cast<char*>(event->key.utf8);
+ if (!WideCharToMultiByte(CP_UTF8, 0, buf, n,
+ charp, 8, NULL, NULL)) {
+ /* error: could not convert to utf-8,
+ GetLastError has details */
+ memset(event->key.utf8, 0, 8);
+ // replacement character
+ event->key.utf8[0] = 0xEF;
+ event->key.utf8[1] = 0xBF;
+ event->key.utf8[2] = 0xBD;
+ }
+
+ event->key.character = utf16_to_code_point(buf, n);
+ } else {
+ // replacement character
+ event->key.utf8[0] = 0xEF;
+ event->key.utf8[1] = 0xBF;
+ event->key.utf8[2] = 0xBD;
+ event->key.character = 0xFFFD;
+ }
+}
+
+static void
+translateMessageParamsToEvent(LPARAM lParam, WPARAM wParam, PuglEvent* event)
+{
+ /* TODO: This is a kludge. Would be nice to use ToUnicode here, but this
+ breaks composed keys because it messes with the keyboard state. Not
+ sure how to correctly handle this on Windows. */
+
+ // This is how I really want to do this, but it breaks composed keys (é,
+ // è, ü, ö, and so on) because ToUnicode messes with the keyboard state.
+
+ //wchar_t buf[5];
+ //BYTE keyboard_state[256];
+ //int wcharCount = 0;
+ //GetKeyboardState(keyboard_state);
+ //wcharCount = ToUnicode(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC),
+ // keyboard_state, buf, 4, 0);
+ //wcharBufToEvent(buf, wcharCount, event);
+
+ // So, since Google refuses to give me a better solution, and if no one
+ // else has a better solution, I will make a hack...
+ wchar_t buf[5] = { 0, 0, 0, 0, 0 };
+ UINT c = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
+ buf[0] = c & 0xffff;
+ // TODO: This does not take caps lock into account
+ // TODO: Dead keys should affect key releases as well
+ if (!(event->key.state && PUGL_MOD_SHIFT))
+ buf[0] = towlower(buf[0]);
+ wcharBufToEvent(buf, 1, event);
+ event->key.filter = ((c >> 31) & 0x1);
+}
+
+static void
+translateCharEventToEvent(WPARAM wParam, PuglEvent* event)
+{
+ wchar_t buf[2];
+ int wcharCount;
+ if (wParam & 0xFFFF0000) {
+ wcharCount = 2;
+ buf[0] = (wParam & 0xFFFF);
+ buf[1] = ((wParam >> 16) & 0xFFFF);
+ } else {
+ wcharCount = 1;
+ buf[0] = (wParam & 0xFFFF);
+ }
+ wcharBufToEvent(buf, wcharCount, event);
+}
+
+static bool
+ignoreKeyEvent(PuglView* view, LPARAM lParam)
+{
+ return view->ignoreKeyRepeat && (lParam & (1 << 30));
+}
+
+static LRESULT
+handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PuglEvent event;
+ void* dummy_ptr = NULL;
+ RECT rect;
+ MINMAXINFO* mmi;
+ POINT pt;
+ bool dispatchThisEvent = true;
+
+ memset(&event, 0, sizeof(event));
+
+ event.any.type = PUGL_NOTHING;
+ event.any.view = view;
+ if (InSendMessageEx(dummy_ptr)) {
+ event.any.flags |= PUGL_IS_SEND_EVENT;
+ }
+
+ switch (message) {
+ case WM_CREATE:
+ case WM_SHOWWINDOW:
+ case WM_SIZE:
+ GetWindowRect(view->impl->hwnd, &rect);
+ event.configure.type = PUGL_CONFIGURE;
+ event.configure.x = rect.left;
+ event.configure.y = rect.top;
+ view->width = rect.right - rect.left;
+ view->height = rect.bottom - rect.top;
+ event.configure.width = view->width;
+ event.configure.height = view->height;
+ break;
+ case WM_GETMINMAXINFO:
+ mmi = (MINMAXINFO*)lParam;
+ mmi->ptMinTrackSize.x = view->min_width;
+ mmi->ptMinTrackSize.y = view->min_height;
+ break;
+ case WM_PAINT:
+ GetUpdateRect(view->impl->hwnd, &rect, false);
+ event.expose.type = PUGL_EXPOSE;
+ event.expose.x = rect.left;
+ event.expose.y = rect.top;
+ event.expose.width = rect.right - rect.left;
+ event.expose.height = rect.bottom - rect.top;
+ event.expose.count = 0;
+ break;
+ case WM_MOUSEMOVE:
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ ClientToScreen(view->impl->hwnd, &pt);
+
+ event.motion.type = PUGL_MOTION_NOTIFY;
+ event.motion.time = GetMessageTime();
+ event.motion.x = GET_X_LPARAM(lParam);
+ event.motion.y = GET_Y_LPARAM(lParam);
+ event.motion.x_root = pt.x;
+ event.motion.y_root = pt.y;
+ event.motion.state = getModifiers();
+ event.motion.is_hint = false;
+ break;
+ case WM_LBUTTONDOWN:
+ initMouseEvent(&event, view, 1, true, lParam);
+ break;
+ case WM_MBUTTONDOWN:
+ initMouseEvent(&event, view, 2, true, lParam);
+ break;
+ case WM_RBUTTONDOWN:
+ initMouseEvent(&event, view, 3, true, lParam);
+ break;
+ case WM_LBUTTONUP:
+ initMouseEvent(&event, view, 1, false, lParam);
+ break;
+ case WM_MBUTTONUP:
+ initMouseEvent(&event, view, 2, false, lParam);
+ break;
+ case WM_RBUTTONUP:
+ initMouseEvent(&event, view, 3, false, lParam);
+ break;
+ case WM_MOUSEWHEEL:
+ initScrollEvent(&event, view, lParam, wParam);
+ event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ break;
+ case WM_MOUSEHWHEEL:
+ initScrollEvent(&event, view, lParam, wParam);
+ event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ break;
+ case WM_KEYDOWN:
+ if (!ignoreKeyEvent(view, lParam)) {
+ initKeyEvent(&event, view, true, lParam);
+ if (!(event.key.special = keySymToSpecial(wParam))) {
+ event.key.type = PUGL_NOTHING;
+ }
+ }
+ break;
+ case WM_CHAR:
+ if (!ignoreKeyEvent(view, lParam)) {
+ initKeyEvent(&event, view, true, lParam);
+ translateCharEventToEvent(wParam, &event);
+ }
+ break;
+ case WM_DEADCHAR:
+ if (!ignoreKeyEvent(view, lParam)) {
+ initKeyEvent(&event, view, true, lParam);
+ translateCharEventToEvent(wParam, &event);
+ event.key.filter = 1;
+ }
+ break;
+ case WM_KEYUP:
+ initKeyEvent(&event, view, false, lParam);
+ if (!(event.key.special = keySymToSpecial(wParam))) {
+ translateMessageParamsToEvent(lParam, wParam, &event);
+ }
+ break;
+ case WM_QUIT:
+ case PUGL_LOCAL_CLOSE_MSG:
+ event.close.type = PUGL_CLOSE;
+ break;
+ default:
+ return DefWindowProc(
+ view->impl->hwnd, message, wParam, lParam);
+ }
+
+ puglDispatchEvent(view, &event);
+
+ return 0;
+}
+
+void
+puglGrabFocus(PuglView* view)
+{
+ // TODO
+}
+
+PuglStatus
+puglWaitForEvent(PuglView* view)
+{
+ WaitMessage();
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ MSG msg;
+ while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ handleMessage(view, msg.message, msg.wParam, msg.lParam);
+ }
+
+ if (view->redisplay) {
+ InvalidateRect(view->impl->hwnd, NULL, FALSE);
+ view->redisplay = false;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+LRESULT CALLBACK
+wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+#ifdef _WIN64
+ PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+#else
+ PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA);
+#endif
+
+ switch (message) {
+ case WM_CREATE:
+ PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
+ return 0;
+ case WM_CLOSE:
+ PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam);
+ return 0;
+ case WM_DESTROY:
+ return 0;
+ default:
+ if (view && hwnd == view->impl->hwnd) {
+ return handleMessage(view, message, wParam, lParam);
+ } else {
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ }
+}
+
+PuglGlFunc
+puglGetProcAddress(const char* name)
+{
+ return (PuglGlFunc)wglGetProcAddress(name);
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return (PuglNativeWindow)view->impl->hwnd;
+}
diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c
new file mode 100644
index 0000000..6315759
--- /dev/null
+++ b/pugl/pugl_x11.c
@@ -0,0 +1,535 @@
+/*
+ Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2013 Robin Gareus <robin@gareus.org>
+ Copyright 2011-2012 Ben Loftis, Harrison Consoles
+
+ 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 pugl_x11.c X11 Pugl Implementation.
+*/
+
+
+#include "pugl/pugl_internal.h"
+#include "pugl/pugl_x11.h"
+#ifdef PUGL_HAVE_GL
+#include "pugl/pugl_x11_gl.h"
+#endif
+#ifdef PUGL_HAVE_CAIRO
+#include "pugl/pugl_x11_cairo.h"
+#endif
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef MIN
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+PuglInternals*
+puglInitInternals(void)
+{
+ return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+}
+
+void
+puglEnterContext(PuglView* view)
+{
+ view->impl->ctx.enter(view);
+}
+
+void
+puglLeaveContext(PuglView* view, bool flush)
+{
+ view->impl->ctx.leave(view, flush);
+}
+
+int
+puglCreateWindow(PuglView* view, const char* title)
+{
+ PuglInternals* const impl = view->impl;
+
+ impl->display = XOpenDisplay(0);
+ impl->screen = DefaultScreen(impl->display);
+
+ if (view->ctx_type == PUGL_GL) {
+#ifdef PUGL_HAVE_GL
+ impl->ctx = puglGetX11GlDrawContext();
+#endif
+ }
+ if (view->ctx_type == PUGL_CAIRO) {
+#ifdef PUGL_HAVE_CAIRO
+ impl->ctx = puglGetX11CairoDrawContext();
+#endif
+ }
+
+ if (!impl->ctx.configure) {
+ return 1;
+ }
+
+ if (impl->ctx.configure(view) || !impl->vi) {
+ impl->ctx.destroy(view);
+ return 2;
+ }
+
+ Window xParent = view->parent
+ ? (Window)view->parent
+ : RootWindow(impl->display, impl->screen);
+
+ Colormap cmap = XCreateColormap(
+ impl->display, xParent, impl->vi->visual, AllocNone);
+
+ XSetWindowAttributes attr;
+ memset(&attr, 0, sizeof(XSetWindowAttributes));
+ attr.colormap = cmap;
+ attr.event_mask = (ExposureMask | StructureNotifyMask |
+ EnterWindowMask | LeaveWindowMask |
+ KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | FocusChangeMask);
+
+ impl->win = XCreateWindow(
+ impl->display, xParent,
+ 0, 0, view->width, view->height, 0, impl->vi->depth, InputOutput,
+ impl->vi->visual, CWColormap | CWEventMask, &attr);
+
+ if (impl->ctx.create(view)) {
+ return 3;
+ }
+
+ XSizeHints sizeHints;
+ memset(&sizeHints, 0, sizeof(sizeHints));
+ if (!view->hints.resizable) {
+ sizeHints.flags = PMinSize|PMaxSize;
+ sizeHints.min_width = view->width;
+ sizeHints.min_height = view->height;
+ sizeHints.max_width = view->width;
+ sizeHints.max_height = view->height;
+ XSetNormalHints(impl->display, impl->win, &sizeHints);
+ } else {
+ if (view->min_width || view->min_height) {
+ sizeHints.flags = PMinSize;
+ sizeHints.min_width = view->min_width;
+ sizeHints.min_height = view->min_height;
+ }
+ if (view->min_aspect_x) {
+ sizeHints.flags |= PAspect;
+ sizeHints.min_aspect.x = view->min_aspect_x;
+ sizeHints.min_aspect.y = view->min_aspect_y;
+ sizeHints.max_aspect.x = view->max_aspect_x;
+ sizeHints.max_aspect.y = view->max_aspect_y;
+ }
+
+ XSetNormalHints(impl->display, impl->win, &sizeHints);
+ }
+
+ if (title) {
+ XStoreName(impl->display, impl->win, title);
+ }
+
+ if (!view->parent) {
+ Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True);
+ XSetWMProtocols(impl->display, impl->win, &wmDelete, 1);
+ }
+
+ if (view->transient_parent) {
+ XSetTransientForHint(impl->display, impl->win,
+ (Window)(view->transient_parent));
+ }
+
+ XSetLocaleModifiers("");
+ if (!(impl->xim = XOpenIM(impl->display, NULL, NULL, NULL))) {
+ XSetLocaleModifiers("@im=");
+ if (!(impl->xim = XOpenIM(impl->display, NULL, NULL, NULL))) {
+ fprintf(stderr, "warning: XOpenIM failed\n");
+ }
+ }
+
+ const XIMStyle im_style = XIMPreeditNothing | XIMStatusNothing;
+ if (!(impl->xic = XCreateIC(impl->xim,
+ XNInputStyle, im_style,
+ XNClientWindow, impl->win,
+ XNFocusWindow, impl->win,
+ NULL))) {
+ fprintf(stderr, "warning: XCreateIC failed\n");
+ }
+
+ return 0;
+}
+
+void
+puglShowWindow(PuglView* view)
+{
+ XMapRaised(view->impl->display, view->impl->win);
+ view->visible = true;
+}
+
+void
+puglHideWindow(PuglView* view)
+{
+ XUnmapWindow(view->impl->display, view->impl->win);
+ view->visible = false;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ if (view) {
+ view->impl->ctx.destroy(view);
+ XDestroyWindow(view->impl->display, view->impl->win);
+ XCloseDisplay(view->impl->display);
+ XFree(view->impl->vi);
+ free(view->windowClass);
+ free(view->impl);
+ free(view);
+ }
+}
+
+static PuglKey
+keySymToSpecial(KeySym sym)
+{
+ switch (sym) {
+ case XK_F1: return PUGL_KEY_F1;
+ case XK_F2: return PUGL_KEY_F2;
+ case XK_F3: return PUGL_KEY_F3;
+ case XK_F4: return PUGL_KEY_F4;
+ case XK_F5: return PUGL_KEY_F5;
+ case XK_F6: return PUGL_KEY_F6;
+ case XK_F7: return PUGL_KEY_F7;
+ case XK_F8: return PUGL_KEY_F8;
+ case XK_F9: return PUGL_KEY_F9;
+ case XK_F10: return PUGL_KEY_F10;
+ case XK_F11: return PUGL_KEY_F11;
+ case XK_F12: return PUGL_KEY_F12;
+ case XK_Left: return PUGL_KEY_LEFT;
+ case XK_Up: return PUGL_KEY_UP;
+ case XK_Right: return PUGL_KEY_RIGHT;
+ case XK_Down: return PUGL_KEY_DOWN;
+ case XK_Page_Up: return PUGL_KEY_PAGE_UP;
+ case XK_Page_Down: return PUGL_KEY_PAGE_DOWN;
+ case XK_Home: return PUGL_KEY_HOME;
+ case XK_End: return PUGL_KEY_END;
+ case XK_Insert: return PUGL_KEY_INSERT;
+ case XK_Shift_L: return PUGL_KEY_SHIFT;
+ case XK_Shift_R: return PUGL_KEY_SHIFT;
+ case XK_Control_L: return PUGL_KEY_CTRL;
+ case XK_Control_R: return PUGL_KEY_CTRL;
+ case XK_Alt_L: return PUGL_KEY_ALT;
+ case XK_Alt_R: return PUGL_KEY_ALT;
+ case XK_Super_L: return PUGL_KEY_SUPER;
+ case XK_Super_R: return PUGL_KEY_SUPER;
+ default: return (PuglKey)0;
+ }
+ return (PuglKey)0;
+}
+
+static void
+translateKey(PuglView* view, XEvent* xevent, PuglEvent* event)
+{
+ KeySym sym = 0;
+ char* str = (char*)event->key.utf8;
+ memset(str, 0, 8);
+ event->key.filter = XFilterEvent(xevent, None);
+ if (xevent->type == KeyRelease || event->key.filter || !view->impl->xic) {
+ if (XLookupString(&xevent->xkey, str, 7, &sym, NULL) == 1) {
+ event->key.character = str[0];
+ }
+ } else {
+ /* TODO: Not sure about this. On my system, some characters work with
+ Xutf8LookupString but not with XmbLookupString, and some are the
+ opposite. */
+ Status status = 0;
+#ifdef X_HAVE_UTF8_STRING
+ const int n = Xutf8LookupString(
+ view->impl->xic, &xevent->xkey, str, 7, &sym, &status);
+#else
+ const int n = XmbLookupString(
+ view->impl->xic, &xevent->xkey, str, 7, &sym, &status);
+#endif
+ if (n > 0) {
+ event->key.character = puglDecodeUTF8((const uint8_t*)str);
+ }
+ }
+ event->key.special = keySymToSpecial(sym);
+ event->key.keycode = xevent->xkey.keycode;
+}
+
+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 PuglEvent
+translateEvent(PuglView* view, XEvent xevent)
+{
+ PuglEvent event;
+ memset(&event, 0, sizeof(event));
+
+ event.any.view = view;
+ if (xevent.xany.send_event) {
+ event.any.flags |= PUGL_IS_SEND_EVENT;
+ }
+
+ switch (xevent.type) {
+ case ClientMessage: {
+ char* type = XGetAtomName(view->impl->display,
+ xevent.xclient.message_type);
+ if (!strcmp(type, "WM_PROTOCOLS")) {
+ event.type = PUGL_CLOSE;
+ }
+ break;
+ }
+ 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;
+ }
+ // fallthru
+ }
+ // fallthru
+ 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.xkey.time;
+ event.key.x = xevent.xkey.x;
+ event.key.y = xevent.xkey.y;
+ event.key.x_root = xevent.xkey.x_root;
+ event.key.y_root = xevent.xkey.y_root;
+ event.key.state = translateModifiers(xevent.xkey.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;
+
+ case FocusIn:
+ case FocusOut:
+ event.type = ((xevent.type == FocusIn)
+ ? PUGL_FOCUS_IN
+ : PUGL_FOCUS_OUT);
+ event.focus.grab = (xevent.xfocus.mode != NotifyNormal);
+ break;
+
+ default:
+ break;
+ }
+
+ return event;
+}
+
+void
+puglGrabFocus(PuglView* view)
+{
+ XSetInputFocus(
+ view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime);
+}
+
+PuglStatus
+puglWaitForEvent(PuglView* view)
+{
+ XEvent xevent;
+ XPeekEvent(view->impl->display, &xevent);
+ return PUGL_SUCCESS;
+}
+
+static void
+merge_expose_events(PuglEvent* dst, const PuglEvent* src)
+{
+ if (!dst->type) {
+ *dst = *src;
+ } else {
+ const double max_x = MAX(dst->expose.x + dst->expose.width,
+ src->expose.x + src->expose.width);
+ const double max_y = MAX(dst->expose.y + dst->expose.height,
+ src->expose.y + src->expose.height);
+
+ dst->expose.x = MIN(dst->expose.x, src->expose.x);
+ 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);
+ }
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ /* Maintain a single expose/configure event to execute after all pending
+ events. This avoids redundant drawing/configuration which prevents a
+ series of window resizes in the same loop from being laggy. */
+ PuglEvent expose_event = { 0 };
+ PuglEvent config_event = { 0 };
+ XEvent xevent;
+ while (XPending(view->impl->display) > 0) {
+ XNextEvent(view->impl->display, &xevent);
+ 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 == xevent.xkey.time &&
+ next.xkey.keycode == xevent.xkey.keycode) {
+ XNextEvent(view->impl->display, &xevent);
+ continue;
+ }
+ }
+ } else if (xevent.type == FocusIn) {
+ XSetICFocus(view->impl->xic);
+ } else if (xevent.type == FocusOut) {
+ XUnsetICFocus(view->impl->xic);
+ }
+
+ // Translate X11 event to Pugl event
+ const PuglEvent event = translateEvent(view, xevent);
+
+ if (event.type == PUGL_EXPOSE) {
+ // Expand expose event to be dispatched after loop
+ merge_expose_events(&expose_event, &event);
+ } else if (event.type == PUGL_CONFIGURE) {
+ // Expand configure event to be dispatched after loop
+ config_event = event;
+ } else {
+ // Dispatch event to application immediately
+ puglDispatchEvent(view, &event);
+ }
+ }
+
+ if (config_event.type) {
+ // Resize drawing context before dispatching
+ view->impl->ctx.resize(view,
+ (int)config_event.configure.width,
+ (int)config_event.configure.height);
+ puglDispatchEvent(view, (const PuglEvent*)&config_event);
+ }
+
+ if (view->redisplay) {
+ expose_event.expose.type = PUGL_EXPOSE;
+ expose_event.expose.view = view;
+ expose_event.expose.x = 0;
+ expose_event.expose.y = 0;
+ expose_event.expose.width = view->width;
+ expose_event.expose.height = view->height;
+ view->redisplay = false;
+ }
+
+ if (expose_event.type) {
+ puglDispatchEvent(view, (const PuglEvent*)&expose_event);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return view->impl->win;
+}
+
+void*
+puglGetContext(PuglView* view)
+{
+ return view->impl->ctx.getHandle(view);
+}
diff --git a/pugl/pugl_x11.h b/pugl/pugl_x11.h
new file mode 100644
index 0000000..9c53c49
--- /dev/null
+++ b/pugl/pugl_x11.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_internal_types.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+struct PuglInternalsImpl {
+ Display* display;
+ int screen;
+ XVisualInfo* vi;
+ Window win;
+ XIM xim;
+ XIC xic;
+ PuglDrawContext ctx;
+ PuglSurface* surface;
+};
diff --git a/pugl/pugl_x11_cairo.c b/pugl/pugl_x11_cairo.c
new file mode 100644
index 0000000..db2e262
--- /dev/null
+++ b/pugl/pugl_x11_cairo.c
@@ -0,0 +1,140 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#include "pugl/pugl_internal_types.h"
+#include "pugl/pugl_x11.h"
+#include "pugl/pugl_x11_cairo.h"
+
+#include <X11/Xutil.h>
+#include <cairo/cairo-xlib.h>
+#include <cairo/cairo.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct {
+ cairo_surface_t* surface;
+ cairo_t* cr;
+} PuglX11CairoSurface;
+
+static int
+puglX11CairoConfigure(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+
+ XVisualInfo pat;
+ int n;
+ pat.screen = impl->screen;
+ impl->vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n);
+
+ return 0;
+}
+
+static int
+puglX11CairoCreate(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface =
+ (PuglX11CairoSurface*)calloc(1, sizeof(PuglX11CairoSurface));
+
+ impl->surface = surface;
+
+ if (view->ctx_type == PUGL_CAIRO) {
+ surface->surface = cairo_xlib_surface_create(
+ impl->display, impl->win, impl->vi->visual, view->width, view->height);
+ }
+
+ if (!surface->surface) {
+ return 1;
+ }
+
+ int st = cairo_surface_status(surface->surface);
+ if (st) {
+ fprintf(stderr, "error: failed to create cairo surface (%s)\n",
+ cairo_status_to_string(st));
+ } else if (!(surface->cr = cairo_create(surface->surface))) {
+ fprintf(stderr, "error: failed to create cairo context\n");
+ } else if ((st = cairo_status(surface->cr))) {
+ cairo_surface_destroy(surface->surface);
+ fprintf(stderr, "error: cairo context is invalid (%s)\n",
+ cairo_status_to_string(st));
+ }
+ return st;
+}
+
+static int
+puglX11CairoDestroy(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ cairo_destroy(surface->cr);
+ cairo_surface_destroy(surface->surface);
+ free(surface);
+ impl->surface = NULL;
+ return 0;
+}
+
+static int
+puglX11CairoEnter(PuglView* view)
+{
+ return 0;
+}
+
+static int
+puglX11CairoLeave(PuglView* view, bool flush)
+{
+ return 0;
+}
+
+static int
+puglX11CairoResize(PuglView* view, int width, int height)
+{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ view->redisplay = true;
+
+ if (view->ctx_type == PUGL_CAIRO) {
+ cairo_xlib_surface_set_size(surface->surface, width, height);
+ }
+
+ return 0;
+}
+
+static void*
+puglX11CairoGetHandle(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ return surface->cr;
+}
+
+PuglDrawContext puglGetX11CairoDrawContext(void)
+{
+ static const PuglDrawContext puglX11CairoDrawContext = {
+ puglX11CairoConfigure,
+ puglX11CairoCreate,
+ puglX11CairoDestroy,
+ puglX11CairoEnter,
+ puglX11CairoLeave,
+ puglX11CairoResize,
+ puglX11CairoGetHandle
+ };
+
+ return puglX11CairoDrawContext;
+}
diff --git a/pugl/pugl_x11_cairo.h b/pugl/pugl_x11_cairo.h
new file mode 100644
index 0000000..ffad90b
--- /dev/null
+++ b/pugl/pugl_x11_cairo.h
@@ -0,0 +1,22 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#ifndef PUGL_X11_CAIRO_H
+#define PUGL_X11_CAIRO_H
+
+PuglDrawContext puglGetX11CairoDrawContext(void);
+
+#endif // PUGL_X11_CAIRO_H
diff --git a/pugl/pugl_x11_gl.c b/pugl/pugl_x11_gl.c
new file mode 100644
index 0000000..f9e3ce2
--- /dev/null
+++ b/pugl/pugl_x11_gl.c
@@ -0,0 +1,208 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#include "pugl/pugl_internal_types.h"
+#include "pugl/pugl_x11.h"
+#include "pugl/pugl_x11_gl.h"
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct {
+ GLXFBConfig fb_config;
+ GLXContext ctx;
+ int double_buffered;
+} PuglX11GlSurface;
+
+static int
+puglX11GlHintValue(const int value)
+{
+ return value == PUGL_DONT_CARE ? (int)GLX_DONT_CARE : value;
+}
+
+static int
+puglX11GlGetAttrib(Display* const display,
+ const GLXFBConfig fb_config,
+ const int attrib)
+{
+ int value = 0;
+ glXGetFBConfigAttrib(display, fb_config, attrib, &value);
+ return value;
+}
+
+static int
+puglX11GlConfigure(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ const int screen = impl->screen;
+ Display* const display = impl->display;
+
+ PuglX11GlSurface* const surface =
+ (PuglX11GlSurface*)calloc(1, sizeof(PuglX11GlSurface));
+ impl->surface = surface;
+
+ const int attrs[] = {
+ GLX_X_RENDERABLE, True,
+ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_SAMPLES, view->hints.samples,
+ GLX_RED_SIZE, puglX11GlHintValue(view->hints.red_bits),
+ GLX_GREEN_SIZE, puglX11GlHintValue(view->hints.green_bits),
+ GLX_BLUE_SIZE, puglX11GlHintValue(view->hints.blue_bits),
+ GLX_ALPHA_SIZE, puglX11GlHintValue(view->hints.alpha_bits),
+ GLX_DEPTH_SIZE, puglX11GlHintValue(view->hints.depth_bits),
+ GLX_STENCIL_SIZE, puglX11GlHintValue(view->hints.stencil_bits),
+ GLX_DOUBLEBUFFER, (view->hints.samples
+ ? GLX_DONT_CARE
+ : view->hints.double_buffer),
+ None
+ };
+
+ int n_fbc = 0;
+ GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc);
+ if (n_fbc <= 0) {
+ fprintf(stderr, "error: Failed to create GL context\n");
+ return 1;
+ }
+
+ surface->fb_config = fbc[0];
+ impl->vi = glXGetVisualFromFBConfig(impl->display, fbc[0]);
+
+ printf("Using visual 0x%lX: R=%d G=%d B=%d A=%d D=%d"
+ " DOUBLE=%d SAMPLES=%d\n",
+ impl->vi->visualid,
+ puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER),
+ puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES));
+
+ return 0;
+}
+
+static int
+puglX11GlCreate(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglX11GlSurface* const surface = (PuglX11GlSurface*)impl->surface;
+ Display* const display = impl->display;
+ const GLXFBConfig fb_config = surface->fb_config;
+
+ const int ctx_attrs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, view->hints.context_version_major,
+ GLX_CONTEXT_MINOR_VERSION_ARB, view->hints.context_version_minor,
+ GLX_CONTEXT_PROFILE_MASK_ARB, (view->hints.use_compat_profile
+ ? GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
+ : GLX_CONTEXT_CORE_PROFILE_BIT_ARB),
+ 0};
+
+ typedef GLXContext (*CreateContextAttribs)(
+ Display*, GLXFBConfig, GLXContext, Bool, const int*);
+
+ CreateContextAttribs create_context =
+ (CreateContextAttribs)glXGetProcAddress(
+ (GLubyte*)"glXCreateContextAttribsARB");
+
+ impl->surface = surface;
+ surface->ctx = create_context(display, fb_config, 0, GL_TRUE, ctx_attrs);
+ if (!surface->ctx) {
+ surface->ctx =
+ glXCreateNewContext(display, fb_config, GLX_RGBA_TYPE, 0, True);
+ }
+
+ glXGetConfig(impl->display,
+ impl->vi,
+ GLX_DOUBLEBUFFER,
+ &surface->double_buffered);
+
+ return 0;
+}
+
+static int
+puglX11GlDestroy(PuglView* view)
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+ if (surface) {
+ glXDestroyContext(view->impl->display, surface->ctx);
+ free(surface);
+ view->impl->surface = NULL;
+ }
+ return 0;
+}
+
+static int
+puglX11GlEnter(PuglView* view)
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+ glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
+ return 0;
+}
+
+static int
+puglX11GlLeave(PuglView* view, bool flush)
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+
+ if (flush) {
+ glFlush();
+ }
+
+ glXMakeCurrent(view->impl->display, None, NULL);
+ if (surface->double_buffered) {
+ glXSwapBuffers(view->impl->display, view->impl->win);
+ }
+
+ return 0;
+}
+
+static int
+puglX11GlResize(PuglView* view, int width, int height)
+{
+ return 0;
+}
+
+static void*
+puglX11GlGetHandle(PuglView* view)
+{
+ return NULL;
+}
+
+PuglGlFunc
+puglGetProcAddress(const char* name)
+{
+ return glXGetProcAddress((const GLubyte*)name);
+}
+
+PuglDrawContext puglGetX11GlDrawContext(void)
+{
+ static const PuglDrawContext puglX11GlDrawContext = {
+ puglX11GlConfigure,
+ puglX11GlCreate,
+ puglX11GlDestroy,
+ puglX11GlEnter,
+ puglX11GlLeave,
+ puglX11GlResize,
+ puglX11GlGetHandle
+ };
+
+ return puglX11GlDrawContext;
+}
diff --git a/pugl/pugl_x11_gl.h b/pugl/pugl_x11_gl.h
new file mode 100644
index 0000000..cba6473
--- /dev/null
+++ b/pugl/pugl_x11_gl.h
@@ -0,0 +1,22 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#ifndef PUGL_X11_GL_H
+#define PUGL_X11_GL_H
+
+PuglDrawContext puglGetX11GlDrawContext(void);
+
+#endif // PUGL_X11_GL_H