aboutsummaryrefslogtreecommitdiffstats
path: root/pugl
diff options
context:
space:
mode:
Diffstat (limited to 'pugl')
-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
10 files changed, 231 insertions, 39 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);
/**