/* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho * * 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. * * THE 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. */ #include "pugl.hpp" /* we will include all header files used in pugl in their C++ friendly form, then pugl stuff in custom namespace */ #include #include #include #include #include #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) # import # include # include # ifdef DGL_CAIRO # include # include # endif # ifdef DGL_OPENGL # include # endif # ifdef DGL_VULKAN # import # include # include # endif #elif defined(DISTRHO_OS_WINDOWS) # include # include # include # ifdef DGL_CAIRO # include # include # endif # ifdef DGL_OPENGL # include # endif # ifdef DGL_VULKAN # include # include # endif #else # include # include # include # include # include # include # include # include # ifdef HAVE_XCURSOR # include # include # endif # ifdef HAVE_XRANDR # include # endif # ifdef HAVE_XSYNC # include # include # endif # ifdef DGL_CAIRO # include # include # endif # ifdef DGL_OPENGL # include # include # endif # ifdef DGL_VULKAN # include # include # endif #endif #ifdef HAVE_X11 # define DBLCLKTME 400 # include "sofd/libsofd.h" # include "sofd/libsofd.c" #endif #ifndef DISTRHO_OS_MAC START_NAMESPACE_DGL #endif // -------------------------------------------------------------------------------------------------------------------- #if defined(DISTRHO_OS_HAIKU) #elif defined(DISTRHO_OS_MAC) # ifndef DISTRHO_MACOS_NAMESPACE_MACRO # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE) # define PuglStubView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglStubView) # define PuglWrapperView DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWrapperView) # define PuglWindow DISTRHO_MACOS_NAMESPACE_MACRO(DGL_NAMESPACE, PuglWindow) # endif # import "pugl-upstream/src/mac.m" # import "pugl-upstream/src/mac_stub.m" # ifdef DGL_CAIRO # import "pugl-upstream/src/mac_cairo.m" # endif # ifdef DGL_OPENGL # import "pugl-upstream/src/mac_gl.m" # endif # ifdef DGL_VULKAN # import "pugl-upstream/src/mac_vulkan.m" # endif #elif defined(DISTRHO_OS_WINDOWS) # include "pugl-upstream/src/win.c" # include "pugl-upstream/src/win_stub.c" # ifdef DGL_CAIRO # include "pugl-upstream/src/win_cairo.c" # endif # ifdef DGL_OPENGL # include "pugl-upstream/src/win_gl.c" # endif # ifdef DGL_VULKAN # include "pugl-upstream/src/win_vulkan.c" # endif #else # include "pugl-upstream/src/x11.c" # include "pugl-upstream/src/x11_stub.c" # ifdef DGL_CAIRO # include "pugl-upstream/src/x11_cairo.c" # endif # ifdef DGL_OPENGL # include "pugl-upstream/src/x11_gl.c" # endif # ifdef DGL_VULKAN # include "pugl-upstream/src/x11_vulkan.c" # endif #endif #include "pugl-upstream/src/implementation.c" // -------------------------------------------------------------------------------------------------------------------- // expose backend enter void puglBackendEnter(PuglView* const view) { view->backend->enter(view, NULL); } // -------------------------------------------------------------------------------------------------------------------- // clear minimum size to 0 void puglClearMinSize(PuglView* const view) { view->minWidth = 0; view->minHeight = 0; } // -------------------------------------------------------------------------------------------------------------------- // missing in pugl, directly returns title char* pointer const char* puglGetWindowTitle(const PuglView* view) { return view->title; } // -------------------------------------------------------------------------------------------------------------------- // bring view window into the foreground, aka "raise" window void puglRaiseWindow(PuglView* view) { #if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) // nothing here yet #elif defined(DISTRHO_OS_WINDOWS) SetForegroundWindow(view->impl->hwnd); SetActiveWindow(view->impl->hwnd); #else XRaiseWindow(view->impl->display, view->impl->win); #endif } // -------------------------------------------------------------------------------------------------------------------- // set backend that matches current build void puglSetMatchingBackendForCurrentBuild(PuglView* const view) { #ifdef DGL_CAIRO puglSetBackend(view, puglCairoBackend()); #endif #ifdef DGL_OPENGL puglSetBackend(view, puglGlBackend()); #endif #ifdef DGL_VULKAN puglSetBackend(view, puglVulkanBackend()); #endif if (view->backend == nullptr) puglSetBackend(view, puglStubBackend()); } // -------------------------------------------------------------------------------------------------------------------- // Combine puglSetMinSize and puglSetAspectRatio PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, const uint height, const bool aspect) { view->minWidth = (int)width; view->minHeight = (int)height; if (aspect) { view->minAspectX = (int)width; view->minAspectY = (int)height; view->maxAspectX = (int)width; view->maxAspectY = (int)height; } #if defined(DISTRHO_OS_HAIKU) // nothing? #elif defined(DISTRHO_OS_MAC) /* if (view->impl->window) { [view->impl->window setContentMinSize:sizePoints(view, view->minWidth, view->minHeight)]; if (aspect) [view->impl->window setContentAspectRatio:sizePoints(view, view->minAspectX, view->minAspectY)]; } */ puglSetMinSize(view, width, height); puglSetAspectRatio(view, width, height, width, height); #elif defined(DISTRHO_OS_WINDOWS) // nothing #else if (const PuglStatus status = updateSizeHints(view)) return status; XFlush(view->impl->display); #endif return PUGL_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- // set window size with default size and without changing frame x/y position PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height) { view->defaultWidth = width; view->defaultHeight = height; #if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) // keep upstream behaviour const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height }; return puglSetFrame(view, frame); #elif defined(DISTRHO_OS_WINDOWS) // matches upstream pugl, except we add SWP_NOMOVE flag if (view->impl->hwnd) { const PuglRect frame = view->frame; RECT rect = { (long)frame.x, (long)frame.y, (long)frame.x + (long)frame.width, (long)frame.y + (long)frame.height }; AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), FALSE, puglWinGetWindowExFlags(view)); if (! SetWindowPos(view->impl->hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER)) return PUGL_UNKNOWN_ERROR; } #else // matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow if (view->impl->win) { Display* const display = view->impl->display; if (! XResizeWindow(display, view->impl->win, width, height)) return PUGL_UNKNOWN_ERROR; #if 0 // custom handling for embed non-resizable windows if (view->parent != 0 && ! view->hints[PUGL_RESIZABLE]) { XSizeHints sizeHints = {}; sizeHints.flags = PSize | PBaseSize | PMinSize | PMaxSize; sizeHints.width = static_cast(width); sizeHints.height = static_cast(height); sizeHints.base_width = width; sizeHints.base_height = height; sizeHints.min_width = width; sizeHints.min_height = height; sizeHints.max_width = width; sizeHints.max_height = height; XSetNormalHints(display, view->impl->win, &sizeHints); } #endif if (const PuglStatus status = updateSizeHints(view)) return status; XFlush(display); } #endif view->frame.width = width; view->frame.height = height; return PUGL_SUCCESS; } // -------------------------------------------------------------------------------------------------------------------- // DGL specific, build-specific drawing prepare void puglOnDisplayPrepare(PuglView*) { #ifdef DGL_OPENGL glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); #endif } // -------------------------------------------------------------------------------------------------------------------- // DGL specific, build-specific fallback resize void puglFallbackOnResize(PuglView* const view) { #ifdef DGL_OPENGL glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, static_cast(view->frame.width), static_cast(view->frame.height), 0.0, 0.0, 1.0); glViewport(0, 0, static_cast(view->frame.width), static_cast(view->frame.height)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); #endif } #ifdef DISTRHO_OS_WINDOWS // -------------------------------------------------------------------------------------------------------------------- // win32 specific, call ShowWindow with SW_RESTORE void puglWin32RestoreWindow(PuglView* const view) { PuglInternals* impl = view->impl; DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); ShowWindow(impl->hwnd, SW_RESTORE); SetFocus(impl->hwnd); } // -------------------------------------------------------------------------------------------------------------------- // win32 specific, center view based on parent coordinates (if there is one) void puglWin32ShowWindowCentered(PuglView* const view) { PuglInternals* impl = view->impl; DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); RECT rectChild, rectParent; if (view->transientParent != 0 && GetWindowRect(impl->hwnd, &rectChild) && GetWindowRect((HWND)view->transientParent, &rectParent)) { SetWindowPos(impl->hwnd, (HWND)view->transientParent, rectParent.left + (rectChild.right-rectChild.left)/2, rectParent.top + (rectChild.bottom-rectChild.top)/2, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); } else { ShowWindow(impl->hwnd, SW_SHOWNORMAL); } SetFocus(impl->hwnd); } // -------------------------------------------------------------------------------------------------------------------- // win32 specific, set or unset WS_SIZEBOX style flag void puglWin32SetWindowResizable(PuglView* const view, const bool resizable) { PuglInternals* impl = view->impl; DISTRHO_SAFE_ASSERT_RETURN(impl->hwnd != nullptr,); const int winFlags = resizable ? GetWindowLong(impl->hwnd, GWL_STYLE) | WS_SIZEBOX : GetWindowLong(impl->hwnd, GWL_STYLE) & ~WS_SIZEBOX; SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); } // -------------------------------------------------------------------------------------------------------------------- #endif #ifdef HAVE_X11 // -------------------------------------------------------------------------------------------------------------------- // X11 specific, setup event loop filter for sofd file dialog static bool sofd_has_action; static char* sofd_filename; static bool sofd_event_filter(Display* const display, XEvent* const xevent) { if (x_fib_handle_events(display, xevent) == 0) return false; if (sofd_filename != nullptr) std::free(sofd_filename); if (x_fib_status() > 0) sofd_filename = x_fib_filename(); else sofd_filename = nullptr; x_fib_close(display); sofd_has_action = true; return true; } void sofdFileDialogSetup(PuglWorld* const world) { puglX11SetEventFilter(world, sofd_event_filter); } // -------------------------------------------------------------------------------------------------------------------- // X11 specific, show file dialog via sofd bool sofdFileDialogShow(PuglView* const view, const char* const startDir, const char* const title, const uint flags, const uint width, const uint height) { DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); /* x_fib_cfg_buttons(3, options.buttons.listAllFiles-1); x_fib_cfg_buttons(1, options.buttons.showHidden-1); x_fib_cfg_buttons(2, options.buttons.showPlaces-1); */ PuglInternals* const impl = view->impl; return (x_fib_show(impl->display, impl->win, width, height) == 0); } // -------------------------------------------------------------------------------------------------------------------- // X11 specific, close sofd file dialog void sofdFileDialogClose(PuglView* const view) { PuglInternals* const impl = view->impl; x_fib_close(impl->display); } // -------------------------------------------------------------------------------------------------------------------- // X11 specific, get path chosen via sofd file dialog bool sofdFileDialogGetPath(char** path) { if (! sofd_has_action) return false; sofd_has_action = false; *path = sofd_filename; return true; } // -------------------------------------------------------------------------------------------------------------------- // X11 specific, free path of sofd file dialog, no longer needed void sofdFileDialogFree(char* const path) { DISTRHO_SAFE_ASSERT_RETURN(path == nullptr || path == sofd_filename,); std::free(sofd_filename); sofd_filename = nullptr; } // -------------------------------------------------------------------------------------------------------------------- #endif // -------------------------------------------------------------------------------------------------------------------- #ifndef DISTRHO_OS_MAC END_NAMESPACE_DGL #endif