From cc5c38b1aaf93157f5558df95383491a6163cc5a Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 3 Oct 2020 19:38:53 +0200 Subject: Add puglGetViewHint() This allows retrieving properties of the view that may be needed, such as the actual bit depth (which may vary from the suggested depth provided as a hint). --- pugl/detail/implementation.c | 10 ++++++ pugl/detail/mac.m | 14 ++++++++ pugl/detail/mac_gl.m | 30 ++++++++++++++-- pugl/detail/win.c | 14 ++++++++ pugl/detail/win_gl.c | 18 ++++++++++ pugl/detail/x11.h | 5 +++ pugl/detail/x11_gl.c | 24 ++++++++++++- pugl/pugl.h | 10 ++++++ pugl/pugl.hpp | 6 ++++ test/test_gl_hints.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ test/test_stub_hints.c | 77 +++++++++++++++++++++++++++++++++++++++ wscript | 24 ++++++++++--- 12 files changed, 310 insertions(+), 8 deletions(-) create mode 100644 test/test_gl_hints.c create mode 100644 test/test_stub_hints.c diff --git a/pugl/detail/implementation.c b/pugl/detail/implementation.c index 5534662..31ee643 100644 --- a/pugl/detail/implementation.c +++ b/pugl/detail/implementation.c @@ -262,6 +262,16 @@ puglSetViewHint(PuglView* view, PuglViewHint hint, int value) return PUGL_BAD_PARAMETER; } +int +puglGetViewHint(const PuglView* view, PuglViewHint hint) +{ + if (hint < PUGL_NUM_VIEW_HINTS) { + return view->hints[hint]; + } + + return PUGL_DONT_CARE; +} + PuglStatus puglSetParentWindow(PuglView* view, PuglNativeView parent) { diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m index 23671ae..0c50d20 100644 --- a/pugl/detail/mac.m +++ b/pugl/detail/mac.m @@ -866,6 +866,20 @@ puglRealize(PuglView* view) const NSScreen* const screen = [NSScreen mainScreen]; const double scaleFactor = [screen backingScaleFactor]; + // Getting depth from the display mode seems tedious, just set usual values + if (view->hints[PUGL_RED_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_RED_BITS] = 8; + } + if (view->hints[PUGL_BLUE_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_BLUE_BITS] = 8; + } + if (view->hints[PUGL_GREEN_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_GREEN_BITS] = 8; + } + if (view->hints[PUGL_ALPHA_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_ALPHA_BITS] = 8; + } + if (view->frame.width == 0.0 && view->frame.height == 0.0) { if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) { return PUGL_BAD_CONFIGURATION; diff --git a/pugl/detail/mac_gl.m b/pugl/detail/mac_gl.m index 4bf6fc1..4d4f324 100644 --- a/pugl/detail/mac_gl.m +++ b/pugl/detail/mac_gl.m @@ -48,12 +48,36 @@ ? NSOpenGLProfileVersion4_1Core : NSOpenGLProfileVersion3_2Core)); - NSOpenGLPixelFormatAttribute pixelAttribs[16] = { + // Set attributes to default if they are unset + // (There is no GLX_DONT_CARE equivalent on MacOS) + if (puglview->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { + puglview->hints[PUGL_DEPTH_BITS] = 0; + } + if (puglview->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { + puglview->hints[PUGL_STENCIL_BITS] = 0; + } + if (puglview->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { + puglview->hints[PUGL_SAMPLES] = 1; + } + if (puglview->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { + puglview->hints[PUGL_DOUBLE_BUFFER] = 1; + } + if (puglview->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { + puglview->hints[PUGL_SWAP_INTERVAL] = 1; + } + + const unsigned colorSize = (unsigned)(puglview->hints[PUGL_RED_BITS] + + puglview->hints[PUGL_BLUE_BITS] + + puglview->hints[PUGL_GREEN_BITS] + + puglview->hints[PUGL_ALPHA_BITS]); + + NSOpenGLPixelFormatAttribute pixelAttribs[17] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, NSOpenGLPFAOpenGLProfile, profile, - NSOpenGLPFAColorSize, 32, - NSOpenGLPFADepthSize, 32, + NSOpenGLPFAColorSize, colorSize, + NSOpenGLPFADepthSize, (unsigned)puglview->hints[PUGL_DEPTH_BITS], + NSOpenGLPFAStencilSize, (unsigned)puglview->hints[PUGL_STENCIL_BITS], NSOpenGLPFAMultisample, samples ? 1 : 0, NSOpenGLPFASampleBuffers, samples ? 1 : 0, NSOpenGLPFASamples, samples, diff --git a/pugl/detail/win.c b/pugl/detail/win.c index 4f7afee..38eca9d 100644 --- a/pugl/detail/win.c +++ b/pugl/detail/win.c @@ -166,6 +166,20 @@ puglRealize(PuglView* view) { PuglInternals* impl = view->impl; + // Getting depth from the display mode seems tedious, just set usual values + if (view->hints[PUGL_RED_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_RED_BITS] = 8; + } + if (view->hints[PUGL_BLUE_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_BLUE_BITS] = 8; + } + if (view->hints[PUGL_GREEN_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_GREEN_BITS] = 8; + } + if (view->hints[PUGL_ALPHA_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_ALPHA_BITS] = 8; + } + // Get refresh rate for resize draw timer DEVMODEA devMode = {0}; EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode); diff --git a/pugl/detail/win_gl.c b/pugl/detail/win_gl.c index 8cdad76..096c715 100644 --- a/pugl/detail/win_gl.c +++ b/pugl/detail/win_gl.c @@ -108,6 +108,24 @@ puglWinGlConfigure(PuglView* view) { PuglInternals* impl = view->impl; + // Set attributes to default if they are unset + // (There is no GLX_DONT_CARE equivalent on Windows) + if (view->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_DEPTH_BITS] = 0; + } + if (view->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { + view->hints[PUGL_STENCIL_BITS] = 0; + } + if (view->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { + view->hints[PUGL_SAMPLES] = 1; + } + if (view->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { + view->hints[PUGL_DOUBLE_BUFFER] = 1; + } + if (view->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { + view->hints[PUGL_SWAP_INTERVAL] = 1; + } + // clang-format off const int pixelAttrs[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, diff --git a/pugl/detail/x11.h b/pugl/detail/x11.h index cf647ed..4b0109d 100644 --- a/pugl/detail/x11.h +++ b/pugl/detail/x11.h @@ -83,5 +83,10 @@ puglX11StubConfigure(PuglView* view) pat.screen = impl->screen; impl->vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); + view->hints[PUGL_RED_BITS] = impl->vi->bits_per_rgb; + view->hints[PUGL_GREEN_BITS] = impl->vi->bits_per_rgb; + view->hints[PUGL_BLUE_BITS] = impl->vi->bits_per_rgb; + view->hints[PUGL_ALPHA_BITS] = 0; + return PUGL_SUCCESS; } diff --git a/pugl/detail/x11_gl.c b/pugl/detail/x11_gl.c index e4a0ea8..228a530 100644 --- a/pugl/detail/x11_gl.c +++ b/pugl/detail/x11_gl.c @@ -71,7 +71,7 @@ puglX11GlConfigure(PuglView* view) GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_SAMPLES, view->hints[PUGL_SAMPLES], + GLX_SAMPLES, puglX11GlHintValue(view->hints[PUGL_SAMPLES]), GLX_RED_SIZE, puglX11GlHintValue(view->hints[PUGL_RED_BITS]), GLX_GREEN_SIZE, puglX11GlHintValue(view->hints[PUGL_GREEN_BITS]), GLX_BLUE_SIZE, puglX11GlHintValue(view->hints[PUGL_BLUE_BITS]), @@ -91,6 +91,23 @@ puglX11GlConfigure(PuglView* view) surface->fb_config = fbc[0]; impl->vi = glXGetVisualFromFBConfig(impl->display, fbc[0]); + view->hints[PUGL_RED_BITS] = puglX11GlGetAttrib( + display, fbc[0], GLX_RED_SIZE); + view->hints[PUGL_GREEN_BITS] = puglX11GlGetAttrib( + display, fbc[0], GLX_GREEN_SIZE); + view->hints[PUGL_BLUE_BITS] = puglX11GlGetAttrib( + display, fbc[0], GLX_BLUE_SIZE); + view->hints[PUGL_ALPHA_BITS] = puglX11GlGetAttrib( + display, fbc[0], GLX_ALPHA_SIZE); + view->hints[PUGL_DEPTH_BITS] = puglX11GlGetAttrib( + display, fbc[0], GLX_DEPTH_SIZE); + view->hints[PUGL_STENCIL_BITS] = puglX11GlGetAttrib( + display, fbc[0], GLX_STENCIL_SIZE); + view->hints[PUGL_SAMPLES] = puglX11GlGetAttrib( + display, fbc[0], GLX_SAMPLES); + view->hints[PUGL_DOUBLE_BUFFER] = puglX11GlGetAttrib( + display, fbc[0], GLX_DOUBLEBUFFER); + char msg[128]; snprintf( @@ -182,6 +199,11 @@ puglX11GlCreate(PuglView* view) GLX_DOUBLEBUFFER, &view->hints[PUGL_DOUBLE_BUFFER]); + glXQueryDrawable(display, + impl->win, + GLX_SWAP_INTERVAL_EXT, + (unsigned int*)&view->hints[PUGL_SWAP_INTERVAL]); + return PUGL_SUCCESS; } diff --git a/pugl/pugl.h b/pugl/pugl.h index c32a17d..7e27425 100644 --- a/pugl/pugl.h +++ b/pugl/pugl.h @@ -938,6 +938,16 @@ puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); PUGL_API PuglStatus puglSetViewHint(PuglView* view, PuglViewHint hint, int value); +/** + Get the value for a view hint. + + If the view has been realized, this can be used to get the actual value of a + hint which was initially set to PUGL_DONT_CARE, or has been adjusted from + the suggested value. +*/ +PUGL_API int +puglGetViewHint(const PuglView* view, PuglViewHint hint); + /** @} @anchor frame diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp index 3072560..4968da4 100644 --- a/pugl/pugl.hpp +++ b/pugl/pugl.hpp @@ -418,6 +418,12 @@ public: puglSetViewHint(cobj(), static_cast(hint), value)); } + /// @copydoc puglGetViewHint + int getHint(ViewHint hint) + { + return puglGetViewHint(cobj(), static_cast(hint)); + } + /** @} @name Frame diff --git a/test/test_gl_hints.c b/test/test_gl_hints.c new file mode 100644 index 0000000..9830e2e --- /dev/null +++ b/test/test_gl_hints.c @@ -0,0 +1,86 @@ +/* + Copyright 2020 David Robillard + + 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. +*/ + +/* + Tests that all hints are set to real values after a view is realized. +*/ + +#undef NDEBUG + +#include "test_utils.h" + +#include "pugl/pugl.h" +#include "pugl/pugl_gl.h" + +#include + +static PuglStatus +onEvent(PuglView* view, const PuglEvent* event) +{ + (void)view; + (void)event; + + return PUGL_SUCCESS; +} + +int +main(void) +{ + PuglWorld* const world = puglNewWorld(PUGL_PROGRAM, 0); + PuglView* const view = puglNewView(world); + + // Set up view + puglSetClassName(world, "Pugl Test"); + puglSetBackend(view, puglGlBackend()); + puglSetEventFunc(view, onEvent); + puglSetDefaultSize(view, 512, 512); + + // Set all hints that support it to PUGL_DONT_CARE + assert(!puglSetViewHint(view, PUGL_RED_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_GREEN_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_BLUE_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_ALPHA_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_DEPTH_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_STENCIL_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_SAMPLES, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_DOUBLE_BUFFER, PUGL_DONT_CARE)); + + // Realize view and print all hints for debugging convenience + assert(!puglRealize(view)); + + // Check that no hints are set to PUGL_DONT_CARE + assert(puglGetViewHint(view, PUGL_USE_COMPAT_PROFILE) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_USE_DEBUG_CONTEXT) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_CONTEXT_VERSION_MINOR) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_RED_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_GREEN_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_BLUE_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_ALPHA_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_DEPTH_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_STENCIL_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_SAMPLES) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_DOUBLE_BUFFER) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_SWAP_INTERVAL) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_RESIZABLE) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_IGNORE_KEY_REPEAT) != PUGL_DONT_CARE); + + // Tear down + puglFreeView(view); + puglFreeWorld(world); + + return 0; +} diff --git a/test/test_stub_hints.c b/test/test_stub_hints.c new file mode 100644 index 0000000..cd4db6a --- /dev/null +++ b/test/test_stub_hints.c @@ -0,0 +1,77 @@ +/* + Copyright 2020 David Robillard + + 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. +*/ + +/* + Tests that all hints are set to real values after a view is realized. +*/ + +#undef NDEBUG + +#include "test_utils.h" + +#include "pugl/pugl.h" +#include "pugl/pugl_stub.h" + +#include + +static PuglStatus +onEvent(PuglView* view, const PuglEvent* event) +{ + (void)view; + (void)event; + + return PUGL_SUCCESS; +} + +int +main(void) +{ + PuglWorld* const world = puglNewWorld(PUGL_PROGRAM, 0); + PuglView* const view = puglNewView(world); + + // Set up view + puglSetClassName(world, "Pugl Test"); + puglSetBackend(view, puglStubBackend()); + puglSetEventFunc(view, onEvent); + puglSetDefaultSize(view, 512, 512); + + // Set all relevant hints that support it to PUGL_DONT_CARE + assert(!puglSetViewHint(view, PUGL_RED_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_GREEN_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_BLUE_BITS, PUGL_DONT_CARE)); + assert(!puglSetViewHint(view, PUGL_ALPHA_BITS, PUGL_DONT_CARE)); + + // Realize view and print all hints for debugging convenience + assert(!puglRealize(view)); + + // Check that no relevant hints are set to PUGL_DONT_CARE + assert(puglGetViewHint(view, PUGL_USE_COMPAT_PROFILE) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_USE_DEBUG_CONTEXT) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_CONTEXT_VERSION_MINOR) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_RED_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_GREEN_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_BLUE_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_ALPHA_BITS) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_RESIZABLE) != PUGL_DONT_CARE); + assert(puglGetViewHint(view, PUGL_IGNORE_KEY_REPEAT) != PUGL_DONT_CARE); + + // Tear down + puglFreeView(view); + puglFreeWorld(world); + + return 0; +} diff --git a/wscript b/wscript index 0855ef1..10be77c 100644 --- a/wscript +++ b/wscript @@ -255,7 +255,9 @@ def _build_pc_file(bld, name, desc, target, libname, deps={}, requires=[]): LIBS=' '.join(link_flags)) -tests = ['redisplay', 'show_hide', 'update', 'timer'] +gl_tests = ['gl_hints'] +basic_tests = ['stub_hints', 'redisplay', 'show_hide', 'update', 'timer'] +tests = ['gl_hints', 'stub_hints', 'redisplay', 'show_hide', 'update', 'timer'] def build(bld): @@ -430,18 +432,28 @@ def build(bld): cflags=glad_cflags, uselib=['DL', 'GL', 'M']) + for test in gl_tests: + bld(features = 'c cprogram', + source = 'test/test_%s.c' % test, + target = 'test/test_%s' % test, + install_path = '', + use = ['pugl_%s_static' % platform, + 'pugl_%s_gl_static' % platform], + uselib = deps[platform]['uselib'] + ['GL']) + if bld.env.HAVE_CAIRO: build_example('pugl_cairo_demo', ['examples/pugl_cairo_demo.c'], platform, 'cairo', uselib=['M', 'CAIRO']) - for test in tests: + for test in basic_tests: bld(features = 'c cprogram', source = 'test/test_%s.c' % test, target = 'test/test_%s' % test, install_path = '', use = ['pugl_%s_static' % platform, - 'pugl_%s_stub_static' % platform], + 'pugl_%s_stub_static' % platform, + 'pugl_%s_gl_static' % platform], uselib = deps[platform]['uselib'] + ['CAIRO']) # Make a hyper strict warning environment for checking API headers @@ -488,9 +500,13 @@ def build(bld): def test(tst): if tst.options.gui_tests: with tst.group('gui') as check: - for test in tests: + for test in basic_tests: check(['test/test_%s' % test]) + if tst.env.HAVE_GL: + for test in gl_tests: + check(['test/test_%s' % test]) + class LintContext(Build.BuildContext): fun = cmd = 'lint' -- cgit v1.2.1