Signed-off-by: falkTX <falktx@falktx.com>wayland
| @@ -83,10 +83,22 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_off) | |||||
| class DISTRHO_API Application | class DISTRHO_API Application | ||||
| { | { | ||||
| public: | public: | ||||
| /** | |||||
| Type of application to setup, either "classic" or "modern". | |||||
| What this means depends on the OS. | |||||
| For now it's only relevant on X11 vs Wayland systems, where X11 is kTypeClassic and Wayland is kTypeModern. | |||||
| */ | |||||
| enum Type { | |||||
| kTypeAuto, | |||||
| kTypeClassic, | |||||
| kTypeModern, | |||||
| }; | |||||
| /** | /** | ||||
| Constructor for standalone or plugin application. | Constructor for standalone or plugin application. | ||||
| */ | */ | ||||
| Application(bool isStandalone = true); | |||||
| Application(bool isStandalone = true, Type type = kTypeAuto); | |||||
| /** | /** | ||||
| Constructor for a standalone application. | Constructor for a standalone application. | ||||
| @@ -141,6 +153,12 @@ public: | |||||
| */ | */ | ||||
| double getTime() const; | double getTime() const; | ||||
| /** | |||||
| Return the application type, either kTypeClassic or kTypeModern. | |||||
| This function never return kTypeAuto. | |||||
| */ | |||||
| Type getType() const noexcept; | |||||
| /** | /** | ||||
| Add a callback function to be triggered on every idle cycle. | Add a callback function to be triggered on every idle cycle. | ||||
| You can add more than one, and remove them at anytime with removeIdleCallback(). | You can add more than one, and remove them at anytime with removeIdleCallback(). | ||||
| @@ -98,8 +98,8 @@ static void app_idle(void* const app) | |||||
| } | } | ||||
| #endif | #endif | ||||
| Application::Application(const bool isStandalone) | |||||
| : pData(new PrivateData(isStandalone)) | |||||
| Application::Application(const bool isStandalone, const Type type) | |||||
| : pData(new PrivateData(isStandalone, type)) | |||||
| { | { | ||||
| // build config sentinels | // build config sentinels | ||||
| #ifdef DPF_DEBUG | #ifdef DPF_DEBUG | ||||
| @@ -126,7 +126,7 @@ Application::Application(const bool isStandalone) | |||||
| } | } | ||||
| Application::Application(int argc, char* argv[]) | Application::Application(int argc, char* argv[]) | ||||
| : pData(new PrivateData(true)) | |||||
| : pData(new PrivateData(true, kTypeAuto)) | |||||
| { | { | ||||
| #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) | #if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) | ||||
| if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | ||||
| @@ -213,6 +213,11 @@ double Application::getTime() const | |||||
| return pData->getTime(); | return pData->getTime(); | ||||
| } | } | ||||
| Application::Type Application::getType() const noexcept | |||||
| { | |||||
| return pData->isModern ? kTypeModern : kTypeClassic; | |||||
| } | |||||
| void Application::addIdleCallback(IdleCallback* const callback) | void Application::addIdleCallback(IdleCallback* const callback) | ||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -53,13 +53,28 @@ const char* Application::getClassName() const noexcept | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| Application::PrivateData::PrivateData(const bool standalone) | |||||
| static constexpr inline uint32_t AppTypePuglWorldFlags(Application::Type type) noexcept | |||||
| { | |||||
| #ifdef DGL_USING_X11_OR_WAYLAND | |||||
| return type == Application::kTypeClassic ? PUGL_WORLD_BACKEND_X11 : | |||||
| type == Application::kTypeModern ? PUGL_WORLD_BACKEND_WAYLAND : 0; | |||||
| #else | |||||
| return 0; | |||||
| #endif | |||||
| } | |||||
| Application::PrivateData::PrivateData(const bool standalone, const Type type) | |||||
| : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | : world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | ||||
| standalone ? PUGL_WORLD_THREADS : 0x0)), | |||||
| (standalone ? PUGL_WORLD_THREADS : 0) | AppTypePuglWorldFlags(type))), | |||||
| #ifdef DGL_USING_X11_OR_WAYLAND | |||||
| isModern(world != nullptr && puglUsingWayland(world)), | |||||
| #else | |||||
| isModern(false), | |||||
| #endif | |||||
| isStandalone(standalone), | isStandalone(standalone), | ||||
| isStarting(true), | |||||
| isQuitting(false), | isQuitting(false), | ||||
| isQuittingInNextCycle(false), | isQuittingInNextCycle(false), | ||||
| isStarting(true), | |||||
| needsRepaint(false), | needsRepaint(false), | ||||
| visibleWindows(0), | visibleWindows(0), | ||||
| mainThreadHandle(getCurrentThreadHandle()), | mainThreadHandle(getCurrentThreadHandle()), | ||||
| @@ -68,15 +83,11 @@ Application::PrivateData::PrivateData(const bool standalone) | |||||
| { | { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | ||||
| #ifdef DGL_USING_SDL | |||||
| SDL_Init(SDL_INIT_EVENTS|SDL_INIT_TIMER|SDL_INIT_VIDEO); | |||||
| #else | |||||
| #ifdef __EMSCRIPTEN__ | #ifdef __EMSCRIPTEN__ | ||||
| puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); | puglSetWorldString(world, PUGL_CLASS_NAME, "canvas"); | ||||
| #else | #else | ||||
| puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | ||||
| #endif | #endif | ||||
| #endif | |||||
| } | } | ||||
| Application::PrivateData::~PrivateData() | Application::PrivateData::~PrivateData() | ||||
| @@ -87,12 +98,8 @@ Application::PrivateData::~PrivateData() | |||||
| windows.clear(); | windows.clear(); | ||||
| idleCallbacks.clear(); | idleCallbacks.clear(); | ||||
| #ifdef DGL_USING_SDL | |||||
| SDL_Quit(); | |||||
| #else | |||||
| if (world != nullptr) | if (world != nullptr) | ||||
| puglFreeWorld(world); | puglFreeWorld(world); | ||||
| #endif | |||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
| @@ -51,18 +51,21 @@ struct Application::PrivateData { | |||||
| /** Pugl world instance. */ | /** Pugl world instance. */ | ||||
| PuglWorld* const world; | PuglWorld* const world; | ||||
| /** Whether the applicating uses modern backend, otherwise classic. */ | |||||
| const bool isModern; | |||||
| /** Whether the application is running as standalone, otherwise it is part of a plugin. */ | /** Whether the application is running as standalone, otherwise it is part of a plugin. */ | ||||
| const bool isStandalone; | const bool isStandalone; | ||||
| /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||||
| bool isStarting; | |||||
| /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ | /** Whether the applicating is about to quit, or already stopped. Defaults to false. */ | ||||
| bool isQuitting; | bool isQuitting; | ||||
| /** Helper for safely close everything from main thread. */ | /** Helper for safely close everything from main thread. */ | ||||
| bool isQuittingInNextCycle; | bool isQuittingInNextCycle; | ||||
| /** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */ | |||||
| bool isStarting; | |||||
| /** When true force all windows to be repainted on next idle. */ | /** When true force all windows to be repainted on next idle. */ | ||||
| bool needsRepaint; | bool needsRepaint; | ||||
| @@ -80,7 +83,7 @@ struct Application::PrivateData { | |||||
| std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks; | ||||
| /** Constructor and destructor */ | /** Constructor and destructor */ | ||||
| explicit PrivateData(bool standalone); | |||||
| explicit PrivateData(bool standalone, Type type); | |||||
| ~PrivateData(); | ~PrivateData(); | ||||
| /** Flag one window as shown, which increments @a visibleWindows. | /** Flag one window as shown, which increments @a visibleWindows. | ||||
| @@ -966,32 +966,85 @@ PuglInternals* puglInitViewInternals(PuglWorld* const world) | |||||
| PuglWorldInternals* puglInitWorldInternals(PuglWorld* const world, const PuglWorldType type, const PuglWorldFlags flags) | PuglWorldInternals* puglInitWorldInternals(PuglWorld* const world, const PuglWorldType type, const PuglWorldFlags flags) | ||||
| { | { | ||||
| bool supportsDecorations = true; | bool supportsDecorations = true; | ||||
| #ifdef HAVE_WAYLAND | |||||
| const bool usingWayland = puglWaylandStatus(&supportsDecorations); | |||||
| #else | |||||
| constexpr bool usingWayland = false; | |||||
| #endif | |||||
| d_stdout("Using wayland: %d, compositor supports decorations: %d", usingWayland, supportsDecorations); | |||||
| bool usingWayland = false; | |||||
| bool usingX11 = false; | |||||
| // initial choice by env var | |||||
| if (const char* const backend = std::getenv("DPF_BACKEND")) | |||||
| { | |||||
| if (std::strcmp(backend, "x11") == 0) | |||||
| usingX11 = true; | |||||
| else if (std::strcmp(backend, "wayland") != 0) | |||||
| usingWayland = true; | |||||
| else | |||||
| d_stderr("Unknown DPF_BACKEND value, must be 'x11' or 'wayland'"); | |||||
| } | |||||
| // overridden if required for operation | |||||
| if (flags & PUGL_WORLD_BACKEND_X11) | |||||
| { | |||||
| usingX11 = true; | |||||
| usingWayland = false; | |||||
| } | |||||
| if (flags & PUGL_WORLD_BACKEND_WAYLAND) | |||||
| { | |||||
| usingX11 = false; | |||||
| usingWayland = true; | |||||
| #ifdef HAVE_WAYLAND | |||||
| puglWaylandStatus(&supportsDecorations); | |||||
| #endif | |||||
| } | |||||
| // pick backend automatically if none selected | |||||
| if (! (usingX11 || usingWayland)) | |||||
| { | |||||
| #ifdef HAVE_WAYLAND | |||||
| usingWayland = puglWaylandStatus(&supportsDecorations); | |||||
| if (! usingWayland) | |||||
| #endif | |||||
| { | |||||
| #ifdef HAVE_X11 | |||||
| supportsDecorations = true; | |||||
| usingX11 = true; | |||||
| #endif | |||||
| } | |||||
| if (! (usingX11 || usingWayland)) | |||||
| { | |||||
| d_stderr("Could not find a usable windowing backend"); | |||||
| return nullptr; | |||||
| } | |||||
| } | |||||
| if (usingX11) | |||||
| { | |||||
| #ifdef HAVE_X11 | |||||
| d_stdout("Using X11"); | |||||
| x11::PuglWorld* const x11world = cast<x11::PuglWorld>(world); | |||||
| x11::puglSetWorldHandle(x11world, const_cast<char*>(kUsingX11Check)); | |||||
| return cast<PuglWorldInternals>(x11::puglInitWorldInternals(x11world, | |||||
| static_cast<x11::PuglWorldType>(type), | |||||
| static_cast<x11::PuglWorldFlags>(flags))); | |||||
| #else | |||||
| return nullptr; | |||||
| #endif | |||||
| } | |||||
| #ifdef HAVE_WAYLAND | |||||
| if (usingWayland) | if (usingWayland) | ||||
| { | { | ||||
| #ifdef HAVE_WAYLAND | |||||
| d_stdout("Using Wayland, compositor supports decorations: %s", supportsDecorations ? "true" : "false"); | |||||
| wl::PuglWorld* const wlworld = cast<wl::PuglWorld>(world); | wl::PuglWorld* const wlworld = cast<wl::PuglWorld>(world); | ||||
| wl::puglSetWorldHandle(wlworld, const_cast<char*>(kUsingWaylandCheck)); | wl::puglSetWorldHandle(wlworld, const_cast<char*>(kUsingWaylandCheck)); | ||||
| return cast<PuglWorldInternals>(wl::puglInitWorldInternals(wlworld, | return cast<PuglWorldInternals>(wl::puglInitWorldInternals(wlworld, | ||||
| static_cast<wl::PuglWorldType>(type), | static_cast<wl::PuglWorldType>(type), | ||||
| static_cast<wl::PuglWorldFlags>(flags))); | static_cast<wl::PuglWorldFlags>(flags))); | ||||
| #else | |||||
| return nullptr; | |||||
| #endif | |||||
| } | } | ||||
| #endif | |||||
| #ifdef HAVE_X11 | |||||
| x11::PuglWorld* const x11world = cast<x11::PuglWorld>(world); | |||||
| x11::puglSetWorldHandle(x11world, const_cast<char*>(kUsingX11Check)); | |||||
| return cast<PuglWorldInternals>(x11::puglInitWorldInternals(x11world, | |||||
| static_cast<x11::PuglWorldType>(type), | |||||
| static_cast<x11::PuglWorldFlags>(flags))); | |||||
| #else | |||||
| return nullptr; | return nullptr; | ||||
| #endif | |||||
| } | } | ||||
| @@ -1182,6 +1235,14 @@ PuglStatus puglViewStringChanged(PuglView* const view, const PuglStringHint key, | |||||
| return PUGL_BAD_BACKEND; | return PUGL_BAD_BACKEND; | ||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // X11 or Wayland specific, check if using wayland | |||||
| bool puglUsingWayland(PuglWorld* const world) | |||||
| { | |||||
| return world->handle == kUsingWaylandCheck; | |||||
| } | |||||
| #ifdef HAVE_X11 | #ifdef HAVE_X11 | ||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -1288,18 +1349,6 @@ bool puglWaylandStatus(bool* supportsDecorations) | |||||
| bool supportsWayland = false; | bool supportsWayland = false; | ||||
| *supportsDecorations = false; | *supportsDecorations = false; | ||||
| if (const char* const backend = std::getenv("DPF_BACKEND")) | |||||
| { | |||||
| if (std::strcmp(backend, "x11") == 0) | |||||
| return false; | |||||
| if (std::strcmp(backend, "wayland") != 0) | |||||
| { | |||||
| d_stderr2("Unknown DPF_BACKEND value, will use X11"); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| if (struct wl_display* const wl_display = wl_display_connect(nullptr)) | if (struct wl_display* const wl_display = wl_display_connect(nullptr)) | ||||
| { | { | ||||
| if (struct wl_registry* const wl_registry = wl_display_get_registry(wl_display)) | if (struct wl_registry* const wl_registry = wl_display_get_registry(wl_display)) | ||||
| @@ -118,6 +118,13 @@ void puglWin32ShowCentered(PuglView* view); | |||||
| #define DGL_USING_X11_OR_WAYLAND | #define DGL_USING_X11_OR_WAYLAND | ||||
| // custom flags for world creation | |||||
| #define PUGL_WORLD_BACKEND_X11 0x1000 | |||||
| #define PUGL_WORLD_BACKEND_WAYLAND 0x2000 | |||||
| // X11 or Wayland specific, check if using wayland | |||||
| bool puglUsingWayland(PuglWorld* world); | |||||
| #ifdef HAVE_X11 | #ifdef HAVE_X11 | ||||
| #define DGL_USING_X11 | #define DGL_USING_X11 | ||||