@@ -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, 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,11 +49,10 @@ 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); | ||||
#if 0 | |||||
extern "C" { | extern "C" { | ||||
BOOL WINAPI | BOOL WINAPI | ||||
DllMain(HINSTANCE hInst, DWORD, LPVOID) | DllMain(HINSTANCE hInst, DWORD, LPVOID) | ||||
@@ -60,6 +61,7 @@ DllMain(HINSTANCE hInst, DWORD, LPVOID) | |||||
return 1; | return 1; | ||||
} | } | ||||
} // extern "C" | } // extern "C" | ||||
#endif | |||||
PuglInternals* | PuglInternals* | ||||
puglInitInternals() | puglInitInternals() | ||||
@@ -67,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) | ||||
{ | { | ||||
@@ -101,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; | ||||
@@ -114,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, 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); | ||||
@@ -162,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; | ||||
@@ -190,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); | ||||
@@ -199,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; | ||||
@@ -212,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 | ||||
@@ -308,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; | ||||
@@ -333,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: | ||||
@@ -357,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); | ||||
} | } | ||||
@@ -374,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) | ||||
{ | { | ||||
@@ -388,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); | ||||
} | } | ||||
@@ -399,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; | |||||
} |