| @@ -15,8 +15,7 @@ | |||
| */ | |||
| // we need this for now | |||
| #define PUGL_HAVE_GL 1 | |||
| #define PUGL_GRAB_FOCUS 1 | |||
| //#define PUGL_GRAB_FOCUS 1 | |||
| #include "AppPrivateData.hpp" | |||
| #include "../Widget.hpp" | |||
| @@ -63,7 +62,7 @@ struct Window::PrivateData { | |||
| PrivateData(App& app, Window* const self) | |||
| : fApp(app), | |||
| fSelf(self), | |||
| fView(puglInit(nullptr, nullptr)), | |||
| fView(puglInit()), | |||
| fFirstInit(true), | |||
| fVisible(false), | |||
| fResizable(true), | |||
| @@ -91,7 +90,7 @@ struct Window::PrivateData { | |||
| PrivateData(App& app, Window* const self, Window& parent) | |||
| : fApp(app), | |||
| fSelf(self), | |||
| fView(puglInit(nullptr, nullptr)), | |||
| fView(puglInit()), | |||
| fFirstInit(true), | |||
| fVisible(false), | |||
| fResizable(true), | |||
| @@ -129,7 +128,7 @@ struct Window::PrivateData { | |||
| PrivateData(App& app, Window* const self, const intptr_t parentId) | |||
| : fApp(app), | |||
| fSelf(self), | |||
| fView(puglInit(nullptr, nullptr)), | |||
| fView(puglInit()), | |||
| fFirstInit(true), | |||
| fVisible(parentId != 0), | |||
| fResizable(parentId == 0), | |||
| @@ -193,7 +192,6 @@ struct Window::PrivateData { | |||
| puglSetCloseFunc(fView, onCloseCallback); | |||
| puglCreateWindow(fView, nullptr); | |||
| puglEnterContext(fView); | |||
| PuglInternals* impl = fView->impl; | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| @@ -458,7 +456,11 @@ struct Window::PrivateData { | |||
| fResizable = yesNo; | |||
| #ifdef CARLA_OS_MAC | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX | |||
| : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; | |||
| SetWindowLong(hwnd, GWL_STYLE, winFlags); | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| // FIXME? | |||
| const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0); | |||
| [mView setAutoresizingMask:flags]; | |||
| @@ -471,7 +473,7 @@ struct Window::PrivateData { | |||
| void setSize(uint width, uint height, const bool forced = false) | |||
| { | |||
| if (width == 0 || height == 0) | |||
| if (width <= 1 || height <= 1) | |||
| { | |||
| DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height); | |||
| return; | |||
| @@ -486,18 +488,15 @@ struct Window::PrivateData { | |||
| fWidth = width; | |||
| fHeight = height; | |||
| DBGp("Window setSize called %s, size %i %i\n", forced ? "(forced)" : "(not forced)", width, height); | |||
| DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false"); | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||
| if (fResizable) | |||
| winFlags |= WS_SIZEBOX; | |||
| const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0); | |||
| RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) }; | |||
| AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); | |||
| AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||
| SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); | |||
| SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, | |||
| SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); | |||
| if (! forced) | |||
| UpdateWindow(hwnd); | |||
| @@ -1,121 +0,0 @@ | |||
| /* | |||
| Copyright 2014 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef PUGL_COMMON_H_INCLUDED | |||
| #define PUGL_COMMON_H_INCLUDED | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /** | |||
| @addtogroup pugl | |||
| @{ | |||
| */ | |||
| /** | |||
| A Pugl view. | |||
| */ | |||
| typedef struct PuglViewImpl PuglView; | |||
| /** | |||
| A native window handle. | |||
| On X11, this is a Window. | |||
| On OSX, this is an NSView*. | |||
| On Windows, this is a HWND. | |||
| */ | |||
| typedef intptr_t PuglNativeWindow; | |||
| /** | |||
| Handle for opaque user data. | |||
| */ | |||
| typedef void* PuglHandle; | |||
| /** | |||
| Return status code. | |||
| */ | |||
| typedef enum { | |||
| PUGL_SUCCESS = 0 | |||
| } PuglStatus; | |||
| /** | |||
| Drawing context type. | |||
| */ | |||
| typedef enum { | |||
| PUGL_GL, | |||
| PUGL_CAIRO | |||
| } PuglContextType; | |||
| /** | |||
| Convenience symbols for ASCII control characters. | |||
| */ | |||
| typedef enum { | |||
| PUGL_CHAR_BACKSPACE = 0x08, | |||
| PUGL_CHAR_ESCAPE = 0x1B, | |||
| PUGL_CHAR_DELETE = 0x7F | |||
| } PuglChar; | |||
| /** | |||
| Keyboard modifier flags. | |||
| */ | |||
| typedef enum { | |||
| PUGL_MOD_SHIFT = 1, /**< Shift key */ | |||
| PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||
| PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||
| PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||
| } PuglMod; | |||
| /** | |||
| Special (non-Unicode) keyboard keys. | |||
| */ | |||
| typedef enum { | |||
| PUGL_KEY_F1 = 1, | |||
| PUGL_KEY_F2, | |||
| PUGL_KEY_F3, | |||
| PUGL_KEY_F4, | |||
| PUGL_KEY_F5, | |||
| PUGL_KEY_F6, | |||
| PUGL_KEY_F7, | |||
| PUGL_KEY_F8, | |||
| PUGL_KEY_F9, | |||
| PUGL_KEY_F10, | |||
| PUGL_KEY_F11, | |||
| PUGL_KEY_F12, | |||
| PUGL_KEY_LEFT, | |||
| PUGL_KEY_UP, | |||
| PUGL_KEY_RIGHT, | |||
| PUGL_KEY_DOWN, | |||
| PUGL_KEY_PAGE_UP, | |||
| PUGL_KEY_PAGE_DOWN, | |||
| PUGL_KEY_HOME, | |||
| PUGL_KEY_END, | |||
| PUGL_KEY_INSERT, | |||
| PUGL_KEY_SHIFT, | |||
| PUGL_KEY_CTRL, | |||
| PUGL_KEY_ALT, | |||
| PUGL_KEY_SUPER | |||
| } PuglKey; | |||
| /** | |||
| @} | |||
| */ | |||
| #ifdef __cplusplus | |||
| } /* extern "C" */ | |||
| #endif | |||
| #endif /* PUGL_COMMON_H_INCLUDED */ | |||
| @@ -1,217 +0,0 @@ | |||
| /* | |||
| Copyright 2014 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef PUGL_EVENT_H_INCLUDED | |||
| #define PUGL_EVENT_H_INCLUDED | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #else | |||
| # include <stdbool.h> | |||
| #endif | |||
| #include "pugl/common.h" | |||
| /** | |||
| @addtogroup pugl | |||
| @{ | |||
| */ | |||
| /** | |||
| The type of a PuglEvent. | |||
| */ | |||
| typedef enum { | |||
| PUGL_BUTTON_PRESS, | |||
| PUGL_BUTTON_RELEASE, | |||
| PUGL_CONFIGURE, | |||
| PUGL_EXPOSE, | |||
| PUGL_KEY_PRESS, | |||
| PUGL_KEY_RELEASE, | |||
| PUGL_ENTER_NOTIFY, | |||
| PUGL_LEAVE_NOTIFY, | |||
| PUGL_MOTION_NOTIFY, | |||
| PUGL_NOTHING, | |||
| PUGL_SCROLL | |||
| } PuglEventType; | |||
| /** | |||
| Reason for a PuglEventCrossing. | |||
| */ | |||
| typedef enum { | |||
| PUGL_CROSSING_NORMAL, /**< Crossing due to pointer motion. */ | |||
| PUGL_CROSSING_GRAB, /**< Crossing due to a grab. */ | |||
| PUGL_CROSSING_UNGRAB /**< Crossing due to a grab release. */ | |||
| } PuglCrossingMode; | |||
| /** | |||
| Common header for all event structs. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< Event type. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| } PuglEventAny; | |||
| /** | |||
| Button press or release event. | |||
| For event types PUGL_BUTTON_PRESS and PUGL_BUTTON_RELEASE. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_BUTTON_PRESS or PUGL_BUTTON_RELEASE. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| uint32_t time; /**< Time in milliseconds. */ | |||
| double x; /**< View-relative X coordinate. */ | |||
| double y; /**< View-relative Y coordinate. */ | |||
| double x_root; /**< Root-relative X coordinate. */ | |||
| double y_root; /**< Root-relative Y coordinate. */ | |||
| unsigned state; /**< Bitwise OR of PuglMod flags. */ | |||
| unsigned button; /**< 1-relative button number. */ | |||
| } PuglEventButton; | |||
| /** | |||
| Configure event for when window size or position has changed. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_CONFIGURE. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| double x; /**< New parent-relative X coordinate. */ | |||
| double y; /**< New parent-relative Y coordinate. */ | |||
| double width; /**< New width. */ | |||
| double height; /**< New height. */ | |||
| } PuglEventConfigure; | |||
| /** | |||
| Expose event for when a region must be redrawn. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_EXPOSE. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| double x; /**< View-relative X coordinate. */ | |||
| double y; /**< View-relative Y coordinate. */ | |||
| double width; /**< Width of exposed region. */ | |||
| double height; /**< Height of exposed region. */ | |||
| int count; /**< Number of expose events to follow. */ | |||
| } PuglEventExpose; | |||
| /** | |||
| Key press event. | |||
| Keys that correspond to a Unicode character are expressed as a character | |||
| code. For other keys, `character` will be 0 and `special` indicates the key | |||
| pressed. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_KEY_PRESS or PUGL_KEY_RELEASE. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| uint32_t time; /**< Time in milliseconds. */ | |||
| double x; /**< View-relative X coordinate. */ | |||
| double y; /**< View-relative Y coordinate. */ | |||
| double x_root; /**< Root-relative X coordinate. */ | |||
| double y_root; /**< Root-relative Y coordinate. */ | |||
| unsigned state; /**< Bitwise OR of PuglMod flags. */ | |||
| uint32_t character; /**< Unicode character code, or 0. */ | |||
| PuglKey special; /**< Special key, if character is 0. */ | |||
| } PuglEventKey; | |||
| /** | |||
| Pointer crossing event (enter and leave). | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_ENTER_NOTIFY or PUGL_LEAVE_NOTIFY. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| uint32_t time; /**< Time in milliseconds. */ | |||
| double x; /**< View-relative X coordinate. */ | |||
| double y; /**< View-relative Y coordinate. */ | |||
| double x_root; /**< Root-relative X coordinate. */ | |||
| double y_root; /**< Root-relative Y coordinate. */ | |||
| unsigned state; /**< Bitwise OR of PuglMod flags. */ | |||
| PuglCrossingMode mode; /**< Reason for crossing. */ | |||
| } PuglEventCrossing; | |||
| /** | |||
| Pointer motion event. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_MOTION_NOTIFY. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| uint32_t time; /**< Time in milliseconds. */ | |||
| double x; /**< View-relative X coordinate. */ | |||
| double y; /**< View-relative Y coordinate. */ | |||
| double x_root; /**< Root-relative X coordinate. */ | |||
| double y_root; /**< Root-relative Y coordinate. */ | |||
| unsigned state; /**< Bitwise OR of PuglMod flags. */ | |||
| bool is_hint; /**< True iff this event is a motion hint. */ | |||
| bool focus; /**< True iff this is the focused window. */ | |||
| } PuglEventMotion; | |||
| /** | |||
| Scroll event. | |||
| The scroll distance is expressed in "lines", an arbitrary unit that | |||
| corresponds to a single tick of a detented mouse wheel. For example, `dy` = | |||
| 1.0 scrolls 1 line up. Some systems and devices support finer resolution | |||
| and/or higher values for fast scrolls, so programs should handle any value | |||
| gracefully. | |||
| */ | |||
| typedef struct { | |||
| PuglEventType type; /**< PUGL_SCROLL. */ | |||
| PuglView* view; /**< View that received this event. */ | |||
| bool send_event; /**< True iff event was sent explicitly. */ | |||
| uint32_t time; /**< Time in milliseconds. */ | |||
| double x; /**< View-relative X coordinate. */ | |||
| double y; /**< View-relative Y coordinate. */ | |||
| double x_root; /**< Root-relative X coordinate. */ | |||
| double y_root; /**< Root-relative Y coordinate. */ | |||
| unsigned state; /**< Bitwise OR of PuglMod flags. */ | |||
| double dx; /**< Scroll X distance in lines. */ | |||
| double dy; /**< Scroll Y distance in lines. */ | |||
| } PuglEventScroll; | |||
| /** | |||
| Interface event. | |||
| This is a union of all event structs. The `type` must be checked to | |||
| determine which fields are safe to access. A pointer to PuglEvent can | |||
| either be cast to the appropriate type, or the union members used. | |||
| */ | |||
| typedef union { | |||
| PuglEventType type; /**< Event type. */ | |||
| PuglEventAny any; /**< Valid for all event types. */ | |||
| PuglEventButton button; /**< PUGL_BUTTON_PRESS, PUGL_BUTTON_RELEASE. */ | |||
| PuglEventConfigure configure; /**< PUGL_CONFIGURE. */ | |||
| PuglEventCrossing crossing; /**< PUGL_ENTER_NOTIFY, PUGL_LEAVE_NOTIFY. */ | |||
| PuglEventExpose expose; /**< PUGL_EXPOSE. */ | |||
| PuglEventKey key; /**< PUGL_KEY_PRESS, PUGL_KEY_RELEASE. */ | |||
| PuglEventMotion motion; /**< PUGL_MOTION_NOTIFY. */ | |||
| PuglEventScroll scroll; /**< PUGL_SCROLL. */ | |||
| } PuglEvent; | |||
| /** | |||
| @} | |||
| */ | |||
| #ifdef __cplusplus | |||
| } /* extern "C" */ | |||
| #endif | |||
| #endif /* PUGL_EVENT_H_INCLUDED */ | |||
| @@ -1,32 +0,0 @@ | |||
| /* | |||
| 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 | |||
| @@ -1,32 +0,0 @@ | |||
| /* | |||
| 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 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 | |||
| @@ -23,8 +23,19 @@ | |||
| #include <stdint.h> | |||
| #include "pugl/common.h" | |||
| #include "pugl/event.h" | |||
| /* | |||
| This API is pure portable C and contains no platform specific elements, or | |||
| even a GL dependency. However, unfortunately GL includes vary across | |||
| platforms so they are included here to allow 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 | |||
| #ifdef PUGL_SHARED | |||
| # ifdef _WIN32 | |||
| @@ -40,7 +51,11 @@ | |||
| # define PUGL_API PUGL_LIB_IMPORT | |||
| # endif | |||
| #else | |||
| # define PUGL_API | |||
| # ifdef _WIN32 | |||
| # define PUGL_API | |||
| # else | |||
| # define PUGL_API __attribute__((visibility("hidden"))) | |||
| # endif | |||
| #endif | |||
| #ifdef __cplusplus | |||
| @@ -56,9 +71,80 @@ extern "C" { | |||
| */ | |||
| /** | |||
| A function called when an event occurs. | |||
| An OpenGL view. | |||
| */ | |||
| typedef struct PuglViewImpl PuglView; | |||
| /** | |||
| A native window handle. | |||
| On X11, this is a Window. | |||
| On OSX, this is an NSView*. | |||
| On Windows, this is a HWND. | |||
| */ | |||
| typedef intptr_t PuglNativeWindow; | |||
| /** | |||
| Return status code. | |||
| */ | |||
| typedef void (*PuglEventFunc)(PuglView* view, const PuglEvent* event); | |||
| typedef enum { | |||
| PUGL_SUCCESS = 0 | |||
| } PuglStatus; | |||
| /** | |||
| Convenience symbols for ASCII control characters. | |||
| */ | |||
| typedef enum { | |||
| PUGL_CHAR_BACKSPACE = 0x08, | |||
| PUGL_CHAR_ESCAPE = 0x1B, | |||
| PUGL_CHAR_DELETE = 0x7F | |||
| } PuglChar; | |||
| /** | |||
| Special (non-Unicode) keyboard keys. | |||
| */ | |||
| typedef enum { | |||
| PUGL_KEY_F1 = 1, | |||
| PUGL_KEY_F2, | |||
| PUGL_KEY_F3, | |||
| PUGL_KEY_F4, | |||
| PUGL_KEY_F5, | |||
| PUGL_KEY_F6, | |||
| PUGL_KEY_F7, | |||
| PUGL_KEY_F8, | |||
| PUGL_KEY_F9, | |||
| PUGL_KEY_F10, | |||
| PUGL_KEY_F11, | |||
| PUGL_KEY_F12, | |||
| PUGL_KEY_LEFT, | |||
| PUGL_KEY_UP, | |||
| PUGL_KEY_RIGHT, | |||
| PUGL_KEY_DOWN, | |||
| PUGL_KEY_PAGE_UP, | |||
| PUGL_KEY_PAGE_DOWN, | |||
| PUGL_KEY_HOME, | |||
| PUGL_KEY_END, | |||
| PUGL_KEY_INSERT, | |||
| PUGL_KEY_SHIFT, | |||
| PUGL_KEY_CTRL, | |||
| PUGL_KEY_ALT, | |||
| PUGL_KEY_SUPER | |||
| } PuglKey; | |||
| /** | |||
| Keyboard modifier flags. | |||
| */ | |||
| typedef enum { | |||
| PUGL_MOD_SHIFT = 1, /**< Shift key */ | |||
| PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||
| PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||
| PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||
| } PuglMod; | |||
| /** | |||
| Handle for opaque user data. | |||
| */ | |||
| typedef void* PuglHandle; | |||
| /** | |||
| A function called when the window is closed. | |||
| @@ -114,19 +200,18 @@ typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); | |||
| so programs should handle any value gracefully. | |||
| @param view The view being scrolled. | |||
| @param x The window-relative x coordinate of the pointer. | |||
| @param y The window-relative y coordinate of the pointer. | |||
| @param dx The scroll x distance. | |||
| @param dx The scroll y distance. | |||
| */ | |||
| typedef void (*PuglScrollFunc)(PuglView* view, | |||
| int x, | |||
| int y, | |||
| float dx, | |||
| float dy); | |||
| typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy); | |||
| /** | |||
| A function called when a special key is pressed or released. | |||
| This callback allows the use of keys that do not have unicode points. | |||
| Note that some are non-printable keys. | |||
| @param view The view the event occured in. | |||
| @param press True if the key was pressed, false if released. | |||
| @@ -134,23 +219,14 @@ typedef void (*PuglScrollFunc)(PuglView* view, | |||
| */ | |||
| typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | |||
| /** | |||
| @name Initialization | |||
| Configuration functions which must be called before creating a window. | |||
| @{ | |||
| */ | |||
| /** | |||
| Create a Pugl context. | |||
| To create a window, call the various puglInit* functions as necessary, then | |||
| call puglCreateWindow(). | |||
| @param pargc Pointer to argument count (unused, for GLUT compatibility). | |||
| @param argv Arguments (unused, for GLUT compatibility). | |||
| */ | |||
| PUGL_API PuglView* | |||
| puglInit(int* pargc, char** argv); | |||
| puglInit(void); | |||
| /** | |||
| Set the parent window before creating a window (for embedding). | |||
| @@ -168,23 +244,7 @@ puglInitWindowSize(PuglView* view, int width, int height); | |||
| Enable or disable resizing before creating a window. | |||
| */ | |||
| PUGL_API void | |||
| puglInitResizable(PuglView* view, bool resizable); | |||
| /** | |||
| Set the context type before creating a window. | |||
| */ | |||
| PUGL_API void | |||
| puglInitContextType(PuglView* view, PuglContextType type); | |||
| /** | |||
| @} | |||
| */ | |||
| /** | |||
| @name Windows | |||
| Window management functions. | |||
| @{ | |||
| */ | |||
| puglInitUserResizable(PuglView* view, bool resizable); | |||
| /** | |||
| Create a window with the settings given by the various puglInit functions. | |||
| @@ -206,16 +266,6 @@ puglShowWindow(PuglView* view); | |||
| 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. | |||
| @@ -234,15 +284,6 @@ puglSetHandle(PuglView* view, PuglHandle handle); | |||
| PUGL_API PuglHandle | |||
| puglGetHandle(PuglView* view); | |||
| /** | |||
| 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); | |||
| /** | |||
| Return the timestamp (if any) of the currently-processing event. | |||
| */ | |||
| @@ -263,18 +304,6 @@ puglGetModifiers(PuglView* view); | |||
| PUGL_API void | |||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore); | |||
| /** | |||
| @name Event Callbacks | |||
| Functions to set event callbacks for handling user input. | |||
| @{ | |||
| */ | |||
| /** | |||
| Set the function to call when an event occurs. | |||
| */ | |||
| PUGL_API void | |||
| puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); | |||
| /** | |||
| Set the function to call when the window is closed. | |||
| */ | |||
| @@ -324,14 +353,10 @@ PUGL_API void | |||
| puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | |||
| /** | |||
| @} | |||
| */ | |||
| /** | |||
| Grab the input focus. | |||
| Return the native window handle. | |||
| */ | |||
| PUGL_API void | |||
| puglGrabFocus(PuglView* view); | |||
| PUGL_API PuglNativeWindow | |||
| puglGetNativeWindow(PuglView* view); | |||
| /** | |||
| Process all pending window events. | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2012 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 | |||
| @@ -24,14 +24,11 @@ | |||
| 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. | |||
| PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus. | |||
| PUGL_VERBOSE: Print GL information to console. | |||
| */ | |||
| #include "pugl/pugl.h" | |||
| #include "pugl/event.h" | |||
| #include "pugl.h" | |||
| #ifdef PUGL_VERBOSE | |||
| # include <stdio.h> | |||
| @@ -46,7 +43,6 @@ typedef struct PuglInternalsImpl PuglInternals; | |||
| struct PuglViewImpl { | |||
| PuglHandle handle; | |||
| PuglEventFunc eventFunc; | |||
| PuglCloseFunc closeFunc; | |||
| PuglDisplayFunc displayFunc; | |||
| PuglKeyboardFunc keyboardFunc; | |||
| @@ -57,9 +53,7 @@ struct PuglViewImpl { | |||
| PuglSpecialFunc specialFunc; | |||
| PuglInternals* impl; | |||
| PuglNativeWindow parent; | |||
| PuglContextType ctx_type; | |||
| int width; | |||
| int height; | |||
| @@ -71,10 +65,10 @@ struct PuglViewImpl { | |||
| uint32_t event_timestamp_ms; | |||
| }; | |||
| PuglInternals* puglInitInternals(); | |||
| PuglInternals* puglInitInternals(void); | |||
| PuglView* | |||
| puglInit(int* pargc, char** argv) | |||
| puglInit(void) | |||
| { | |||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||
| if (!view) { | |||
| @@ -92,9 +86,6 @@ puglInit(int* pargc, char** argv) | |||
| view->height = 480; | |||
| return view; | |||
| // unused | |||
| (void)pargc; (void)argv; | |||
| } | |||
| void | |||
| @@ -116,12 +107,6 @@ puglInitResizable(PuglView* view, bool resizable) | |||
| view->resizable = resizable; | |||
| } | |||
| void | |||
| puglInitContextType(PuglView* view, PuglContextType type) | |||
| { | |||
| view->ctx_type = type; | |||
| } | |||
| void | |||
| puglSetHandle(PuglView* view, PuglHandle handle) | |||
| { | |||
| @@ -146,16 +131,26 @@ puglGetModifiers(PuglView* view) | |||
| return view->mods; | |||
| } | |||
| void | |||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore) | |||
| static void | |||
| puglDefaultReshape(PuglView* view, int width, int height) | |||
| { | |||
| view->ignoreKeyRepeat = ignore; | |||
| glMatrixMode(GL_PROJECTION); | |||
| glLoadIdentity(); | |||
| glOrtho(0, width, height, 0, 0, 1); | |||
| glViewport(0, 0, width, height); | |||
| glMatrixMode(GL_MODELVIEW); | |||
| glLoadIdentity(); | |||
| return; | |||
| // unused | |||
| (void)view; | |||
| } | |||
| void | |||
| puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc) | |||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore) | |||
| { | |||
| view->eventFunc = eventFunc; | |||
| view->ignoreKeyRepeat = ignore; | |||
| } | |||
| void | |||
| @@ -205,81 +200,3 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||
| { | |||
| view->specialFunc = specialFunc; | |||
| } | |||
| void | |||
| puglEnterContext(PuglView* view); | |||
| void | |||
| puglLeaveContext(PuglView* view, bool flush); | |||
| static void | |||
| puglDispatchEvent(PuglView* view, const PuglEvent* event) | |||
| { | |||
| if (view->eventFunc) { | |||
| view->eventFunc(view, event); | |||
| } | |||
| switch (event->type) { | |||
| case PUGL_CONFIGURE: | |||
| puglEnterContext(view); | |||
| view->width = event->configure.width; | |||
| view->height = event->configure.height; | |||
| if (view->reshapeFunc) { | |||
| view->reshapeFunc(view, view->width, view->height); | |||
| } | |||
| puglLeaveContext(view, false); | |||
| break; | |||
| case PUGL_EXPOSE: | |||
| if (event->expose.count == 0) { | |||
| puglEnterContext(view); | |||
| if (view->displayFunc) { | |||
| view->displayFunc(view); | |||
| } | |||
| view->redisplay = false; | |||
| puglLeaveContext(view, true); | |||
| } | |||
| break; | |||
| case PUGL_MOTION_NOTIFY: | |||
| view->event_timestamp_ms = event->motion.time; | |||
| view->mods = event->motion.state; | |||
| if (view->motionFunc) { | |||
| view->motionFunc(view, event->motion.x, event->motion.y); | |||
| } | |||
| break; | |||
| case PUGL_SCROLL: | |||
| if (view->scrollFunc) { | |||
| view->scrollFunc(view, | |||
| event->scroll.x, event->scroll.y, | |||
| event->scroll.dx, event->scroll.dy); | |||
| } | |||
| break; | |||
| case PUGL_BUTTON_PRESS: | |||
| case PUGL_BUTTON_RELEASE: | |||
| view->event_timestamp_ms = event->button.time; | |||
| view->mods = event->button.state; | |||
| if (view->mouseFunc) { | |||
| view->mouseFunc(view, | |||
| event->button.button, | |||
| event->type == PUGL_BUTTON_PRESS, | |||
| event->button.x, | |||
| event->button.y); | |||
| } | |||
| break; | |||
| case PUGL_KEY_PRESS: | |||
| case PUGL_KEY_RELEASE: | |||
| view->event_timestamp_ms = event->key.time; | |||
| view->mods = event->key.state; | |||
| if (event->key.special && view->specialFunc) { | |||
| view->specialFunc(view, | |||
| event->type == PUGL_KEY_PRESS, | |||
| event->key.special); | |||
| } else if (event->key.character && view->keyboardFunc) { | |||
| view->keyboardFunc(view, | |||
| event->type == PUGL_KEY_PRESS, | |||
| event->key.character); | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2012 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 | |||
| @@ -22,7 +22,7 @@ | |||
| #import <Cocoa/Cocoa.h> | |||
| #include "pugl/pugl_internal.h" | |||
| #include "pugl_internal.h" | |||
| @interface PuglWindow : NSWindow | |||
| { | |||
| @@ -36,8 +36,6 @@ | |||
| defer:(BOOL)flag; | |||
| - (void) setPuglview:(PuglView*)view; | |||
| - (BOOL) windowShouldClose:(id)sender; | |||
| - (BOOL) canBecomeKeyWindow:(id)sender; | |||
| - (BOOL) canBecomeMainWindow:(id)sender; | |||
| @end | |||
| @implementation PuglWindow | |||
| @@ -78,22 +76,6 @@ | |||
| (void)sender; | |||
| } | |||
| - (BOOL) canBecomeKeyWindow:(id)sender | |||
| { | |||
| return YES; | |||
| // unused | |||
| (void)sender; | |||
| } | |||
| - (BOOL) canBecomeMainWindow:(id)sender | |||
| { | |||
| return YES; | |||
| // unused | |||
| (void)sender; | |||
| } | |||
| @end | |||
| static void | |||
| @@ -214,6 +196,8 @@ puglDisplay(PuglView* view) | |||
| if (puglview->reshapeFunc) { | |||
| puglview->reshapeFunc(puglview, width, height); | |||
| } else { | |||
| puglDefaultReshape(puglview, width, height); | |||
| } | |||
| puglview->width = width; | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2012 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 | |||
| @@ -22,11 +22,11 @@ | |||
| #include <windowsx.h> | |||
| #include <GL/gl.h> | |||
| #include <ctime> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <time.h> | |||
| #include "pugl/pugl_internal.h" | |||
| #include "pugl_internal.h" | |||
| #ifndef WM_MOUSEWHEEL | |||
| # define WM_MOUSEWHEEL 0x020A | |||
| @@ -38,7 +38,9 @@ | |||
| # define WHEEL_DELTA 120 | |||
| #endif | |||
| #define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) | |||
| const int LOCAL_CLOSE_MSG = WM_USER + 50; | |||
| HINSTANCE hInstance = NULL; | |||
| struct PuglInternalsImpl { | |||
| HWND hwnd; | |||
| @@ -47,8 +49,6 @@ struct PuglInternalsImpl { | |||
| WNDCLASS wc; | |||
| }; | |||
| static HINSTANCE hInstance = NULL; | |||
| LRESULT CALLBACK | |||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||
| @@ -69,27 +69,6 @@ puglInitInternals() | |||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| } | |||
| void | |||
| puglEnterContext(PuglView* view) | |||
| { | |||
| #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 | |||
| } | |||
| int | |||
| puglCreateWindow(PuglView* view, const char* title) | |||
| { | |||
| @@ -103,8 +82,9 @@ puglCreateWindow(PuglView* view, const char* title) | |||
| // Should class be a parameter? Does this make sense on other platforms? | |||
| static int wc_count = 0; | |||
| char classNameBuf[256]; | |||
| srand((time(NULL))); | |||
| _snprintf(classNameBuf, sizeof(classNameBuf), "%d-%d_%s\n", ++wc_count, rand(), title); | |||
| std::srand((std::time(NULL))); | |||
| _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||
| classNameBuf[sizeof(classNameBuf)-1] = '\0'; | |||
| impl->wc.style = CS_OWNDC; | |||
| impl->wc.lpfnWndProc = wndProc; | |||
| @@ -116,37 +96,35 @@ puglCreateWindow(PuglView* view, const char* title) | |||
| impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||
| impl->wc.lpszMenuName = NULL; | |||
| impl->wc.lpszClassName = strdup(classNameBuf); | |||
| RegisterClass(&impl->wc); | |||
| int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||
| if (view->resizable) { | |||
| winFlags |= WS_SIZEBOX; | |||
| if (!RegisterClass(&impl->wc)) { | |||
| free((void*)impl->wc.lpszClassName); | |||
| free(impl); | |||
| free(view); | |||
| return 1; | |||
| } | |||
| // Adjust the overall window size to accomodate our requested client size | |||
| const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (view->resizable ? WS_SIZEBOX : 0x0); | |||
| RECT wr = { 0, 0, view->width, view->height }; | |||
| AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); | |||
| AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||
| impl->hwnd = CreateWindowEx( | |||
| WS_EX_TOPMOST, | |||
| classNameBuf, title, | |||
| view->parent ? WS_CHILD : winFlags, | |||
| view->parent ? (WS_CHILD | WS_VISIBLE) : winFlags, | |||
| CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, | |||
| (HWND)view->parent, NULL, hInstance, NULL); | |||
| if (!impl->hwnd) { | |||
| UnregisterClass(impl->wc.lpszClassName, hInstance); | |||
| UnregisterClass(impl->wc.lpszClassName, NULL); | |||
| 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); | |||
| @@ -164,6 +142,16 @@ puglCreateWindow(PuglView* view, const char* title) | |||
| 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 1; | |||
| } | |||
| wglMakeCurrent(impl->hdc, impl->hglrc); | |||
| return 0; | |||
| @@ -192,7 +180,7 @@ puglDestroy(PuglView* view) | |||
| wglDeleteContext(view->impl->hglrc); | |||
| ReleaseDC(view->impl->hwnd, view->impl->hdc); | |||
| DestroyWindow(view->impl->hwnd); | |||
| UnregisterClass(view->impl->wc.lpszClassName, hInstance); | |||
| UnregisterClass(view->impl->wc.lpszClassName, NULL); | |||
| free((void*)view->impl->wc.lpszClassName); | |||
| free(view->impl); | |||
| free(view); | |||
| @@ -201,10 +189,12 @@ puglDestroy(PuglView* view) | |||
| static void | |||
| puglReshape(PuglView* view, int width, int height) | |||
| { | |||
| puglEnterContext(view); | |||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
| if (view->reshapeFunc) { | |||
| view->reshapeFunc(view, width, height); | |||
| } else { | |||
| puglDefaultReshape(view, width, height); | |||
| } | |||
| view->width = width; | |||
| @@ -214,14 +204,15 @@ puglReshape(PuglView* view, int width, int height) | |||
| static void | |||
| puglDisplay(PuglView* view) | |||
| { | |||
| puglEnterContext(view); | |||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||
| view->redisplay = false; | |||
| if (view->displayFunc) { | |||
| view->displayFunc(view); | |||
| } | |||
| puglLeaveContext(view, true); | |||
| view->redisplay = false; | |||
| glFlush(); | |||
| SwapBuffers(view->impl->hdc); | |||
| } | |||
| static PuglKey | |||
| @@ -310,7 +301,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| break; | |||
| case WM_MOUSEMOVE: | |||
| if (view->motionFunc) { | |||
| view->event_timestamp_ms = GetMessageTime(); | |||
| view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | |||
| } | |||
| break; | |||
| @@ -335,17 +325,21 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| case WM_MOUSEWHEEL: | |||
| if (view->scrollFunc) { | |||
| view->event_timestamp_ms = GetMessageTime(); | |||
| POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||
| ScreenToClient(view->impl->hwnd, &pt); | |||
| view->scrollFunc( | |||
| view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), | |||
| 0.0f, (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); | |||
| view, pt.x, pt.y, | |||
| GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0); | |||
| } | |||
| break; | |||
| case WM_MOUSEHWHEEL: | |||
| if (view->scrollFunc) { | |||
| view->event_timestamp_ms = GetMessageTime(); | |||
| POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||
| ScreenToClient(view->impl->hwnd, &pt); | |||
| view->scrollFunc( | |||
| view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), | |||
| (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0.0f); | |||
| view, pt.x, pt.y, | |||
| GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0); | |||
| } | |||
| break; | |||
| case WM_KEYDOWN: | |||
| @@ -359,11 +353,18 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| view->specialFunc(view, message == WM_KEYDOWN, key); | |||
| } | |||
| } else if (view->keyboardFunc) { | |||
| view->keyboardFunc(view, message == WM_KEYDOWN, wParam); | |||
| static BYTE kbs[256]; | |||
| if (GetKeyboardState(kbs) != FALSE) { | |||
| char lb[2]; | |||
| UINT scanCode = (lParam >> 8) & 0xFFFFFF00; | |||
| if ( 1 == ToAscii(wParam, scanCode, kbs, (LPWORD)lb, 0)) { | |||
| view->keyboardFunc(view, message == WM_KEYDOWN, (char)lb[0]); | |||
| } | |||
| } | |||
| } | |||
| break; | |||
| case WM_QUIT: | |||
| case PUGL_LOCAL_CLOSE_MSG: | |||
| case LOCAL_CLOSE_MSG: | |||
| if (view->closeFunc) { | |||
| view->closeFunc(view); | |||
| } | |||
| @@ -376,12 +377,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||
| return 0; | |||
| } | |||
| void | |||
| puglGrabFocus(PuglView* /*view*/) | |||
| { | |||
| // TODO | |||
| } | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| @@ -390,7 +385,6 @@ puglProcessEvents(PuglView* view) | |||
| handleMessage(view, msg.message, msg.wParam, msg.lParam); | |||
| } | |||
| if (view->redisplay) { | |||
| InvalidateRect(view->impl->hwnd, NULL, FALSE); | |||
| } | |||
| @@ -401,23 +395,19 @@ puglProcessEvents(PuglView* view) | |||
| 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); | |||
| PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); | |||
| return 0; | |||
| case WM_DESTROY: | |||
| return 0; | |||
| default: | |||
| if (view) { | |||
| if (view && hwnd == view->impl->hwnd) { | |||
| return handleMessage(view, message, wParam, lParam); | |||
| } else { | |||
| return DefWindowProc(hwnd, message, wParam, lParam); | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2013 Robin Gareus <robin@gareus.org> | |||
| Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||
| Copyright 2013 Robin Gareus <robin@gareus.org> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| @@ -24,170 +24,101 @@ | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <GL/gl.h> | |||
| #include <GL/glx.h> | |||
| #include <X11/Xatom.h> | |||
| #include <X11/Xlib.h> | |||
| #include <X11/Xutil.h> | |||
| #include <X11/keysym.h> | |||
| #ifdef PUGL_HAVE_GL | |||
| #include <GL/gl.h> | |||
| #include <GL/glx.h> | |||
| #endif | |||
| #ifdef PUGL_HAVE_CAIRO | |||
| #include <cairo/cairo.h> | |||
| #include <cairo/cairo-xlib.h> | |||
| #endif | |||
| #include "pugl/event.h" | |||
| #include "pugl/pugl_internal.h" | |||
| #include "pugl_internal.h" | |||
| struct PuglInternalsImpl { | |||
| Display* display; | |||
| int screen; | |||
| Window win; | |||
| #ifdef PUGL_HAVE_CAIRO | |||
| cairo_t* cr; | |||
| #endif | |||
| #ifdef PUGL_HAVE_GL | |||
| GLXContext ctx; | |||
| Bool doubleBuffered; | |||
| #endif | |||
| }; | |||
| PuglInternals* | |||
| puglInitInternals() | |||
| { | |||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| } | |||
| static XVisualInfo* | |||
| getVisual(PuglView* view) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| XVisualInfo* vi = NULL; | |||
| #ifdef PUGL_HAVE_GL | |||
| if (view->ctx_type == PUGL_GL) { | |||
| // Try to create double-buffered visual | |||
| int double_attrs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, | |||
| GLX_RED_SIZE, 4, | |||
| GLX_GREEN_SIZE, 4, | |||
| GLX_BLUE_SIZE, 4, | |||
| GLX_DEPTH_SIZE, 16, | |||
| None }; | |||
| vi = glXChooseVisual(impl->display, impl->screen, double_attrs); | |||
| if (!vi) { | |||
| // Failed, create single-buffered visual | |||
| int single_attrs[] = { GLX_RGBA, | |||
| GLX_RED_SIZE, 4, | |||
| GLX_GREEN_SIZE, 4, | |||
| GLX_BLUE_SIZE, 4, | |||
| GLX_DEPTH_SIZE, 16, | |||
| None }; | |||
| vi = glXChooseVisual(impl->display, impl->screen, single_attrs); | |||
| impl->doubleBuffered = False; | |||
| } else { | |||
| impl->doubleBuffered = True; | |||
| } | |||
| #ifdef PUGL_VERBOSE | |||
| int glxMajor, glxMinor; | |||
| glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||
| PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); | |||
| #endif | |||
| } | |||
| #endif | |||
| #ifdef PUGL_HAVE_CAIRO | |||
| if (view->ctx_type == PUGL_CAIRO) { | |||
| XVisualInfo pat; | |||
| int n; | |||
| pat.screen = impl->screen; | |||
| vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); | |||
| } | |||
| #endif | |||
| return vi; | |||
| } | |||
| static void | |||
| createContext(PuglView* view, XVisualInfo* vi) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| #ifdef PUGL_HAVE_GL | |||
| if (view->ctx_type == PUGL_GL) { | |||
| impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||
| #ifdef PUGL_VERBOSE | |||
| if (glXIsDirect(impl->display, impl->ctx)) { | |||
| PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||
| } else { | |||
| PUGL_LOG("No DRI available\n"); | |||
| } | |||
| #endif | |||
| } | |||
| #endif | |||
| #ifdef PUGL_HAVE_CAIRO | |||
| if (view->ctx_type == PUGL_CAIRO) { | |||
| cairo_surface_t* surface = cairo_xlib_surface_create( | |||
| impl->display, impl->win, vi->visual, view->width, view->height); | |||
| if (!(impl->cr = cairo_create(surface))) { | |||
| fprintf(stderr, "failed to create cairo context\n"); | |||
| } | |||
| } | |||
| #endif | |||
| } | |||
| /** | |||
| Attributes for single-buffered RGBA with at least | |||
| 4 bits per color and a 16 bit depth buffer. | |||
| */ | |||
| static int attrListSgl[] = { | |||
| GLX_RGBA, | |||
| GLX_RED_SIZE, 4, | |||
| GLX_GREEN_SIZE, 4, | |||
| GLX_BLUE_SIZE, 4, | |||
| GLX_DEPTH_SIZE, 16, | |||
| GLX_ARB_multisample, 1, | |||
| None | |||
| }; | |||
| static void | |||
| destroyContext(PuglView* view) | |||
| { | |||
| #ifdef PUGL_HAVE_GL | |||
| if (view->ctx_type == PUGL_GL) { | |||
| glXDestroyContext(view->impl->display, view->impl->ctx); | |||
| } | |||
| #endif | |||
| #ifdef PUGL_HAVE_CAIRO | |||
| if (view->ctx_type == PUGL_CAIRO) { | |||
| glXDestroyContext(view->impl->display, view->impl->ctx); | |||
| } | |||
| #endif | |||
| } | |||
| /** | |||
| Attributes for double-buffered RGBA with at least | |||
| 4 bits per color and a 16 bit depth buffer. | |||
| */ | |||
| static int attrListDbl[] = { | |||
| GLX_RGBA, GLX_DOUBLEBUFFER, | |||
| GLX_RED_SIZE, 4, | |||
| GLX_GREEN_SIZE, 4, | |||
| GLX_BLUE_SIZE, 4, | |||
| GLX_DEPTH_SIZE, 16, | |||
| GLX_ARB_multisample, 1, | |||
| None | |||
| }; | |||
| void | |||
| puglEnterContext(PuglView* view) | |||
| { | |||
| #ifdef PUGL_HAVE_GL | |||
| if (view->ctx_type == PUGL_GL) { | |||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
| } | |||
| #endif | |||
| } | |||
| /** | |||
| Attributes for double-buffered RGBA with multi-sampling | |||
| (antialiasing) | |||
| */ | |||
| static int attrListDblMS[] = { | |||
| GLX_RGBA, | |||
| GLX_DOUBLEBUFFER , True, | |||
| GLX_RED_SIZE , 4, | |||
| GLX_GREEN_SIZE , 4, | |||
| GLX_BLUE_SIZE , 4, | |||
| GLX_ALPHA_SIZE , 4, | |||
| GLX_DEPTH_SIZE , 16, | |||
| GLX_SAMPLE_BUFFERS , 1, | |||
| GLX_SAMPLES , 4, | |||
| None | |||
| }; | |||
| void | |||
| puglLeaveContext(PuglView* view, bool flush) | |||
| PuglInternals* | |||
| puglInitInternals(void) | |||
| { | |||
| #ifdef PUGL_HAVE_GL | |||
| if (view->ctx_type == PUGL_GL && flush) { | |||
| glFlush(); | |||
| if (view->impl->doubleBuffered) { | |||
| glXSwapBuffers(view->impl->display, view->impl->win); | |||
| } | |||
| } | |||
| #endif | |||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||
| } | |||
| int | |||
| puglCreateWindow(PuglView* view, const char* title) | |||
| { | |||
| PuglInternals* const impl = view->impl; | |||
| PuglInternals* impl = view->impl; | |||
| impl->display = XOpenDisplay(0); | |||
| impl->screen = DefaultScreen(impl->display); | |||
| impl->doubleBuffered = True; | |||
| XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); | |||
| if (!vi) { | |||
| vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||
| PUGL_LOG("multisampling (antialiasing) is not available\n"); | |||
| } | |||
| XVisualInfo* const vi = getVisual(view); | |||
| if (!vi) { | |||
| return 1; | |||
| vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||
| impl->doubleBuffered = False; | |||
| PUGL_LOG("singlebuffered rendering will be used, no doublebuffering available\n"); | |||
| } | |||
| int glxMajor, glxMinor; | |||
| glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||
| PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); | |||
| impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||
| Window xParent = view->parent | |||
| ? (Window)view->parent | |||
| : RootWindow(impl->display, impl->screen); | |||
| @@ -197,21 +128,20 @@ puglCreateWindow(PuglView* view, const char* title) | |||
| XSetWindowAttributes attr; | |||
| memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||
| attr.background_pixel = BlackPixel(impl->display, impl->screen); | |||
| attr.border_pixel = BlackPixel(impl->display, impl->screen); | |||
| attr.colormap = cmap; | |||
| attr.event_mask = (ExposureMask | StructureNotifyMask | | |||
| EnterWindowMask | LeaveWindowMask | | |||
| KeyPressMask | KeyReleaseMask | | |||
| ButtonPressMask | ButtonReleaseMask | | |||
| PointerMotionMask); | |||
| attr.colormap = cmap; | |||
| attr.border_pixel = 0; | |||
| attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | |||
| | ButtonPressMask | ButtonReleaseMask | |||
| #ifdef PUGL_GRAB_FOCUS | |||
| | EnterWindowMask | |||
| #endif | |||
| | PointerMotionMask | StructureNotifyMask; | |||
| impl->win = XCreateWindow( | |||
| impl->display, xParent, | |||
| 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||
| CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr); | |||
| createContext(view, vi); | |||
| CWBorderPixel | CWColormap | CWEventMask, &attr); | |||
| XSizeHints sizeHints; | |||
| memset(&sizeHints, 0, sizeof(sizeHints)); | |||
| @@ -233,21 +163,33 @@ puglCreateWindow(PuglView* view, const char* title) | |||
| XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||
| } | |||
| if (glXIsDirect(impl->display, impl->ctx)) { | |||
| PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||
| } else { | |||
| PUGL_LOG("No DRI available\n"); | |||
| } | |||
| XFree(vi); | |||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
| return 0; | |||
| } | |||
| void | |||
| puglShowWindow(PuglView* view) | |||
| { | |||
| XMapRaised(view->impl->display, view->impl->win); | |||
| PuglInternals* impl = view->impl; | |||
| XMapRaised(impl->display, impl->win); | |||
| } | |||
| void | |||
| puglHideWindow(PuglView* view) | |||
| { | |||
| XUnmapWindow(view->impl->display, view->impl->win); | |||
| PuglInternals* impl = view->impl; | |||
| XUnmapWindow(impl->display, impl->win); | |||
| } | |||
| void | |||
| @@ -257,13 +199,46 @@ puglDestroy(PuglView* view) | |||
| return; | |||
| } | |||
| destroyContext(view); | |||
| glXDestroyContext(view->impl->display, view->impl->ctx); | |||
| XDestroyWindow(view->impl->display, view->impl->win); | |||
| XCloseDisplay(view->impl->display); | |||
| free(view->impl); | |||
| free(view); | |||
| } | |||
| static void | |||
| puglReshape(PuglView* view, int width, int height) | |||
| { | |||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
| if (view->reshapeFunc) { | |||
| view->reshapeFunc(view, width, height); | |||
| } else { | |||
| puglDefaultReshape(view, width, height); | |||
| } | |||
| view->width = width; | |||
| view->height = height; | |||
| } | |||
| static void | |||
| puglDisplay(PuglView* view) | |||
| { | |||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||
| view->redisplay = false; | |||
| if (view->displayFunc) { | |||
| view->displayFunc(view); | |||
| } | |||
| glFlush(); | |||
| if (view->impl->doubleBuffered) { | |||
| glXSwapBuffers(view->impl->display, view->impl->win); | |||
| } | |||
| } | |||
| static PuglKey | |||
| keySymToSpecial(KeySym sym) | |||
| { | |||
| @@ -302,184 +277,145 @@ keySymToSpecial(KeySym sym) | |||
| } | |||
| static void | |||
| translateKey(XEvent* xevent, PuglEvent* event) | |||
| setModifiers(PuglView* view, unsigned xstate, unsigned xtime) | |||
| { | |||
| KeySym sym; | |||
| char str[5]; | |||
| const int n = XLookupString(&xevent->xkey, str, 4, &sym, NULL); | |||
| if (n == 1) { | |||
| event->key.character = str[0]; // TODO: multi-byte support | |||
| } | |||
| event->key.special = keySymToSpecial(sym); | |||
| } | |||
| view->event_timestamp_ms = xtime; | |||
| 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; | |||
| view->mods = 0; | |||
| view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; | |||
| view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; | |||
| view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; | |||
| view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; | |||
| } | |||
| static PuglEvent | |||
| translateEvent(PuglView* view, XEvent xevent) | |||
| static void | |||
| dispatchKey(PuglView* view, XEvent* event, bool press) | |||
| { | |||
| PuglEvent event; | |||
| memset(&event, 0, sizeof(event)); | |||
| event.any.view = view; | |||
| event.any.send_event = xevent.xany.send_event; | |||
| switch (xevent.type) { | |||
| case ConfigureNotify: | |||
| event.type = PUGL_CONFIGURE; | |||
| event.configure.x = xevent.xconfigure.x; | |||
| event.configure.y = xevent.xconfigure.y; | |||
| event.configure.width = xevent.xconfigure.width; | |||
| event.configure.height = xevent.xconfigure.height; | |||
| break; | |||
| case Expose: | |||
| event.type = PUGL_EXPOSE; | |||
| event.expose.x = xevent.xexpose.x; | |||
| event.expose.y = xevent.xexpose.y; | |||
| event.expose.width = xevent.xexpose.width; | |||
| event.expose.height = xevent.xexpose.height; | |||
| event.expose.count = xevent.xexpose.count; | |||
| break; | |||
| case MotionNotify: | |||
| event.type = PUGL_MOTION_NOTIFY; | |||
| event.motion.time = xevent.xmotion.time; | |||
| event.motion.x = xevent.xmotion.x; | |||
| event.motion.y = xevent.xmotion.y; | |||
| event.motion.x_root = xevent.xmotion.x_root; | |||
| event.motion.y_root = xevent.xmotion.y_root; | |||
| event.motion.state = translateModifiers(xevent.xmotion.state); | |||
| event.motion.is_hint = (xevent.xmotion.is_hint == NotifyHint); | |||
| break; | |||
| case ButtonPress: | |||
| if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) { | |||
| event.type = PUGL_SCROLL; | |||
| event.scroll.time = xevent.xbutton.time; | |||
| event.scroll.x = xevent.xbutton.x; | |||
| event.scroll.y = xevent.xbutton.y; | |||
| event.scroll.x_root = xevent.xbutton.x_root; | |||
| event.scroll.y_root = xevent.xbutton.y_root; | |||
| event.scroll.state = translateModifiers(xevent.xbutton.state); | |||
| event.scroll.dx = 0.0; | |||
| event.scroll.dy = 0.0; | |||
| switch (xevent.xbutton.button) { | |||
| case 4: event.scroll.dy = 1.0f; break; | |||
| case 5: event.scroll.dy = -1.0f; break; | |||
| case 6: event.scroll.dx = -1.0f; break; | |||
| case 7: event.scroll.dx = 1.0f; break; | |||
| } | |||
| } | |||
| // nobreak | |||
| case ButtonRelease: | |||
| if (xevent.xbutton.button < 4 || xevent.xbutton.button > 7) { | |||
| event.button.type = ((xevent.type == ButtonPress) | |||
| ? PUGL_BUTTON_PRESS | |||
| : PUGL_BUTTON_RELEASE); | |||
| event.button.time = xevent.xbutton.time; | |||
| event.button.x = xevent.xbutton.x; | |||
| event.button.y = xevent.xbutton.y; | |||
| event.button.x_root = xevent.xbutton.x_root; | |||
| event.button.y_root = xevent.xbutton.y_root; | |||
| event.button.state = translateModifiers(xevent.xbutton.state); | |||
| event.button.button = xevent.xbutton.button; | |||
| } | |||
| break; | |||
| case KeyPress: | |||
| case KeyRelease: | |||
| event.type = ((xevent.type == KeyPress) | |||
| ? PUGL_KEY_PRESS | |||
| : PUGL_KEY_RELEASE); | |||
| event.key.time = xevent.xbutton.time; | |||
| event.key.x = xevent.xbutton.x; | |||
| event.key.y = xevent.xbutton.y; | |||
| event.key.x_root = xevent.xbutton.x_root; | |||
| event.key.y_root = xevent.xbutton.y_root; | |||
| event.key.state = translateModifiers(xevent.xbutton.state); | |||
| translateKey(&xevent, &event); | |||
| break; | |||
| case EnterNotify: | |||
| case LeaveNotify: | |||
| event.type = ((xevent.type == EnterNotify) | |||
| ? PUGL_ENTER_NOTIFY | |||
| : PUGL_LEAVE_NOTIFY); | |||
| event.crossing.time = xevent.xcrossing.time; | |||
| event.crossing.x = xevent.xcrossing.x; | |||
| event.crossing.y = xevent.xcrossing.y; | |||
| event.crossing.x_root = xevent.xcrossing.x_root; | |||
| event.crossing.y_root = xevent.xcrossing.y_root; | |||
| event.crossing.state = translateModifiers(xevent.xcrossing.state); | |||
| event.crossing.mode = PUGL_CROSSING_NORMAL; | |||
| if (xevent.xcrossing.mode == NotifyGrab) { | |||
| event.crossing.mode = PUGL_CROSSING_GRAB; | |||
| } else if (xevent.xcrossing.mode == NotifyUngrab) { | |||
| event.crossing.mode = PUGL_CROSSING_UNGRAB; | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| KeySym sym; | |||
| char str[5]; | |||
| const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); | |||
| return event; | |||
| } | |||
| if (sym == XK_Escape && view->closeFunc && !press && !view->parent) { | |||
| view->closeFunc(view); | |||
| view->redisplay = false; | |||
| return; | |||
| } | |||
| if (n == 0) { | |||
| return; | |||
| } | |||
| if (n > 1) { | |||
| fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); | |||
| return; | |||
| } | |||
| void | |||
| puglGrabFocus(PuglView* view) | |||
| { | |||
| XSetInputFocus( | |||
| view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); | |||
| const PuglKey special = keySymToSpecial(sym); | |||
| if (special && view->specialFunc) { | |||
| view->specialFunc(view, press, special); | |||
| } else if (!special && view->keyboardFunc) { | |||
| view->keyboardFunc(view, press, str[0]); | |||
| } | |||
| } | |||
| PuglStatus | |||
| puglProcessEvents(PuglView* view) | |||
| { | |||
| XEvent xevent; | |||
| XEvent event; | |||
| while (XPending(view->impl->display) > 0) { | |||
| XNextEvent(view->impl->display, &xevent); | |||
| bool ignore = false; | |||
| if (xevent.type == ClientMessage) { | |||
| // Handle close message | |||
| char* type = XGetAtomName(view->impl->display, | |||
| xevent.xclient.message_type); | |||
| if (!strcmp(type, "WM_PROTOCOLS") && view->closeFunc) { | |||
| view->closeFunc(view); | |||
| XNextEvent(view->impl->display, &event); | |||
| switch (event.type) { | |||
| case MapNotify: | |||
| puglReshape(view, view->width, view->height); | |||
| break; | |||
| case ConfigureNotify: | |||
| if ((event.xconfigure.width != view->width) || | |||
| (event.xconfigure.height != view->height)) { | |||
| puglReshape(view, | |||
| event.xconfigure.width, | |||
| event.xconfigure.height); | |||
| } | |||
| XFree(type); | |||
| continue; | |||
| } else if (xevent.type == KeyRelease) { | |||
| // Ignore key repeat if necessary | |||
| break; | |||
| case Expose: | |||
| if (event.xexpose.count != 0) { | |||
| break; | |||
| } | |||
| puglDisplay(view); | |||
| break; | |||
| case MotionNotify: | |||
| setModifiers(view, event.xmotion.state, event.xmotion.time); | |||
| if (view->motionFunc) { | |||
| view->motionFunc(view, event.xmotion.x, event.xmotion.y); | |||
| } | |||
| break; | |||
| case ButtonPress: | |||
| setModifiers(view, event.xbutton.state, event.xbutton.time); | |||
| if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { | |||
| if (view->scrollFunc) { | |||
| float dx = 0, dy = 0; | |||
| switch (event.xbutton.button) { | |||
| case 4: dy = 1.0f; break; | |||
| case 5: dy = -1.0f; break; | |||
| case 6: dx = -1.0f; break; | |||
| case 7: dx = 1.0f; break; | |||
| } | |||
| view->scrollFunc(view, event.xbutton.x, event.xbutton.y, dx, dy); | |||
| } | |||
| break; | |||
| } | |||
| // nobreak | |||
| case ButtonRelease: | |||
| setModifiers(view, event.xbutton.state, event.xbutton.time); | |||
| if (view->mouseFunc && | |||
| (event.xbutton.button < 4 || event.xbutton.button > 7)) { | |||
| view->mouseFunc(view, | |||
| event.xbutton.button, event.type == ButtonPress, | |||
| event.xbutton.x, event.xbutton.y); | |||
| } | |||
| break; | |||
| case KeyPress: | |||
| setModifiers(view, event.xkey.state, event.xkey.time); | |||
| dispatchKey(view, &event, true); | |||
| break; | |||
| case KeyRelease: { | |||
| setModifiers(view, event.xkey.state, event.xkey.time); | |||
| bool repeated = false; | |||
| 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); | |||
| ignore = true; | |||
| next.xkey.time == event.xkey.time && | |||
| next.xkey.keycode == event.xkey.keycode) { | |||
| XNextEvent(view->impl->display, &event); | |||
| repeated = true; | |||
| } | |||
| } | |||
| } | |||
| if (!ignore) { | |||
| // Translate and dispatch event | |||
| const PuglEvent event = translateEvent(view, xevent); | |||
| puglDispatchEvent(view, &event); | |||
| if (!repeated) { | |||
| dispatchKey(view, &event, false); | |||
| } | |||
| } break; | |||
| case ClientMessage: { | |||
| char* type = XGetAtomName(view->impl->display, | |||
| event.xclient.message_type); | |||
| if (!strcmp(type, "WM_PROTOCOLS")) { | |||
| if (view->closeFunc) { | |||
| view->closeFunc(view); | |||
| view->redisplay = false; | |||
| } | |||
| } | |||
| XFree(type); | |||
| } break; | |||
| #ifdef PUGL_GRAB_FOCUS | |||
| case EnterNotify: | |||
| XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); | |||
| break; | |||
| #endif | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| if (view->redisplay) { | |||
| const PuglEventExpose expose = { | |||
| PUGL_EXPOSE, view, true, 0.0, 0.0, (double)view->width, (double)view->height, 0 | |||
| }; | |||
| puglDispatchEvent(view, (const PuglEvent*)&expose); | |||
| puglDisplay(view); | |||
| } | |||
| return PUGL_SUCCESS; | |||
| @@ -496,17 +432,3 @@ puglGetNativeWindow(PuglView* view) | |||
| { | |||
| return view->impl->win; | |||
| } | |||
| void* | |||
| puglGetContext(PuglView* view) | |||
| { | |||
| #ifdef PUGL_HAVE_CAIRO | |||
| if (view->ctx_type == PUGL_CAIRO) { | |||
| return view->impl->cr; | |||
| } | |||
| #endif | |||
| return NULL; | |||
| // possibly unused | |||
| (void)view; | |||
| } | |||