diff options
-rw-r--r-- | pugl/detail/implementation.c | 70 | ||||
-rw-r--r-- | pugl/detail/implementation.h | 13 | ||||
-rw-r--r-- | pugl/detail/mac.h | 5 | ||||
-rw-r--r-- | pugl/detail/mac.m | 38 | ||||
-rw-r--r-- | pugl/detail/types.h | 14 | ||||
-rw-r--r-- | pugl/detail/win.c | 30 | ||||
-rw-r--r-- | pugl/detail/win.h | 5 | ||||
-rw-r--r-- | pugl/detail/x11.c | 32 | ||||
-rw-r--r-- | pugl/detail/x11.h | 4 | ||||
-rw-r--r-- | pugl/pugl.h | 59 | ||||
-rw-r--r-- | test/pugl_cairo_test.c | 6 | ||||
-rw-r--r-- | test/pugl_test.c | 29 |
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; } |