| @@ -15,8 +15,7 @@ | |||||
| */ | */ | ||||
| // we need this for now | // we need this for now | ||||
| #define PUGL_HAVE_GL 1 | |||||
| #define PUGL_GRAB_FOCUS 1 | |||||
| //#define PUGL_GRAB_FOCUS 1 | |||||
| #include "AppPrivateData.hpp" | #include "AppPrivateData.hpp" | ||||
| #include "../Widget.hpp" | #include "../Widget.hpp" | ||||
| @@ -63,7 +62,7 @@ struct Window::PrivateData { | |||||
| PrivateData(App& app, Window* const self) | PrivateData(App& app, Window* const self) | ||||
| : fApp(app), | : fApp(app), | ||||
| fSelf(self), | fSelf(self), | ||||
| fView(puglInit(nullptr, nullptr)), | |||||
| fView(puglInit()), | |||||
| fFirstInit(true), | fFirstInit(true), | ||||
| fVisible(false), | fVisible(false), | ||||
| fResizable(true), | fResizable(true), | ||||
| @@ -91,7 +90,7 @@ struct Window::PrivateData { | |||||
| PrivateData(App& app, Window* const self, Window& parent) | PrivateData(App& app, Window* const self, Window& parent) | ||||
| : fApp(app), | : fApp(app), | ||||
| fSelf(self), | fSelf(self), | ||||
| fView(puglInit(nullptr, nullptr)), | |||||
| fView(puglInit()), | |||||
| fFirstInit(true), | fFirstInit(true), | ||||
| fVisible(false), | fVisible(false), | ||||
| fResizable(true), | fResizable(true), | ||||
| @@ -129,7 +128,7 @@ struct Window::PrivateData { | |||||
| PrivateData(App& app, Window* const self, const intptr_t parentId) | PrivateData(App& app, Window* const self, const intptr_t parentId) | ||||
| : fApp(app), | : fApp(app), | ||||
| fSelf(self), | fSelf(self), | ||||
| fView(puglInit(nullptr, nullptr)), | |||||
| fView(puglInit()), | |||||
| fFirstInit(true), | fFirstInit(true), | ||||
| fVisible(parentId != 0), | fVisible(parentId != 0), | ||||
| fResizable(parentId == 0), | fResizable(parentId == 0), | ||||
| @@ -193,7 +192,6 @@ struct Window::PrivateData { | |||||
| puglSetCloseFunc(fView, onCloseCallback); | puglSetCloseFunc(fView, onCloseCallback); | ||||
| puglCreateWindow(fView, nullptr); | puglCreateWindow(fView, nullptr); | ||||
| puglEnterContext(fView); | |||||
| PuglInternals* impl = fView->impl; | PuglInternals* impl = fView->impl; | ||||
| #if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
| @@ -458,7 +456,11 @@ struct Window::PrivateData { | |||||
| fResizable = yesNo; | 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? | // FIXME? | ||||
| const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0); | const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0); | ||||
| [mView setAutoresizingMask:flags]; | [mView setAutoresizingMask:flags]; | ||||
| @@ -471,7 +473,7 @@ struct Window::PrivateData { | |||||
| void setSize(uint width, uint height, const bool forced = false) | 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); | DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height); | ||||
| return; | return; | ||||
| @@ -486,18 +488,15 @@ struct Window::PrivateData { | |||||
| fWidth = width; | fWidth = width; | ||||
| fHeight = height; | 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) | #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) }; | 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) | if (! forced) | ||||
| UpdateWindow(hwnd); | 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 <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 PUGL_SHARED | ||||
| # ifdef _WIN32 | # ifdef _WIN32 | ||||
| @@ -40,7 +51,11 @@ | |||||
| # define PUGL_API PUGL_LIB_IMPORT | # define PUGL_API PUGL_LIB_IMPORT | ||||
| # endif | # endif | ||||
| #else | #else | ||||
| # define PUGL_API | |||||
| # ifdef _WIN32 | |||||
| # define PUGL_API | |||||
| # else | |||||
| # define PUGL_API __attribute__((visibility("hidden"))) | |||||
| # endif | |||||
| #endif | #endif | ||||
| #ifdef __cplusplus | #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. | 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. | so programs should handle any value gracefully. | ||||
| @param view The view being scrolled. | @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 x distance. | ||||
| @param dx The scroll y 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. | A function called when a special key is pressed or released. | ||||
| This callback allows the use of keys that do not have unicode points. | 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 view The view the event occured in. | ||||
| @param press True if the key was pressed, false if released. | @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); | 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. | Create a Pugl context. | ||||
| To create a window, call the various puglInit* functions as necessary, then | To create a window, call the various puglInit* functions as necessary, then | ||||
| call puglCreateWindow(). | call puglCreateWindow(). | ||||
| @param pargc Pointer to argument count (unused, for GLUT compatibility). | |||||
| @param argv Arguments (unused, for GLUT compatibility). | |||||
| */ | */ | ||||
| PUGL_API PuglView* | PUGL_API PuglView* | ||||
| puglInit(int* pargc, char** argv); | |||||
| puglInit(void); | |||||
| /** | /** | ||||
| Set the parent window before creating a window (for embedding). | 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. | Enable or disable resizing before creating a window. | ||||
| */ | */ | ||||
| PUGL_API void | 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. | Create a window with the settings given by the various puglInit functions. | ||||
| @@ -206,16 +266,6 @@ puglShowWindow(PuglView* view); | |||||
| PUGL_API void | PUGL_API void | ||||
| puglHideWindow(PuglView* view); | puglHideWindow(PuglView* view); | ||||
| /** | |||||
| Return the native window handle. | |||||
| */ | |||||
| PUGL_API PuglNativeWindow | |||||
| puglGetNativeWindow(PuglView* view); | |||||
| /** | |||||
| @} | |||||
| */ | |||||
| /** | /** | ||||
| Set the handle to be passed to all callbacks. | Set the handle to be passed to all callbacks. | ||||
| @@ -234,15 +284,6 @@ puglSetHandle(PuglView* view, PuglHandle handle); | |||||
| PUGL_API PuglHandle | PUGL_API PuglHandle | ||||
| puglGetHandle(PuglView* view); | 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. | Return the timestamp (if any) of the currently-processing event. | ||||
| */ | */ | ||||
| @@ -263,18 +304,6 @@ puglGetModifiers(PuglView* view); | |||||
| PUGL_API void | PUGL_API void | ||||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore); | 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. | Set the function to call when the window is closed. | ||||
| */ | */ | ||||
| @@ -324,14 +353,10 @@ PUGL_API void | |||||
| puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | 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. | 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 | Permission to use, copy, modify, and/or distribute this software for any | ||||
| purpose with or without fee is hereby granted, provided that the above | 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 | If you are copying the pugl code into your source tree, the following | ||||
| symbols can be defined to tweak pugl behaviour: | 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_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus. | ||||
| PUGL_VERBOSE: Print GL information to console. | PUGL_VERBOSE: Print GL information to console. | ||||
| */ | */ | ||||
| #include "pugl/pugl.h" | |||||
| #include "pugl/event.h" | |||||
| #include "pugl.h" | |||||
| #ifdef PUGL_VERBOSE | #ifdef PUGL_VERBOSE | ||||
| # include <stdio.h> | # include <stdio.h> | ||||
| @@ -46,7 +43,6 @@ typedef struct PuglInternalsImpl PuglInternals; | |||||
| struct PuglViewImpl { | struct PuglViewImpl { | ||||
| PuglHandle handle; | PuglHandle handle; | ||||
| PuglEventFunc eventFunc; | |||||
| PuglCloseFunc closeFunc; | PuglCloseFunc closeFunc; | ||||
| PuglDisplayFunc displayFunc; | PuglDisplayFunc displayFunc; | ||||
| PuglKeyboardFunc keyboardFunc; | PuglKeyboardFunc keyboardFunc; | ||||
| @@ -57,9 +53,7 @@ struct PuglViewImpl { | |||||
| PuglSpecialFunc specialFunc; | PuglSpecialFunc specialFunc; | ||||
| PuglInternals* impl; | PuglInternals* impl; | ||||
| PuglNativeWindow parent; | PuglNativeWindow parent; | ||||
| PuglContextType ctx_type; | |||||
| int width; | int width; | ||||
| int height; | int height; | ||||
| @@ -71,10 +65,10 @@ struct PuglViewImpl { | |||||
| uint32_t event_timestamp_ms; | uint32_t event_timestamp_ms; | ||||
| }; | }; | ||||
| PuglInternals* puglInitInternals(); | |||||
| PuglInternals* puglInitInternals(void); | |||||
| PuglView* | PuglView* | ||||
| puglInit(int* pargc, char** argv) | |||||
| puglInit(void) | |||||
| { | { | ||||
| PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | ||||
| if (!view) { | if (!view) { | ||||
| @@ -92,9 +86,6 @@ puglInit(int* pargc, char** argv) | |||||
| view->height = 480; | view->height = 480; | ||||
| return view; | return view; | ||||
| // unused | |||||
| (void)pargc; (void)argv; | |||||
| } | } | ||||
| void | void | ||||
| @@ -116,12 +107,6 @@ puglInitResizable(PuglView* view, bool resizable) | |||||
| view->resizable = resizable; | view->resizable = resizable; | ||||
| } | } | ||||
| void | |||||
| puglInitContextType(PuglView* view, PuglContextType type) | |||||
| { | |||||
| view->ctx_type = type; | |||||
| } | |||||
| void | void | ||||
| puglSetHandle(PuglView* view, PuglHandle handle) | puglSetHandle(PuglView* view, PuglHandle handle) | ||||
| { | { | ||||
| @@ -146,16 +131,26 @@ puglGetModifiers(PuglView* view) | |||||
| return view->mods; | 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 | void | ||||
| puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc) | |||||
| puglIgnoreKeyRepeat(PuglView* view, bool ignore) | |||||
| { | { | ||||
| view->eventFunc = eventFunc; | |||||
| view->ignoreKeyRepeat = ignore; | |||||
| } | } | ||||
| void | void | ||||
| @@ -205,81 +200,3 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||||
| { | { | ||||
| view->specialFunc = 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 | Permission to use, copy, modify, and/or distribute this software for any | ||||
| purpose with or without fee is hereby granted, provided that the above | purpose with or without fee is hereby granted, provided that the above | ||||
| @@ -22,7 +22,7 @@ | |||||
| #import <Cocoa/Cocoa.h> | #import <Cocoa/Cocoa.h> | ||||
| #include "pugl/pugl_internal.h" | |||||
| #include "pugl_internal.h" | |||||
| @interface PuglWindow : NSWindow | @interface PuglWindow : NSWindow | ||||
| { | { | ||||
| @@ -36,8 +36,6 @@ | |||||
| defer:(BOOL)flag; | defer:(BOOL)flag; | ||||
| - (void) setPuglview:(PuglView*)view; | - (void) setPuglview:(PuglView*)view; | ||||
| - (BOOL) windowShouldClose:(id)sender; | - (BOOL) windowShouldClose:(id)sender; | ||||
| - (BOOL) canBecomeKeyWindow:(id)sender; | |||||
| - (BOOL) canBecomeMainWindow:(id)sender; | |||||
| @end | @end | ||||
| @implementation PuglWindow | @implementation PuglWindow | ||||
| @@ -78,22 +76,6 @@ | |||||
| (void)sender; | (void)sender; | ||||
| } | } | ||||
| - (BOOL) canBecomeKeyWindow:(id)sender | |||||
| { | |||||
| return YES; | |||||
| // unused | |||||
| (void)sender; | |||||
| } | |||||
| - (BOOL) canBecomeMainWindow:(id)sender | |||||
| { | |||||
| return YES; | |||||
| // unused | |||||
| (void)sender; | |||||
| } | |||||
| @end | @end | ||||
| static void | static void | ||||
| @@ -214,6 +196,8 @@ puglDisplay(PuglView* view) | |||||
| if (puglview->reshapeFunc) { | if (puglview->reshapeFunc) { | ||||
| puglview->reshapeFunc(puglview, width, height); | puglview->reshapeFunc(puglview, width, height); | ||||
| } else { | |||||
| puglDefaultReshape(puglview, width, height); | |||||
| } | } | ||||
| puglview->width = width; | 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 | Permission to use, copy, modify, and/or distribute this software for any | ||||
| purpose with or without fee is hereby granted, provided that the above | purpose with or without fee is hereby granted, provided that the above | ||||
| @@ -22,11 +22,11 @@ | |||||
| #include <windowsx.h> | #include <windowsx.h> | ||||
| #include <GL/gl.h> | #include <GL/gl.h> | ||||
| #include <ctime> | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <time.h> | |||||
| #include "pugl/pugl_internal.h" | |||||
| #include "pugl_internal.h" | |||||
| #ifndef WM_MOUSEWHEEL | #ifndef WM_MOUSEWHEEL | ||||
| # define WM_MOUSEWHEEL 0x020A | # define WM_MOUSEWHEEL 0x020A | ||||
| @@ -38,7 +38,9 @@ | |||||
| # define WHEEL_DELTA 120 | # define WHEEL_DELTA 120 | ||||
| #endif | #endif | ||||
| #define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) | |||||
| const int LOCAL_CLOSE_MSG = WM_USER + 50; | |||||
| HINSTANCE hInstance = NULL; | |||||
| struct PuglInternalsImpl { | struct PuglInternalsImpl { | ||||
| HWND hwnd; | HWND hwnd; | ||||
| @@ -47,8 +49,6 @@ struct PuglInternalsImpl { | |||||
| WNDCLASS wc; | WNDCLASS wc; | ||||
| }; | }; | ||||
| static HINSTANCE hInstance = NULL; | |||||
| LRESULT CALLBACK | LRESULT CALLBACK | ||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | ||||
| @@ -69,27 +69,6 @@ puglInitInternals() | |||||
| return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | 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 | int | ||||
| puglCreateWindow(PuglView* view, const char* title) | 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? | // Should class be a parameter? Does this make sense on other platforms? | ||||
| static int wc_count = 0; | static int wc_count = 0; | ||||
| char classNameBuf[256]; | 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.style = CS_OWNDC; | ||||
| impl->wc.lpfnWndProc = wndProc; | impl->wc.lpfnWndProc = wndProc; | ||||
| @@ -116,37 +96,35 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
| impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | ||||
| impl->wc.lpszMenuName = NULL; | impl->wc.lpszMenuName = NULL; | ||||
| impl->wc.lpszClassName = strdup(classNameBuf); | 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 | // 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 }; | 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( | impl->hwnd = CreateWindowEx( | ||||
| WS_EX_TOPMOST, | WS_EX_TOPMOST, | ||||
| classNameBuf, title, | 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, | CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, | ||||
| (HWND)view->parent, NULL, hInstance, NULL); | (HWND)view->parent, NULL, hInstance, NULL); | ||||
| if (!impl->hwnd) { | if (!impl->hwnd) { | ||||
| UnregisterClass(impl->wc.lpszClassName, hInstance); | |||||
| UnregisterClass(impl->wc.lpszClassName, NULL); | |||||
| free((void*)impl->wc.lpszClassName); | free((void*)impl->wc.lpszClassName); | ||||
| free(impl); | free(impl); | ||||
| free(view); | free(view); | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| #ifdef _WIN64 | |||||
| SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); | SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); | ||||
| #else | |||||
| SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view); | |||||
| #endif | |||||
| impl->hdc = GetDC(impl->hwnd); | impl->hdc = GetDC(impl->hwnd); | ||||
| @@ -164,6 +142,16 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
| SetPixelFormat(impl->hdc, format, &pfd); | SetPixelFormat(impl->hdc, format, &pfd); | ||||
| impl->hglrc = wglCreateContext(impl->hdc); | 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); | wglMakeCurrent(impl->hdc, impl->hglrc); | ||||
| return 0; | return 0; | ||||
| @@ -192,7 +180,7 @@ puglDestroy(PuglView* view) | |||||
| wglDeleteContext(view->impl->hglrc); | wglDeleteContext(view->impl->hglrc); | ||||
| ReleaseDC(view->impl->hwnd, view->impl->hdc); | ReleaseDC(view->impl->hwnd, view->impl->hdc); | ||||
| DestroyWindow(view->impl->hwnd); | DestroyWindow(view->impl->hwnd); | ||||
| UnregisterClass(view->impl->wc.lpszClassName, hInstance); | |||||
| UnregisterClass(view->impl->wc.lpszClassName, NULL); | |||||
| free((void*)view->impl->wc.lpszClassName); | free((void*)view->impl->wc.lpszClassName); | ||||
| free(view->impl); | free(view->impl); | ||||
| free(view); | free(view); | ||||
| @@ -201,10 +189,12 @@ puglDestroy(PuglView* view) | |||||
| static void | static void | ||||
| puglReshape(PuglView* view, int width, int height) | puglReshape(PuglView* view, int width, int height) | ||||
| { | { | ||||
| puglEnterContext(view); | |||||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||||
| if (view->reshapeFunc) { | if (view->reshapeFunc) { | ||||
| view->reshapeFunc(view, width, height); | view->reshapeFunc(view, width, height); | ||||
| } else { | |||||
| puglDefaultReshape(view, width, height); | |||||
| } | } | ||||
| view->width = width; | view->width = width; | ||||
| @@ -214,14 +204,15 @@ puglReshape(PuglView* view, int width, int height) | |||||
| static void | static void | ||||
| puglDisplay(PuglView* view) | puglDisplay(PuglView* view) | ||||
| { | { | ||||
| puglEnterContext(view); | |||||
| wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||||
| view->redisplay = false; | |||||
| if (view->displayFunc) { | if (view->displayFunc) { | ||||
| view->displayFunc(view); | view->displayFunc(view); | ||||
| } | } | ||||
| puglLeaveContext(view, true); | |||||
| view->redisplay = false; | |||||
| glFlush(); | |||||
| SwapBuffers(view->impl->hdc); | |||||
| } | } | ||||
| static PuglKey | static PuglKey | ||||
| @@ -310,7 +301,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| break; | break; | ||||
| case WM_MOUSEMOVE: | case WM_MOUSEMOVE: | ||||
| if (view->motionFunc) { | if (view->motionFunc) { | ||||
| view->event_timestamp_ms = GetMessageTime(); | |||||
| view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -335,17 +325,21 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| case WM_MOUSEWHEEL: | case WM_MOUSEWHEEL: | ||||
| if (view->scrollFunc) { | if (view->scrollFunc) { | ||||
| view->event_timestamp_ms = GetMessageTime(); | view->event_timestamp_ms = GetMessageTime(); | ||||
| POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||||
| ScreenToClient(view->impl->hwnd, &pt); | |||||
| view->scrollFunc( | 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; | break; | ||||
| case WM_MOUSEHWHEEL: | case WM_MOUSEHWHEEL: | ||||
| if (view->scrollFunc) { | if (view->scrollFunc) { | ||||
| view->event_timestamp_ms = GetMessageTime(); | view->event_timestamp_ms = GetMessageTime(); | ||||
| POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; | |||||
| ScreenToClient(view->impl->hwnd, &pt); | |||||
| view->scrollFunc( | 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; | break; | ||||
| case WM_KEYDOWN: | case WM_KEYDOWN: | ||||
| @@ -359,11 +353,18 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| view->specialFunc(view, message == WM_KEYDOWN, key); | view->specialFunc(view, message == WM_KEYDOWN, key); | ||||
| } | } | ||||
| } else if (view->keyboardFunc) { | } 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; | break; | ||||
| case WM_QUIT: | case WM_QUIT: | ||||
| case PUGL_LOCAL_CLOSE_MSG: | |||||
| case LOCAL_CLOSE_MSG: | |||||
| if (view->closeFunc) { | if (view->closeFunc) { | ||||
| view->closeFunc(view); | view->closeFunc(view); | ||||
| } | } | ||||
| @@ -376,12 +377,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| void | |||||
| puglGrabFocus(PuglView* /*view*/) | |||||
| { | |||||
| // TODO | |||||
| } | |||||
| PuglStatus | PuglStatus | ||||
| puglProcessEvents(PuglView* view) | puglProcessEvents(PuglView* view) | ||||
| { | { | ||||
| @@ -390,7 +385,6 @@ puglProcessEvents(PuglView* view) | |||||
| handleMessage(view, msg.message, msg.wParam, msg.lParam); | handleMessage(view, msg.message, msg.wParam, msg.lParam); | ||||
| } | } | ||||
| if (view->redisplay) { | if (view->redisplay) { | ||||
| InvalidateRect(view->impl->hwnd, NULL, FALSE); | InvalidateRect(view->impl->hwnd, NULL, FALSE); | ||||
| } | } | ||||
| @@ -401,23 +395,19 @@ puglProcessEvents(PuglView* view) | |||||
| LRESULT CALLBACK | LRESULT CALLBACK | ||||
| wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| #ifdef _WIN64 | |||||
| PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); | PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); | ||||
| #else | |||||
| PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA); | |||||
| #endif | |||||
| switch (message) { | switch (message) { | ||||
| case WM_CREATE: | case WM_CREATE: | ||||
| PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | ||||
| return 0; | return 0; | ||||
| case WM_CLOSE: | case WM_CLOSE: | ||||
| PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); | |||||
| PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); | |||||
| return 0; | return 0; | ||||
| case WM_DESTROY: | case WM_DESTROY: | ||||
| return 0; | return 0; | ||||
| default: | default: | ||||
| if (view) { | |||||
| if (view && hwnd == view->impl->hwnd) { | |||||
| return handleMessage(view, message, wParam, lParam); | return handleMessage(view, message, wParam, lParam); | ||||
| } else { | } else { | ||||
| return DefWindowProc(hwnd, message, wParam, lParam); | return DefWindowProc(hwnd, message, wParam, lParam); | ||||
| @@ -1,7 +1,7 @@ | |||||
| /* | /* | ||||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | Copyright 2012-2014 David Robillard <http://drobilla.net> | ||||
| Copyright 2013 Robin Gareus <robin@gareus.org> | |||||
| Copyright 2011-2012 Ben Loftis, Harrison Consoles | 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 | Permission to use, copy, modify, and/or distribute this software for any | ||||
| purpose with or without fee is hereby granted, provided that the above | purpose with or without fee is hereby granted, provided that the above | ||||
| @@ -24,170 +24,101 @@ | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <GL/gl.h> | |||||
| #include <GL/glx.h> | |||||
| #include <X11/Xatom.h> | #include <X11/Xatom.h> | ||||
| #include <X11/Xlib.h> | #include <X11/Xlib.h> | ||||
| #include <X11/Xutil.h> | |||||
| #include <X11/keysym.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 { | struct PuglInternalsImpl { | ||||
| Display* display; | Display* display; | ||||
| int screen; | int screen; | ||||
| Window win; | Window win; | ||||
| #ifdef PUGL_HAVE_CAIRO | |||||
| cairo_t* cr; | |||||
| #endif | |||||
| #ifdef PUGL_HAVE_GL | |||||
| GLXContext ctx; | GLXContext ctx; | ||||
| Bool doubleBuffered; | 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 | int | ||||
| puglCreateWindow(PuglView* view, const char* title) | puglCreateWindow(PuglView* view, const char* title) | ||||
| { | { | ||||
| PuglInternals* const impl = view->impl; | |||||
| PuglInternals* impl = view->impl; | |||||
| impl->display = XOpenDisplay(0); | impl->display = XOpenDisplay(0); | ||||
| impl->screen = DefaultScreen(impl->display); | 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) { | 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 xParent = view->parent | ||||
| ? (Window)view->parent | ? (Window)view->parent | ||||
| : RootWindow(impl->display, impl->screen); | : RootWindow(impl->display, impl->screen); | ||||
| @@ -197,21 +128,20 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
| XSetWindowAttributes attr; | XSetWindowAttributes attr; | ||||
| memset(&attr, 0, sizeof(XSetWindowAttributes)); | 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->win = XCreateWindow( | ||||
| impl->display, xParent, | impl->display, xParent, | ||||
| 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | 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; | XSizeHints sizeHints; | ||||
| memset(&sizeHints, 0, sizeof(sizeHints)); | memset(&sizeHints, 0, sizeof(sizeHints)); | ||||
| @@ -233,21 +163,33 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
| XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | 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); | XFree(vi); | ||||
| glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| void | void | ||||
| puglShowWindow(PuglView* view) | puglShowWindow(PuglView* view) | ||||
| { | { | ||||
| XMapRaised(view->impl->display, view->impl->win); | |||||
| PuglInternals* impl = view->impl; | |||||
| XMapRaised(impl->display, impl->win); | |||||
| } | } | ||||
| void | void | ||||
| puglHideWindow(PuglView* view) | puglHideWindow(PuglView* view) | ||||
| { | { | ||||
| XUnmapWindow(view->impl->display, view->impl->win); | |||||
| PuglInternals* impl = view->impl; | |||||
| XUnmapWindow(impl->display, impl->win); | |||||
| } | } | ||||
| void | void | ||||
| @@ -257,13 +199,46 @@ puglDestroy(PuglView* view) | |||||
| return; | return; | ||||
| } | } | ||||
| destroyContext(view); | |||||
| glXDestroyContext(view->impl->display, view->impl->ctx); | |||||
| XDestroyWindow(view->impl->display, view->impl->win); | XDestroyWindow(view->impl->display, view->impl->win); | ||||
| XCloseDisplay(view->impl->display); | XCloseDisplay(view->impl->display); | ||||
| free(view->impl); | free(view->impl); | ||||
| free(view); | 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 | static PuglKey | ||||
| keySymToSpecial(KeySym sym) | keySymToSpecial(KeySym sym) | ||||
| { | { | ||||
| @@ -302,184 +277,145 @@ keySymToSpecial(KeySym sym) | |||||
| } | } | ||||
| static void | 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 | PuglStatus | ||||
| puglProcessEvents(PuglView* view) | puglProcessEvents(PuglView* view) | ||||
| { | { | ||||
| XEvent xevent; | |||||
| XEvent event; | |||||
| while (XPending(view->impl->display) > 0) { | 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 && | if (view->ignoreKeyRepeat && | ||||
| XEventsQueued(view->impl->display, QueuedAfterReading)) { | XEventsQueued(view->impl->display, QueuedAfterReading)) { | ||||
| XEvent next; | XEvent next; | ||||
| XPeekEvent(view->impl->display, &next); | XPeekEvent(view->impl->display, &next); | ||||
| if (next.type == KeyPress && | 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) { | 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; | return PUGL_SUCCESS; | ||||
| @@ -496,17 +432,3 @@ puglGetNativeWindow(PuglView* view) | |||||
| { | { | ||||
| return view->impl->win; | 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; | |||||
| } | |||||