aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-07-22 16:30:53 +0200
committerDavid Robillard <d@drobilla.net>2019-09-03 08:32:16 +0200
commite83c2b421d140244a6b9edb051b3e0d4aacda332 (patch)
tree398e49f43c96f4602496874fa7b3680236138720
parent5081d49f9f08596c07a8ed32430a4fa3e1baf352 (diff)
downloadpugl-e83c2b421d140244a6b9edb051b3e0d4aacda332.tar.gz
pugl-e83c2b421d140244a6b9edb051b3e0d4aacda332.tar.bz2
pugl-e83c2b421d140244a6b9edb051b3e0d4aacda332.zip
Add PuglWorld
The old API was broken for programs that manage multiple views, since it was impossible to wait for events on any view. There are also several functions in the API which are not actually associated with views at all, so those can now be moved to the more appropriate PuglWorld to make this more clear. The old puglInit() and puglDestroy() functions are preserved for compatibility, but marked as deprecated.
-rw-r--r--pugl/detail/implementation.c70
-rw-r--r--pugl/detail/implementation.h13
-rw-r--r--pugl/detail/mac.h5
-rw-r--r--pugl/detail/mac.m38
-rw-r--r--pugl/detail/types.h14
-rw-r--r--pugl/detail/win.c30
-rw-r--r--pugl/detail/win.h5
-rw-r--r--pugl/detail/x11.c32
-rw-r--r--pugl/detail/x11.h4
-rw-r--r--pugl/pugl.h59
-rw-r--r--test/pugl_cairo_test.c6
-rw-r--r--test/pugl_test.c29
12 files changed, 252 insertions, 53 deletions
diff --git a/pugl/detail/implementation.c b/pugl/detail/implementation.c
index 04b36d3..29daf15 100644
--- a/pugl/detail/implementation.c
+++ b/pugl/detail/implementation.c
@@ -46,27 +46,87 @@ puglSetDefaultHints(PuglHints hints)
PuglView*
puglInit(int* PUGL_UNUSED(pargc), char** PUGL_UNUSED(argv))
{
- PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
- if (!view) {
+ return puglNewView(puglNewWorld());
+}
+
+void
+puglDestroy(PuglView* const view)
+{
+ PuglWorld* const world = view->world;
+
+ puglFreeView(view);
+ puglFreeWorld(world);
+}
+
+PuglWorld*
+puglNewWorld(void)
+{
+ PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld));
+ if (!world || !(world->impl = puglInitWorldInternals())) {
+ free(world);
return NULL;
}
- PuglInternals* impl = puglInitInternals();
- if (!impl) {
+ return world;
+}
+
+void
+puglFreeWorld(PuglWorld* const world)
+{
+ puglFreeWorldInternals(world);
+ free(world->views);
+ free(world);
+}
+
+PuglView*
+puglNewView(PuglWorld* const world)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ if (!view || !(view->impl = puglInitViewInternals())) {
free(view);
return NULL;
}
- view->impl = impl;
+ view->world = world;
view->width = 640;
view->height = 480;
view->start_time = puglGetTime(view);
puglSetDefaultHints(view->hints);
+
+ // Add to world view list
+ ++world->numViews;
+ world->views = (PuglView**)realloc(world->views,
+ world->numViews * sizeof(PuglView*));
+ world->views[world->numViews - 1] = view;
+
return view;
}
void
+puglFreeView(PuglView* view)
+{
+ // Remove from world view list
+ PuglWorld* world = view->world;
+ for (size_t i = 0; i < world->numViews; ++i) {
+ if (world->views[i] == view) {
+ if (i == world->numViews - 1) {
+ world->views[i] = NULL;
+ } else {
+ memmove(world->views + i, world->views + i + 1,
+ sizeof(PuglView*) * (world->numViews - i - 1));
+ world->views[world->numViews - 1] = NULL;
+ }
+ --world->numViews;
+ }
+ }
+
+ puglFreeViewInternals(view);
+ free(view->windowClass);
+ free(view);
+}
+
+void
puglInitWindowHint(PuglView* view, PuglWindowHint hint, int value)
{
if (hint < PUGL_NUM_WINDOW_HINTS) {
diff --git a/pugl/detail/implementation.h b/pugl/detail/implementation.h
index 50f54f5..d6288a3 100644
--- a/pugl/detail/implementation.h
+++ b/pugl/detail/implementation.h
@@ -30,8 +30,17 @@
extern "C" {
#endif
-/** Allocate and initialise internals (implemented once per platform) */
-PuglInternals* puglInitInternals(void);
+/** Allocate and initialise world internals (implemented once per platform) */
+PuglWorldInternals* puglInitWorldInternals(void);
+
+/** Destroy and free world internals (implemented once per platform) */
+void puglFreeWorldInternals(PuglWorld* world);
+
+/** Allocate and initialise view internals (implemented once per platform) */
+PuglInternals* puglInitViewInternals(void);
+
+/** Destroy and free view internals (implemented once per platform) */
+void puglFreeViewInternals(PuglView* view);
/** Return the Unicode code point for `buf` or the replacement character. */
uint32_t puglDecodeUTF8(const uint8_t* buf);
diff --git a/pugl/detail/mac.h b/pugl/detail/mac.h
index 8146373..f8cfbed 100644
--- a/pugl/detail/mac.h
+++ b/pugl/detail/mac.h
@@ -50,6 +50,11 @@
@end
+struct PuglWorldInternalsImpl {
+ NSApplication* app;
+ NSAutoreleasePool* autoreleasePool;
+};
+
struct PuglInternalsImpl {
NSApplication* app;
PuglWrapperView* wrapperView;
diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m
index 385070d..23344ed 100644
--- a/pugl/detail/mac.m
+++ b/pugl/detail/mac.m
@@ -566,7 +566,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
- (void) urgentTick
{
- [NSApp requestUserAttention:NSInformationalRequest];
+ [puglview->world->impl->app requestUserAttention:NSInformationalRequest];
}
- (void) viewDidEndLiveResize
@@ -636,8 +636,27 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
@end
+PuglWorldInternals*
+puglInitWorldInternals(void)
+{
+ PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
+ 1, sizeof(PuglWorldInternals));
+
+ impl->app = [NSApplication sharedApplication];
+ impl->autoreleasePool = [NSAutoreleasePool new];
+
+ return impl;
+}
+
+void
+puglFreeWorldInternals(PuglWorld* world)
+{
+ [world->impl->autoreleasePool drain];
+ free(world->impl);
+}
+
PuglInternals*
-puglInitInternals(void)
+puglInitViewInternals(void)
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
@@ -660,9 +679,6 @@ puglCreateWindow(PuglView* view, const char* title)
{
PuglInternals* impl = view->impl;
- [NSAutoreleasePool new];
- impl->app = [NSApplication sharedApplication];
-
// Create wrapper view to handle input
impl->wrapperView = [PuglWrapperView alloc];
impl->wrapperView->puglview = view;
@@ -727,7 +743,7 @@ puglCreateWindow(PuglView* view, const char* title)
}
[window setContentView:impl->wrapperView];
- [impl->app activateIgnoringOtherApps:YES];
+ [view->world->impl->app activateIgnoringOtherApps:YES];
[window makeFirstResponder:impl->wrapperView];
[window makeKeyAndOrderFront:window];
}
@@ -752,7 +768,7 @@ puglHideWindow(PuglView* view)
}
void
-puglDestroy(PuglView* view)
+puglFreeViewInternals(PuglView* view)
{
view->backend->destroy(view);
[view->impl->wrapperView removeFromSuperview];
@@ -764,9 +780,7 @@ puglDestroy(PuglView* view)
if (view->impl->window) {
[view->impl->window release];
}
- free(view->windowClass);
free(view->impl);
- free(view);
}
void
@@ -791,7 +805,7 @@ void
puglRequestAttention(PuglView* view)
{
if (![view->impl->window isKeyWindow]) {
- [NSApp requestUserAttention:NSInformationalRequest];
+ [view->world->impl->app requestUserAttention:NSInformationalRequest];
view->impl->wrapperView->urgentTimer =
[NSTimer scheduledTimerWithTimeInterval:2.0
target:view->impl->wrapperView
@@ -820,7 +834,7 @@ puglProcessEvents(PuglView* view)
{
if (view->impl->nextEvent) {
// Process event that was dequeued earier by puglWaitForEvent
- [view->impl->app sendEvent: view->impl->nextEvent];
+ [view->world->impl->app sendEvent: view->impl->nextEvent];
view->impl->nextEvent = NULL;
}
@@ -830,7 +844,7 @@ puglProcessEvents(PuglView* view)
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES]);) {
- [view->impl->app sendEvent: ev];
+ [view->world->impl->app sendEvent: ev];
}
return PUGL_SUCCESS;
diff --git a/pugl/detail/types.h b/pugl/detail/types.h
index b538091..fdfb0f6 100644
--- a/pugl/detail/types.h
+++ b/pugl/detail/types.h
@@ -24,6 +24,7 @@
#include "pugl/pugl.h"
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
// Unused parameter macro to suppresses warnings and make it impossible to use
@@ -35,7 +36,10 @@
# define PUGL_UNUSED(name)
#endif
-/** Platform-specific internals. */
+/** Platform-specific world internals. */
+typedef struct PuglWorldInternalsImpl PuglWorldInternals;
+
+/** Platform-specific view internals. */
typedef struct PuglInternalsImpl PuglInternals;
/** View hints. */
@@ -43,6 +47,7 @@ typedef int PuglHints[PUGL_NUM_WINDOW_HINTS];
/** Cross-platform view definition. */
struct PuglViewImpl {
+ PuglWorld* world;
const PuglBackend* backend;
PuglInternals* impl;
PuglHandle handle;
@@ -63,6 +68,13 @@ struct PuglViewImpl {
bool visible;
};
+/** Cross-platform world definition. */
+struct PuglWorldImpl {
+ PuglWorldInternals* impl;
+ size_t numViews;
+ PuglView** views;
+};
+
/** Opaque surface used by graphics backend. */
typedef void PuglSurface;
diff --git a/pugl/detail/win.c b/pugl/detail/win.c
index a4597b5..804c883 100644
--- a/pugl/detail/win.c
+++ b/pugl/detail/win.c
@@ -88,10 +88,14 @@ puglRegisterWindowClass(const char* name)
return RegisterClassEx(&wc);
}
-PuglInternals*
-puglInitInternals(void)
+PuglWorldInternals*
+puglInitWorldInternals(void)
{
- PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
+ 1, sizeof(PuglWorldInternals));
+ if (!impl) {
+ return NULL;
+ }
HMODULE user32 = LoadLibrary("user32.dll");
if (user32) {
@@ -110,6 +114,12 @@ puglInitInternals(void)
return impl;
}
+PuglInternals*
+puglInitViewInternals(void)
+{
+ return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+}
+
int
puglCreateWindow(PuglView* view, const char* title)
{
@@ -171,19 +181,23 @@ puglHideWindow(PuglView* view)
}
void
-puglDestroy(PuglView* view)
+puglFreeViewInternals(PuglView* view)
{
if (view) {
view->backend->destroy(view);
ReleaseDC(view->impl->hwnd, view->impl->hdc);
DestroyWindow(view->impl->hwnd);
UnregisterClass(view->windowClass ? view->windowClass : DEFAULT_CLASSNAME, NULL);
- free(view->windowClass);
free(view->impl);
- free(view);
}
}
+void
+puglFreeWorldInternals(PuglWorld* world)
+{
+ free(world->impl);
+}
+
static PuglKey
keySymToSpecial(WPARAM sym)
{
@@ -694,8 +708,8 @@ puglGetTime(PuglView* view)
{
LARGE_INTEGER count;
QueryPerformanceCounter(&count);
- const double now = (double)count.QuadPart / view->impl->timerFrequency;
- return now - view->start_time;
+ return ((double)count.QuadPart / view->world->impl->timerFrequency -
+ view->start_time);
}
void
diff --git a/pugl/detail/win.h b/pugl/detail/win.h
index 88cb1a1..16d22c0 100644
--- a/pugl/detail/win.h
+++ b/pugl/detail/win.h
@@ -26,6 +26,10 @@
typedef PIXELFORMATDESCRIPTOR PuglWinPFD;
+struct PuglWorldInternalsImpl {
+ double timerFrequency;
+};
+
struct PuglInternalsImpl {
PuglWinPFD pfd;
int pfId;
@@ -33,7 +37,6 @@ struct PuglInternalsImpl {
HDC hdc;
PuglSurface* surface;
DWORD refreshRate;
- double timerFrequency;
bool flashing;
bool resizing;
bool mouseTracked;
diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c
index cb9f0e2..1098f72 100644
--- a/pugl/detail/x11.c
+++ b/pugl/detail/x11.c
@@ -60,8 +60,24 @@ static const long eventMask =
EnterWindowMask | LeaveWindowMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask);
+PuglWorldInternals*
+puglInitWorldInternals(void)
+{
+ Display* display = XOpenDisplay(NULL);
+ if (!display) {
+ return NULL;
+ }
+
+ PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
+ 1, sizeof(PuglWorldInternals));
+
+ impl->display = display;
+
+ return impl;
+}
+
PuglInternals*
-puglInitInternals(void)
+puglInitViewInternals(void)
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
@@ -70,7 +86,7 @@ int
puglCreateWindow(PuglView* view, const char* title)
{
PuglInternals* const impl = view->impl;
- Display* const display = XOpenDisplay(0);
+ Display* const display = view->world->impl->display;
impl->display = display;
impl->screen = DefaultScreen(display);
@@ -183,7 +199,7 @@ puglHideWindow(PuglView* view)
}
void
-puglDestroy(PuglView* view)
+puglFreeViewInternals(PuglView* view)
{
if (view) {
if (view->impl->xic) {
@@ -194,14 +210,18 @@ puglDestroy(PuglView* view)
}
view->backend->destroy(view);
XDestroyWindow(view->impl->display, view->impl->win);
- XCloseDisplay(view->impl->display);
XFree(view->impl->vi);
- free(view->windowClass);
free(view->impl);
- free(view);
}
}
+void
+puglFreeWorldInternals(PuglWorld* world)
+{
+ XCloseDisplay(world->impl->display);
+ free(world->impl);
+}
+
static PuglKey
keySymToSpecial(KeySym sym)
{
diff --git a/pugl/detail/x11.h b/pugl/detail/x11.h
index 1ead119..6671a95 100644
--- a/pugl/detail/x11.h
+++ b/pugl/detail/x11.h
@@ -23,6 +23,10 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+struct PuglWorldInternalsImpl {
+ Display* display;
+};
+
struct PuglInternalsImpl {
Display* display;
int screen;
diff --git a/pugl/pugl.h b/pugl/pugl.h
index b176696..0dc5fc3 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -415,25 +415,74 @@ typedef union {
} PuglEvent;
/**
+ @name World
+ The top level context of a Pugl application.
+ @{
+*/
+
+/**
+ The "world" of application state.
+
+ The world represents things that are not associated with a particular view.
+ Several worlds can be created in a process (which is the case when many
+ plugins use Pugl, for example), but code using different worlds must be
+ isolated so they are never mixed. Views are strongly associated with the
+ world they were created for.
+*/
+typedef struct PuglWorldImpl PuglWorld;
+
+/**
+ Create a new world.
+
+ @return A newly created world.
+*/
+PUGL_API PuglWorld*
+puglNewWorld(void);
+
+/**
+ Free a world allocated with puglNewWorld().
+*/
+PUGL_API void
+puglFreeWorld(PuglWorld* world);
+
+/**
+ @}
@name Initialization
Configuration functions which must be called before creating a window.
@{
*/
/**
- Create a Pugl view.
+ Create a Pugl application and view.
To create a window, call the various puglInit* functions as necessary, then
call puglCreateWindow().
+ @deprecated Use puglNewApp() and puglNewView().
+
@param pargc Pointer to argument count (currently unused).
@param argv Arguments (currently unused).
@return A newly created view.
*/
-PUGL_API PuglView*
+PUGL_API PUGL_DEPRECATED_BY("puglNewView") PuglView*
puglInit(int* pargc, char** argv);
/**
+ Create a new view.
+
+ A view represents a window, but a window will not be shown until configured
+ with the various puglInit functions and shown with puglShowWindow().
+*/
+PUGL_API PuglView*
+puglNewView(PuglWorld* world);
+
+/**
+ Free a view created with puglNewView().
+*/
+PUGL_API void
+puglFreeView(PuglView* view);
+
+/**
Set a hint before creating a window.
*/
PUGL_API void
@@ -715,9 +764,11 @@ PUGL_API void
puglPostRedisplay(PuglView* view);
/**
- Destroy a GL window.
+ Destroy an app and view created with `puglInit()`.
+
+ @deprecated Use puglFreeApp() and puglFreeView().
*/
-PUGL_API void
+PUGL_API PUGL_DEPRECATED_BY("puglFreeView") void
puglDestroy(PuglView* view);
/**
diff --git a/test/pugl_cairo_test.c b/test/pugl_cairo_test.c
index a16c821..3cdf904 100644
--- a/test/pugl_cairo_test.c
+++ b/test/pugl_cairo_test.c
@@ -202,7 +202,8 @@ main(int argc, char** argv)
}
}
- PuglView* view = puglInit(NULL, NULL);
+ PuglWorld* world = puglNewWorld();
+ PuglView* view = puglNewView(world);
puglInitWindowClass(view, "PuglCairoTest");
puglInitWindowSize(view, 512, 512);
puglInitWindowMinSize(view, 256, 256);
@@ -233,6 +234,7 @@ main(int argc, char** argv)
}
}
- puglDestroy(view);
+ puglFreeView(view);
+ puglFreeWorld(world);
return 0;
}
diff --git a/test/pugl_test.c b/test/pugl_test.c
index b8a0a42..b012868 100644
--- a/test/pugl_test.c
+++ b/test/pugl_test.c
@@ -34,16 +34,17 @@
typedef struct
{
- bool continuous;
- int quit;
- float xAngle;
- float yAngle;
- float dist;
- double lastMouseX;
- double lastMouseY;
- double lastDrawTime;
- unsigned framesDrawn;
- bool mouseEntered;
+ PuglWorld* world;
+ bool continuous;
+ int quit;
+ float xAngle;
+ float yAngle;
+ float dist;
+ double lastMouseX;
+ double lastMouseY;
+ double lastDrawTime;
+ unsigned framesDrawn;
+ bool mouseEntered;
} PuglTestApp;
static void
@@ -178,7 +179,9 @@ main(int argc, char** argv)
}
}
- PuglView* view = puglInit(NULL, NULL);
+ app.world = puglNewWorld();
+
+ PuglView* view = puglNewView(app.world);
puglInitWindowClass(view, "PuglTest");
puglInitWindowSize(view, 512, 512);
puglInitWindowMinSize(view, 256, 256);
@@ -224,6 +227,8 @@ main(int argc, char** argv)
}
}
- puglDestroy(view);
+ puglFreeView(view);
+ puglFreeWorld(app.world);
+
return 0;
}