@@ -1,492 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/** | |||||
@file pugl.h API for Pugl, a minimal portable API for OpenGL. | |||||
*/ | |||||
#ifndef PUGL_H_INCLUDED | |||||
#define PUGL_H_INCLUDED | |||||
#include <stdint.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 <winsock2.h> | |||||
# include <windows.h> /* Broken Windows GL headers require this */ | |||||
# endif | |||||
# include <GL/gl.h> | |||||
#endif | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#else | |||||
# include <stdbool.h> | |||||
#endif | |||||
/** | |||||
@defgroup pugl Pugl | |||||
A minimal portable API for OpenGL. | |||||
@{ | |||||
*/ | |||||
/** | |||||
A Pugl view. | |||||
*/ | |||||
typedef struct PuglViewImpl PuglView; | |||||
/** | |||||
A native window handle. | |||||
On X11, this is a Window. | |||||
On OSX, this is an NSView*. | |||||
On Windows, this is a HWND. | |||||
*/ | |||||
typedef intptr_t PuglNativeWindow; | |||||
/** | |||||
Return status code. | |||||
*/ | |||||
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 << 0, /**< 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. | |||||
*/ | |||||
typedef void (*PuglCloseFunc)(PuglView* view); | |||||
/** | |||||
A function called to draw the view contents with OpenGL. | |||||
*/ | |||||
typedef void (*PuglDisplayFunc)(PuglView* view); | |||||
/** | |||||
A function called when a key is pressed or released. | |||||
@param view The view the event occured in. | |||||
@param press True if the key was pressed, false if released. | |||||
@param key Unicode point of the key pressed. | |||||
@return 0 if event was handled, otherwise send event to parent window. | |||||
*/ | |||||
typedef int (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); | |||||
/** | |||||
A function called when the pointer moves. | |||||
@param view The view the event occured in. | |||||
@param x The window-relative x coordinate of the pointer. | |||||
@param y The window-relative y coordinate of the pointer. | |||||
*/ | |||||
typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); | |||||
/** | |||||
A function called when a mouse button is pressed or released. | |||||
@param view The view the event occured in. | |||||
@param button The button number (1 = left, 2 = middle, 3 = right). | |||||
@param press True if the key was pressed, false if released. | |||||
@param x The window-relative x coordinate of the pointer. | |||||
@param y The window-relative y coordinate of the pointer. | |||||
*/ | |||||
typedef void (*PuglMouseFunc)( | |||||
PuglView* view, int button, bool press, int x, int y); | |||||
/** | |||||
A function called when the view is resized. | |||||
@param view The view being resized. | |||||
@param width The new view width. | |||||
@param height The new view height. | |||||
*/ | |||||
typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); | |||||
/** | |||||
A function called outside of gl-context when the plugin schedules a resize via puglPostResize. | |||||
@param view The view being resized. | |||||
@param width The new width to resize to (variable is initialized to current size) | |||||
@param height The new height to resize to (variable is initialized to current size) | |||||
@param set_hints If not null, set window-hints | |||||
*/ | |||||
typedef void (*PuglResizeFunc)(PuglView* view, int *width, int *height, int *set_hints); | |||||
/** | |||||
A function called on scrolling (e.g. mouse wheel or track pad). | |||||
The distances used here are in "lines", a single tick of a clicking mouse | |||||
wheel. For example, @p 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. | |||||
@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); | |||||
/** | |||||
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. | |||||
@param key The key pressed. | |||||
@return 0 if event was handled, otherwise send event to parent window. | |||||
*/ | |||||
typedef int (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | |||||
/** | |||||
A function called when a filename is selected via file-browser. | |||||
@param view The view the event occured in. | |||||
@param filename The selected file name or NULL if the dialog was canceled. | |||||
*/ | |||||
typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename); | |||||
/** | |||||
@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(). | |||||
*/ | |||||
PuglView* | |||||
puglInit(void); | |||||
/** | |||||
Set the parent window before creating a window (for embedding). | |||||
*/ | |||||
void | |||||
puglInitWindowParent(PuglView* view, PuglNativeWindow parent); | |||||
/** | |||||
Set the window size before creating a window. | |||||
*/ | |||||
void | |||||
puglInitWindowSize(PuglView* view, int width, int height); | |||||
/** | |||||
Set the minimum window size before creating a window. | |||||
*/ | |||||
void | |||||
puglInitWindowMinSize(PuglView* view, int width, int height); | |||||
/** | |||||
Enable or disable resizing before creating a window. | |||||
*/ | |||||
void | |||||
puglInitUserResizable(PuglView* view, bool resizable); | |||||
/** | |||||
Set transient parent before creating a window. | |||||
On X11, parent_id must be a Window. | |||||
On OSX, parent_id must be an NSView*. | |||||
*/ | |||||
void | |||||
puglInitTransientFor(PuglView* view, uintptr_t parent); | |||||
/** | |||||
@} | |||||
*/ | |||||
/** | |||||
@name Windows | |||||
Window management functions. | |||||
@{ | |||||
*/ | |||||
/** | |||||
Create a window with the settings given by the various puglInit functions. | |||||
@return 1 (pugl does not currently support multiple windows). | |||||
*/ | |||||
int | |||||
puglCreateWindow(PuglView* view, const char* title); | |||||
/** | |||||
Create a new GL window. | |||||
@param parent Parent window, or 0 for top level. | |||||
@param title Window title, or NULL. | |||||
@param width Window width in pixels. | |||||
@param height Window height in pixels. | |||||
@param resizable Whether window should be user resizable. | |||||
*/ | |||||
PuglView* | |||||
puglCreate(PuglNativeWindow parent, | |||||
const char* title, | |||||
int min_width, | |||||
int min_height, | |||||
int width, | |||||
int height, | |||||
bool resizable, | |||||
unsigned long transientId); | |||||
/** | |||||
Show Window (external ui) | |||||
*/ | |||||
void | |||||
puglShowWindow(PuglView* view); | |||||
/** | |||||
Hide Window (external ui) | |||||
*/ | |||||
void | |||||
puglHideWindow(PuglView* view); | |||||
/** | |||||
Return the native window handle. | |||||
*/ | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view); | |||||
/** | |||||
@} | |||||
*/ | |||||
/** | |||||
Set the handle to be passed to all callbacks. | |||||
This is generally a pointer to a struct which contains all necessary state. | |||||
Everything needed in callbacks should be here, not in static variables. | |||||
Note the lack of this facility makes GLUT unsuitable for plugins or | |||||
non-trivial programs; this mistake is largely why Pugl exists. | |||||
*/ | |||||
void | |||||
puglSetHandle(PuglView* view, PuglHandle handle); | |||||
/** | |||||
Get the handle to be passed to all callbacks. | |||||
*/ | |||||
PuglHandle | |||||
puglGetHandle(PuglView* view); | |||||
/** | |||||
Get the drawing context. | |||||
For Cairo contexts, this returns a pointer to a cairo_t. | |||||
For everything else, this is unused and returns NULL. | |||||
*/ | |||||
void* | |||||
puglGetContext(PuglView* view); | |||||
/** | |||||
Return the timestamp (if any) of the currently-processing event. | |||||
*/ | |||||
uint32_t | |||||
puglGetEventTimestamp(PuglView* view); | |||||
/** | |||||
Get the currently active modifiers (PuglMod flags). | |||||
This should only be called from an event handler. | |||||
*/ | |||||
int | |||||
puglGetModifiers(PuglView* view); | |||||
/** | |||||
Ignore synthetic repeated key events. | |||||
*/ | |||||
void | |||||
puglIgnoreKeyRepeat(PuglView* view, bool ignore); | |||||
/** | |||||
@name Event Callbacks | |||||
Functions to set event callbacks for handling user input. | |||||
@{ | |||||
*/ | |||||
/** | |||||
Set the function to call when the window is closed. | |||||
*/ | |||||
void | |||||
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); | |||||
/** | |||||
Set the display function which should draw the UI using GL. | |||||
*/ | |||||
void | |||||
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); | |||||
/** | |||||
Set the function to call on keyboard events. | |||||
*/ | |||||
void | |||||
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); | |||||
/** | |||||
Set the function to call on mouse motion. | |||||
*/ | |||||
void | |||||
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); | |||||
/** | |||||
Set the function to call on mouse button events. | |||||
*/ | |||||
void | |||||
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); | |||||
/** | |||||
Set the function to call on scroll events. | |||||
*/ | |||||
void | |||||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); | |||||
/** | |||||
Set the function to call on special events. | |||||
*/ | |||||
void | |||||
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||||
/** | |||||
Set the function to call when the window size changes. | |||||
*/ | |||||
void | |||||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | |||||
/** | |||||
Set callback function to change window size. | |||||
*/ | |||||
void | |||||
puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc); | |||||
/** | |||||
Set the function to call on file-browser selections. | |||||
*/ | |||||
void | |||||
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); | |||||
/** | |||||
@} | |||||
*/ | |||||
/** | |||||
TODO document this. | |||||
*/ | |||||
int | |||||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect); | |||||
/** | |||||
Grab the input focus. | |||||
*/ | |||||
void | |||||
puglGrabFocus(PuglView* view); | |||||
/** | |||||
Process all pending window events. | |||||
This handles input events as well as rendering, so it should be called | |||||
regularly and rapidly enough to keep the UI responsive. | |||||
*/ | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view); | |||||
/** | |||||
Request a redisplay on the next call to puglProcessEvents(). | |||||
*/ | |||||
void | |||||
puglPostRedisplay(PuglView* view); | |||||
/** | |||||
Request a resize on the next call to puglProcessEvents(). | |||||
*/ | |||||
void | |||||
puglPostResize(PuglView* view); | |||||
/** | |||||
Destroy a GL window. | |||||
*/ | |||||
void | |||||
puglDestroy(PuglView* view); | |||||
/** | |||||
@} | |||||
*/ | |||||
#ifdef __cplusplus | |||||
} /* extern "C" */ | |||||
#endif | |||||
#endif /* PUGL_H_INCLUDED */ |
@@ -1,441 +0,0 @@ | |||||
/* | |||||
Copyright 2019 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/** | |||||
@file pugl_haiku.cpp BeOS/HaikuOS Pugl Implementation. | |||||
*/ | |||||
#include <Application.h> | |||||
#include <interface/Window.h> | |||||
#ifdef PUGL_CAIRO | |||||
#include <cairo/cairo.h> | |||||
typedef BView BViewType; | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
#include <GL/gl.h> | |||||
#include <opengl/GLView.h> | |||||
typedef BGLView BViewType; | |||||
#endif | |||||
#include "pugl_internal.h" | |||||
class DWindow; | |||||
struct PuglInternalsImpl { | |||||
BApplication* app; | |||||
BViewType* view; | |||||
DWindow* window; | |||||
}; | |||||
static void | |||||
puglReshape(PuglView* view, int width, int height) | |||||
{ | |||||
puglEnterContext(view); | |||||
if (view->reshapeFunc) { | |||||
view->reshapeFunc(view, width, height); | |||||
} else { | |||||
puglDefaultReshape(width, height); | |||||
} | |||||
puglLeaveContext(view, false); | |||||
view->width = width; | |||||
view->height = height; | |||||
} | |||||
static void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
puglEnterContext(view); | |||||
view->redisplay = false; | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
puglLeaveContext(view, true); | |||||
} | |||||
void | |||||
puglEnterContext(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
#ifdef PUGL_OPENGL | |||||
// FIXME without the first unlock we freeze | |||||
impl->view->UnlockGL(); | |||||
impl->view->LockGL(); | |||||
#endif | |||||
} | |||||
void | |||||
puglLeaveContext(PuglView* view, bool flush) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
#ifdef PUGL_OPENGL | |||||
if (flush) | |||||
impl->view->SwapBuffers(); | |||||
impl->view->UnlockGL(); | |||||
#endif | |||||
} | |||||
PuglInternals* | |||||
puglInitInternals() | |||||
{ | |||||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
} | |||||
class DView : public BViewType | |||||
{ | |||||
public: | |||||
#ifdef PUGL_CAIRO | |||||
DView(PuglView* const v) | |||||
: BView(nullptr, | |||||
B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_FRAME_EVENTS|B_NAVIGABLE|B_INPUT_METHOD_AWARE), | |||||
puglView(v) {} | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
DView(PuglView* const v) | |||||
: BGLView(BRect(), // causes "bitmap bounds is much too large: BRect(0.0, 0.0, 4294967296.0, 4294967296.0)" | |||||
"DPF-GLView", | |||||
0x0, // resize mode | |||||
B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_NAVIGABLE_JUMP|B_FRAME_EVENTS|B_NAVIGABLE|B_INPUT_METHOD_AWARE, | |||||
BGL_RGB|BGL_DOUBLE|BGL_ALPHA|BGL_DEPTH|BGL_STENCIL), | |||||
puglView(v) | |||||
{ | |||||
} | |||||
#endif | |||||
protected: | |||||
void GetPreferredSize(float* width, float* height) override | |||||
{ | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
if (width != nullptr) | |||||
*width = puglView->width; | |||||
if (height != nullptr) | |||||
*height = puglView->height; | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
} | |||||
void Draw(BRect updateRect) override | |||||
{ | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
puglDisplay(puglView); | |||||
#ifdef PUGL_OPENGL | |||||
BGLView::Draw(updateRect); | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
#endif | |||||
} | |||||
void MessageReceived(BMessage* message) | |||||
{ | |||||
d_stdout("MessageReceived %p", message); | |||||
BViewType::MessageReceived(message); | |||||
} | |||||
void MouseDown(BPoint where) override | |||||
{ | |||||
if (puglView->mouseFunc) { | |||||
// puglView->event_timestamp_ms = GetMessageTime(); | |||||
d_stdout("MouseDown mask %u", EventMask()); | |||||
puglView->mouseFunc(puglView, 1, true, where.x, where.y); | |||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); | |||||
} | |||||
//BViewType::MouseDown(where); | |||||
} | |||||
void MouseUp(BPoint where) override | |||||
{ | |||||
if (puglView->mouseFunc) { | |||||
d_stdout("MouseUp mask %u", EventMask()); | |||||
// puglView->event_timestamp_ms = GetMessageTime(); | |||||
puglView->mouseFunc(puglView, 1, false, where.x, where.y); | |||||
} | |||||
//BViewType::MouseUp(where); | |||||
} | |||||
void MouseMoved(BPoint where, uint32, const BMessage*) override | |||||
{ | |||||
if (puglView->motionFunc) { | |||||
// puglView->event_timestamp_ms = GetMessageTime(); | |||||
puglView->motionFunc(puglView, where.x, where.y); | |||||
} | |||||
} | |||||
void KeyDown(const char* bytes, int32 numBytes) override | |||||
{ | |||||
d_stdout("KeyDown %i", numBytes); | |||||
if (numBytes != 1) | |||||
return; // TODO | |||||
if (puglView->keyboardFunc) { | |||||
puglView->keyboardFunc(puglView, true, bytes[0]); | |||||
} | |||||
} | |||||
void KeyUp(const char* bytes, int32 numBytes) override | |||||
{ | |||||
d_stdout("KeyUp %i", numBytes); | |||||
if (numBytes != 1) | |||||
return; // TODO | |||||
if (puglView->keyboardFunc) { | |||||
puglView->keyboardFunc(puglView, false, bytes[0]); | |||||
} | |||||
} | |||||
void ScrollTo(BPoint where) override | |||||
{ | |||||
d_stdout("ScrollTo mask %u", EventMask()); | |||||
BViewType::ScrollTo(where); | |||||
} | |||||
void FrameResized(float newWidth, float newHeight) override | |||||
{ | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
puglReshape(puglView, static_cast<int>(newWidth), static_cast<int>(newHeight)); | |||||
#ifdef PUGL_OPENGL | |||||
BGLView::FrameResized(newWidth, newHeight); | |||||
#endif | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
} | |||||
private: | |||||
PuglView* const puglView; | |||||
}; | |||||
class DWindow : public BWindow | |||||
{ | |||||
public: | |||||
DWindow(PuglView* const v) | |||||
: BWindow(BRect(1.0f), "DPF-Window", B_TITLED_WINDOW, 0x0), | |||||
puglView(v), | |||||
needsQuit(true) | |||||
{ | |||||
} | |||||
bool NeedsQuit() const | |||||
{ | |||||
return needsQuit; | |||||
} | |||||
protected: | |||||
bool QuitRequested() override | |||||
{ | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
if (puglView->closeFunc) { | |||||
puglView->closeFunc(puglView); | |||||
puglView->redisplay = false; | |||||
} | |||||
needsQuit = false; | |||||
d_stdout("%s %i", __func__, __LINE__); | |||||
return true; | |||||
} | |||||
private: | |||||
PuglView* const puglView; | |||||
bool needsQuit; | |||||
}; | |||||
int | |||||
puglCreateWindow(PuglView* view, const char* title) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (be_app == nullptr) | |||||
{ | |||||
d_stdout("creating app"); | |||||
status_t status; | |||||
BApplication* const app = new BApplication("application/x-vnd.dpf-application", &status); | |||||
if (status != B_OK) | |||||
{ | |||||
d_stdout("app status error %u", status); | |||||
delete app; | |||||
return 1; | |||||
} | |||||
impl->app = app; | |||||
} | |||||
else | |||||
{ | |||||
d_stdout("using existing app"); | |||||
} | |||||
if (view->parent == 0) { | |||||
impl->window = new DWindow(view); | |||||
impl->window->Lock(); | |||||
} | |||||
impl->view = new DView(view); | |||||
if (view->parent != 0) { | |||||
BView* const pview = (BView*)view->parent; | |||||
pview->AddChild(impl->view); | |||||
impl->view->LockGL(); | |||||
return 0; | |||||
} | |||||
if (title != nullptr) { | |||||
impl->window->SetTitle(title); | |||||
} | |||||
impl->window->AddChild(impl->view); | |||||
impl->view->LockGL(); | |||||
//puglEnterContext(view); | |||||
impl->window->Unlock(); | |||||
return 0; | |||||
} | |||||
void | |||||
puglShowWindow(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (impl->window != nullptr) | |||||
{ | |||||
if (impl->window->LockLooper()) | |||||
{ | |||||
impl->window->Show(); | |||||
impl->window->UnlockLooper(); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
impl->view->Show(); | |||||
} | |||||
} | |||||
void | |||||
puglHideWindow(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (impl->window != nullptr) | |||||
{ | |||||
if (impl->window->LockLooper()) | |||||
{ | |||||
impl->window->Hide(); | |||||
impl->window->UnlockLooper(); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
impl->view->Show(); | |||||
} | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (impl->window != nullptr) | |||||
{ | |||||
// impl->window->Lock(); | |||||
puglLeaveContext(view, false); | |||||
impl->window->RemoveChild(impl->view); | |||||
// impl->window->Unlock(); | |||||
if (impl->window->NeedsQuit()) | |||||
impl->window->Quit(); | |||||
} | |||||
delete impl->view; | |||||
impl->view = nullptr; | |||||
impl->window = nullptr; | |||||
if (impl->app != nullptr && impl->app->CountWindows() == 0) | |||||
{ | |||||
d_stdout("deleting app"); | |||||
delete impl->app; | |||||
impl->app = nullptr; | |||||
} else | |||||
d_stdout("NOT deleting app"); | |||||
} | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
return PUGL_SUCCESS; | |||||
} | |||||
void | |||||
puglPostRedisplay(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
view->redisplay = true; | |||||
if (impl->window != nullptr) | |||||
{ | |||||
if (impl->window->LockLooper()) | |||||
{ | |||||
impl->view->Invalidate(); | |||||
impl->window->UnlockLooper(); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
impl->view->Invalidate(); | |||||
} | |||||
} | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
#ifdef PUGL_OPENGL | |||||
// return (PuglNativeWindow)impl->view->EmbeddedView(); | |||||
#endif | |||||
return (PuglNativeWindow)(BView*)impl->view; | |||||
} | |||||
void* | |||||
puglGetContext(PuglView* view) | |||||
{ | |||||
return NULL; | |||||
} | |||||
int | |||||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
d_stdout("puglUpdateGeometryConstraints %i %i %i %i", min_width, min_height, view->width, view->height); | |||||
if (impl->window->LockLooper()) | |||||
{ | |||||
impl->window->SetSizeLimits(min_width, | |||||
view->user_resizable ? 4096 : min_width, | |||||
min_height, | |||||
view->user_resizable ? 4096 : min_height); | |||||
impl->window->UnlockLooper(); | |||||
return 0; | |||||
} | |||||
return 1; | |||||
// TODO | |||||
(void)aspect; | |||||
} |
@@ -1,263 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/** | |||||
@file pugl_internal.h Private platform-independent definitions. | |||||
Note this file contains function definitions, so it must be compiled into | |||||
the final binary exactly once. Each platform specific implementation file | |||||
including it once should achieve this. | |||||
*/ | |||||
#include "pugl.h" | |||||
typedef struct PuglInternalsImpl PuglInternals; | |||||
struct PuglViewImpl { | |||||
PuglHandle handle; | |||||
PuglCloseFunc closeFunc; | |||||
PuglDisplayFunc displayFunc; | |||||
PuglKeyboardFunc keyboardFunc; | |||||
PuglMotionFunc motionFunc; | |||||
PuglMouseFunc mouseFunc; | |||||
PuglReshapeFunc reshapeFunc; | |||||
PuglResizeFunc resizeFunc; | |||||
PuglScrollFunc scrollFunc; | |||||
PuglSpecialFunc specialFunc; | |||||
PuglFileSelectedFunc fileSelectedFunc; | |||||
PuglInternals* impl; | |||||
PuglNativeWindow parent; | |||||
uintptr_t transient_parent; | |||||
int width; | |||||
int height; | |||||
int min_width; | |||||
int min_height; | |||||
int mods; | |||||
bool mouse_in_view; | |||||
bool ignoreKeyRepeat; | |||||
bool redisplay; | |||||
bool user_resizable; | |||||
bool pending_resize; | |||||
uint32_t event_timestamp_ms; | |||||
}; | |||||
PuglInternals* puglInitInternals(void); | |||||
PuglView* | |||||
puglInit(void) | |||||
{ | |||||
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||||
if (!view) { | |||||
return NULL; | |||||
} | |||||
PuglInternals* impl = puglInitInternals(); | |||||
if (!impl) { | |||||
free(view); | |||||
return NULL; | |||||
} | |||||
view->impl = impl; | |||||
view->width = 640; | |||||
view->height = 480; | |||||
return view; | |||||
} | |||||
void | |||||
puglInitWindowSize(PuglView* view, int width, int height) | |||||
{ | |||||
view->width = width; | |||||
view->height = height; | |||||
} | |||||
void | |||||
puglInitWindowMinSize(PuglView* view, int width, int height) | |||||
{ | |||||
view->min_width = width; | |||||
view->min_height = height; | |||||
} | |||||
void | |||||
puglInitWindowParent(PuglView* view, PuglNativeWindow parent) | |||||
{ | |||||
view->parent = parent; | |||||
} | |||||
void | |||||
puglInitUserResizable(PuglView* view, bool resizable) | |||||
{ | |||||
view->user_resizable = resizable; | |||||
} | |||||
void | |||||
puglInitTransientFor(PuglView* view, uintptr_t parent) | |||||
{ | |||||
view->transient_parent = parent; | |||||
} | |||||
PuglView* | |||||
puglCreate(PuglNativeWindow parent, | |||||
const char* title, | |||||
int min_width, | |||||
int min_height, | |||||
int width, | |||||
int height, | |||||
bool resizable, | |||||
unsigned long transientId) | |||||
{ | |||||
PuglView* view = puglInit(); | |||||
if (!view) { | |||||
return NULL; | |||||
} | |||||
puglInitWindowParent(view, parent); | |||||
puglInitWindowMinSize(view, min_width, min_height); | |||||
puglInitWindowSize(view, width, height); | |||||
puglInitUserResizable(view, resizable); | |||||
puglInitTransientFor(view, transientId); | |||||
if (!puglCreateWindow(view, title)) { | |||||
free(view); | |||||
return NULL; | |||||
} | |||||
return view; | |||||
} | |||||
void | |||||
puglSetHandle(PuglView* view, PuglHandle handle) | |||||
{ | |||||
view->handle = handle; | |||||
} | |||||
PuglHandle | |||||
puglGetHandle(PuglView* view) | |||||
{ | |||||
return view->handle; | |||||
} | |||||
uint32_t | |||||
puglGetEventTimestamp(PuglView* view) | |||||
{ | |||||
return view->event_timestamp_ms; | |||||
} | |||||
int | |||||
puglGetModifiers(PuglView* view) | |||||
{ | |||||
return view->mods; | |||||
} | |||||
void | |||||
puglIgnoreKeyRepeat(PuglView* view, bool ignore) | |||||
{ | |||||
view->ignoreKeyRepeat = ignore; | |||||
} | |||||
void | |||||
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) | |||||
{ | |||||
view->closeFunc = closeFunc; | |||||
} | |||||
void | |||||
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) | |||||
{ | |||||
view->displayFunc = displayFunc; | |||||
} | |||||
void | |||||
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) | |||||
{ | |||||
view->keyboardFunc = keyboardFunc; | |||||
} | |||||
void | |||||
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) | |||||
{ | |||||
view->motionFunc = motionFunc; | |||||
} | |||||
void | |||||
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) | |||||
{ | |||||
view->mouseFunc = mouseFunc; | |||||
} | |||||
void | |||||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) | |||||
{ | |||||
view->reshapeFunc = reshapeFunc; | |||||
} | |||||
void | |||||
puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc) | |||||
{ | |||||
view->resizeFunc = resizeFunc; | |||||
} | |||||
void | |||||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | |||||
{ | |||||
view->scrollFunc = scrollFunc; | |||||
} | |||||
void | |||||
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||||
{ | |||||
view->specialFunc = specialFunc; | |||||
} | |||||
void | |||||
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc) | |||||
{ | |||||
view->fileSelectedFunc = fileSelectedFunc; | |||||
} | |||||
void | |||||
puglEnterContext(PuglView* view); | |||||
void | |||||
puglLeaveContext(PuglView* view, bool flush); | |||||
static void | |||||
puglDefaultReshape(int width, int height) | |||||
{ | |||||
#ifdef PUGL_OPENGL | |||||
#ifdef ROBTK_HERE | |||||
glViewport(0, 0, width, height); | |||||
glMatrixMode(GL_PROJECTION); | |||||
glLoadIdentity(); | |||||
glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); | |||||
glClear(GL_COLOR_BUFFER_BIT); | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
#else | |||||
glMatrixMode(GL_PROJECTION); | |||||
glLoadIdentity(); | |||||
glOrtho(0, width, height, 0, 0, 1); | |||||
glViewport(0, 0, width, height); | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
#endif | |||||
#endif // PUGL_OPENGL | |||||
} |
@@ -1,974 +0,0 @@ | |||||
/* | |||||
Copyright 2012 David Robillard <http://drobilla.net> | |||||
Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/** | |||||
@file pugl_osx.m OSX/Cocoa Pugl Implementation. | |||||
*/ | |||||
#include <stdlib.h> | |||||
#ifdef PUGL_CAIRO | |||||
#import <cairo.h> | |||||
#import <cairo-quartz.h> | |||||
#endif | |||||
#import <Cocoa/Cocoa.h> | |||||
#include "pugl_internal.h" | |||||
@interface PuglWindow : NSWindow | |||||
{ | |||||
@public | |||||
PuglView* puglview; | |||||
} | |||||
- (id) initWithContentRect:(NSRect)contentRect | |||||
styleMask:(unsigned int)aStyle | |||||
backing:(NSBackingStoreType)bufferingType | |||||
defer:(BOOL)flag; | |||||
- (void) setPuglview:(PuglView*)view; | |||||
- (BOOL) canBecomeKeyWindow; | |||||
- (BOOL) windowShouldClose:(id)sender; | |||||
@end | |||||
@implementation PuglWindow | |||||
- (id)initWithContentRect:(NSRect)contentRect | |||||
styleMask:(unsigned int)aStyle | |||||
backing:(NSBackingStoreType)bufferingType | |||||
defer:(BOOL)flag | |||||
{ | |||||
NSWindow* result = [super initWithContentRect:contentRect | |||||
styleMask:(NSClosableWindowMask | | |||||
NSTitledWindowMask | | |||||
NSResizableWindowMask) | |||||
backing:NSBackingStoreBuffered defer:NO]; | |||||
[result setAcceptsMouseMovedEvents:YES]; | |||||
[result setLevel: CGShieldingWindowLevel() + 1]; | |||||
return (PuglWindow*)result; | |||||
// unused | |||||
(void)aStyle; (void)bufferingType; (void)flag; | |||||
} | |||||
- (void)setPuglview:(PuglView*)view | |||||
{ | |||||
puglview = view; | |||||
[self setContentSize:NSMakeSize(view->width, view->height)]; | |||||
} | |||||
- (BOOL)canBecomeKeyWindow | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL)windowShouldClose:(id)sender | |||||
{ | |||||
if (puglview->closeFunc) | |||||
puglview->closeFunc(puglview); | |||||
return YES; | |||||
// unused | |||||
(void)sender; | |||||
} | |||||
@end | |||||
static void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
view->redisplay = false; | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
} | |||||
@protocol PuglGenericView | |||||
@required | |||||
- (PuglView *) puglview; | |||||
- (void) setPuglview:(PuglView *)pv; | |||||
- (NSTrackingArea *) puglTrackingArea; | |||||
- (void) setPuglTrackingArea:(NSTrackingArea *)area; | |||||
@end | |||||
static unsigned | |||||
getModifiers(PuglView* view, NSEvent* ev) | |||||
{ | |||||
const unsigned modifierFlags = [ev modifierFlags]; | |||||
view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); | |||||
unsigned mods = 0; | |||||
mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; | |||||
mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; | |||||
mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; | |||||
mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; | |||||
return mods; | |||||
} | |||||
static int | |||||
getFixedAppKitButton(NSInteger button) | |||||
{ | |||||
switch (button) { | |||||
case 0: return 1; | |||||
case 1: return 3; | |||||
case 2: return 2; | |||||
default: return button; | |||||
} | |||||
} | |||||
static void | |||||
cursorUpdate(NSView<PuglGenericView> *self, NSEvent* event) | |||||
{ | |||||
[[NSCursor arrowCursor] set]; | |||||
(void)self; | |||||
(void)event; | |||||
} | |||||
static void | |||||
updateTrackingAreas(NSView<PuglGenericView> *self) | |||||
{ | |||||
static const int opts = NSTrackingMouseEnteredAndExited | |||||
| NSTrackingMouseMoved | |||||
| NSTrackingEnabledDuringMouseDrag | |||||
| NSTrackingInVisibleRect | |||||
| NSTrackingActiveAlways | |||||
| NSTrackingCursorUpdate; | |||||
NSTrackingArea *trackingArea = [self puglTrackingArea]; | |||||
if (trackingArea != nil) { | |||||
[self removeTrackingArea:trackingArea]; | |||||
[trackingArea release]; | |||||
} | |||||
trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] | |||||
options:opts | |||||
owner:self | |||||
userInfo:nil]; | |||||
[self setPuglTrackingArea:trackingArea]; | |||||
[self addTrackingArea:trackingArea]; | |||||
} | |||||
static void | |||||
viewWillMoveToWindow(NSView<PuglGenericView> *self, NSWindow* newWindow) | |||||
{ | |||||
if (newWindow != nil) { | |||||
[newWindow setAcceptsMouseMovedEvents:YES]; | |||||
[newWindow makeFirstResponder:self]; | |||||
} | |||||
} | |||||
static void | |||||
reshape(NSView<PuglGenericView> *self) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
NSRect bounds = [self bounds]; | |||||
int width = bounds.size.width; | |||||
int height = bounds.size.height; | |||||
puglEnterContext(puglview); | |||||
if (puglview->reshapeFunc) { | |||||
puglview->reshapeFunc(puglview, width, height); | |||||
} else { | |||||
puglDefaultReshape(width, height); | |||||
} | |||||
puglLeaveContext(puglview, false); | |||||
puglview->width = width; | |||||
puglview->height = height; | |||||
} | |||||
static void | |||||
mouseMoved(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->motionFunc) { | |||||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->motionFunc(puglview, loc.x, loc.y); | |||||
} | |||||
} | |||||
static void | |||||
mouseDown(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->mouseFunc) { | |||||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y); | |||||
} | |||||
} | |||||
static void | |||||
mouseUp(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->mouseFunc) { | |||||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y); | |||||
} | |||||
} | |||||
static void | |||||
scrollWheel(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->scrollFunc) { | |||||
NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->scrollFunc(puglview, | |||||
loc.x, loc.y, | |||||
[event deltaX], [event deltaY]); | |||||
} | |||||
} | |||||
static void | |||||
keyDown(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { | |||||
NSString* chars = [event characters]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); | |||||
} | |||||
} | |||||
static void | |||||
keyUp(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->keyboardFunc) { | |||||
NSString* chars = [event characters]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); | |||||
} | |||||
} | |||||
static void | |||||
flagsChanged(NSView<PuglGenericView> *self, NSEvent *event) | |||||
{ | |||||
PuglView* puglview = [self puglview]; | |||||
if (puglview->specialFunc) { | |||||
const unsigned mods = getModifiers(puglview, event); | |||||
if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { | |||||
puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); | |||||
} else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { | |||||
puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); | |||||
} else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { | |||||
puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); | |||||
} else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { | |||||
puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); | |||||
} | |||||
puglview->mods = mods; | |||||
} | |||||
} | |||||
#ifdef PUGL_OPENGL | |||||
@interface PuglOpenGLView : NSOpenGLView<PuglGenericView> | |||||
{ | |||||
@public | |||||
PuglView* puglview; | |||||
NSTrackingArea* trackingArea; | |||||
bool doubleBuffered; | |||||
} | |||||
- (PuglView *) puglview; | |||||
- (void) setPuglview:(PuglView *)pv; | |||||
- (NSTrackingArea *) puglTrackingArea; | |||||
- (void) setPuglTrackingArea:(NSTrackingArea *)area; | |||||
- (BOOL) acceptsFirstMouse:(NSEvent*)e; | |||||
- (BOOL) acceptsFirstResponder; | |||||
- (BOOL) isFlipped; | |||||
- (BOOL) isOpaque; | |||||
- (BOOL) preservesContentInLiveResize; | |||||
- (id) initWithFrame:(NSRect)frame; | |||||
- (void) reshape; | |||||
- (void) drawRect:(NSRect)r; | |||||
- (void) cursorUpdate:(NSEvent*)e; | |||||
- (void) updateTrackingAreas; | |||||
- (void) viewWillMoveToWindow:(NSWindow*)newWindow; | |||||
- (void) mouseMoved:(NSEvent*)event; | |||||
- (void) mouseDragged:(NSEvent*)event; | |||||
- (void) rightMouseDragged:(NSEvent*)event; | |||||
- (void) otherMouseDragged:(NSEvent*)event; | |||||
- (void) mouseDown:(NSEvent*)event; | |||||
- (void) rightMouseDown:(NSEvent*)event; | |||||
- (void) otherMouseDown:(NSEvent*)event; | |||||
- (void) mouseUp:(NSEvent*)event; | |||||
- (void) rightMouseUp:(NSEvent*)event; | |||||
- (void) otherMouseUp:(NSEvent*)event; | |||||
- (void) scrollWheel:(NSEvent*)event; | |||||
- (void) keyDown:(NSEvent*)event; | |||||
- (void) keyUp:(NSEvent*)event; | |||||
- (void) flagsChanged:(NSEvent*)event; | |||||
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize; | |||||
@end | |||||
@implementation PuglOpenGLView | |||||
- (PuglView *) puglview { | |||||
return self->puglview; | |||||
} | |||||
- (void) setPuglview:(PuglView *)pv { | |||||
self->puglview = pv; | |||||
} | |||||
- (NSTrackingArea *) puglTrackingArea { | |||||
return self->trackingArea; | |||||
} | |||||
- (void) setPuglTrackingArea:(NSTrackingArea *)area { | |||||
self->trackingArea = area; | |||||
} | |||||
- (BOOL) acceptsFirstMouse:(NSEvent*)e | |||||
{ | |||||
return YES; | |||||
// unused | |||||
(void)e; | |||||
} | |||||
- (BOOL) acceptsFirstResponder | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL) isFlipped | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL) isOpaque | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL) preservesContentInLiveResize | |||||
{ | |||||
return NO; | |||||
} | |||||
- (id) initWithFrame:(NSRect)frame | |||||
{ | |||||
puglview = nil; | |||||
trackingArea = nil; | |||||
doubleBuffered = true; | |||||
NSOpenGLPixelFormatAttribute pixelAttribs[] = { | |||||
NSOpenGLPFAColorSize, 24, | |||||
NSOpenGLPFAAlphaSize, 8, | |||||
NSOpenGLPFADepthSize, 16, | |||||
NSOpenGLPFADoubleBuffer, | |||||
NSOpenGLPFAAccelerated, | |||||
0 | |||||
}; | |||||
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] | |||||
initWithAttributes:pixelAttribs]; | |||||
if (pixelFormat) { | |||||
self = [super initWithFrame:frame pixelFormat:pixelFormat]; | |||||
[pixelFormat release]; | |||||
printf("Is doubleBuffered? TRUE\n"); | |||||
} else { | |||||
self = [super initWithFrame:frame]; | |||||
doubleBuffered = false; | |||||
printf("Is doubleBuffered? FALSE\n"); | |||||
} | |||||
if (self) { | |||||
GLint swapInterval = 1; | |||||
[[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; | |||||
[self reshape]; | |||||
} | |||||
return self; | |||||
} | |||||
- (void) reshape | |||||
{ | |||||
if (!puglview) { | |||||
/* NOTE: Apparently reshape gets called when the GC gets around to | |||||
deleting the view (?), so we must have reset puglview to NULL when | |||||
this comes around. | |||||
*/ | |||||
return; | |||||
} | |||||
[[self openGLContext] update]; | |||||
reshape(self); | |||||
} | |||||
- (void) drawRect:(NSRect)r | |||||
{ | |||||
puglEnterContext(puglview); | |||||
puglDisplay(puglview); | |||||
puglLeaveContext(puglview, true); | |||||
// unused | |||||
return; (void)r; | |||||
} | |||||
- (void) cursorUpdate:(NSEvent*)e | |||||
{ | |||||
cursorUpdate(self, e); | |||||
} | |||||
- (void) updateTrackingAreas | |||||
{ | |||||
updateTrackingAreas(self); | |||||
[super updateTrackingAreas]; | |||||
} | |||||
- (void) viewWillMoveToWindow:(NSWindow*)newWindow | |||||
{ | |||||
viewWillMoveToWindow(self, newWindow); | |||||
[super viewWillMoveToWindow:newWindow]; | |||||
} | |||||
- (void) mouseMoved:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) mouseDragged:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) rightMouseDragged:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) otherMouseDragged:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) mouseDown:(NSEvent*)event | |||||
{ | |||||
mouseDown(self, event); | |||||
} | |||||
- (void) rightMouseDown:(NSEvent*)event | |||||
{ | |||||
mouseDown(self, event); | |||||
} | |||||
- (void) otherMouseDown:(NSEvent*)event | |||||
{ | |||||
mouseDown(self, event); | |||||
} | |||||
- (void) mouseUp:(NSEvent*)event | |||||
{ | |||||
mouseUp(self, event); | |||||
} | |||||
- (void) rightMouseUp:(NSEvent*)event | |||||
{ | |||||
mouseUp(self, event); | |||||
} | |||||
- (void) otherMouseUp:(NSEvent*)event | |||||
{ | |||||
mouseUp(self, event); | |||||
} | |||||
- (void) scrollWheel:(NSEvent*)event | |||||
{ | |||||
scrollWheel(self, event); | |||||
} | |||||
- (void) keyDown:(NSEvent*)event | |||||
{ | |||||
keyDown(self, event); | |||||
} | |||||
- (void) keyUp:(NSEvent*)event | |||||
{ | |||||
keyUp(self, event); | |||||
} | |||||
- (void) flagsChanged:(NSEvent*)event | |||||
{ | |||||
flagsChanged(self, event); | |||||
} | |||||
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize | |||||
{ | |||||
PuglView *pv = self->puglview; | |||||
if (pv->width <= 1 && pv->height <= 1) | |||||
{ | |||||
/* NOTE: if the view size was not initialized yet, don't perform an | |||||
autoresize; it fixes manual resizing in Reaper. | |||||
*/ | |||||
return; | |||||
} | |||||
[super resizeWithOldSuperviewSize:oldSize]; | |||||
} | |||||
@end | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
@interface PuglCairoView : NSView<PuglGenericView> | |||||
{ | |||||
PuglView* puglview; | |||||
cairo_t* cr; | |||||
NSTrackingArea* trackingArea; | |||||
} | |||||
- (PuglView *) puglview; | |||||
- (void) setPuglview:(PuglView *)pv; | |||||
- (NSTrackingArea *) puglTrackingArea; | |||||
- (void) setPuglTrackingArea:(NSTrackingArea *)area; | |||||
- (cairo_t *) cairoContext; | |||||
- (BOOL) acceptsFirstMouse:(NSEvent*)e; | |||||
- (BOOL) acceptsFirstResponder; | |||||
- (BOOL) isFlipped; | |||||
- (BOOL) isOpaque; | |||||
- (BOOL) preservesContentInLiveResize; | |||||
- (id) initWithFrame:(NSRect)frame; | |||||
- (void) reshape; | |||||
- (void) drawRect:(NSRect)r; | |||||
- (void) cursorUpdate:(NSEvent*)e; | |||||
- (void) updateTrackingAreas; | |||||
- (void) viewWillMoveToWindow:(NSWindow*)newWindow; | |||||
- (void) mouseMoved:(NSEvent*)event; | |||||
- (void) mouseDragged:(NSEvent*)event; | |||||
- (void) rightMouseDragged:(NSEvent*)event; | |||||
- (void) otherMouseDragged:(NSEvent*)event; | |||||
- (void) mouseDown:(NSEvent*)event; | |||||
- (void) rightMouseDown:(NSEvent*)event; | |||||
- (void) otherMouseDown:(NSEvent*)event; | |||||
- (void) mouseUp:(NSEvent*)event; | |||||
- (void) rightMouseUp:(NSEvent*)event; | |||||
- (void) otherMouseUp:(NSEvent*)event; | |||||
- (void) scrollWheel:(NSEvent*)event; | |||||
- (void) keyDown:(NSEvent*)event; | |||||
- (void) keyUp:(NSEvent*)event; | |||||
- (void) flagsChanged:(NSEvent*)event; | |||||
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize; | |||||
@end | |||||
@implementation PuglCairoView | |||||
- (PuglView *) puglview { | |||||
return self->puglview; | |||||
} | |||||
- (void) setPuglview:(PuglView *)pv { | |||||
self->puglview = pv; | |||||
} | |||||
- (NSTrackingArea *) puglTrackingArea { | |||||
return self->trackingArea; | |||||
} | |||||
- (void) setPuglTrackingArea:(NSTrackingArea *)area { | |||||
self->trackingArea = area; | |||||
} | |||||
- (cairo_t *) cairoContext { | |||||
return cr; | |||||
} | |||||
- (BOOL) acceptsFirstMouse:(NSEvent*)e | |||||
{ | |||||
return YES; | |||||
// unused | |||||
(void)e; | |||||
} | |||||
- (BOOL) acceptsFirstResponder | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL) isFlipped | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL) isOpaque | |||||
{ | |||||
return YES; | |||||
} | |||||
- (BOOL) preservesContentInLiveResize | |||||
{ | |||||
return NO; | |||||
} | |||||
- (id) initWithFrame:(NSRect)frame { | |||||
puglview = nil; | |||||
cr = NULL; | |||||
trackingArea = nil; | |||||
[super initWithFrame:frame]; | |||||
return self; | |||||
} | |||||
- (void) reshape | |||||
{ | |||||
if (!puglview) { | |||||
/* NOTE: Apparently reshape gets called when the GC gets around to | |||||
deleting the view (?), so we must have reset puglview to NULL when | |||||
this comes around. | |||||
*/ | |||||
return; | |||||
} | |||||
reshape(self); | |||||
} | |||||
- (void) drawRect:(NSRect)r { | |||||
CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; | |||||
NSRect bounds = [self bounds]; | |||||
cairo_surface_t* surface; | |||||
cairo_t* cairo; | |||||
surface = cairo_quartz_surface_create_for_cg_context(ctx, bounds.size.width, bounds.size.height); | |||||
if (surface) { | |||||
cairo = cairo_create(surface); | |||||
if (cairo) { | |||||
self->cr = cairo; | |||||
puglEnterContext(puglview); | |||||
puglDisplay(puglview); | |||||
puglLeaveContext(puglview, true); | |||||
self->cr = NULL; | |||||
cairo_destroy(cairo); | |||||
} | |||||
cairo_surface_destroy(surface); | |||||
} | |||||
} | |||||
- (void) cursorUpdate:(NSEvent*)e | |||||
{ | |||||
cursorUpdate(self, e); | |||||
} | |||||
- (void) updateTrackingAreas | |||||
{ | |||||
updateTrackingAreas(self); | |||||
[super updateTrackingAreas]; | |||||
} | |||||
- (void) viewWillMoveToWindow:(NSWindow*)newWindow | |||||
{ | |||||
viewWillMoveToWindow(self, newWindow); | |||||
[super viewWillMoveToWindow:newWindow]; | |||||
} | |||||
- (void) mouseMoved:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) mouseDragged:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) rightMouseDragged:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) otherMouseDragged:(NSEvent*)event | |||||
{ | |||||
mouseMoved(self, event); | |||||
} | |||||
- (void) mouseDown:(NSEvent*)event | |||||
{ | |||||
mouseDown(self, event); | |||||
} | |||||
- (void) rightMouseDown:(NSEvent*)event | |||||
{ | |||||
mouseDown(self, event); | |||||
} | |||||
- (void) otherMouseDown:(NSEvent*)event | |||||
{ | |||||
mouseDown(self, event); | |||||
} | |||||
- (void) mouseUp:(NSEvent*)event | |||||
{ | |||||
mouseUp(self, event); | |||||
} | |||||
- (void) rightMouseUp:(NSEvent*)event | |||||
{ | |||||
mouseUp(self, event); | |||||
} | |||||
- (void) otherMouseUp:(NSEvent*)event | |||||
{ | |||||
mouseUp(self, event); | |||||
} | |||||
- (void) scrollWheel:(NSEvent*)event | |||||
{ | |||||
scrollWheel(self, event); | |||||
} | |||||
- (void) keyDown:(NSEvent*)event | |||||
{ | |||||
keyDown(self, event); | |||||
} | |||||
- (void) keyUp:(NSEvent*)event | |||||
{ | |||||
keyUp(self, event); | |||||
} | |||||
- (void) flagsChanged:(NSEvent*)event | |||||
{ | |||||
flagsChanged(self, event); | |||||
} | |||||
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize | |||||
{ | |||||
PuglView *pv = self->puglview; | |||||
if (pv->width <= 1 && pv->height <= 1) | |||||
{ | |||||
/* NOTE: if the view size was not initialized yet, don't perform an | |||||
autoresize; it fixes manual resizing in Reaper. | |||||
*/ | |||||
return; | |||||
} | |||||
[super resizeWithOldSuperviewSize:oldSize]; | |||||
} | |||||
@end | |||||
#endif | |||||
struct PuglInternalsImpl { | |||||
union { | |||||
NSView<PuglGenericView>* view; | |||||
#ifdef PUGL_OPENGL | |||||
PuglOpenGLView* glview; | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
PuglCairoView* cairoview; | |||||
#endif | |||||
}; | |||||
id window; | |||||
}; | |||||
PuglInternals* | |||||
puglInitInternals() | |||||
{ | |||||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
} | |||||
void | |||||
puglEnterContext(PuglView* view) | |||||
{ | |||||
#ifdef PUGL_OPENGL | |||||
[[view->impl->glview openGLContext] makeCurrentContext]; | |||||
#endif | |||||
} | |||||
void | |||||
puglLeaveContext(PuglView* view, bool flush) | |||||
{ | |||||
if (flush) { | |||||
#ifdef PUGL_OPENGL | |||||
if (view->impl->glview->doubleBuffered) { | |||||
[[view->impl->glview openGLContext] flushBuffer]; | |||||
} else { | |||||
glFlush(); | |||||
} | |||||
//[NSOpenGLContext clearCurrentContext]; | |||||
#endif | |||||
} | |||||
} | |||||
int | |||||
puglCreateWindow(PuglView* view, const char* title) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
[NSAutoreleasePool new]; | |||||
[NSApplication sharedApplication]; | |||||
#ifdef PUGL_OPENGL | |||||
impl->glview = [PuglOpenGLView new]; | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
impl->cairoview = [PuglCairoView new]; | |||||
#endif | |||||
if (!impl->view) { | |||||
return 1; | |||||
} | |||||
[impl->view setPuglview:view]; | |||||
if (view->user_resizable) { | |||||
[impl->view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; | |||||
} | |||||
if (view->parent) { | |||||
[impl->view retain]; | |||||
NSView* pview = (NSView*)view->parent; | |||||
[pview addSubview:impl->view]; | |||||
return 0; | |||||
} | |||||
id window = [[PuglWindow new]retain]; | |||||
if (title) { | |||||
NSString* titleString = [[NSString alloc] | |||||
initWithBytes:title | |||||
length:strlen(title) | |||||
encoding:NSUTF8StringEncoding]; | |||||
[window setTitle:titleString]; | |||||
} | |||||
[window setPuglview:view]; | |||||
[window setContentView:impl->view]; | |||||
[window makeFirstResponder:impl->view]; | |||||
[window makeKeyAndOrderFront:window]; | |||||
// wait for first puglShowWindow | |||||
[window setIsVisible:NO]; | |||||
[NSApp activateIgnoringOtherApps:YES]; | |||||
[window center]; | |||||
impl->window = window; | |||||
return 0; | |||||
} | |||||
void | |||||
puglShowWindow(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (impl->window) { | |||||
[impl->window setIsVisible:YES]; | |||||
} else { | |||||
[view->impl->view setHidden:NO]; | |||||
} | |||||
} | |||||
void | |||||
puglHideWindow(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (impl->window) { | |||||
[impl->window setIsVisible:NO]; | |||||
} else { | |||||
[impl->view setHidden:YES]; | |||||
} | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
[view->impl->view setPuglview:NULL]; | |||||
if (view->impl->window) { | |||||
[view->impl->window close]; | |||||
[view->impl->view release]; | |||||
[view->impl->window release]; | |||||
} else { | |||||
[view->impl->view release]; | |||||
} | |||||
free(view->impl); | |||||
free(view); | |||||
} | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
return PUGL_SUCCESS; | |||||
// unused | |||||
(void)view; | |||||
} | |||||
void | |||||
puglPostRedisplay(PuglView* view) | |||||
{ | |||||
view->redisplay = true; | |||||
[view->impl->view setNeedsDisplay:YES]; | |||||
} | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
return (PuglNativeWindow)view->impl->view; | |||||
} | |||||
void* | |||||
puglGetContext(PuglView* view) | |||||
{ | |||||
#ifdef PUGL_CAIRO | |||||
return [view->impl->cairoview cairoContext]; | |||||
#endif | |||||
return NULL; | |||||
// may be unused | |||||
(void)view; | |||||
} | |||||
int | |||||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||||
{ | |||||
// TODO | |||||
return 1; | |||||
(void)view; | |||||
(void)min_width; | |||||
(void)min_height; | |||||
(void)aspect; | |||||
} |
@@ -1,565 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/** | |||||
@file pugl_win.cpp Windows/WGL Pugl Implementation. | |||||
*/ | |||||
#include <ctime> | |||||
#include <cstdio> | |||||
#include <cstdlib> | |||||
#include <winsock2.h> | |||||
#include <windows.h> | |||||
#include <windowsx.h> | |||||
#ifdef PUGL_CAIRO | |||||
#include <cairo/cairo.h> | |||||
#include <cairo/cairo-win32.h> | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
#include <GL/gl.h> | |||||
#endif | |||||
#include "pugl_internal.h" | |||||
#ifndef WM_MOUSEWHEEL | |||||
# define WM_MOUSEWHEEL 0x020A | |||||
#endif | |||||
#ifndef WM_MOUSEHWHEEL | |||||
# define WM_MOUSEHWHEEL 0x020E | |||||
#endif | |||||
#ifndef WHEEL_DELTA | |||||
# define WHEEL_DELTA 120 | |||||
#endif | |||||
#ifndef GWLP_USERDATA | |||||
# define GWLP_USERDATA (-21) | |||||
#endif | |||||
#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) | |||||
HINSTANCE hInstance = NULL; | |||||
struct PuglInternalsImpl { | |||||
HWND hwnd; | |||||
#ifdef PUGL_OPENGL | |||||
HDC hdc; | |||||
HGLRC hglrc; | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
cairo_t* buffer_cr; | |||||
cairo_surface_t* buffer_surface; | |||||
#endif | |||||
HDC paintHdc; | |||||
WNDCLASS wc; | |||||
}; | |||||
LRESULT CALLBACK | |||||
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||||
#if 0 | |||||
extern "C" { | |||||
BOOL WINAPI | |||||
DllMain(HINSTANCE hInst, DWORD, LPVOID) | |||||
{ | |||||
hInstance = hInst; | |||||
return 1; | |||||
} | |||||
} // extern "C" | |||||
#endif | |||||
PuglInternals* | |||||
puglInitInternals() | |||||
{ | |||||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
} | |||||
void | |||||
puglEnterContext(PuglView* view) | |||||
{ | |||||
#ifdef PUGL_OPENGL | |||||
wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||||
#endif | |||||
} | |||||
void | |||||
puglLeaveContext(PuglView* view, bool flush) | |||||
{ | |||||
#ifdef PUGL_OPENGL | |||||
if (flush) { | |||||
glFlush(); | |||||
SwapBuffers(view->impl->hdc); | |||||
} | |||||
wglMakeCurrent(NULL, NULL); | |||||
#endif | |||||
} | |||||
int | |||||
puglCreateWindow(PuglView* view, const char* title) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (!title) { | |||||
title = "Window"; | |||||
} | |||||
// FIXME: This is nasty, and pugl should not have static anything. | |||||
// Should class be a parameter? Does this make sense on other platforms? | |||||
static int wc_count = 0; | |||||
char classNameBuf[256]; | |||||
std::srand((std::time(NULL))); | |||||
#ifdef __WINE__ | |||||
std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||||
#else | |||||
_snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||||
#endif | |||||
classNameBuf[sizeof(classNameBuf)-1] = '\0'; | |||||
impl->wc.style = CS_OWNDC; | |||||
impl->wc.lpfnWndProc = wndProc; | |||||
impl->wc.cbClsExtra = 0; | |||||
impl->wc.cbWndExtra = 0; | |||||
impl->wc.hInstance = hInstance; | |||||
impl->wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); | |||||
impl->wc.hCursor = LoadCursor(hInstance, IDC_ARROW); | |||||
impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||||
impl->wc.lpszMenuName = NULL; | |||||
impl->wc.lpszClassName = strdup(classNameBuf); | |||||
if (!RegisterClass(&impl->wc)) { | |||||
free((void*)impl->wc.lpszClassName); | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||||
if (view->user_resizable) { | |||||
winFlags |= WS_SIZEBOX; | |||||
if (view->min_width > 0 && view->min_height > 0) { | |||||
// Adjust the minimum window size to accomodate requested view size | |||||
RECT mr = { 0, 0, view->min_width, view->min_height }; | |||||
AdjustWindowRectEx(&mr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||||
view->min_width = mr.right - mr.left; | |||||
view->min_height = mr.bottom - mr.top; | |||||
} | |||||
} | |||||
// Adjust the window size to accomodate requested view size | |||||
RECT wr = { 0, 0, view->width, view->height }; | |||||
AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | |||||
impl->hwnd = CreateWindowEx( | |||||
WS_EX_TOPMOST, | |||||
classNameBuf, title, | |||||
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, NULL); | |||||
free((void*)impl->wc.lpszClassName); | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); | |||||
#ifdef PUGL_OPENGL | |||||
impl->hdc = GetDC(impl->hwnd); | |||||
PIXELFORMATDESCRIPTOR pfd; | |||||
ZeroMemory(&pfd, sizeof(pfd)); | |||||
pfd.nSize = sizeof(pfd); | |||||
pfd.nVersion = 1; | |||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; | |||||
pfd.iPixelType = PFD_TYPE_RGBA; | |||||
pfd.cColorBits = 24; | |||||
pfd.cDepthBits = 16; | |||||
pfd.iLayerType = PFD_MAIN_PLANE; | |||||
int format = ChoosePixelFormat(impl->hdc, &pfd); | |||||
SetPixelFormat(impl->hdc, format, &pfd); | |||||
impl->hglrc = wglCreateContext(impl->hdc); | |||||
if (!impl->hglrc) { | |||||
ReleaseDC (impl->hwnd, impl->hdc); | |||||
DestroyWindow (impl->hwnd); | |||||
UnregisterClass (impl->wc.lpszClassName, NULL); | |||||
free((void*)impl->wc.lpszClassName); | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
#endif | |||||
return PUGL_SUCCESS; | |||||
} | |||||
void | |||||
puglShowWindow(PuglView* view) | |||||
{ | |||||
ShowWindow(view->impl->hwnd, SW_SHOWNORMAL); | |||||
} | |||||
void | |||||
puglHideWindow(PuglView* view) | |||||
{ | |||||
ShowWindow(view->impl->hwnd, SW_HIDE); | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
if (!view) { | |||||
return; | |||||
} | |||||
PuglInternals* const impl = view->impl; | |||||
#ifdef PUGL_OPENGL | |||||
wglMakeCurrent(NULL, NULL); | |||||
wglDeleteContext(impl->hglrc); | |||||
ReleaseDC(impl->hwnd, impl->hdc); | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
cairo_destroy(impl->buffer_cr); | |||||
cairo_surface_destroy(impl->buffer_surface); | |||||
#endif | |||||
DestroyWindow(impl->hwnd); | |||||
UnregisterClass(impl->wc.lpszClassName, NULL); | |||||
free((void*)impl->wc.lpszClassName); | |||||
free(impl); | |||||
free(view); | |||||
} | |||||
static void | |||||
puglReshape(PuglView* view, int width, int height) | |||||
{ | |||||
puglEnterContext(view); | |||||
if (view->reshapeFunc) { | |||||
view->reshapeFunc(view, width, height); | |||||
} else { | |||||
puglDefaultReshape(width, height); | |||||
} | |||||
view->width = width; | |||||
view->height = height; | |||||
} | |||||
static void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
bool success = true; | |||||
puglEnterContext(view); | |||||
#ifdef PUGL_CAIRO | |||||
cairo_t *wc = NULL; | |||||
cairo_t *bc = NULL; | |||||
cairo_surface_t *ws = NULL; | |||||
cairo_surface_t *bs = NULL; | |||||
HDC hdc = impl->paintHdc; | |||||
bc = impl->buffer_cr; | |||||
bs = impl->buffer_surface; | |||||
int w = view->width; | |||||
int h = view->height; | |||||
int bw = bs ? cairo_image_surface_get_width(bs) : -1; | |||||
int bh = bs ? cairo_image_surface_get_height(bs) : -1; | |||||
ws = hdc ? cairo_win32_surface_create(hdc) : NULL; | |||||
wc = ws ? cairo_create(ws) : NULL; | |||||
if (wc && (!bc || bw != w || bh != h)) { | |||||
cairo_destroy(bc); | |||||
cairo_surface_destroy(bs); | |||||
bs = cairo_surface_create_similar_image(ws, CAIRO_FORMAT_ARGB32, w, h); | |||||
bc = bs ? cairo_create(bs) : NULL; | |||||
impl->buffer_cr = bc; | |||||
impl->buffer_surface = bs; | |||||
} | |||||
success = wc != NULL && bc != NULL; | |||||
#endif | |||||
if (success) { | |||||
view->redisplay = false; | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
#ifdef PUGL_CAIRO | |||||
cairo_set_source_surface(wc, bs, 0, 0); | |||||
cairo_paint(wc); | |||||
#endif | |||||
} | |||||
puglLeaveContext(view, success); | |||||
#ifdef PUGL_CAIRO | |||||
cairo_destroy(wc); | |||||
cairo_surface_destroy(ws); | |||||
#endif | |||||
return; | |||||
(void)impl; | |||||
} | |||||
static PuglKey | |||||
keySymToSpecial(int sym) | |||||
{ | |||||
switch (sym) { | |||||
case VK_F1: return PUGL_KEY_F1; | |||||
case VK_F2: return PUGL_KEY_F2; | |||||
case VK_F3: return PUGL_KEY_F3; | |||||
case VK_F4: return PUGL_KEY_F4; | |||||
case VK_F5: return PUGL_KEY_F5; | |||||
case VK_F6: return PUGL_KEY_F6; | |||||
case VK_F7: return PUGL_KEY_F7; | |||||
case VK_F8: return PUGL_KEY_F8; | |||||
case VK_F9: return PUGL_KEY_F9; | |||||
case VK_F10: return PUGL_KEY_F10; | |||||
case VK_F11: return PUGL_KEY_F11; | |||||
case VK_F12: return PUGL_KEY_F12; | |||||
case VK_LEFT: return PUGL_KEY_LEFT; | |||||
case VK_UP: return PUGL_KEY_UP; | |||||
case VK_RIGHT: return PUGL_KEY_RIGHT; | |||||
case VK_DOWN: return PUGL_KEY_DOWN; | |||||
case VK_PRIOR: return PUGL_KEY_PAGE_UP; | |||||
case VK_NEXT: return PUGL_KEY_PAGE_DOWN; | |||||
case VK_HOME: return PUGL_KEY_HOME; | |||||
case VK_END: return PUGL_KEY_END; | |||||
case VK_INSERT: return PUGL_KEY_INSERT; | |||||
case VK_SHIFT: return PUGL_KEY_SHIFT; | |||||
case VK_CONTROL: return PUGL_KEY_CTRL; | |||||
case VK_MENU: return PUGL_KEY_ALT; | |||||
case VK_LWIN: return PUGL_KEY_SUPER; | |||||
case VK_RWIN: return PUGL_KEY_SUPER; | |||||
} | |||||
return (PuglKey)0; | |||||
} | |||||
static void | |||||
processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) | |||||
{ | |||||
view->event_timestamp_ms = GetMessageTime(); | |||||
if (press) { | |||||
SetCapture(view->impl->hwnd); | |||||
} else { | |||||
ReleaseCapture(); | |||||
} | |||||
if (view->mouseFunc) { | |||||
view->mouseFunc(view, button, press, | |||||
GET_X_LPARAM(lParam), | |||||
GET_Y_LPARAM(lParam)); | |||||
} | |||||
} | |||||
static void | |||||
setModifiers(PuglView* view) | |||||
{ | |||||
view->mods = 0; | |||||
view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; | |||||
view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; | |||||
view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; | |||||
view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; | |||||
view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; | |||||
} | |||||
static LRESULT | |||||
handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) | |||||
{ | |||||
PAINTSTRUCT ps; | |||||
PuglKey key; | |||||
RECT rect; | |||||
MINMAXINFO* mmi; | |||||
setModifiers(view); | |||||
switch (message) { | |||||
case WM_CREATE: | |||||
case WM_SHOWWINDOW: | |||||
case WM_SIZE: | |||||
GetClientRect(view->impl->hwnd, &rect); | |||||
puglReshape(view, rect.right, rect.bottom); | |||||
break; | |||||
case WM_GETMINMAXINFO: | |||||
mmi = (MINMAXINFO*)lParam; | |||||
mmi->ptMinTrackSize.x = view->min_width; | |||||
mmi->ptMinTrackSize.y = view->min_height; | |||||
break; | |||||
case WM_PAINT: | |||||
view->impl->paintHdc = BeginPaint(view->impl->hwnd, &ps); | |||||
puglDisplay(view); | |||||
view->impl->paintHdc = NULL; | |||||
EndPaint(view->impl->hwnd, &ps); | |||||
break; | |||||
case WM_MOUSEMOVE: | |||||
if (view->motionFunc) { | |||||
view->event_timestamp_ms = GetMessageTime(); | |||||
view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); | |||||
} | |||||
break; | |||||
case WM_LBUTTONDOWN: | |||||
processMouseEvent(view, 1, true, lParam); | |||||
break; | |||||
case WM_MBUTTONDOWN: | |||||
processMouseEvent(view, 2, true, lParam); | |||||
break; | |||||
case WM_RBUTTONDOWN: | |||||
processMouseEvent(view, 3, true, lParam); | |||||
break; | |||||
case WM_LBUTTONUP: | |||||
processMouseEvent(view, 1, false, lParam); | |||||
break; | |||||
case WM_MBUTTONUP: | |||||
processMouseEvent(view, 2, false, lParam); | |||||
break; | |||||
case WM_RBUTTONUP: | |||||
processMouseEvent(view, 3, false, lParam); | |||||
break; | |||||
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, pt.x, pt.y, | |||||
0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); | |||||
} | |||||
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, pt.x, pt.y, | |||||
GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); | |||||
} | |||||
break; | |||||
case WM_KEYDOWN: | |||||
if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { | |||||
break; | |||||
} // else nobreak | |||||
case WM_KEYUP: | |||||
view->event_timestamp_ms = GetMessageTime(); | |||||
if ((key = keySymToSpecial(wParam))) { | |||||
if (view->specialFunc) { | |||||
view->specialFunc(view, message == WM_KEYDOWN, key); | |||||
} | |||||
} else if (view->keyboardFunc) { | |||||
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: | |||||
if (view->closeFunc) { | |||||
view->closeFunc(view); | |||||
view->redisplay = false; | |||||
} | |||||
break; | |||||
default: | |||||
return DefWindowProc( | |||||
view->impl->hwnd, message, wParam, lParam); | |||||
} | |||||
return 0; | |||||
} | |||||
void | |||||
puglGrabFocus(PuglView* /*view*/) | |||||
{ | |||||
// TODO | |||||
} | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
MSG msg; | |||||
while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { | |||||
handleMessage(view, msg.message, msg.wParam, msg.lParam); | |||||
} | |||||
if (view->redisplay) { | |||||
InvalidateRect(view->impl->hwnd, NULL, FALSE); | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
LRESULT CALLBACK | |||||
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||||
{ | |||||
PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); | |||||
switch (message) { | |||||
case WM_CREATE: | |||||
PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | |||||
return 0; | |||||
case WM_CLOSE: | |||||
PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); | |||||
return 0; | |||||
case WM_DESTROY: | |||||
return 0; | |||||
default: | |||||
if (view && hwnd == view->impl->hwnd) { | |||||
return handleMessage(view, message, wParam, lParam); | |||||
} else { | |||||
return DefWindowProc(hwnd, message, wParam, lParam); | |||||
} | |||||
} | |||||
} | |||||
void | |||||
puglPostRedisplay(PuglView* view) | |||||
{ | |||||
view->redisplay = true; | |||||
} | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
return (PuglNativeWindow)view->impl->hwnd; | |||||
} | |||||
void* | |||||
puglGetContext(PuglView* view) | |||||
{ | |||||
#ifdef PUGL_CAIRO | |||||
return view->impl->buffer_cr; | |||||
#endif | |||||
return NULL; | |||||
// may be unused | |||||
(void)view; | |||||
} | |||||
int | |||||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||||
{ | |||||
// TODO | |||||
return 1; | |||||
(void)view; | |||||
(void)min_width; | |||||
(void)min_height; | |||||
(void)aspect; | |||||
} |
@@ -1,739 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||||
Copyright 2013,2015 Robin Gareus <robin@gareus.org> | |||||
Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
copyright notice and this permission notice appear in all copies. | |||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
/** | |||||
@file pugl_x11.c X11 Pugl Implementation. | |||||
*/ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#ifdef PUGL_CAIRO | |||||
#include <cairo/cairo.h> | |||||
#include <cairo/cairo-xlib.h> | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
#include <GL/gl.h> | |||||
#include <GL/glx.h> | |||||
#endif | |||||
#include <X11/Xatom.h> | |||||
#include <X11/Xlib.h> | |||||
#include <X11/Xutil.h> | |||||
#include <X11/keysym.h> | |||||
#include "pugl_internal.h" | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
#define SOFD_HAVE_X11 | |||||
#include "../sofd/libsofd.h" | |||||
#include "../sofd/libsofd.c" | |||||
#endif | |||||
/* work around buggy re-parent & focus issues on some systems | |||||
* where no keyboard events are passed through even if the | |||||
* app has mouse-focus and all other events are working. | |||||
*/ | |||||
//#define PUGL_GRAB_FOCUS | |||||
/* show messages during initalization | |||||
*/ | |||||
//#define PUGL_VERBOSE | |||||
struct PuglInternalsImpl { | |||||
Display* display; | |||||
int screen; | |||||
Window win; | |||||
#ifdef PUGL_CAIRO | |||||
cairo_t* xlib_cr; | |||||
cairo_t* buffer_cr; | |||||
cairo_surface_t* xlib_surface; | |||||
cairo_surface_t* buffer_surface; | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
GLXContext ctx; | |||||
Bool doubleBuffered; | |||||
#endif | |||||
}; | |||||
#ifdef PUGL_OPENGL | |||||
/** | |||||
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_STENCIL_SIZE, 8, | |||||
GLX_ARB_multisample, 1, | |||||
None | |||||
}; | |||||
/** | |||||
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, True, | |||||
GLX_RED_SIZE, 4, | |||||
GLX_GREEN_SIZE, 4, | |||||
GLX_BLUE_SIZE, 4, | |||||
GLX_DEPTH_SIZE, 16, | |||||
GLX_STENCIL_SIZE, 8, | |||||
GLX_ARB_multisample, 1, | |||||
None | |||||
}; | |||||
/** | |||||
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_STENCIL_SIZE, 8, | |||||
GLX_SAMPLE_BUFFERS, 1, | |||||
GLX_SAMPLES, 4, | |||||
None | |||||
}; | |||||
#endif | |||||
PuglInternals* | |||||
puglInitInternals(void) | |||||
{ | |||||
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
} | |||||
void | |||||
puglEnterContext(PuglView* view) | |||||
{ | |||||
#ifdef PUGL_OPENGL | |||||
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||||
#endif | |||||
} | |||||
void | |||||
puglLeaveContext(PuglView* view, bool flush) | |||||
{ | |||||
#ifdef PUGL_OPENGL | |||||
if (flush) { | |||||
glFlush(); | |||||
if (view->impl->doubleBuffered) { | |||||
glXSwapBuffers(view->impl->display, view->impl->win); | |||||
} | |||||
} | |||||
glXMakeCurrent(view->impl->display, None, NULL); | |||||
#endif | |||||
} | |||||
int | |||||
puglCreateWindow(PuglView* view, const char* title) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
if (!impl) { | |||||
return 1; | |||||
} | |||||
view->impl = impl; | |||||
impl->display = XOpenDisplay(NULL); | |||||
if (!impl->display) { | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
impl->screen = DefaultScreen(impl->display); | |||||
XVisualInfo* vi = NULL; | |||||
#ifdef PUGL_OPENGL | |||||
impl->doubleBuffered = True; | |||||
vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); | |||||
if (!vi) { | |||||
vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||||
#ifdef PUGL_VERBOSE | |||||
printf("puGL: multisampling (antialiasing) is not available\n"); | |||||
#endif | |||||
} | |||||
if (!vi) { | |||||
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||||
impl->doubleBuffered = False; | |||||
} | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
XVisualInfo pat; | |||||
int n; | |||||
pat.screen = impl->screen; | |||||
vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); | |||||
#endif | |||||
if (!vi) { | |||||
XCloseDisplay(impl->display); | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
#ifdef PUGL_VERBOSE | |||||
#ifdef PUGL_OPENGL | |||||
int glxMajor, glxMinor; | |||||
glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||||
printf("puGL: GLX-Version : %d.%d\n", glxMajor, glxMinor); | |||||
#endif | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||||
if (!impl->ctx) { | |||||
XFree(vi); | |||||
XCloseDisplay(impl->display); | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
#endif | |||||
Window xParent = view->parent | |||||
? (Window)view->parent | |||||
: RootWindow(impl->display, impl->screen); | |||||
Colormap cmap = XCreateColormap( | |||||
impl->display, xParent, vi->visual, AllocNone); | |||||
XSetWindowAttributes attr; | |||||
memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||||
attr.border_pixel = BlackPixel(impl->display, impl->screen); | |||||
attr.colormap = cmap; | |||||
attr.event_mask = (ExposureMask | StructureNotifyMask | | |||||
EnterWindowMask | LeaveWindowMask | | |||||
KeyPressMask | KeyReleaseMask | | |||||
ButtonPressMask | ButtonReleaseMask | | |||||
PointerMotionMask | FocusChangeMask); | |||||
impl->win = XCreateWindow( | |||||
impl->display, xParent, | |||||
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||||
CWBorderPixel | CWColormap | CWEventMask, &attr); | |||||
if (!impl->win) { | |||||
#ifdef PUGL_OPENGL | |||||
glXDestroyContext(impl->display, impl->ctx); | |||||
#endif | |||||
XFree(vi); | |||||
XCloseDisplay(impl->display); | |||||
free(impl); | |||||
return 1; | |||||
} | |||||
#ifdef PUGL_CAIRO | |||||
impl->xlib_surface = cairo_xlib_surface_create( | |||||
impl->display, impl->win, vi->visual, view->width, view->height); | |||||
if (impl->xlib_surface == NULL || cairo_surface_status(impl->xlib_surface) != CAIRO_STATUS_SUCCESS) { | |||||
printf("puGL: failed to create cairo surface\n"); | |||||
} | |||||
else { | |||||
impl->xlib_cr = cairo_create(impl->xlib_surface); | |||||
} | |||||
if (impl->xlib_cr == NULL || cairo_status(impl->xlib_cr) != CAIRO_STATUS_SUCCESS) { | |||||
cairo_destroy(impl->xlib_cr); | |||||
cairo_surface_destroy(impl->xlib_surface); | |||||
XDestroyWindow(impl->display, impl->win); | |||||
XFree(vi); | |||||
XCloseDisplay(impl->display); | |||||
free(impl); | |||||
printf("puGL: failed to create cairo context\n"); | |||||
return 1; | |||||
} | |||||
#endif | |||||
if (view->width > 1 || view->height > 1) { | |||||
puglUpdateGeometryConstraints(view, view->min_width, view->min_height, view->min_width != view->width); | |||||
XResizeWindow(view->impl->display, view->impl->win, view->width, view->height); | |||||
} | |||||
if (title) { | |||||
XStoreName(impl->display, impl->win, title); | |||||
Atom netWmName = XInternAtom(impl->display, "_NET_WM_NAME", False); | |||||
Atom utf8String = XInternAtom(impl->display, "UTF8_STRING", False); | |||||
XChangeProperty(impl->display, impl->win, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); | |||||
} | |||||
if (view->transient_parent > 0) { | |||||
XSetTransientForHint(impl->display, impl->win, (Window)view->transient_parent); | |||||
} | |||||
if (view->parent) { | |||||
XMapRaised(impl->display, impl->win); | |||||
} else { | |||||
Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | |||||
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||||
} | |||||
#ifdef PUGL_VERBOSE | |||||
#ifdef PUGL_OPENGL | |||||
if (glXIsDirect(impl->display, impl->ctx)) { | |||||
printf("puGL: DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||||
} else { | |||||
printf("puGL: No DRI available\n"); | |||||
} | |||||
#endif | |||||
#endif | |||||
XFree(vi); | |||||
return 0; | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
if (!view) { | |||||
return; | |||||
} | |||||
PuglInternals* const impl = view->impl; | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
x_fib_close(impl->display); | |||||
#endif | |||||
#ifdef PUGL_OPENGL | |||||
glXDestroyContext(impl->display, impl->ctx); | |||||
#endif | |||||
#ifdef PUGL_CAIRO | |||||
cairo_destroy(impl->xlib_cr); | |||||
cairo_destroy(impl->buffer_cr); | |||||
cairo_surface_destroy(impl->xlib_surface); | |||||
cairo_surface_destroy(impl->buffer_surface); | |||||
#endif | |||||
XDestroyWindow(impl->display, impl->win); | |||||
XCloseDisplay(impl->display); | |||||
free(impl); | |||||
free(view); | |||||
} | |||||
void | |||||
puglShowWindow(PuglView* view) | |||||
{ | |||||
XMapRaised(view->impl->display, view->impl->win); | |||||
} | |||||
void | |||||
puglHideWindow(PuglView* view) | |||||
{ | |||||
XUnmapWindow(view->impl->display, view->impl->win); | |||||
} | |||||
static void | |||||
puglReshape(PuglView* view, int width, int height) | |||||
{ | |||||
puglEnterContext(view); | |||||
if (view->reshapeFunc) { | |||||
view->reshapeFunc(view, width, height); | |||||
} else { | |||||
puglDefaultReshape(width, height); | |||||
} | |||||
puglLeaveContext(view, false); | |||||
view->width = width; | |||||
view->height = height; | |||||
} | |||||
static void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
PuglInternals* impl = view->impl; | |||||
puglEnterContext(view); | |||||
#ifdef PUGL_CAIRO | |||||
cairo_t* bc = impl->buffer_cr; | |||||
cairo_surface_t* xs = impl->xlib_surface; | |||||
cairo_surface_t* bs = impl->buffer_surface; | |||||
int w = cairo_xlib_surface_get_width(xs); | |||||
int h = cairo_xlib_surface_get_height(xs); | |||||
int bw = bs ? cairo_image_surface_get_width(bs) : -1; | |||||
int bh = bs ? cairo_image_surface_get_height(bs) : -1; | |||||
if (!bc || bw != w || bh != h) { | |||||
cairo_destroy(bc); | |||||
cairo_surface_destroy(bs); | |||||
bs = cairo_surface_create_similar_image(xs, CAIRO_FORMAT_ARGB32, w, h); | |||||
bc = bs ? cairo_create(bs) : NULL; | |||||
impl->buffer_cr = bc; | |||||
impl->buffer_surface = bs; | |||||
} | |||||
if (!bc) { | |||||
puglLeaveContext(view, false); | |||||
return; | |||||
} | |||||
#endif | |||||
view->redisplay = false; | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
#ifdef PUGL_CAIRO | |||||
cairo_t* xc = impl->xlib_cr; | |||||
cairo_set_source_surface(xc, impl->buffer_surface, 0, 0); | |||||
cairo_paint(xc); | |||||
#endif | |||||
puglLeaveContext(view, true); | |||||
(void)impl; | |||||
} | |||||
static void | |||||
puglResize(PuglView* view) | |||||
{ | |||||
int set_hints = 1; | |||||
view->pending_resize = false; | |||||
if (!view->resizeFunc) { return; } | |||||
/* ask the plugin about the new size */ | |||||
view->resizeFunc(view, &view->width, &view->height, &set_hints); | |||||
if (set_hints) { | |||||
XSizeHints sizeHints; | |||||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||||
sizeHints.flags = PMinSize|PMaxSize; | |||||
sizeHints.min_width = view->width; | |||||
sizeHints.min_height = view->height; | |||||
sizeHints.max_width = view->user_resizable ? 4096 : view->width; | |||||
sizeHints.max_height = view->user_resizable ? 4096 : view->height; | |||||
XSetWMNormalHints(view->impl->display, view->impl->win, &sizeHints); | |||||
} | |||||
XResizeWindow(view->impl->display, view->impl->win, view->width, view->height); | |||||
XFlush(view->impl->display); | |||||
#ifdef PUGL_VERBOSE | |||||
printf("puGL: window resize (%dx%d)\n", view->width, view->height); | |||||
#endif | |||||
/* and call Reshape in glX context */ | |||||
puglReshape(view, view->width, view->height); | |||||
} | |||||
static PuglKey | |||||
keySymToSpecial(KeySym sym) | |||||
{ | |||||
switch (sym) { | |||||
case XK_F1: return PUGL_KEY_F1; | |||||
case XK_F2: return PUGL_KEY_F2; | |||||
case XK_F3: return PUGL_KEY_F3; | |||||
case XK_F4: return PUGL_KEY_F4; | |||||
case XK_F5: return PUGL_KEY_F5; | |||||
case XK_F6: return PUGL_KEY_F6; | |||||
case XK_F7: return PUGL_KEY_F7; | |||||
case XK_F8: return PUGL_KEY_F8; | |||||
case XK_F9: return PUGL_KEY_F9; | |||||
case XK_F10: return PUGL_KEY_F10; | |||||
case XK_F11: return PUGL_KEY_F11; | |||||
case XK_F12: return PUGL_KEY_F12; | |||||
case XK_Left: return PUGL_KEY_LEFT; | |||||
case XK_Up: return PUGL_KEY_UP; | |||||
case XK_Right: return PUGL_KEY_RIGHT; | |||||
case XK_Down: return PUGL_KEY_DOWN; | |||||
case XK_Page_Up: return PUGL_KEY_PAGE_UP; | |||||
case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; | |||||
case XK_Home: return PUGL_KEY_HOME; | |||||
case XK_End: return PUGL_KEY_END; | |||||
case XK_Insert: return PUGL_KEY_INSERT; | |||||
case XK_Shift_L: return PUGL_KEY_SHIFT; | |||||
case XK_Shift_R: return PUGL_KEY_SHIFT; | |||||
case XK_Control_L: return PUGL_KEY_CTRL; | |||||
case XK_Control_R: return PUGL_KEY_CTRL; | |||||
case XK_Alt_L: return PUGL_KEY_ALT; | |||||
case XK_Alt_R: return PUGL_KEY_ALT; | |||||
case XK_Super_L: return PUGL_KEY_SUPER; | |||||
case XK_Super_R: return PUGL_KEY_SUPER; | |||||
} | |||||
return (PuglKey)0; | |||||
} | |||||
static void | |||||
setModifiers(PuglView* view, unsigned xstate, unsigned xtime) | |||||
{ | |||||
view->event_timestamp_ms = xtime; | |||||
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 void | |||||
dispatchKey(PuglView* view, XEvent* event, bool press) | |||||
{ | |||||
KeySym sym; | |||||
char str[5]; | |||||
PuglKey special; | |||||
const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); | |||||
if (sym == XK_Escape && view->closeFunc && !press && !view->parent) { | |||||
view->closeFunc(view); | |||||
view->redisplay = false; | |||||
return; | |||||
} | |||||
if (n == 0 && sym == 0) { | |||||
goto send_event; | |||||
return; | |||||
} | |||||
if (n > 1) { | |||||
fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); | |||||
goto send_event; | |||||
return; | |||||
} | |||||
special = keySymToSpecial(sym); | |||||
if (special && view->specialFunc) { | |||||
if (view->specialFunc(view, press, special) == 0) { | |||||
return; | |||||
} | |||||
} else if (!special && view->keyboardFunc) { | |||||
if (view->keyboardFunc(view, press, str[0]) == 0) { | |||||
return; | |||||
} | |||||
} | |||||
send_event: | |||||
if (view->parent != 0) { | |||||
event->xkey.time = 0; // purposefully set an invalid time, used for feedback detection on bad hosts | |||||
event->xany.window = view->parent; | |||||
XSendEvent(view->impl->display, view->parent, False, NoEventMask, event); | |||||
} | |||||
} | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
int conf_width = -1; | |||||
int conf_height = -1; | |||||
XEvent event; | |||||
while (XPending(view->impl->display) > 0) { | |||||
XNextEvent(view->impl->display, &event); | |||||
#ifndef DGL_FILE_BROWSER_DISABLED | |||||
if (x_fib_handle_events(view->impl->display, &event)) { | |||||
const int status = x_fib_status(); | |||||
if (status > 0) { | |||||
char* const filename = x_fib_filename(); | |||||
x_fib_close(view->impl->display); | |||||
if (view->fileSelectedFunc) { | |||||
view->fileSelectedFunc(view, filename); | |||||
} | |||||
free(filename); | |||||
} else if (status < 0) { | |||||
x_fib_close(view->impl->display); | |||||
if (view->fileSelectedFunc) { | |||||
view->fileSelectedFunc(view, NULL); | |||||
} | |||||
} | |||||
break; | |||||
} | |||||
#endif | |||||
if (event.xany.window != view->impl->win && | |||||
(view->parent == 0 || event.xany.window != (Window)view->parent)) { | |||||
continue; | |||||
} | |||||
if ((event.type == KeyPress || event.type == KeyRelease) && event.xkey.time == 0) { | |||||
continue; | |||||
} | |||||
switch (event.type) { | |||||
case UnmapNotify: | |||||
if (view->motionFunc) { | |||||
view->motionFunc(view, -1, -1); | |||||
} | |||||
break; | |||||
case MapNotify: | |||||
puglReshape(view, view->width, view->height); | |||||
break; | |||||
case ConfigureNotify: | |||||
if ((event.xconfigure.width != view->width) || | |||||
(event.xconfigure.height != view->height)) { | |||||
conf_width = event.xconfigure.width; | |||||
conf_height = event.xconfigure.height; | |||||
} | |||||
break; | |||||
case Expose: | |||||
if (event.xexpose.count != 0) { | |||||
break; | |||||
} | |||||
view->redisplay = true; | |||||
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 == event.xkey.time && | |||||
next.xkey.keycode == event.xkey.keycode) { | |||||
XNextEvent(view->impl->display, &event); | |||||
repeated = true; | |||||
} | |||||
} | |||||
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 (conf_width != -1) { | |||||
#ifdef PUGL_CAIRO | |||||
// Resize surfaces/contexts before dispatching | |||||
view->redisplay = true; | |||||
cairo_xlib_surface_set_size(view->impl->xlib_surface, | |||||
conf_width, conf_height); | |||||
#endif | |||||
puglReshape(view, conf_width, conf_height); | |||||
} | |||||
if (view->pending_resize) { | |||||
puglResize(view); | |||||
} | |||||
if (view->redisplay) { | |||||
puglDisplay(view); | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
void | |||||
puglPostRedisplay(PuglView* view) | |||||
{ | |||||
view->redisplay = true; | |||||
} | |||||
void | |||||
puglPostResize(PuglView* view) | |||||
{ | |||||
view->pending_resize = true; | |||||
} | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
return view->impl->win; | |||||
} | |||||
void* | |||||
puglGetContext(PuglView* view) | |||||
{ | |||||
#ifdef PUGL_CAIRO | |||||
return view->impl->buffer_cr; | |||||
#endif | |||||
return NULL; | |||||
// may be unused | |||||
(void)view; | |||||
} | |||||
int | |||||
puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) | |||||
{ | |||||
XSizeHints sizeHints; | |||||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||||
sizeHints.flags = PMinSize|PMaxSize; | |||||
sizeHints.min_width = min_width; | |||||
sizeHints.min_height = min_height; | |||||
sizeHints.max_width = view->user_resizable ? 4096 : min_width; | |||||
sizeHints.max_height = view->user_resizable ? 4096 : min_height; | |||||
if (aspect) { | |||||
sizeHints.flags |= PAspect; | |||||
sizeHints.min_aspect.x = min_width; | |||||
sizeHints.min_aspect.y = min_height; | |||||
sizeHints.max_aspect.x = min_width; | |||||
sizeHints.max_aspect.y = min_height; | |||||
} | |||||
XSetWMNormalHints(view->impl->display, view->impl->win, &sizeHints); | |||||
return 0; | |||||
} |