Browse Source

Remove old pugl code

Signed-off-by: falkTX <falktx@falktx.com>
pull/397/head
falkTX 2 years ago
parent
commit
a325bf55b0
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
6 changed files with 0 additions and 3474 deletions
  1. +0
    -492
      dgl/src/pugl-custom/pugl.h
  2. +0
    -441
      dgl/src/pugl-custom/pugl_haiku.cpp
  3. +0
    -263
      dgl/src/pugl-custom/pugl_internal.h
  4. +0
    -974
      dgl/src/pugl-custom/pugl_osx.m
  5. +0
    -565
      dgl/src/pugl-custom/pugl_win.cpp
  6. +0
    -739
      dgl/src/pugl-custom/pugl_x11.c

+ 0
- 492
dgl/src/pugl-custom/pugl.h View File

@@ -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 */

+ 0
- 441
dgl/src/pugl-custom/pugl_haiku.cpp View File

@@ -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;
}

+ 0
- 263
dgl/src/pugl-custom/pugl_internal.h View File

@@ -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
}

+ 0
- 974
dgl/src/pugl-custom/pugl_osx.m View File

@@ -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;
}

+ 0
- 565
dgl/src/pugl-custom/pugl_win.cpp View File

@@ -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;
}

+ 0
- 739
dgl/src/pugl-custom/pugl_x11.c View File

@@ -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;
}

Loading…
Cancel
Save