@@ -49,6 +49,7 @@ public: | |||||
void setOrientation(Orientation orientation); | void setOrientation(Orientation orientation); | ||||
void setRange(float min, float max); | void setRange(float min, float max); | ||||
void setStep(float step); | |||||
void setValue(float value, bool sendCallback = false); | void setValue(float value, bool sendCallback = false); | ||||
void setRotationAngle(int angle); | void setRotationAngle(int angle); | ||||
@@ -65,7 +66,9 @@ private: | |||||
Image fImage; | Image fImage; | ||||
float fMinimum; | float fMinimum; | ||||
float fMaximum; | float fMaximum; | ||||
float fStep; | |||||
float fValue; | float fValue; | ||||
float fValueTmp; | |||||
Orientation fOrientation; | Orientation fOrientation; | ||||
int fRotationAngle; | int fRotationAngle; | ||||
@@ -48,8 +48,8 @@ public: | |||||
void setEndPos(int x, int y); | void setEndPos(int x, int y); | ||||
void setRange(float min, float max); | void setRange(float min, float max); | ||||
void setStep(float step); | |||||
void setValue(float value, bool sendCallback = false); | void setValue(float value, bool sendCallback = false); | ||||
void setIsSwitch(bool yesNo); | |||||
void setCallback(Callback* callback); | void setCallback(Callback* callback); | ||||
@@ -62,9 +62,10 @@ private: | |||||
Image fImage; | Image fImage; | ||||
float fMinimum; | float fMinimum; | ||||
float fMaximum; | float fMaximum; | ||||
float fStep; | |||||
float fValue; | float fValue; | ||||
float fValueTmp; | |||||
bool fIsSwitch; | |||||
bool fDragging; | bool fDragging; | ||||
int fStartedX; | int fStartedX; | ||||
int fStartedY; | int fStartedY; | ||||
@@ -40,8 +40,6 @@ public: | |||||
Window(App& app, intptr_t parentId); | Window(App& app, intptr_t parentId); | ||||
virtual ~Window(); | virtual ~Window(); | ||||
void setTransient(const intptr_t win); | |||||
void show(); | void show(); | ||||
void hide(); | void hide(); | ||||
void close(); | void close(); | ||||
@@ -16,7 +16,7 @@ | |||||
#include "../ImageKnob.hpp" | #include "../ImageKnob.hpp" | ||||
#include <cassert> | |||||
#include <cmath> | |||||
#include <cstdio> | #include <cstdio> | ||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
@@ -28,7 +28,9 @@ ImageKnob::ImageKnob(Window& parent, const Image& image, Orientation orientation | |||||
fImage(image), | fImage(image), | ||||
fMinimum(0.0f), | fMinimum(0.0f), | ||||
fMaximum(1.0f), | fMaximum(1.0f), | ||||
fStep(0.0f), | |||||
fValue(0.5f), | fValue(0.5f), | ||||
fValueTmp(fValue), | |||||
fOrientation(orientation), | fOrientation(orientation), | ||||
fRotationAngle(0), | fRotationAngle(0), | ||||
fDragging(false), | fDragging(false), | ||||
@@ -49,7 +51,9 @@ ImageKnob::ImageKnob(Widget* widget, const Image& image, Orientation orientation | |||||
fImage(image), | fImage(image), | ||||
fMinimum(0.0f), | fMinimum(0.0f), | ||||
fMaximum(1.0f), | fMaximum(1.0f), | ||||
fStep(0.0f), | |||||
fValue(0.5f), | fValue(0.5f), | ||||
fValueTmp(fValue), | |||||
fOrientation(orientation), | fOrientation(orientation), | ||||
fRotationAngle(0), | fRotationAngle(0), | ||||
fDragging(false), | fDragging(false), | ||||
@@ -70,7 +74,9 @@ ImageKnob::ImageKnob(const ImageKnob& imageKnob) | |||||
fImage(imageKnob.fImage), | fImage(imageKnob.fImage), | ||||
fMinimum(imageKnob.fMinimum), | fMinimum(imageKnob.fMinimum), | ||||
fMaximum(imageKnob.fMaximum), | fMaximum(imageKnob.fMaximum), | ||||
fStep(imageKnob.fStep), | |||||
fValue(imageKnob.fValue), | fValue(imageKnob.fValue), | ||||
fValueTmp(fValue), | |||||
fOrientation(imageKnob.fOrientation), | fOrientation(imageKnob.fOrientation), | ||||
fRotationAngle(imageKnob.fRotationAngle), | fRotationAngle(imageKnob.fRotationAngle), | ||||
fDragging(false), | fDragging(false), | ||||
@@ -129,12 +135,21 @@ void ImageKnob::setRange(float min, float max) | |||||
fMaximum = max; | fMaximum = max; | ||||
} | } | ||||
void ImageKnob::setStep(float step) | |||||
{ | |||||
fStep = step; | |||||
} | |||||
void ImageKnob::setValue(float value, bool sendCallback) | void ImageKnob::setValue(float value, bool sendCallback) | ||||
{ | { | ||||
if (fValue == value) | if (fValue == value) | ||||
return; | return; | ||||
fValue = value; | fValue = value; | ||||
if (fStep == 0.0f) | |||||
fValueTmp = value; | |||||
repaint(); | repaint(); | ||||
if (sendCallback && fCallback != nullptr) | if (sendCallback && fCallback != nullptr) | ||||
@@ -280,40 +295,49 @@ bool ImageKnob::onMotion(int x, int y) | |||||
if (! fDragging) | if (! fDragging) | ||||
return false; | return false; | ||||
bool doVal = false; | |||||
float d, value; | |||||
if (fOrientation == ImageKnob::Horizontal) | if (fOrientation == ImageKnob::Horizontal) | ||||
{ | { | ||||
int movX = x - fLastX; | |||||
if (movX != 0) | |||||
if (int movX = x - fLastX) | |||||
{ | { | ||||
float d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; | |||||
float value = fValue + (float(fMaximum - fMinimum) / d * float(movX)); | |||||
if (value < fMinimum) | |||||
value = fMinimum; | |||||
else if (value > fMaximum) | |||||
value = fMaximum; | |||||
setValue(value, true); | |||||
d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; | |||||
value = fValueTmp + (float(fMaximum - fMinimum) / d * float(movX)); | |||||
doVal = true; | |||||
} | } | ||||
} | } | ||||
else if (fOrientation == ImageKnob::Vertical) | else if (fOrientation == ImageKnob::Vertical) | ||||
{ | { | ||||
int movY = fLastY - y; | |||||
if (movY != 0) | |||||
if (int movY = fLastY - y) | |||||
{ | { | ||||
float d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; | |||||
float value = fValue + (float(fMaximum - fMinimum) / d * float(movY)); | |||||
d = (getModifiers() & MODIFIER_SHIFT) ? 2000.0f : 200.0f; | |||||
value = fValueTmp + (float(fMaximum - fMinimum) / d * float(movY)); | |||||
doVal = true; | |||||
} | |||||
} | |||||
if (value < fMinimum) | |||||
value = fMinimum; | |||||
else if (value > fMaximum) | |||||
value = fMaximum; | |||||
if (! doVal) | |||||
return false; | |||||
setValue(value, true); | |||||
} | |||||
if (value < fMinimum) | |||||
{ | |||||
value = fMinimum; | |||||
fValueTmp = value; | |||||
} | } | ||||
else if (value > fMaximum) | |||||
{ | |||||
value = fMaximum; | |||||
fValueTmp = value; | |||||
} | |||||
else if (fStep != 0.0f) | |||||
{ | |||||
fValueTmp = value; | |||||
const float rest = std::fmod(value, fStep); | |||||
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||||
} | |||||
setValue(value, true); | |||||
fLastX = x; | fLastX = x; | ||||
fLastY = y; | fLastY = y; | ||||
@@ -16,6 +16,8 @@ | |||||
#include "../ImageSlider.hpp" | #include "../ImageSlider.hpp" | ||||
#include <cmath> | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -25,8 +27,9 @@ ImageSlider::ImageSlider(Window& parent, const Image& image) | |||||
fImage(image), | fImage(image), | ||||
fMinimum(0.0f), | fMinimum(0.0f), | ||||
fMaximum(1.0f), | fMaximum(1.0f), | ||||
fStep(0.0f), | |||||
fValue(0.5f), | fValue(0.5f), | ||||
fIsSwitch(false), | |||||
fValueTmp(fValue), | |||||
fDragging(false), | fDragging(false), | ||||
fStartedX(0), | fStartedX(0), | ||||
fStartedY(0), | fStartedY(0), | ||||
@@ -40,8 +43,9 @@ ImageSlider::ImageSlider(Widget* widget, const Image& image) | |||||
fImage(image), | fImage(image), | ||||
fMinimum(0.0f), | fMinimum(0.0f), | ||||
fMaximum(1.0f), | fMaximum(1.0f), | ||||
fStep(0.0f), | |||||
fValue(0.5f), | fValue(0.5f), | ||||
fIsSwitch(false), | |||||
fValueTmp(fValue), | |||||
fDragging(false), | fDragging(false), | ||||
fStartedX(0), | fStartedX(0), | ||||
fStartedY(0), | fStartedY(0), | ||||
@@ -55,8 +59,9 @@ ImageSlider::ImageSlider(const ImageSlider& imageSlider) | |||||
fImage(imageSlider.fImage), | fImage(imageSlider.fImage), | ||||
fMinimum(imageSlider.fMinimum), | fMinimum(imageSlider.fMinimum), | ||||
fMaximum(imageSlider.fMaximum), | fMaximum(imageSlider.fMaximum), | ||||
fStep(imageSlider.fStep), | |||||
fValue(imageSlider.fValue), | fValue(imageSlider.fValue), | ||||
fIsSwitch(imageSlider.fIsSwitch), | |||||
fValueTmp(fValue), | |||||
fDragging(false), | fDragging(false), | ||||
fStartedX(0), | fStartedX(0), | ||||
fStartedY(0), | fStartedY(0), | ||||
@@ -118,27 +123,27 @@ void ImageSlider::setRange(float min, float max) | |||||
fMaximum = max; | fMaximum = max; | ||||
} | } | ||||
void ImageSlider::setStep(float step) | |||||
{ | |||||
fStep = step; | |||||
} | |||||
void ImageSlider::setValue(float value, bool sendCallback) | void ImageSlider::setValue(float value, bool sendCallback) | ||||
{ | { | ||||
if (fValue == value) | if (fValue == value) | ||||
return; | return; | ||||
fValue = value; | fValue = value; | ||||
if (fStep == 0.0f) | |||||
fValueTmp = value; | |||||
repaint(); | repaint(); | ||||
if (sendCallback && fCallback != nullptr) | if (sendCallback && fCallback != nullptr) | ||||
fCallback->imageSliderValueChanged(this, fValue); | fCallback->imageSliderValueChanged(this, fValue); | ||||
} | } | ||||
void ImageSlider::setIsSwitch(bool yesNo) | |||||
{ | |||||
if (fIsSwitch == yesNo) | |||||
return; | |||||
fIsSwitch = yesNo; | |||||
repaint(); | |||||
} | |||||
void ImageSlider::setCallback(Callback* callback) | void ImageSlider::setCallback(Callback* callback) | ||||
{ | { | ||||
fCallback = callback; | fCallback = callback; | ||||
@@ -198,21 +203,23 @@ bool ImageSlider::onMouse(int button, bool press, int x, int y) | |||||
float value; | float value; | ||||
if (fIsSwitch) | |||||
value = fMaximum - vper * (fMaximum - fMinimum); | |||||
if (value < fMinimum) | |||||
{ | { | ||||
if (vper < 0.5f) | |||||
value = fMaximum; | |||||
else | |||||
value = fMinimum; | |||||
value = fMinimum; | |||||
fValueTmp = value; | |||||
} | } | ||||
else | |||||
else if (value > fMaximum) | |||||
{ | { | ||||
value = fMaximum - vper * (fMaximum - fMinimum); | |||||
if (value < fMinimum) | |||||
value = fMinimum; | |||||
else if (value > fMaximum) | |||||
value = fMaximum; | |||||
value = fMaximum; | |||||
fValueTmp = value; | |||||
} | |||||
else if (fStep != 0.0f) | |||||
{ | |||||
fValueTmp = value; | |||||
const float rest = std::fmod(value, fStep); | |||||
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||||
} | } | ||||
fDragging = true; | fDragging = true; | ||||
@@ -262,21 +269,23 @@ bool ImageSlider::onMotion(int x, int y) | |||||
float value; | float value; | ||||
if (fIsSwitch) | |||||
value = fMaximum - vper * (fMaximum - fMinimum); | |||||
if (value < fMinimum) | |||||
{ | { | ||||
if (vper < 0.5f) | |||||
value = fMaximum; | |||||
else | |||||
value = fMinimum; | |||||
value = fMinimum; | |||||
fValueTmp = value; | |||||
} | } | ||||
else | |||||
else if (value > fMaximum) | |||||
{ | { | ||||
value = fMaximum - vper * (fMaximum - fMinimum); | |||||
if (value < fMinimum) | |||||
value = fMinimum; | |||||
else if (value > fMaximum) | |||||
value = fMaximum; | |||||
value = fMaximum; | |||||
fValueTmp = value; | |||||
} | |||||
else if (fStep != 0.0f) | |||||
{ | |||||
fValueTmp = value; | |||||
const float rest = std::fmod(value, fStep); | |||||
value = value - rest + (rest > fStep/2.0f ? fStep : 0.0f); | |||||
} | } | ||||
setValue(value, true); | setValue(value, true); | ||||
@@ -119,11 +119,11 @@ public: | |||||
PrivateData(App& app, Window* const self, const intptr_t parentId) | PrivateData(App& app, Window* const self, const intptr_t parentId) | ||||
: fApp(app), | : fApp(app), | ||||
fSelf(self), | fSelf(self), | ||||
fView(puglCreate(parentId, "Window", 100, 100, true, true)), | |||||
fView(puglCreate(parentId, "Window", 100, 100, (parentId == 0), (parentId != 0))), | |||||
fFirstInit(true), | fFirstInit(true), | ||||
fVisible(true), | |||||
fResizable(false), | |||||
fUsingEmbed(true), | |||||
fVisible(parentId != 0), | |||||
fResizable(parentId == 0), | |||||
fUsingEmbed(parentId != 0), | |||||
#if DGL_OS_WINDOWS | #if DGL_OS_WINDOWS | ||||
hwnd(0) | hwnd(0) | ||||
#elif DGL_OS_LINUX | #elif DGL_OS_LINUX | ||||
@@ -133,12 +133,20 @@ public: | |||||
_dummy('\0') | _dummy('\0') | ||||
#endif | #endif | ||||
{ | { | ||||
DBG("Creating embedded window..."); DBGF; | |||||
if (parentId != 0) { | |||||
DBG("Creating embedded window..."); DBGF; | |||||
} else { | |||||
DBG("Creating window without parent..."); DBGF; | |||||
} | |||||
init(); | init(); | ||||
DBG("NOTE: Embed window is always visible and non-resizable\n"); | |||||
fApp._oneShown(); | |||||
fFirstInit = false; | |||||
if (parentId != 0) | |||||
{ | |||||
DBG("NOTE: Embed window is always visible and non-resizable\n"); | |||||
fApp._oneShown(); | |||||
fFirstInit = false; | |||||
} | |||||
} | } | ||||
void init() | void init() | ||||
@@ -210,17 +218,6 @@ public: | |||||
DBG("Success!\n"); | DBG("Success!\n"); | ||||
} | } | ||||
// TESTING | |||||
void setTransient(const intptr_t win) | |||||
{ | |||||
#if DGL_OS_LINUX | |||||
XSetTransientForHint(xDisplay, xWindow, (::Window)win); | |||||
XFlush(xDisplay); | |||||
#else | |||||
return; (void)win; | |||||
#endif | |||||
} | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
void close() | void close() | ||||
@@ -791,11 +788,6 @@ Window::~Window() | |||||
delete pData; | delete pData; | ||||
} | } | ||||
void Window::setTransient(const intptr_t win) | |||||
{ | |||||
pData->setTransient(win); | |||||
} | |||||
void Window::show() | void Window::show() | ||||
{ | { | ||||
pData->setVisible(true); | pData->setVisible(true); | ||||
@@ -1,357 +0,0 @@ | |||||
/* | |||||
Copyright 2012 David Robillard <http://drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
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 <windows.h> /* Broken Windows GL headers require this */ | |||||
# endif | |||||
# include "GL/gl.h" | |||||
#endif | |||||
#ifdef PUGL_SHARED | |||||
# ifdef _WIN32 | |||||
# define PUGL_LIB_IMPORT __declspec(dllimport) | |||||
# define PUGL_LIB_EXPORT __declspec(dllexport) | |||||
# else | |||||
# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) | |||||
# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) | |||||
# endif | |||||
# ifdef PUGL_INTERNAL | |||||
# define PUGL_API PUGL_LIB_EXPORT | |||||
# else | |||||
# define PUGL_API PUGL_LIB_IMPORT | |||||
# endif | |||||
#else | |||||
# define PUGL_API | |||||
#endif | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#else | |||||
# include <stdbool.h> | |||||
#endif | |||||
/** | |||||
@defgroup pugl Pugl | |||||
A minimal portable API for OpenGL. | |||||
@{ | |||||
*/ | |||||
/** | |||||
An OpenGL view. | |||||
*/ | |||||
typedef struct PuglViewImpl PuglView; | |||||
/** | |||||
A native window handle. | |||||
On X11, this is a Window. | |||||
On OSX, this is an NSView*. | |||||
On Windows, this is a HWND. | |||||
*/ | |||||
typedef intptr_t PuglNativeWindow; | |||||
/** | |||||
Return status code. | |||||
*/ | |||||
typedef enum { | |||||
PUGL_SUCCESS = 0 | |||||
} PuglStatus; | |||||
/** | |||||
Convenience symbols for ASCII control characters. | |||||
*/ | |||||
typedef enum { | |||||
PUGL_CHAR_BACKSPACE = 0x08, | |||||
PUGL_CHAR_ESCAPE = 0x1B, | |||||
PUGL_CHAR_DELETE = 0x7F | |||||
} PuglChar; | |||||
/** | |||||
Special (non-Unicode) keyboard keys. | |||||
*/ | |||||
typedef enum { | |||||
PUGL_KEY_F1 = 1, | |||||
PUGL_KEY_F2, | |||||
PUGL_KEY_F3, | |||||
PUGL_KEY_F4, | |||||
PUGL_KEY_F5, | |||||
PUGL_KEY_F6, | |||||
PUGL_KEY_F7, | |||||
PUGL_KEY_F8, | |||||
PUGL_KEY_F9, | |||||
PUGL_KEY_F10, | |||||
PUGL_KEY_F11, | |||||
PUGL_KEY_F12, | |||||
PUGL_KEY_LEFT, | |||||
PUGL_KEY_UP, | |||||
PUGL_KEY_RIGHT, | |||||
PUGL_KEY_DOWN, | |||||
PUGL_KEY_PAGE_UP, | |||||
PUGL_KEY_PAGE_DOWN, | |||||
PUGL_KEY_HOME, | |||||
PUGL_KEY_END, | |||||
PUGL_KEY_INSERT, | |||||
PUGL_KEY_SHIFT, | |||||
PUGL_KEY_CTRL, | |||||
PUGL_KEY_ALT, | |||||
PUGL_KEY_SUPER | |||||
} PuglKey; | |||||
/** | |||||
Keyboard modifier flags. | |||||
*/ | |||||
typedef enum { | |||||
PUGL_MOD_SHIFT = 1, /**< Shift key */ | |||||
PUGL_MOD_CTRL = 1 << 1, /**< Control key */ | |||||
PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ | |||||
PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ | |||||
} PuglMod; | |||||
/** | |||||
Handle for opaque user data. | |||||
*/ | |||||
typedef void* PuglHandle; | |||||
/** | |||||
A function called when the window is closed. | |||||
*/ | |||||
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. | |||||
*/ | |||||
typedef void (*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 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 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. | |||||
@param view The view the event occured in. | |||||
@param press True if the key was pressed, false if released. | |||||
@param key The key pressed. | |||||
*/ | |||||
typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | |||||
/** | |||||
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. | |||||
@param visible Whether window should be initially visible. | |||||
*/ | |||||
PUGL_API PuglView* | |||||
puglCreate(PuglNativeWindow parent, | |||||
const char* title, | |||||
int width, | |||||
int height, | |||||
bool resizable, | |||||
bool visible); | |||||
/** | |||||
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. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetHandle(PuglView* view, PuglHandle handle); | |||||
/** | |||||
Get the handle to be passed to all callbacks. | |||||
*/ | |||||
PUGL_API PuglHandle | |||||
puglGetHandle(PuglView* view); | |||||
/** | |||||
Return the timestamp (if any) of the currently-processing event. | |||||
*/ | |||||
PUGL_API uint32_t | |||||
puglGetEventTimestamp(PuglView* view); | |||||
/** | |||||
Get the currently active modifiers (PuglMod flags). | |||||
This should only be called from an event handler. | |||||
*/ | |||||
PUGL_API int | |||||
puglGetModifiers(PuglView* view); | |||||
/** | |||||
Ignore synthetic repeated key events. | |||||
*/ | |||||
PUGL_API void | |||||
puglIgnoreKeyRepeat(PuglView* view, bool ignore); | |||||
/** | |||||
Set the function to call when the window is closed. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); | |||||
/** | |||||
Set the display function which should draw the UI using GL. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); | |||||
/** | |||||
Set the function to call on keyboard events. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); | |||||
/** | |||||
Set the function to call on mouse motion. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); | |||||
/** | |||||
Set the function to call on mouse button events. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); | |||||
/** | |||||
Set the function to call on scroll events. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); | |||||
/** | |||||
Set the function to call on special events. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||||
/** | |||||
Set the function to call when the window size changes. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | |||||
/** | |||||
Return the native window handle. | |||||
*/ | |||||
PUGL_API PuglNativeWindow | |||||
puglGetNativeWindow(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. | |||||
*/ | |||||
PUGL_API PuglStatus | |||||
puglProcessEvents(PuglView* view); | |||||
/** | |||||
Request a redisplay on the next call to puglProcessEvents(). | |||||
*/ | |||||
PUGL_API void | |||||
puglPostRedisplay(PuglView* view); | |||||
/** | |||||
Destroy a GL window. | |||||
*/ | |||||
PUGL_API void | |||||
puglDestroy(PuglView* view); | |||||
/** | |||||
@} | |||||
*/ | |||||
#ifdef __cplusplus | |||||
} /* extern "C" */ | |||||
#endif | |||||
#endif /* PUGL_H_INCLUDED */ |
@@ -1,160 +0,0 @@ | |||||
/* | |||||
Copyright 2012 David Robillard <http://drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
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. | |||||
If you are copying the pugl code into your source tree, the following | |||||
symbols can be defined to tweak pugl behaviour: | |||||
PUGL_GRAB_FOCUS: Work around reparent keyboard issues by grabbing focus. | |||||
PUGL_VERBOSE: Print GL information to console. | |||||
*/ | |||||
#include "pugl.h" | |||||
#ifdef PUGL_VERBOSE | |||||
# include <stdio.h> | |||||
# define PUGL_LOG(str) fprintf(stderr, "pugl: " str) | |||||
# define PUGL_LOGF(fmt, ...) fprintf(stderr, "pugl: " fmt, __VA_ARGS__) | |||||
#else | |||||
# define PUGL_LOG(str) | |||||
# define PUGL_LOGF(fmt, ...) | |||||
#endif | |||||
void puglDefaultReshape(PuglView* view, int width, int height); | |||||
typedef struct PuglInternalsImpl PuglInternals; | |||||
struct PuglViewImpl { | |||||
PuglHandle handle; | |||||
PuglCloseFunc closeFunc; | |||||
PuglDisplayFunc displayFunc; | |||||
PuglKeyboardFunc keyboardFunc; | |||||
PuglMotionFunc motionFunc; | |||||
PuglMouseFunc mouseFunc; | |||||
PuglReshapeFunc reshapeFunc; | |||||
PuglScrollFunc scrollFunc; | |||||
PuglSpecialFunc specialFunc; | |||||
PuglInternals* impl; | |||||
int width; | |||||
int height; | |||||
int mods; | |||||
bool mouse_in_view; | |||||
bool ignoreKeyRepeat; | |||||
bool redisplay; | |||||
uint32_t event_timestamp_ms; | |||||
}; | |||||
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 | |||||
puglDefaultReshape(PuglView* view, int width, int height) | |||||
{ | |||||
glMatrixMode(GL_PROJECTION); | |||||
glLoadIdentity(); | |||||
glOrtho(0, width, height, 0, 0, 1); | |||||
glViewport(0, 0, width, height); | |||||
glMatrixMode(GL_MODELVIEW); | |||||
glLoadIdentity(); | |||||
return; | |||||
// unused | |||||
(void)view; | |||||
} | |||||
void | |||||
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 | |||||
puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) | |||||
{ | |||||
view->scrollFunc = scrollFunc; | |||||
} | |||||
void | |||||
puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||||
{ | |||||
view->specialFunc = specialFunc; | |||||
} |
@@ -1,442 +0,0 @@ | |||||
/* | |||||
Copyright 2012 David Robillard <http://drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
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> | |||||
#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) windowShouldClose:(id)sender; | |||||
- (BOOL) canBecomeKeyWindow:(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 result; | |||||
} | |||||
- (void)setPuglview:(PuglView*)view | |||||
{ | |||||
puglview = view; | |||||
[self setContentSize:NSMakeSize(view->width, view->height) ]; | |||||
} | |||||
- (BOOL)windowShouldClose:(id)sender | |||||
{ | |||||
if (puglview->closeFunc) | |||||
puglview->closeFunc(puglview); | |||||
return YES; | |||||
} | |||||
- (BOOL) canBecomeKeyWindow:(id)sender | |||||
{ | |||||
return NO; | |||||
} | |||||
@end | |||||
void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
} | |||||
@interface PuglOpenGLView : NSOpenGLView | |||||
{ | |||||
int colorBits; | |||||
int depthBits; | |||||
@public | |||||
PuglView* puglview; | |||||
NSTrackingArea* trackingArea; | |||||
} | |||||
- (id) initWithFrame:(NSRect)frame | |||||
colorBits:(int)numColorBits | |||||
depthBits:(int)numDepthBits; | |||||
- (void) reshape; | |||||
- (void) drawRect:(NSRect)rect; | |||||
- (void) mouseMoved:(NSEvent*)event; | |||||
- (void) mouseDragged:(NSEvent*)event; | |||||
- (void) mouseDown:(NSEvent*)event; | |||||
- (void) mouseUp:(NSEvent*)event; | |||||
- (void) rightMouseDragged:(NSEvent*)event; | |||||
- (void) rightMouseDown:(NSEvent*)event; | |||||
- (void) rightMouseUp:(NSEvent*)event; | |||||
- (void) keyDown:(NSEvent*)event; | |||||
- (void) keyUp:(NSEvent*)event; | |||||
- (void) flagsChanged:(NSEvent*)event; | |||||
@end | |||||
@implementation PuglOpenGLView | |||||
- (id) initWithFrame:(NSRect)frame | |||||
colorBits:(int)numColorBits | |||||
depthBits:(int)numDepthBits | |||||
{ | |||||
colorBits = numColorBits; | |||||
depthBits = numDepthBits; | |||||
NSOpenGLPixelFormatAttribute pixelAttribs[16] = { | |||||
NSOpenGLPFADoubleBuffer, | |||||
NSOpenGLPFAAccelerated, | |||||
NSOpenGLPFAColorSize, | |||||
colorBits, | |||||
NSOpenGLPFADepthSize, | |||||
depthBits, | |||||
0 | |||||
}; | |||||
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] | |||||
initWithAttributes:pixelAttribs]; | |||||
if (pixelFormat) { | |||||
self = [super initWithFrame:frame pixelFormat:pixelFormat]; | |||||
[pixelFormat release]; | |||||
if (self) { | |||||
[[self openGLContext] makeCurrentContext]; | |||||
[self reshape]; | |||||
} | |||||
} else { | |||||
self = nil; | |||||
} | |||||
return self; | |||||
} | |||||
- (void) reshape | |||||
{ | |||||
[[self openGLContext] update]; | |||||
NSRect bounds = [self bounds]; | |||||
int width = bounds.size.width; | |||||
int height = bounds.size.height; | |||||
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. | |||||
*/ | |||||
if (puglview->reshapeFunc) { | |||||
puglview->reshapeFunc(puglview, width, height); | |||||
} else { | |||||
puglDefaultReshape(puglview, width, height); | |||||
} | |||||
puglview->width = width; | |||||
puglview->height = height; | |||||
} | |||||
} | |||||
- (void) drawRect:(NSRect)rect | |||||
{ | |||||
puglDisplay(puglview); | |||||
glFlush(); | |||||
glSwapAPPLE(); | |||||
} | |||||
static unsigned | |||||
getModifiers(PuglView* view, NSEvent* ev) | |||||
{ | |||||
const unsigned modifierFlags = [ev modifierFlags]; | |||||
view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); | |||||
double ts = [ev timestamp] * 1000.0; | |||||
ts = (uint32)ts % 500000; //ridiculously large vals won't fit | |||||
view->event_timestamp_ms = ts; | |||||
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; | |||||
} | |||||
-(void)updateTrackingAreas | |||||
{ | |||||
if (trackingArea != nil) { | |||||
[self removeTrackingArea:trackingArea]; | |||||
[trackingArea release]; | |||||
} | |||||
const int opts = (NSTrackingMouseEnteredAndExited | | |||||
NSTrackingMouseMoved | | |||||
NSTrackingActiveAlways); | |||||
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] | |||||
options:opts | |||||
owner:self | |||||
userInfo:nil]; | |||||
[self addTrackingArea:trackingArea]; | |||||
} | |||||
- (void)mouseEntered:(NSEvent*)theEvent | |||||
{ | |||||
[self updateTrackingAreas]; | |||||
} | |||||
- (void)mouseExited:(NSEvent*)theEvent | |||||
{ | |||||
} | |||||
- (void) mouseMoved:(NSEvent*)event | |||||
{ | |||||
if (puglview->motionFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); | |||||
} | |||||
} | |||||
- (void) mouseDragged:(NSEvent*)event | |||||
{ | |||||
if (puglview->motionFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); | |||||
} | |||||
} | |||||
- (void) rightMouseDragged:(NSEvent*)event | |||||
{ | |||||
if (puglview->motionFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); | |||||
} | |||||
} | |||||
- (void) mouseDown:(NSEvent*)event | |||||
{ | |||||
if (puglview->mouseFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y); | |||||
} | |||||
} | |||||
- (void) mouseUp:(NSEvent*)event | |||||
{ | |||||
if (puglview->mouseFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y); | |||||
} | |||||
[self updateTrackingAreas]; | |||||
} | |||||
- (void) rightMouseDown:(NSEvent*)event | |||||
{ | |||||
if (puglview->mouseFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y); | |||||
} | |||||
} | |||||
- (void) rightMouseUp:(NSEvent*)event | |||||
{ | |||||
if (puglview->mouseFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y); | |||||
} | |||||
} | |||||
- (void) scrollWheel:(NSEvent*)event | |||||
{ | |||||
if (puglview->scrollFunc) { | |||||
NSPoint loc = [event locationInWindow]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->scrollFunc(puglview, | |||||
loc.x, puglview->height - loc.y, | |||||
[event deltaX], [event deltaY]); | |||||
} | |||||
[self updateTrackingAreas]; | |||||
} | |||||
- (void) keyDown:(NSEvent*)event | |||||
{ | |||||
if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { | |||||
NSString* chars = [event characters]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); | |||||
} | |||||
} | |||||
- (void) keyUp:(NSEvent*)event | |||||
{ | |||||
if (puglview->keyboardFunc) { | |||||
NSString* chars = [event characters]; | |||||
puglview->mods = getModifiers(puglview, event); | |||||
puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); | |||||
} | |||||
} | |||||
- (void) flagsChanged:(NSEvent*)event | |||||
{ | |||||
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; | |||||
} | |||||
} | |||||
@end | |||||
struct PuglInternalsImpl { | |||||
PuglOpenGLView* glview; | |||||
id window; | |||||
}; | |||||
PuglView* | |||||
puglCreate(PuglNativeWindow parent, | |||||
const char* title, | |||||
int width, | |||||
int height, | |||||
bool resizable, | |||||
bool visible) | |||||
{ | |||||
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||||
PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
if (!view || !impl) { | |||||
return NULL; | |||||
} | |||||
view->impl = impl; | |||||
view->width = width; | |||||
view->height = height; | |||||
[NSAutoreleasePool new]; | |||||
[NSApplication sharedApplication]; | |||||
NSString* titleString = [[NSString alloc] | |||||
initWithBytes:title | |||||
length:strlen(title) | |||||
encoding:NSUTF8StringEncoding]; | |||||
id window = [[PuglWindow new]retain]; | |||||
[window setPuglview:view]; | |||||
[window setTitle:titleString]; | |||||
impl->glview = [PuglOpenGLView new]; | |||||
impl->window = window; | |||||
impl->glview->puglview = view; | |||||
[window setContentView:impl->glview]; | |||||
[NSApp activateIgnoringOtherApps:YES]; | |||||
[window makeFirstResponder:impl->glview]; | |||||
[window makeKeyAndOrderFront:window]; | |||||
if (! visible) { | |||||
[window setIsVisible:NO]; | |||||
} | |||||
return view; | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
view->impl->glview->puglview = NULL; | |||||
[view->impl->window close]; | |||||
[view->impl->glview release]; | |||||
[view->impl->window release]; | |||||
free(view->impl); | |||||
free(view); | |||||
} | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
[view->impl->glview setNeedsDisplay: YES]; | |||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | |||||
NSEvent* event; | |||||
for (;;) { | |||||
event = [view->impl->window | |||||
nextEventMatchingMask:NSAnyEventMask | |||||
untilDate:[NSDate distantPast] | |||||
inMode:NSDefaultRunLoopMode | |||||
dequeue:YES]; | |||||
if (event == nil) | |||||
break; | |||||
[view->impl->window sendEvent: event]; | |||||
} | |||||
[pool release]; | |||||
return PUGL_SUCCESS; | |||||
} | |||||
void | |||||
puglPostRedisplay(PuglView* view) | |||||
{ | |||||
view->redisplay = true; | |||||
} | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
return (PuglNativeWindow)view->impl->glview; | |||||
} |
@@ -1,387 +0,0 @@ | |||||
/* | |||||
Copyright 2012 David Robillard <http://drobilla.net> | |||||
Permission to use, copy, modify, and/or distribute this software for any | |||||
purpose with or without fee is hereby granted, provided that the above | |||||
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 <windows.h> | |||||
#include <windowsx.h> | |||||
#include <GL/gl.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#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 | |||||
const int LOCAL_CLOSE_MSG = WM_USER + 50; | |||||
struct PuglInternalsImpl { | |||||
HWND hwnd; | |||||
HDC hdc; | |||||
HGLRC hglrc; | |||||
WNDCLASS wc; | |||||
}; | |||||
LRESULT CALLBACK | |||||
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | |||||
PuglView* | |||||
puglCreate(PuglNativeWindow parent, | |||||
const char* title, | |||||
int width, | |||||
int height, | |||||
bool resizable, | |||||
bool visible) | |||||
{ | |||||
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||||
PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
if (!view || !impl) { | |||||
return NULL; | |||||
} | |||||
view->impl = impl; | |||||
view->width = width; | |||||
view->height = height; | |||||
// 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]; | |||||
_snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++); | |||||
impl->wc.style = CS_OWNDC; | |||||
impl->wc.lpfnWndProc = wndProc; | |||||
impl->wc.cbClsExtra = 0; | |||||
impl->wc.cbWndExtra = 0; | |||||
impl->wc.hInstance = 0; | |||||
impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |||||
impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW); | |||||
impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |||||
impl->wc.lpszMenuName = NULL; | |||||
impl->wc.lpszClassName = classNameBuf; | |||||
RegisterClass(&impl->wc); | |||||
int winFlags = WS_POPUPWINDOW | WS_CAPTION; | |||||
if (resizable) { | |||||
winFlags |= WS_SIZEBOX; | |||||
} | |||||
// Adjust the overall window size to accomodate our requested client size | |||||
RECT wr = { 0, 0, width, height }; | |||||
AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); | |||||
impl->hwnd = CreateWindowEx( | |||||
WS_EX_TOPMOST, | |||||
classNameBuf, title, | |||||
(visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags), | |||||
0, 0, wr.right-wr.left, wr.bottom-wr.top, | |||||
(HWND)parent, NULL, NULL, NULL); | |||||
if (!impl->hwnd) { | |||||
free(impl); | |||||
free(view); | |||||
return NULL; | |||||
} | |||||
#ifdef _WIN64 | |||||
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); | |||||
#else | |||||
SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view); | |||||
#endif | |||||
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); | |||||
wglMakeCurrent(impl->hdc, impl->hglrc); | |||||
view->width = width; | |||||
view->height = height; | |||||
return view; | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
wglMakeCurrent(NULL, NULL); | |||||
wglDeleteContext(view->impl->hglrc); | |||||
ReleaseDC(view->impl->hwnd, view->impl->hdc); | |||||
DestroyWindow(view->impl->hwnd); | |||||
UnregisterClass(view->impl->wc.lpszClassName, NULL); | |||||
free(view->impl); | |||||
free(view); | |||||
} | |||||
void | |||||
puglReshape(PuglView* view, int width, int height) | |||||
{ | |||||
wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||||
if (view->reshapeFunc) { | |||||
view->reshapeFunc(view, width, height); | |||||
} else { | |||||
puglDefaultReshape(view, width, height); | |||||
} | |||||
view->width = width; | |||||
view->height = height; | |||||
} | |||||
void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
wglMakeCurrent(view->impl->hdc, view->impl->hglrc); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
glLoadIdentity(); | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
glFlush(); | |||||
SwapBuffers(view->impl->hdc); | |||||
view->redisplay = false; | |||||
} | |||||
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; | |||||
setModifiers(view); | |||||
switch (message) { | |||||
case WM_CREATE: | |||||
case WM_SHOWWINDOW: | |||||
case WM_SIZE: | |||||
RECT rect; | |||||
GetClientRect(view->impl->hwnd, &rect); | |||||
puglReshape(view, rect.right, rect.bottom); | |||||
view->width = rect.right; | |||||
view->height = rect.bottom; | |||||
break; | |||||
case WM_PAINT: | |||||
BeginPaint(view->impl->hwnd, &ps); | |||||
puglDisplay(view); | |||||
EndPaint(view->impl->hwnd, &ps); | |||||
break; | |||||
case WM_MOUSEMOVE: | |||||
if (view->motionFunc) { | |||||
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(); | |||||
view->scrollFunc( | |||||
view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), | |||||
0.0f, (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); | |||||
} | |||||
break; | |||||
case WM_MOUSEHWHEEL: | |||||
if (view->scrollFunc) { | |||||
view->event_timestamp_ms = GetMessageTime(); | |||||
view->scrollFunc( | |||||
view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), | |||||
(int16_t)HIWORD(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) { | |||||
view->keyboardFunc(view, message == WM_KEYDOWN, wParam); | |||||
} | |||||
break; | |||||
case WM_QUIT: | |||||
case LOCAL_CLOSE_MSG: | |||||
if (view->closeFunc) { | |||||
view->closeFunc(view); | |||||
} | |||||
break; | |||||
default: | |||||
return DefWindowProc( | |||||
view->impl->hwnd, message, wParam, lParam); | |||||
} | |||||
return 0; | |||||
} | |||||
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) | |||||
{ | |||||
#ifdef _WIN64 | |||||
PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); | |||||
#else | |||||
PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA); | |||||
#endif | |||||
switch (message) { | |||||
case WM_CREATE: | |||||
PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); | |||||
return 0; | |||||
case WM_CLOSE: | |||||
PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); | |||||
return 0; | |||||
case WM_DESTROY: | |||||
return 0; | |||||
default: | |||||
if (view) { | |||||
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; | |||||
} |
@@ -1,397 +0,0 @@ | |||||
/* | |||||
Copyright 2012-2014 David Robillard <http://drobilla.net> | |||||
Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||||
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> | |||||
#include <GL/gl.h> | |||||
#include <GL/glx.h> | |||||
#include <X11/Xatom.h> | |||||
#include <X11/Xlib.h> | |||||
#include <X11/keysym.h> | |||||
#include "pugl_internal.h" | |||||
struct PuglInternalsImpl { | |||||
Display* display; | |||||
int screen; | |||||
Window win; | |||||
GLXContext ctx; | |||||
Bool doubleBuffered; | |||||
}; | |||||
/** | |||||
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, | |||||
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, | |||||
GLX_RED_SIZE, 4, | |||||
GLX_GREEN_SIZE, 4, | |||||
GLX_BLUE_SIZE, 4, | |||||
GLX_DEPTH_SIZE, 16, | |||||
None | |||||
}; | |||||
PuglView* | |||||
puglCreate(PuglNativeWindow parent, | |||||
const char* title, | |||||
int width, | |||||
int height, | |||||
bool resizable, | |||||
bool visible) | |||||
{ | |||||
PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); | |||||
PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); | |||||
if (!view || !impl) { | |||||
return NULL; | |||||
} | |||||
view->impl = impl; | |||||
view->width = width; | |||||
view->height = height; | |||||
impl->display = XOpenDisplay(0); | |||||
impl->screen = DefaultScreen(impl->display); | |||||
XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); | |||||
if (!vi) { | |||||
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); | |||||
impl->doubleBuffered = False; | |||||
PUGL_LOG("No double buffering available\n"); | |||||
} else { | |||||
impl->doubleBuffered = True; | |||||
PUGL_LOG("Double buffered rendering enabled\n"); | |||||
} | |||||
int glxMajor, glxMinor; | |||||
glXQueryVersion(impl->display, &glxMajor, &glxMinor); | |||||
PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); | |||||
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); | |||||
Window xParent = parent | |||||
? (Window)parent | |||||
: RootWindow(impl->display, impl->screen); | |||||
Colormap cmap = XCreateColormap( | |||||
impl->display, xParent, vi->visual, AllocNone); | |||||
XSetWindowAttributes attr; | |||||
memset(&attr, 0, sizeof(XSetWindowAttributes)); | |||||
attr.colormap = cmap; | |||||
attr.border_pixel = 0; | |||||
attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | |||||
| ButtonPressMask | ButtonReleaseMask | |||||
#ifdef XKEYFOCUSGRAB | |||||
| EnterWindowMask | |||||
#endif | |||||
| PointerMotionMask | StructureNotifyMask; | |||||
impl->win = XCreateWindow( | |||||
impl->display, xParent, | |||||
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, | |||||
CWBorderPixel | CWColormap | CWEventMask, &attr); | |||||
XSizeHints sizeHints; | |||||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||||
if (!resizable) { | |||||
sizeHints.flags = PMinSize|PMaxSize; | |||||
sizeHints.min_width = width; | |||||
sizeHints.min_height = height; | |||||
sizeHints.max_width = width; | |||||
sizeHints.max_height = height; | |||||
XSetNormalHints(impl->display, impl->win, &sizeHints); | |||||
} | |||||
if (title) { | |||||
XStoreName(impl->display, impl->win, title); | |||||
} | |||||
if (!parent) { | |||||
Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); | |||||
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); | |||||
} | |||||
if (visible) { | |||||
XMapRaised(impl->display, impl->win); | |||||
} | |||||
if (glXIsDirect(impl->display, impl->ctx)) { | |||||
PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); | |||||
} else { | |||||
PUGL_LOG("No DRI available\n"); | |||||
} | |||||
XFree(vi); | |||||
return view; | |||||
} | |||||
void | |||||
puglDestroy(PuglView* view) | |||||
{ | |||||
if (!view) { | |||||
return; | |||||
} | |||||
glXDestroyContext(view->impl->display, view->impl->ctx); | |||||
XDestroyWindow(view->impl->display, view->impl->win); | |||||
XCloseDisplay(view->impl->display); | |||||
free(view->impl); | |||||
free(view); | |||||
} | |||||
void | |||||
puglReshape(PuglView* view, int width, int height) | |||||
{ | |||||
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||||
if (view->reshapeFunc) { | |||||
view->reshapeFunc(view, width, height); | |||||
} else { | |||||
puglDefaultReshape(view, width, height); | |||||
} | |||||
view->width = width; | |||||
view->height = height; | |||||
} | |||||
void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |||||
glLoadIdentity(); | |||||
if (view->displayFunc) { | |||||
view->displayFunc(view); | |||||
} | |||||
glFlush(); | |||||
if (view->impl->doubleBuffered) { | |||||
glXSwapBuffers(view->impl->display, view->impl->win); | |||||
} | |||||
view->redisplay = false; | |||||
} | |||||
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]; | |||||
const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); | |||||
if (n == 0) { | |||||
return; | |||||
} else if (n > 1) { | |||||
fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); | |||||
return; | |||||
} | |||||
const PuglKey special = keySymToSpecial(sym); | |||||
if (special && view->specialFunc) { | |||||
view->specialFunc(view, press, special); | |||||
} else if (!special && view->keyboardFunc) { | |||||
view->keyboardFunc(view, press, str[0]); | |||||
} | |||||
} | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
XEvent event; | |||||
while (XPending(view->impl->display) > 0) { | |||||
XNextEvent(view->impl->display, &event); | |||||
switch (event.type) { | |||||
case MapNotify: | |||||
puglReshape(view, view->width, view->height); | |||||
break; | |||||
case ConfigureNotify: | |||||
if ((event.xconfigure.width != view->width) || | |||||
(event.xconfigure.height != view->height)) { | |||||
puglReshape(view, | |||||
event.xconfigure.width, | |||||
event.xconfigure.height); | |||||
} | |||||
break; | |||||
case Expose: | |||||
if (event.xexpose.count != 0) { | |||||
break; | |||||
} | |||||
puglDisplay(view); | |||||
break; | |||||
case MotionNotify: | |||||
setModifiers(view, event.xmotion.state, event.xmotion.time); | |||||
if (view->motionFunc) { | |||||
view->motionFunc(view, event.xmotion.x, event.xmotion.y); | |||||
} | |||||
break; | |||||
case ButtonPress: | |||||
setModifiers(view, event.xbutton.state, event.xbutton.time); | |||||
if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { | |||||
if (view->scrollFunc) { | |||||
float dx = 0, dy = 0; | |||||
switch (event.xbutton.button) { | |||||
case 4: dy = 1.0f; break; | |||||
case 5: dy = -1.0f; break; | |||||
case 6: dx = -1.0f; break; | |||||
case 7: dx = 1.0f; break; | |||||
} | |||||
view->scrollFunc(view, | |||||
event.xbutton.x, event.xbutton.y, | |||||
dx, dy); | |||||
} | |||||
break; | |||||
} | |||||
// nobreak | |||||
case ButtonRelease: | |||||
setModifiers(view, event.xbutton.state, event.xbutton.time); | |||||
if (view->mouseFunc && | |||||
(event.xbutton.button < 4 || event.xbutton.button > 7)) { | |||||
view->mouseFunc(view, | |||||
event.xbutton.button, event.type == ButtonPress, | |||||
event.xbutton.x, event.xbutton.y); | |||||
} | |||||
break; | |||||
case KeyPress: | |||||
setModifiers(view, event.xkey.state, event.xkey.time); | |||||
dispatchKey(view, &event, true); | |||||
break; | |||||
case KeyRelease: | |||||
setModifiers(view, event.xkey.state, event.xkey.time); | |||||
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); | |||||
break; | |||||
} | |||||
} | |||||
dispatchKey(view, &event, false); | |||||
break; | |||||
case ClientMessage: | |||||
if (!strcmp(XGetAtomName(view->impl->display, | |||||
event.xclient.message_type), | |||||
"WM_PROTOCOLS")) { | |||||
if (view->closeFunc) { | |||||
view->closeFunc(view); | |||||
} | |||||
} | |||||
break; | |||||
#ifdef PUGL_GRAB_FOCUS | |||||
case EnterNotify: | |||||
XSetInputFocus(view->impl->display, | |||||
view->impl->win, | |||||
RevertToPointerRoot, | |||||
CurrentTime); | |||||
break; | |||||
#endif | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
if (view->redisplay) { | |||||
puglDisplay(view); | |||||
} | |||||
return PUGL_SUCCESS; | |||||
} | |||||
void | |||||
puglPostRedisplay(PuglView* view) | |||||
{ | |||||
view->redisplay = true; | |||||
} | |||||
PuglNativeWindow | |||||
puglGetNativeWindow(PuglView* view) | |||||
{ | |||||
return view->impl->win; | |||||
} |
@@ -1,117 +0,0 @@ | |||||
--- GIT-other/pugl/pugl/pugl_osx.m 2014-01-27 21:32:59.399032199 +0000 | |||||
+++ GIT-mine/DPF/dgl/src/pugl/pugl_osx.m 2014-01-27 22:43:50.754197989 +0000 | |||||
@@ -36,6 +36,7 @@ | |||||
defer:(BOOL)flag; | |||||
- (void) setPuglview:(PuglView*)view; | |||||
- (BOOL) windowShouldClose:(id)sender; | |||||
+- (BOOL) canBecomeKeyWindow:(id)sender; | |||||
@end | |||||
@implementation PuglWindow | |||||
@@ -70,9 +71,14 @@ | |||||
return YES; | |||||
} | |||||
+- (BOOL) canBecomeKeyWindow:(id)sender | |||||
+{ | |||||
+ return NO; | |||||
+} | |||||
+ | |||||
@end | |||||
-static void | |||||
+void | |||||
puglDisplay(PuglView* view) | |||||
{ | |||||
if (view->displayFunc) { | |||||
@@ -95,13 +101,16 @@ | |||||
depthBits:(int)numDepthBits; | |||||
- (void) reshape; | |||||
- (void) drawRect:(NSRect)rect; | |||||
+- (void) mouseEntered:(NSEvent*)event; | |||||
+- (void) mouseExited:(NSEvent*)event; | |||||
- (void) mouseMoved:(NSEvent*)event; | |||||
- (void) mouseDragged:(NSEvent*)event; | |||||
+- (void) rightMouseDragged:(NSEvent*)event; | |||||
- (void) mouseDown:(NSEvent*)event; | |||||
- (void) mouseUp:(NSEvent*)event; | |||||
- (void) rightMouseDown:(NSEvent*)event; | |||||
- (void) rightMouseUp:(NSEvent*)event; | |||||
+- (void) scrollWheel:(NSEvent*)event; | |||||
- (void) keyDown:(NSEvent*)event; | |||||
- (void) keyUp:(NSEvent*)event; | |||||
- (void) flagsChanged:(NSEvent*)event; | |||||
@@ -170,7 +179,7 @@ | |||||
- (void) drawRect:(NSRect)rect | |||||
{ | |||||
- display(puglview); | |||||
+ puglDisplay(puglview); | |||||
glFlush(); | |||||
glSwapAPPLE(); | |||||
} | |||||
@@ -182,6 +191,10 @@ | |||||
view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); | |||||
+ double ts = [ev timestamp] * 1000.0; | |||||
+ ts = (uint32)ts % 500000; //ridiculously large vals won't fit | |||||
+ view->event_timestamp_ms = ts; | |||||
+ | |||||
unsigned mods = 0; | |||||
mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; | |||||
mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; | |||||
@@ -332,6 +345,7 @@ | |||||
struct PuglInternalsImpl { | |||||
PuglOpenGLView* glview; | |||||
id window; | |||||
+ bool isEmbed; | |||||
}; | |||||
PuglView* | |||||
@@ -367,6 +381,7 @@ | |||||
impl->glview = [PuglOpenGLView new]; | |||||
impl->window = window; | |||||
+ impl->isEmbed = (parent != 0); | |||||
impl->glview->puglview = view; | |||||
[window setContentView:impl->glview]; | |||||
@@ -396,6 +411,36 @@ | |||||
PuglStatus | |||||
puglProcessEvents(PuglView* view) | |||||
{ | |||||
+ if (! view->impl->isEmbed) | |||||
+ { | |||||
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | |||||
+ NSEvent* event; | |||||
+ | |||||
+ static const NSUInteger eventMask = (NSLeftMouseDownMask | NSLeftMouseUpMask | | |||||
+ NSRightMouseDownMask | NSRightMouseUpMask | | |||||
+ NSMouseMovedMask | | |||||
+ NSLeftMouseDraggedMask | NSRightMouseDraggedMask | | |||||
+ NSMouseEnteredMask | NSMouseExitedMask | | |||||
+ NSKeyDownMask | NSKeyUpMask | | |||||
+ NSFlagsChangedMask | | |||||
+ NSCursorUpdateMask | NSScrollWheelMask); | |||||
+ | |||||
+ for (;;) { | |||||
+ event = [view->impl->window | |||||
+ nextEventMatchingMask:eventMask | |||||
+ untilDate:[NSDate distantPast] | |||||
+ inMode:NSEventTrackingRunLoopMode | |||||
+ dequeue:YES]; | |||||
+ | |||||
+ if (event == nil) | |||||
+ break; | |||||
+ | |||||
+ [view->impl->window sendEvent: event]; | |||||
+ } | |||||
+ | |||||
+ [pool release]; | |||||
+ } | |||||
+ | |||||
[view->impl->glview setNeedsDisplay: YES]; | |||||
return PUGL_SUCCESS; |