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