diff options
author | David Robillard <d@drobilla.net> | 2019-07-22 16:30:53 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2019-09-03 08:32:16 +0200 |
commit | e83c2b421d140244a6b9edb051b3e0d4aacda332 (patch) | |
tree | 398e49f43c96f4602496874fa7b3680236138720 | |
parent | 5081d49f9f08596c07a8ed32430a4fa3e1baf352 (diff) | |
download | pugl-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.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; } |