From 93e71bf20291ed6cabf3ad5f974fbc4c4785a316 Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Sun, 23 Jul 2023 22:00:34 +0300 Subject: [PATCH 01/13] XCB support --- src/CrossWindow/Win32/Win32Window.cpp | 23 +++++++++++++++-------- src/CrossWindow/XCB/XCBWindow.cpp | 24 ++++++++++++------------ src/CrossWindow/XCB/XCBWindow.h | 11 +++++------ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/CrossWindow/Win32/Win32Window.cpp b/src/CrossWindow/Win32/Win32Window.cpp index 3c02e10..f5e8de3 100644 --- a/src/CrossWindow/Win32/Win32Window.cpp +++ b/src/CrossWindow/Win32/Win32Window.cpp @@ -265,17 +265,20 @@ void Window::setSize(unsigned width, unsigned height) { RECT rect, frame, border; GetWindowRect(hwnd, &rect); - DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT)); + DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, + sizeof(RECT)); border.left = frame.left - rect.left; border.top = frame.top - rect.top; border.right = rect.right - frame.right; border.bottom = rect.bottom - frame.bottom; - int titlebarHeight = (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) + - GetSystemMetrics(SM_CXPADDEDBORDER)); + int titlebarHeight = + (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) + + GetSystemMetrics(SM_CXPADDEDBORDER)); - SetWindowPos(hwnd, nullptr, -1, -1, width + border.right + border.left, height + border.top + border.bottom + titlebarHeight, + SetWindowPos(hwnd, nullptr, -1, -1, width + border.right + border.left, + height + border.top + border.bottom + titlebarHeight, SWP_NOMOVE | SWP_NOREDRAW); } @@ -302,9 +305,13 @@ UVec2 Window::getPosition() const UVec2 Window::getWindowSize() const { RECT lpRect; - DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &lpRect, sizeof(lpRect)); - int titlebarHeight = (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXPADDEDBORDER)); - return UVec2(lpRect.right - lpRect.left, lpRect.bottom - lpRect.top - titlebarHeight); + DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &lpRect, + sizeof(lpRect)); + int titlebarHeight = + (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) + + GetSystemMetrics(SM_CXPADDEDBORDER)); + return UVec2(lpRect.right - lpRect.left, + lpRect.bottom - lpRect.top - titlebarHeight); } UVec2 Window::getCurrentDisplaySize() const @@ -368,4 +375,4 @@ LRESULT Window::WindowProc(UINT msg, WPARAM wparam, LPARAM lparam) if (result > 0) return result; return DefWindowProc(hwnd, msg, wparam, lparam); } -} \ No newline at end of file +} diff --git a/src/CrossWindow/XCB/XCBWindow.cpp b/src/CrossWindow/XCB/XCBWindow.cpp index 43f1a10..ce4d0c7 100644 --- a/src/CrossWindow/XCB/XCBWindow.cpp +++ b/src/CrossWindow/XCB/XCBWindow.cpp @@ -7,36 +7,36 @@ Window::Window() {} bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) { const XWinState& xwinState = getXWinState(); - mConnection = xwinState.connection; - mScreen = xwinState.screen; + connection = xwinState.connection; + screen = xwinState.screen; - mXcbWindowId = xcb_generate_id(mConnection); + window = xcb_generate_id(connection); uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; uint32_t value_list[2] = { - mScreen->black_pixel, + screen->black_pixel, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE}; - xcb_create_window(mConnection, XCB_COPY_FROM_PARENT, mXcbWindowId, - mScreen->root, desc.x, desc.y, desc.width, desc.height, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, mScreen->root_visual, mask, + xcb_create_window(connection, XCB_COPY_FROM_PARENT, window, screen->root, + desc.x, desc.y, desc.width, desc.height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, value_list); - xcb_map_window(mConnection, mXcbWindowId); + xcb_map_window(connection, window); const unsigned coords[] = {static_cast(desc.x), static_cast(desc.y)}; - xcb_configure_window(mConnection, mXcbWindowId, + xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, coords); - xcb_flush(mConnection); + xcb_flush(connection); return true; } -void Window::close() { xcb_destroy_window(mConnection, mXcbWindowId); } +void Window::close() { xcb_destroy_window(connection, window); } -} \ No newline at end of file +} diff --git a/src/CrossWindow/XCB/XCBWindow.h b/src/CrossWindow/XCB/XCBWindow.h index 6eb36d6..000906e 100644 --- a/src/CrossWindow/XCB/XCBWindow.h +++ b/src/CrossWindow/XCB/XCBWindow.h @@ -16,7 +16,7 @@ namespace xwin */ class Window { -public: + public: Window(); // Initialize this window with the XCB API. @@ -24,10 +24,9 @@ class Window void close(); - protected: - xcb_connection_t* mConnection = nullptr; - xcb_screen_t* mScreen = nullptr; - unsigned mXcbWindowId = 0; - unsigned mDisplay = 0; + xcb_connection_t* connection = nullptr; + xcb_screen_t* screen = nullptr; + xcb_window_t window = 0; + unsigned display = 0; }; } From 93bda9a6c8949efc3faa1c0fb8ea692c1357cafa Mon Sep 17 00:00:00 2001 From: Mitya <96876822+mitya-y@users.noreply.github.com> Date: Wed, 26 Jul 2023 19:59:51 +0300 Subject: [PATCH 02/13] WinAPI compilation fix (#1) --- CMakeLists.txt | 2 +- src/CrossWindow/Win32/Win32Dialogs.cpp | 4 ++-- src/CrossWindow/Win32/Win32Window.h | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc399fb..302fbd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ enable_language(CXX) # CMake Settings set(CMAKE_SUPPRESS_REGENERATION true) set(DCMAKE_GENERATOR_PLATFORM "x64") -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/src/CrossWindow/Win32/Win32Dialogs.cpp b/src/CrossWindow/Win32/Win32Dialogs.cpp index d3d4397..f380648 100644 --- a/src/CrossWindow/Win32/Win32Dialogs.cpp +++ b/src/CrossWindow/Win32/Win32Dialogs.cpp @@ -94,7 +94,7 @@ bool showOpenDialog(const OpenSaveDialogDesc& odesc, std::string& outPath) hr = pFileOpen->GetResult(&pItem); if (SUCCEEDED(hr)) { - PWSTR pszFilePath = L""; + PWSTR pszFilePath = (PWSTR)L""; hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); if (pszFilePath != 0x0) { @@ -186,7 +186,7 @@ bool showSaveDialog(const OpenSaveDialogDesc& sdesc, std::string& outPath) hr = pFileSave->GetResult(&pItem); if (SUCCEEDED(hr)) { - PWSTR pszFilePath = L""; + PWSTR pszFilePath = (PWSTR)L""; hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); if (pszFilePath != 0x0) { diff --git a/src/CrossWindow/Win32/Win32Window.h b/src/CrossWindow/Win32/Win32Window.h index 7ce7c7e..5b03c48 100644 --- a/src/CrossWindow/Win32/Win32Window.h +++ b/src/CrossWindow/Win32/Win32Window.h @@ -146,12 +146,14 @@ class Window LRESULT WindowProc(UINT msg, WPARAM wparam, LPARAM lparam); + public: // This window handle HWND hwnd = nullptr; // This window's application instance. HINSTANCE hinstance = nullptr; + protected: // Pointer to this window's event queue EventQueue* mEventQueue = nullptr; From 64dcfa92cb961502c9cacbc962f0826fa0a07cff Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Sat, 19 Aug 2023 01:50:25 +0300 Subject: [PATCH 03/13] Initial wayland files added --- src/CrossWindow/Wayland/WaylandEventQueue.cpp | 0 src/CrossWindow/Wayland/WaylandEventQueue.h | 32 +++++++++++++++++++ src/CrossWindow/Wayland/WaylandWindow.cpp | 0 src/CrossWindow/Wayland/WaylandWindow.h | 20 ++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 src/CrossWindow/Wayland/WaylandEventQueue.cpp create mode 100644 src/CrossWindow/Wayland/WaylandEventQueue.h create mode 100644 src/CrossWindow/Wayland/WaylandWindow.cpp create mode 100644 src/CrossWindow/Wayland/WaylandWindow.h diff --git a/src/CrossWindow/Wayland/WaylandEventQueue.cpp b/src/CrossWindow/Wayland/WaylandEventQueue.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/CrossWindow/Wayland/WaylandEventQueue.h b/src/CrossWindow/Wayland/WaylandEventQueue.h new file mode 100644 index 0000000..3e4de07 --- /dev/null +++ b/src/CrossWindow/Wayland/WaylandEventQueue.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../Common/Event.h" + +#include + +namespace xwin +{ +class Window; + +/** + * Events - https://xcb.freedesktop.org/tutorial/events/ + */ +class EventQueue +{ + public: + EventQueue(); + + void update(); + + const Event& front(); + + void pop(); + + bool empty(); + + protected: + // void pushEvent(); + + std::queue mQueue; +}; +} diff --git a/src/CrossWindow/Wayland/WaylandWindow.cpp b/src/CrossWindow/Wayland/WaylandWindow.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/CrossWindow/Wayland/WaylandWindow.h b/src/CrossWindow/Wayland/WaylandWindow.h new file mode 100644 index 0000000..78cc7ae --- /dev/null +++ b/src/CrossWindow/Wayland/WaylandWindow.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../Common/EventQueue.h" +#include "../Common/Init.h" +#include "../Common/WindowDesc.h" + +namespace xwin +{ +class Window +{ + public: + Window(); + ~Window(); + + // Initialize this window with the XCB API. + bool create(const WindowDesc& desc, EventQueue& eventQueue); + + void close(); +}; +} From fea8513e93d36bb61e7df72e5ca0531be0d1030b Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Sat, 19 Aug 2023 01:51:28 +0300 Subject: [PATCH 04/13] Default linux implementation set to Xorg, Xlib implementation fixed --- CMakeLists.txt | 17 +++++++++-------- src/CrossWindow/Main/XLibMain.cpp | 9 ++------- src/CrossWindow/XLib/XLibEventQueue.cpp | 19 +++++++++++++++---- src/CrossWindow/XLib/XLibEventQueue.h | 14 +++++++++++++- src/CrossWindow/XLib/XLibWindow.cpp | 14 ++++++++++++-- src/CrossWindow/XLib/XLibWindow.h | 6 ++++-- 6 files changed, 55 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc399fb..4fd11b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ if(XWIN_API STREQUAL "AUTO") elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") set(XWIN_API "COCOA" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux") - set(XWIN_API "XCB" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) + set(XWIN_API "XLIB" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) endif() endif() @@ -72,7 +72,7 @@ elseif(XWIN_API STREQUAL "UIKIT") elseif(XWIN_API STREQUAL "XCB") set(XWIN_API_PATH "XCB") elseif(XWIN_API STREQUAL "XLIB") - set(XWIN_API_PATH "XLIB") + set(XWIN_API_PATH "XLib") elseif(XWIN_API STREQUAL "ANDROID") set(XWIN_API_PATH "Android") elseif(XWIN_API STREQUAL "UIKIT") @@ -148,7 +148,6 @@ function(xwin_add_executable targetProject targetSources) ${targetProject} MACOSX_BUNDLE ${XWIN_FILES} - ) elseif(XWIN_API STREQUAL "XCB" OR XWIN_API STREQUAL "XLIB") add_executable( @@ -236,11 +235,13 @@ elseif(XWIN_API STREQUAL "XLIB") endif() elseif(XWIN_API STREQUAL "XCB") find_package(X11 REQUIRED) - message("Found XCB Libraries.") - message("XCB Include Path = ${X11_xcb_INCLUDE_PATH}") - message("XCB Lib = ${X11_xcb_LIB}") - target_link_libraries(${PROJECT_NAME} ${X11_xcb_LIB}) - target_include_directories(${PROJECT_NAME} PUBLIC ${X11_xcb_INCLUDE_PATH}) + if(X11_FOUND) + message("Found XCB Libraries.") + message("XCB Include Path = ${X11_xcb_INCLUDE_PATH}") + message("XCB Lib = ${X11_xcb_LIB}") + target_link_libraries(${PROJECT_NAME} ${X11_xcb_LIB}) + target_include_directories(${PROJECT_NAME} PUBLIC ${X11_xcb_INCLUDE_PATH}) + endif() endif() # ============================================================= diff --git a/src/CrossWindow/Main/XLibMain.cpp b/src/CrossWindow/Main/XLibMain.cpp index ae8900b..1fe2ecc 100644 --- a/src/CrossWindow/Main/XLibMain.cpp +++ b/src/CrossWindow/Main/XLibMain.cpp @@ -1,16 +1,11 @@ #include "../Common/Init.h" #include "Main.h" -int main(int argc, char** argv) +int main(int argc, const char** argv) { xwin::init(argc, argv); - - xmain(argc, argv); - XDestroyWindow(display, xlib_window); - XCloseDisplay(display); - return 0; -} \ No newline at end of file +} diff --git a/src/CrossWindow/XLib/XLibEventQueue.cpp b/src/CrossWindow/XLib/XLibEventQueue.cpp index 0982374..0b3942d 100644 --- a/src/CrossWindow/XLib/XLibEventQueue.cpp +++ b/src/CrossWindow/XLib/XLibEventQueue.cpp @@ -1,5 +1,6 @@ #include "XLibEventQueue.h" #include "../Common/Window.h" +#include "CrossWindow/Common/WindowDesc.h" namespace xwin { @@ -7,13 +8,23 @@ void EventQueue::update() { XEvent event; - while (XPending(demo->display) > 0) + while (XPending(mParent->display) > 0) { - XNextEvent(demo->display, &event); - pushEvent(event, mParent); + XNextEvent(mParent->display, &event); + pushEvent(&event, mParent); } } +EventQueue::EventQueue() {} + +EventQueue::EventQueue(Window* parent) : mParent(parent) {} + +const Event& EventQueue::front() { return mQueue.front(); } + +void EventQueue::pop() { mQueue.pop(); } + +bool EventQueue::empty() { return mQueue.empty(); } + void EventQueue::pushEvent(const XEvent* event, Window* window) { switch (event->type) @@ -62,4 +73,4 @@ void EventQueue::pushEvent(const XEvent* event, Window* window) } } } -} \ No newline at end of file +} diff --git a/src/CrossWindow/XLib/XLibEventQueue.h b/src/CrossWindow/XLib/XLibEventQueue.h index 6015b77..57bafcb 100644 --- a/src/CrossWindow/XLib/XLibEventQueue.h +++ b/src/CrossWindow/XLib/XLibEventQueue.h @@ -14,9 +14,21 @@ class Window; class EventQueue { public: + EventQueue(); + EventQueue(Window* parent); + + void update(); + + const Event& front(); + + void pop(); + + bool empty(); + void pushEvent(const XEvent* event, Window* window); protected: std::queue mQueue; + Window* mParent; }; -} \ No newline at end of file +} diff --git a/src/CrossWindow/XLib/XLibWindow.cpp b/src/CrossWindow/XLib/XLibWindow.cpp index e8b376b..1eb8dbc 100644 --- a/src/CrossWindow/XLib/XLibWindow.cpp +++ b/src/CrossWindow/XLib/XLibWindow.cpp @@ -1,7 +1,12 @@ #include "XLibWindow.h" +#include "CrossWindow/Common/WindowDesc.h" namespace xwin { +Window::Window() {} + +const WindowDesc Window::getDesc() { return mDesc; } + bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) { XInitThreads(); @@ -29,7 +34,12 @@ bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) XSelectInput(display, window, ExposureMask | KeyPressMask); XMapWindow(display, window); XFlush(display); + + return window; } -bool Window::destroy() { XDestroyWindow(display, window); } -} \ No newline at end of file +bool Window::close() +{ + return (XDestroyWindow(display, window)) || XCloseDisplay(display); +} +} diff --git a/src/CrossWindow/XLib/XLibWindow.h b/src/CrossWindow/XLib/XLibWindow.h index ee9476c..65bcd0c 100644 --- a/src/CrossWindow/XLib/XLibWindow.h +++ b/src/CrossWindow/XLib/XLibWindow.h @@ -17,9 +17,11 @@ class Window bool create(const WindowDesc& desc, EventQueue& eventQueue); - bool destroy(); + bool close(); - protected: + const WindowDesc getDesc(); + + WindowDesc mDesc; Display* display = nullptr; XLibWindow window; }; From ba9390f3a2ddeaacecbff099169062ce54397e5d Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Sat, 19 Aug 2023 01:51:34 +0300 Subject: [PATCH 05/13] Style fixes --- src/CrossWindow/Win32/Win32EventQueue.h | 4 +-- src/CrossWindow/XCB/XCBEventQueue.h | 34 ++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/CrossWindow/Win32/Win32EventQueue.h b/src/CrossWindow/Win32/Win32EventQueue.h index b940893..4e78703 100644 --- a/src/CrossWindow/Win32/Win32EventQueue.h +++ b/src/CrossWindow/Win32/Win32EventQueue.h @@ -23,7 +23,7 @@ class EventQueue bool empty(); - size_t size(); + size_t size(); enum class ProcessingMode { @@ -58,4 +58,4 @@ class EventQueue /*VK_RBUTTON 0x02*/ Key::KeysMax, /*VK_CANCEL 0x03*/ Key::KeysMax}; }; -} \ No newline at end of file +} diff --git a/src/CrossWindow/XCB/XCBEventQueue.h b/src/CrossWindow/XCB/XCBEventQueue.h index c40fe43..ca23b1b 100644 --- a/src/CrossWindow/XCB/XCBEventQueue.h +++ b/src/CrossWindow/XCB/XCBEventQueue.h @@ -8,27 +8,27 @@ namespace xwin { - class Window; +class Window; - /** - * Events - https://xcb.freedesktop.org/tutorial/events/ - */ - class EventQueue - { - public: - EventQueue(); +/** + * Events - https://xcb.freedesktop.org/tutorial/events/ + */ +class EventQueue +{ + public: + EventQueue(); - void update(); + void update(); - const Event &front(); + const Event& front(); - void pop(); + void pop(); - bool empty(); + bool empty(); - protected: - void pushEvent(const xcb_generic_event_t* e); + protected: + void pushEvent(const xcb_generic_event_t* e); - std::queue mQueue; - }; -} \ No newline at end of file + std::queue mQueue; +}; +} From 197c5bc8f124479606fb80377cb95eb540ee098d Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Sat, 19 Aug 2023 01:52:05 +0300 Subject: [PATCH 06/13] Wayland include added --- src/CrossWindow/Common/Window.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CrossWindow/Common/Window.h b/src/CrossWindow/Common/Window.h index 84ecf5e..5a03a35 100644 --- a/src/CrossWindow/Common/Window.h +++ b/src/CrossWindow/Common/Window.h @@ -10,6 +10,8 @@ #include "../XCB/XCBWindow.h" #elif XWIN_XLIB #include "../XLib/XLibWindow.h" +#elif XWIN_WAYLAND +#include "../Wayland/WaylandWindow.h" #elif XWIN_ANDROID #include "../Android/AndroidWindow.h" #elif XWIN_UIKIT @@ -26,4 +28,4 @@ namespace xwin { typedef std::shared_ptr WindowPtr; typedef std::weak_ptr WindowWeakPtr; -} \ No newline at end of file +} From 5200ff3c8cdec85a00f283063f4d6d68ff5005d6 Mon Sep 17 00:00:00 2001 From: Michael Tsukanov <73912760+cone-forest@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:39:43 +0300 Subject: [PATCH 07/13] Wayland impl (#2) * Basic wayland implementation * Xorg (XCB) event callback improved * style fix --- src/CrossWindow/Common/Event.h | 21 +++---- src/CrossWindow/Common/EventQueue.h | 4 +- src/CrossWindow/Common/State.h | 16 ++++++ src/CrossWindow/Main/WaylandMain.cpp | 31 ++++++++++ src/CrossWindow/Wayland/WaylandEventQueue.cpp | 20 +++++++ src/CrossWindow/Wayland/WaylandEventQueue.h | 2 + src/CrossWindow/Wayland/WaylandWindow.cpp | 33 +++++++++++ src/CrossWindow/Wayland/WaylandWindow.h | 27 +++++++++ src/CrossWindow/XCB/XCBWindow.cpp | 56 +++++++++++++++++++ src/CrossWindow/XCB/XCBWindow.h | 29 ++++++++++ src/CrossWindow/XLib/XLibEventQueue.cpp | 42 ++++++++------ src/CrossWindow/XLib/XLibWindow.h | 24 ++++++++ 12 files changed, 277 insertions(+), 28 deletions(-) create mode 100644 src/CrossWindow/Main/WaylandMain.cpp diff --git a/src/CrossWindow/Common/Event.h b/src/CrossWindow/Common/Event.h index 1a3e16e..df3289b 100644 --- a/src/CrossWindow/Common/Event.h +++ b/src/CrossWindow/Common/Event.h @@ -14,7 +14,7 @@ class Window; enum class EventType : size_t { - None = 0, + None, // Closing a window Close, @@ -89,8 +89,8 @@ struct ResizeData // New height of window viewport unsigned height; - // In the process of resizing - bool resizing; + // In the process of resizing + bool resizing; ResizeData(unsigned width, unsigned height, bool resizing); @@ -480,7 +480,8 @@ struct GamepadData * SDL does something similar: * */ -union EventData { +union EventData +{ FocusData focus; ResizeData resize; DpiData dpi; @@ -508,8 +509,10 @@ class Event // Inner data of the event EventData data; - - Event(EventType type = EventType::None, Window* window = nullptr); + + Event(Window* window = nullptr) { Event(EventType::None, window); } + + Event(EventType type, Window* window = nullptr); Event(FocusData data, Window* window = nullptr); @@ -519,7 +522,7 @@ class Event Event(MouseMoveData data, Window* window = nullptr); - Event(MouseRawData data, Window* window = nullptr); + Event(MouseRawData data, Window* window = nullptr); Event(MouseInputData data, Window* window = nullptr); @@ -532,12 +535,10 @@ class Event Event(DpiData data, Window* window = nullptr); ~Event(); - + bool operator==(const Event& other) const { return type == other.type && window == other.window; } - - }; } diff --git a/src/CrossWindow/Common/EventQueue.h b/src/CrossWindow/Common/EventQueue.h index 630c01d..23c2151 100644 --- a/src/CrossWindow/Common/EventQueue.h +++ b/src/CrossWindow/Common/EventQueue.h @@ -14,6 +14,8 @@ #include "../XCB/XCBEventQueue.h" #elif XWIN_XLIB #include "../XLib/XLibEventQueue.h" +#elif XWIN_WAYLAND +#include "../Wayland/WaylandEventQueue.h" #elif XWIN_ANDROID #include "../Android/AndroidEventQueue.h" #elif XWIN_UIKIT @@ -22,4 +24,4 @@ #include "../WASM/WASMEventQueue.h" #elif XWIN_NOOP #include "../Noop/NoopEventQueue.h" -#endif \ No newline at end of file +#endif diff --git a/src/CrossWindow/Common/State.h b/src/CrossWindow/Common/State.h index 8479c85..992788a 100644 --- a/src/CrossWindow/Common/State.h +++ b/src/CrossWindow/Common/State.h @@ -6,6 +6,9 @@ #include #elif defined(XWIN_XLIB) #include +#elif defined(XWIN_WAYLAND) +#include +#include #endif namespace xwin @@ -46,6 +49,19 @@ struct XWinState : argc(argc), argv(argv), connection(connection), screen(screen) { } +#elif defined(XWIN_WAYLAND) + int argc; + const char** argv; + struct wl_display* display; + struct wl_registry* registry; + struct wl_compositor* compositor; + struct wl_surface* surface; + struct wl_list* monitors; + + XWinState(int argc, const char** argv, struct wl_display* display) + : argc(argc), argv(argv), display(display) + { + } #elif defined(XWIN_ANDROID) diff --git a/src/CrossWindow/Main/WaylandMain.cpp b/src/CrossWindow/Main/WaylandMain.cpp new file mode 100644 index 0000000..08e3c0a --- /dev/null +++ b/src/CrossWindow/Main/WaylandMain.cpp @@ -0,0 +1,31 @@ +#include "../Common/Init.h" +#include "Main.h" + +#include +#include +#include + +int main(int argc, const char** argv) +{ + struct wl_display* display = wl_display_connect(nullptr); + if (!display) return 1; + + struct wl_registry* registry = wl_display_get_registry(display); + const struct wl_registry_listener registry_listener + { + nullptr, nullptr // callbacks + }; + struct wl_compositor* compositor; + wl_registry_add_listener(registry, ®istry_listener, &compositor); + wl_display_roundtrip(display); + wl_registry_destroy(registry); + + xwin::init(argc, argv, display); + + xmain(argc, argv); + + wl_compositor_destroy(compositor); + wl_display_disconnect(display); + + return 0; +} diff --git a/src/CrossWindow/Wayland/WaylandEventQueue.cpp b/src/CrossWindow/Wayland/WaylandEventQueue.cpp index e69de29..b9ccd73 100644 --- a/src/CrossWindow/Wayland/WaylandEventQueue.cpp +++ b/src/CrossWindow/Wayland/WaylandEventQueue.cpp @@ -0,0 +1,20 @@ +#include "WaylandEventQueue.h" +#include "../Common/Window.h" +#include "CrossWindow/Common/WindowDesc.h" + +namespace xwin +{ +EventQueue::EventQueue() +{ + const XWinState& state = getXWinState(); + event_queue = wl_display_create_queue(state.display); +} + +void EventQueue::update() {} + +const Event& EventQueue::front() { return mQueue.front(); } + +void EventQueue::pop() { mQueue.pop(); } + +bool EventQueue::empty() { return mQueue.empty(); } +} diff --git a/src/CrossWindow/Wayland/WaylandEventQueue.h b/src/CrossWindow/Wayland/WaylandEventQueue.h index 3e4de07..b10a96b 100644 --- a/src/CrossWindow/Wayland/WaylandEventQueue.h +++ b/src/CrossWindow/Wayland/WaylandEventQueue.h @@ -3,6 +3,7 @@ #include "../Common/Event.h" #include +#include namespace xwin { @@ -26,6 +27,7 @@ class EventQueue protected: // void pushEvent(); + struct wl_event_queue* event_queue; std::queue mQueue; }; diff --git a/src/CrossWindow/Wayland/WaylandWindow.cpp b/src/CrossWindow/Wayland/WaylandWindow.cpp index e69de29..a2d5dd4 100644 --- a/src/CrossWindow/Wayland/WaylandWindow.cpp +++ b/src/CrossWindow/Wayland/WaylandWindow.cpp @@ -0,0 +1,33 @@ +#include "WaylandWindow.h" +#include "CrossWindow/Common/WindowDesc.h" + +#include + +namespace xwin +{ +Window::Window() { create(); } + +Window::~Window() { close(); } + +void Window::minimize() {} + +void Window::maximize() {} + +const WindowDesc Window::getDesc() { return mDesc; } + +void Window::trackEventsAsync( + const std::function& fun) +{ + mCallback = fun; +} +// Executes an event callback asynchronously, use this for non-blocking +// events (resizing while rendering, etc.) +void Window::executeEventCallback(const xwin::Event e) +{ + if (mCallback) mCallback(e); +} + +bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) {} + +bool Window::close() {} +} diff --git a/src/CrossWindow/Wayland/WaylandWindow.h b/src/CrossWindow/Wayland/WaylandWindow.h index 78cc7ae..79a7639 100644 --- a/src/CrossWindow/Wayland/WaylandWindow.h +++ b/src/CrossWindow/Wayland/WaylandWindow.h @@ -16,5 +16,32 @@ class Window bool create(const WindowDesc& desc, EventQueue& eventQueue); void close(); + + // Request that this window be minimized. + void minimize(); + + // Request that this window be maximized. + void maximize(); + + // Set callback func + void trackEventsAsync(const std::function& fun); + + // Get window description + const WindowDesc getDesc(); + + protected: + // Pointer to this window's event queue + EventQueue* mEventQueue = nullptr; + + // Executes an event callback asynchronously, use this for non-blocking + // events (resizing while rendering, etc.) + void executeEventCallback(const xwin::Event e); + + std::function mCallback; + + // Window description + WindowDesc mDesc; + + public: }; } diff --git a/src/CrossWindow/XCB/XCBWindow.cpp b/src/CrossWindow/XCB/XCBWindow.cpp index ce4d0c7..fe67f21 100644 --- a/src/CrossWindow/XCB/XCBWindow.cpp +++ b/src/CrossWindow/XCB/XCBWindow.cpp @@ -4,6 +4,62 @@ namespace xwin { Window::Window() {} +// Request that this window be minimized. +void Window::minimize() {} + +// Request that this window be maximized. +void Window::maximize() {} + +// Set callback func +void Window::trackEventsAsync( + const std::function& fun) +{ + mCallback = fun; +} + +// Executes an event callback asynchronously, use this for non-blocking +// events (resizing while rendering, etc.) +void Window::executeEventCallback(const xwin::Event e) +{ + switch (e.type) + { + case EventType::Create: + break; + case EventType::Close: + break; + case EventType::Focus: + break; + case EventType::Paint: + break; + case EventType::Resize: + break; + case EventType::DPI: + break; + case EventType::Keyboard: + break; + case EventType::MouseMove: + break; + case EventType::MouseRaw: + break; + case EventType::MouseWheel: + break; + case EventType::MouseInput: + break; + case EventType::Touch: + break; + case EventType::Gamepad: + break; + case EventType::DropFile: + break; + case EventType::HoverFile: + break; + default: + // EventType::None + // EventType::EventTypeMax + break; + } +} + bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) { const XWinState& xwinState = getXWinState(); diff --git a/src/CrossWindow/XCB/XCBWindow.h b/src/CrossWindow/XCB/XCBWindow.h index 000906e..6c28db8 100644 --- a/src/CrossWindow/XCB/XCBWindow.h +++ b/src/CrossWindow/XCB/XCBWindow.h @@ -6,6 +6,8 @@ #include +#include + namespace xwin { /** @@ -22,8 +24,35 @@ class Window // Initialize this window with the XCB API. bool create(const WindowDesc& desc, EventQueue& eventQueue); + // Request that this window be closed. void close(); + // Request that this window be minimized. + void minimize(); + + // Request that this window be maximized. + void maximize(); + + // Set callback func + void trackEventsAsync(const std::function& fun); + + // Get window description + const WindowDesc getDesc(); + + protected: + // Pointer to this window's event queue + EventQueue* mEventQueue = nullptr; + + // Executes an event callback asynchronously, use this for non-blocking + // events (resizing while rendering, etc.) + void executeEventCallback(const xwin::Event e); + + std::function mCallback; + + // Window description + WindowDesc mDesc; + + public: xcb_connection_t* connection = nullptr; xcb_screen_t* screen = nullptr; xcb_window_t window = 0; diff --git a/src/CrossWindow/XLib/XLibEventQueue.cpp b/src/CrossWindow/XLib/XLibEventQueue.cpp index 0b3942d..68947f4 100644 --- a/src/CrossWindow/XLib/XLibEventQueue.cpp +++ b/src/CrossWindow/XLib/XLibEventQueue.cpp @@ -1,6 +1,7 @@ #include "XLibEventQueue.h" #include "../Common/Window.h" #include "CrossWindow/Common/WindowDesc.h" +#include namespace xwin { @@ -27,49 +28,56 @@ bool EventQueue::empty() { return mQueue.empty(); } void EventQueue::pushEvent(const XEvent* event, Window* window) { + xwin::Event e = xwin::Event(window); // xwin::EventType::None, window); + switch (event->type) { - case ConfigureNotify: + case CreateNotify: { - WindowDesc desc = window->getDesc(); - if ((desc.width != event->xconfigure.width) || - (desc.height != event->xconfigure.height)) - { - unsigned width, height; - width = static_cast(event->xconfigure.width); - height = static_cast(event->xconfigure.height); + e = xwin::Event(xwin::EventType::Create, window); + break; + } + case ResizeRequest: + { + unsigned width, height; + width = static_cast(event->xconfigure.width); + height = static_cast(event->xconfigure.height); - mQueue.emplace(ResizeData(width, height, true), window); - } + e = xwin::Event(ResizeData(width, height, false), window); break; } - case ClientMessage: + case DestroyNotify: { - mQueue.emplace(xwin::EventType::Close, window); + e = xwin::Event(xwin::EventType::Close, window); break; } + case KeyRelease: case KeyPress: { Key d = Key::KeysMax; switch (event->xkey.keycode) { - case 0x9: // Escape + case XK_Escape: // Escape d = Key::Escape; break; case XK_KP_Left: // left arrow key d = Key::Left; break; - case 0x72: // right arrow key + case XK_KP_Right: // right arrow key d = Key::Right; break; - case 0x41: // space bar + case XK_KP_Space: // space bar d = Key::Space; break; } break; - mQueue.emplace(KeyboardData(d, ButtonState::Pressed, ModifierState()), - window); + e = xwin::Event(KeyboardData(d, + event->type == KeyPress + ? ButtonState::Pressed + : ButtonState::Released, + ModifierState()), + window); } } } diff --git a/src/CrossWindow/XLib/XLibWindow.h b/src/CrossWindow/XLib/XLibWindow.h index 65bcd0c..64c3589 100644 --- a/src/CrossWindow/XLib/XLibWindow.h +++ b/src/CrossWindow/XLib/XLibWindow.h @@ -6,6 +6,8 @@ #include +#include + typedef Window XLibWindow; namespace xwin @@ -19,9 +21,31 @@ class Window bool close(); + // Request that this window be minimized. + void minimize(); + + // Request that this window be maximized. + void maximize(); + + // Set callback func + void trackEventsAsync(const std::function& fun); + + // Get window description const WindowDesc getDesc(); + protected: + // Pointer to this window's event queue + EventQueue* mEventQueue = nullptr; + + // Executes an event callback asynchronously, use this for non-blocking + // events (resizing while rendering, etc.) + void executeEventCallback(const xwin::Event e); + + std::function mCallback; + WindowDesc mDesc; + + public: Display* display = nullptr; XLibWindow window; }; From c64c8906f3f5d47549dc4b06034b1170f136cc60 Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Tue, 24 Oct 2023 21:27:35 +0300 Subject: [PATCH 08/13] Default linux lib changed back to XCB --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 953774b..e5ab785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ if(XWIN_API STREQUAL "AUTO") elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") set(XWIN_API "COCOA" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux") - set(XWIN_API "XLIB" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) + set(XWIN_API "XCB" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) endif() endif() From 53e3de0ce88d62bfca87936d00e4762b10fb72e0 Mon Sep 17 00:00:00 2001 From: cone-forest Date: Thu, 30 Nov 2023 02:19:41 +0300 Subject: [PATCH 09/13] Somewhat working Wayland implementation --- CMakeLists.txt | 2 ++ src/CrossWindow/Common/Init.h | 4 +-- src/CrossWindow/Common/State.h | 2 -- src/CrossWindow/Main/WaylandMain.cpp | 4 +-- src/CrossWindow/Wayland/WaylandWindow.cpp | 30 ++++++++++++++++++++--- src/CrossWindow/Wayland/WaylandWindow.h | 15 +++++++----- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e5ab785..19e68a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,8 @@ elseif(XWIN_API STREQUAL "XCB") set(XWIN_API_PATH "XCB") elseif(XWIN_API STREQUAL "XLIB") set(XWIN_API_PATH "XLib") +elseif(XWIN_API STREQUAL "WAYLAND") + set(XWIN_API_PATH "Wayland") elseif(XWIN_API STREQUAL "ANDROID") set(XWIN_API_PATH "Android") elseif(XWIN_API STREQUAL "UIKIT") diff --git a/src/CrossWindow/Common/Init.h b/src/CrossWindow/Common/Init.h index 7a8c8e0..9726b62 100644 --- a/src/CrossWindow/Common/Init.h +++ b/src/CrossWindow/Common/Init.h @@ -19,8 +19,8 @@ #define MainArgsVars argc, argv, connection, screen #elif defined(XWIN_XLIB) || defined(XWIN_MIR) || defined(XWIN_WAYLAND) || \ defined(XWIN_WASM) || defined(XWIN_NOOP) -#define MainArgs int argc, const char *argv[] -#define MainArgsVars argc, argv +#define MainArgs int argc, const char** argv, struct wl_display* display +#define MainArgsVars argc, argv, display #endif namespace xwin diff --git a/src/CrossWindow/Common/State.h b/src/CrossWindow/Common/State.h index 992788a..e1fd53d 100644 --- a/src/CrossWindow/Common/State.h +++ b/src/CrossWindow/Common/State.h @@ -55,8 +55,6 @@ struct XWinState struct wl_display* display; struct wl_registry* registry; struct wl_compositor* compositor; - struct wl_surface* surface; - struct wl_list* monitors; XWinState(int argc, const char** argv, struct wl_display* display) : argc(argc), argv(argv), display(display) diff --git a/src/CrossWindow/Main/WaylandMain.cpp b/src/CrossWindow/Main/WaylandMain.cpp index 08e3c0a..9c2eff7 100644 --- a/src/CrossWindow/Main/WaylandMain.cpp +++ b/src/CrossWindow/Main/WaylandMain.cpp @@ -16,14 +16,14 @@ int main(int argc, const char** argv) nullptr, nullptr // callbacks }; struct wl_compositor* compositor; + wl_registry_add_listener(registry, ®istry_listener, &compositor); wl_display_roundtrip(display); - wl_registry_destroy(registry); xwin::init(argc, argv, display); - xmain(argc, argv); + wl_registry_destroy(registry); wl_compositor_destroy(compositor); wl_display_disconnect(display); diff --git a/src/CrossWindow/Wayland/WaylandWindow.cpp b/src/CrossWindow/Wayland/WaylandWindow.cpp index a2d5dd4..0508651 100644 --- a/src/CrossWindow/Wayland/WaylandWindow.cpp +++ b/src/CrossWindow/Wayland/WaylandWindow.cpp @@ -5,7 +5,9 @@ namespace xwin { -Window::Window() { create(); } +Window::Window() { + create(mDesc, mEventQueue); +} Window::~Window() { close(); } @@ -24,10 +26,30 @@ void Window::trackEventsAsync( // events (resizing while rendering, etc.) void Window::executeEventCallback(const xwin::Event e) { - if (mCallback) mCallback(e); + if (mCallback) { + mCallback(e); + } } -bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) {} +bool Window::create(const WindowDesc& desc, EventQueue& eventQueue) { + const XWinState &xwinState = getXWinState(); + + surface = wl_compositor_create_surface(xwinState.compositor); + if (surface == nullptr) { + return false; + } + + shell_surface = wl_shell_get_shell_surface(shell, surface); + if (shell_surface == nullptr) { + return false; + } + + wl_shell_surface_set_toplevel(shell_surface); + wl_shell_surface_set_title(shell_surface, mDesc.title.c_str()); -bool Window::close() {} + return true; +} + +void Window::close() { +} } diff --git a/src/CrossWindow/Wayland/WaylandWindow.h b/src/CrossWindow/Wayland/WaylandWindow.h index 79a7639..27ba0a0 100644 --- a/src/CrossWindow/Wayland/WaylandWindow.h +++ b/src/CrossWindow/Wayland/WaylandWindow.h @@ -4,6 +4,8 @@ #include "../Common/Init.h" #include "../Common/WindowDesc.h" +#include + namespace xwin { class Window @@ -29,19 +31,20 @@ class Window // Get window description const WindowDesc getDesc(); - protected: - // Pointer to this window's event queue - EventQueue* mEventQueue = nullptr; - + public: // Executes an event callback asynchronously, use this for non-blocking // events (resizing while rendering, etc.) void executeEventCallback(const xwin::Event e); std::function mCallback; + struct wl_surface* surface; + struct wl_shell* shell; + struct wl_shell_surface *shell_surface; + // Window description WindowDesc mDesc; - - public: + // Window's event queue + EventQueue mEventQueue; }; } From 495b83f3ec91af040e5f6dc21b799665998f59da Mon Sep 17 00:00:00 2001 From: cone-forest Date: Thu, 30 Nov 2023 04:08:21 +0300 Subject: [PATCH 10/13] Further wayland integration --- CMakeLists.txt | 41 +++++++++++++++++++++------- cmake/FindWayland.cmake | 59 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 cmake/FindWayland.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 19e68a0..2e281e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Project Info +# cmake_minimum_required(VERSION 3.18 FATAL_ERROR) cmake_policy(VERSION 3.18) project(CrossWindow) @@ -7,10 +7,10 @@ enable_language(CXX) # CMake Settings set(CMAKE_SUPPRESS_REGENERATION true) -set(DCMAKE_GENERATOR_PLATFORM "x64") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # ============================================================= @@ -22,20 +22,32 @@ set_property( STRINGS AUTO WIN32 UWP COCOA UIKIT XCB XLIB MIR WAYLAND ANDROID WASM NOOP ) -set(XWIN_OS AUTO CACHE STRING "Optional: Choose the OS to build for, defaults to AUTO, but can be WINDOWS, MACOS, LINUX, ANDROID, IOS, WASM.") +set(XWIN_OS AUTO CACHE STRING "Optional: Choose the OS to build for, defaults to AUTO, but can be WINDOWS, MACOS, LINUX, ANDROID, IOS, WASM.") set_property( CACHE XWIN_OS PROPERTY STRINGS AUTO WINDOWS MACOS LINUX ANDROID IOS WASM NOOP ) +function(deduce_linux_display_server) + if ($ENV{XDG_SESSION_TYPE} STREQUAL "wayland") + set(XWIN_API "WAYLAND" PARENT_SCOPE) + elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "X11" PARENT_SCOPE) + set(XWIN_API "XCB") + elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "") + message(SEND_ERROR "No linux display server detected") + else () + message(SEND_ERROR "Detected $ENV{XDG_SESSION_TYPE}; Supported libraries: XCB, XLIB, MIR*, WAYLAND") + endif() +endfunction() + if( NOT (XWIN_OS STREQUAL "AUTO") AND XWIN_API STREQUAL "AUTO") if(XWIN_OS STREQUAL "WINDOWS") set(XWIN_API "WIN32") elseif(XWIN_OS STREQUAL "MACOS") set(XWIN_API "COCOA") elseif(XWIN_OS STREQUAL "LINUX") - set(XWIN_API "XLIB") + deduce_linux_display_server() elseif(XWIN_OS STREQUAL "ANDROID") set(XWIN_API "ANDROID") elseif(XWIN_OS STREQUAL "IOS") @@ -51,11 +63,11 @@ endif() if(XWIN_API STREQUAL "AUTO") if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows") - set(XWIN_API "WIN32" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) + set(XWIN_API "WIN32" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Darwin") - set(XWIN_API "COCOA" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) + set(XWIN_API "COCOA" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) elseif (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux") - set(XWIN_API "XCB" CACHE STRING "A more specific platform selector to choose from, choose the exact OS protocol to use, can be WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, WASM, NOOP." FORCE) + deduce_linux_display_server() endif() endif() @@ -74,7 +86,7 @@ elseif(XWIN_API STREQUAL "XCB") elseif(XWIN_API STREQUAL "XLIB") set(XWIN_API_PATH "XLib") elseif(XWIN_API STREQUAL "WAYLAND") - set(XWIN_API_PATH "Wayland") + set(XWIN_API_PATH "Wayland") elseif(XWIN_API STREQUAL "ANDROID") set(XWIN_API_PATH "Android") elseif(XWIN_API STREQUAL "UIKIT") @@ -84,7 +96,7 @@ elseif(XWIN_API STREQUAL "WASM") elseif(XWIN_API STREQUAL "NOOP") set(XWIN_API_PATH "Noop") else() - message( SEND_ERROR "XWIN_API can only be either AUTO, NOOP, WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, or WASM.") + message( SEND_ERROR "Detected: ${XWIN_API}; XWIN_API can only be either AUTO, NOOP, WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, or WASM.") endif() message( STATUS "Building CrossWindow for " ${XWIN_API_PATH} ) @@ -212,7 +224,7 @@ elseif(XWIN_API STREQUAL "UIKIT") find_library(MOBILECORESERVICES MobileCoreServices) find_library(CFNETWORK CFNetwork) find_library(SYSTEMCONFIGURATION SystemConfiguration) - + target_link_libraries( ${PROJECT_NAME} ${UIKIT} @@ -235,6 +247,15 @@ elseif(XWIN_API STREQUAL "XLIB") target_link_libraries(${PROJECT_NAME} ${X11_LIBRARIES}) target_include_directories(${PROJECT_NAME} PUBLIC ${X11_INCLUDE_DIR}) endif() +elseif(XWIN_API STREQUAL "WAYLAND") + find_package(Wayland REQUIRED) + if(Wayland_FOUND) + message("Found Wayland Libraries.") + message("Wayland includes = ${Wayland_INCLUDE_DIRS}") + message("Wayland Libraries = ${Wayland_LIBRARIES}") + target_link_libraries(${PROJECT_NAME} ${Wayland_LIBRARIES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${Wayland_INCLUDE_DIRS}) + endif() elseif(XWIN_API STREQUAL "XCB") find_package(X11 REQUIRED) if(X11_FOUND) diff --git a/cmake/FindWayland.cmake b/cmake/FindWayland.cmake new file mode 100644 index 0000000..e9009f5 --- /dev/null +++ b/cmake/FindWayland.cmake @@ -0,0 +1,59 @@ +find_path( + WAYLAND_CLIENT_INCLUDE_DIR + NAMES wayland-client.h +) + +find_library( + WAYLAND_CLIENT_LIBRARY + NAMES wayland-client libwayland-client +) + +if(WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY) + add_library(wayland::client UNKNOWN IMPORTED) + + set_target_properties( + wayland::client PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CLIENT_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${WAYLAND_CLIENT_LIBRARY}" + ) +endif() + +find_path( + WAYLAND_SERVER_INCLUDE_DIR + NAMES wayland-server.h +) + +find_library( + WAYLAND_SERVER_LIBRARY + NAMES wayland-server libwayland-server +) + +if(WAYLAND_SERVER_INCLUDE_DIR AND WAYLAND_SERVER_LIBRARY) + add_library(wayland::server UNKNOWN IMPORTED) + + set_target_properties( + wayland::server PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_SERVER_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${WAYLAND_SERVER_LIBRARY}" + ) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + WAYLAND_CLIENT + REQUIRED_VARS WAYLAND_CLIENT_LIBRARY WAYLAND_CLIENT_INCLUDE_DIR +) + +find_package_handle_standard_args( + WAYLAND_SERVER + REQUIRED_VARS WAYLAND_SERVER_LIBRARY WAYLAND_SERVER_INCLUDE_DIR +) + +mark_as_advanced( + WAYLAND_CLIENT_INCLUDE_DIR + WAYLAND_CLIENT_LIBRARY + WAYLAND_SERVER_INCLUDE_DIR + WAYLAND_SERVER_LIBRARY +) \ No newline at end of file From 1c82b4691431e541da3400e85a6f7efb636abfd6 Mon Sep 17 00:00:00 2001 From: Michael Tsukanov Date: Wed, 27 Dec 2023 22:42:53 +0300 Subject: [PATCH 11/13] XCB compilation fixed --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e281e1..d0aaa70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,8 +32,8 @@ set_property( function(deduce_linux_display_server) if ($ENV{XDG_SESSION_TYPE} STREQUAL "wayland") set(XWIN_API "WAYLAND" PARENT_SCOPE) - elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "X11" PARENT_SCOPE) - set(XWIN_API "XCB") + elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "x11") + set(XWIN_API "XCB" PARENT_SCOPE) elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "") message(SEND_ERROR "No linux display server detected") else () From 285a837bc8931862f2dd1bebf7ca06219868c02a Mon Sep 17 00:00:00 2001 From: Michael Tsukanov <73912760+cone-forest@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:00:35 +0300 Subject: [PATCH 12/13] Create cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/cmake-multi-platform.yml diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..9ec0432 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,75 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v3 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} From 55d05307b0f8c0867ba0a91205bf1ea8bb35bc18 Mon Sep 17 00:00:00 2001 From: "Michael Tsukanov (MT6)" <73912760+cone-forest@users.noreply.github.com> Date: Mon, 3 Jun 2024 06:02:30 +0300 Subject: [PATCH 13/13] Detect linux display server (#3) Count number of wayland/X11 entries in the env CMake refactor: - xwin_add_executable ditched for variables (XMAIN_SOURCES and XWIN_DEFINITIONS) - xwin_setup removed --- CMakeLists.txt | 95 +++------ cmake/ECMFindModuleHelpersStub.cmake | 277 +++++++++++++++++++++++++++ cmake/FindWayland.cmake | 170 +++++++++++----- 3 files changed, 429 insertions(+), 113 deletions(-) create mode 100644 cmake/ECMFindModuleHelpersStub.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d0aaa70..eb81d6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,3 @@ -# cmake_minimum_required(VERSION 3.18 FATAL_ERROR) cmake_policy(VERSION 3.18) project(CrossWindow) @@ -30,14 +29,26 @@ set_property( ) function(deduce_linux_display_server) - if ($ENV{XDG_SESSION_TYPE} STREQUAL "wayland") + message( STATUS "XDG session type: " "$ENV{XDG_SESSION_TYPE}" ) + if ("$ENV{XDG_SESSION_TYPE}" STREQUAL "wayland") set(XWIN_API "WAYLAND" PARENT_SCOPE) - elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "x11") + elseif ("$ENV{XDG_SESSION_TYPE}" STREQUAL "x11") set(XWIN_API "XCB" PARENT_SCOPE) - elseif ($ENV{XDG_SESSION_TYPE} STREQUAL "") - message(SEND_ERROR "No linux display server detected") - else () - message(SEND_ERROR "Detected $ENV{XDG_SESSION_TYPE}; Supported libraries: XCB, XLIB, MIR*, WAYLAND") + else() + execute_process ( + COMMAND bash -c "env | awk -F= '$2 ~ /wayland/ {count++} END {print count}'" + OUTPUT_VARIABLE WaylandCount + ) + execute_process ( + COMMAND bash -c "env | awk -F= '$2 ~ /x11/ {count++} END {print count}'" + OUTPUT_VARIABLE X11Count + ) + message(STATUS "Display server counts: " ${X11Count} " " ${WaylandCount}) + if (${X11Count} LESS ${WaylandCount}) + set(XWIN_API "WAYLAND" PARENT_SCOPE) + else() + set(XWIN_API "XCB" PARENT_SCOPE) + endif() endif() endfunction() @@ -96,7 +107,7 @@ elseif(XWIN_API STREQUAL "WASM") elseif(XWIN_API STREQUAL "NOOP") set(XWIN_API_PATH "Noop") else() - message( SEND_ERROR "Detected: ${XWIN_API}; XWIN_API can only be either AUTO, NOOP, WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, or WASM.") + message( SEND_ERROR "Detected: ${XWIN_API}; XWIN_API can only be either AUTO, NOOP, WIN32, UWP, COCOA, UIKIT, XCB, XLIB, MIR, WAYLAND, ANDROID, or WASM.") endif() message( STATUS "Building CrossWindow for " ${XWIN_API_PATH} ) @@ -137,57 +148,7 @@ set(XMAIN_SOURCES ${MAIN_SOURCES} CACHE STRING "Global Variable - The source fil # ============================================================= -# CrossWindow Functions -function(xwin_setup versionMajor versionMinor versionPatch versionRevision companyName iconPath) - # @TODO - implement - message("Warning: xwin_setup has not yet been implemented.") -endfunction() - -function(xwin_add_executable targetProject targetSources) - message("Creating CrossWindow executable:") - - foreach(source IN LISTS XMAIN_SOURCES) - source_group("" FILES "${source}") - endforeach() - set(XWIN_FILES "${XMAIN_SOURCES}" "${targetSources}") - - if(XWIN_API STREQUAL "WIN32" OR XWIN_API STREQUAL "UWP") - add_executable( - ${targetProject} - WIN32 - "${XWIN_FILES}" - ) - elseif(XWIN_API STREQUAL "COCOA" OR XWIN_API STREQUAL "UIKIT") - add_executable( - ${targetProject} - MACOSX_BUNDLE - ${XWIN_FILES} - ) - elseif(XWIN_API STREQUAL "XCB" OR XWIN_API STREQUAL "XLIB") - add_executable( - ${targetProject} - ${XWIN_FILES} - ) - elseif(XWIN_API STREQUAL "ANDROID") - add_executable( - ${targetProject} - ${XWIN_FILES} - ) - elseif(XWIN_API STREQUAL "WASM") - add_executable( - ${targetProject} - ${XWIN_FILES} - ) - elseif(XWIN_API STREQUAL "NOOP") - add_executable( - ${targetProject} - ${XWIN_FILES} - ) - endif() - - target_compile_definitions(${targetProject} PRIVATE XWIN_${XWIN_API}=1) - -endfunction() +set(XWIN_DEFINITIONS XWIN_${XWIN_API}=1 CACHE STRING "Global Variable - The compile definitions for the currently active protocol.") # ============================================================= @@ -248,22 +209,22 @@ elseif(XWIN_API STREQUAL "XLIB") target_include_directories(${PROJECT_NAME} PUBLIC ${X11_INCLUDE_DIR}) endif() elseif(XWIN_API STREQUAL "WAYLAND") - find_package(Wayland REQUIRED) - if(Wayland_FOUND) + find_package(Wayland REQUIRED) + if(Wayland_FOUND) message("Found Wayland Libraries.") message("Wayland includes = ${Wayland_INCLUDE_DIRS}") message("Wayland Libraries = ${Wayland_LIBRARIES}") target_link_libraries(${PROJECT_NAME} ${Wayland_LIBRARIES}) target_include_directories(${PROJECT_NAME} PUBLIC ${Wayland_INCLUDE_DIRS}) - endif() + endif() elseif(XWIN_API STREQUAL "XCB") find_package(X11 REQUIRED) if(X11_FOUND) - message("Found XCB Libraries.") - message("XCB Include Path = ${X11_xcb_INCLUDE_PATH}") - message("XCB Lib = ${X11_xcb_LIB}") - target_link_libraries(${PROJECT_NAME} ${X11_xcb_LIB}) - target_include_directories(${PROJECT_NAME} PUBLIC ${X11_xcb_INCLUDE_PATH}) + message("Found XCB Libraries.") + message("XCB Include Path = ${X11_xcb_INCLUDE_PATH}") + message("XCB Lib = ${X11_xcb_LIB}") + target_link_libraries(${PROJECT_NAME} ${X11_xcb_LIB}) + target_include_directories(${PROJECT_NAME} PUBLIC ${X11_xcb_INCLUDE_PATH}) endif() endif() # ============================================================= diff --git a/cmake/ECMFindModuleHelpersStub.cmake b/cmake/ECMFindModuleHelpersStub.cmake new file mode 100644 index 0000000..92c796e --- /dev/null +++ b/cmake/ECMFindModuleHelpersStub.cmake @@ -0,0 +1,277 @@ +# SPDX-FileCopyrightText: 2014 Alex Merry +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +ECMFindModuleHelpers +-------------------- + +Helper macros for find modules: ``ecm_find_package_version_check()``, +``ecm_find_package_parse_components()`` and +``ecm_find_package_handle_library_components()``. + +:: + + ecm_find_package_version_check() + +Prints warnings if the CMake version or the project's required CMake version +is older than that required by extra-cmake-modules. + +:: + + ecm_find_package_parse_components( + RESULT_VAR + KNOWN_COMPONENTS [ [...]] + [SKIP_DEPENDENCY_HANDLING]) + +This macro will populate with a list of components found in +_FIND_COMPONENTS, after checking that all those components are in the +list of ``KNOWN_COMPONENTS``; if there are any unknown components, it will print +an error or warning (depending on the value of _FIND_REQUIRED) and call +``return()``. + +The order of components in is guaranteed to match the order they +are listed in the ``KNOWN_COMPONENTS`` argument. + +If ``SKIP_DEPENDENCY_HANDLING`` is not set, for each component the variable +__component_deps will be checked for dependent components. +If is listed in _FIND_COMPONENTS, then all its (transitive) +dependencies will also be added to . + +:: + + ecm_find_package_handle_library_components( + COMPONENTS [ [...]] + [SKIP_DEPENDENCY_HANDLING]) + [SKIP_PKG_CONFIG]) + +Creates an imported library target for each component. The operation of this +macro depends on the presence of a number of CMake variables. + +The __lib variable should contain the name of this library, +and __header variable should contain the name of a header +file associated with it (whatever relative path is normally passed to +'#include'). __header_subdir variable can be used to specify +which subdirectory of the include path the headers will be found in. +``ecm_find_package_components()`` will then search for the library +and include directory (creating appropriate cache variables) and create an +imported library target named ::. + +Additional variables can be used to provide additional information: + +If ``SKIP_PKG_CONFIG``, the __pkg_config variable is set, and +pkg-config is found, the pkg-config module given by +__pkg_config will be searched for and used to help locate the +library and header file. It will also be used to set +__VERSION. + +Note that if version information is found via pkg-config, +__FIND_VERSION can be set to require a particular version +for each component. + +If ``SKIP_DEPENDENCY_HANDLING`` is not set, the ``INTERFACE_LINK_LIBRARIES`` property +of the imported target for will be set to contain the imported +targets for the components listed in __component_deps. +_FOUND will also be set to ``FALSE`` if any of the components in +__component_deps are not found. This requires the components +in __component_deps to be listed before in the +``COMPONENTS`` argument. + +The following variables will be set: + +``_TARGETS`` + the imported targets +``_LIBRARIES`` + the found libraries +``_INCLUDE_DIRS`` + the combined required include directories for the components +``_DEFINITIONS`` + the "other" CFLAGS provided by pkg-config, if any +``_VERSION`` + the value of ``__VERSION`` for the first component that + has this variable set (note that components are searched for in the order + they are passed to the macro), although if it is already set, it will not + be altered + +.. note:: + These variables are never cleared, so if + ``ecm_find_package_handle_library_components()`` is called multiple times with + different components (typically because of multiple ``find_package()`` calls) then + ``_TARGETS``, for example, will contain all the targets found in any + call (although no duplicates). + +Since pre-1.0.0. +#]=======================================================================] + +macro(ecm_find_package_version_check module_name) + if(CMAKE_VERSION VERSION_LESS 3.16.0) + message(FATAL_ERROR "CMake 3.16.0 is required by Find${module_name}.cmake") + endif() + if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0) + message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use Find${module_name}.cmake") + endif() +endmacro() + +macro(ecm_find_package_parse_components module_name) + set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING) + set(ecm_fppc_oneValueArgs RESULT_VAR) + set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS) + cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN}) + + if(ECM_FPPC_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}") + endif() + if(NOT ECM_FPPC_RESULT_VAR) + message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components") + endif() + if(NOT ECM_FPPC_KNOWN_COMPONENTS) + message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components") + endif() + if(NOT ECM_FPPC_DEFAULT_COMPONENTS) + set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS}) + endif() + + if(${module_name}_FIND_COMPONENTS) + set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS}) + + if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING) + # Make sure deps are included + foreach(ecm_fppc_comp ${ecm_fppc_requestedComps}) + foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps}) + list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index) + if("${ecm_fppc_index}" STREQUAL "-1") + if(NOT ${module_name}_FIND_QUIETLY) + message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}") + endif() + list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}") + endif() + endforeach() + endforeach() + else() + message(STATUS "Skipping dependency handling for ${module_name}") + endif() + list(REMOVE_DUPLICATES ecm_fppc_requestedComps) + + # This makes sure components are listed in the same order as + # KNOWN_COMPONENTS (potentially important for inter-dependencies) + set(${ECM_FPPC_RESULT_VAR}) + foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS}) + list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index) + if(NOT "${ecm_fppc_index}" STREQUAL "-1") + list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}") + list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index}) + endif() + endforeach() + # if there are any left, they are unknown components + if(ecm_fppc_requestedComps) + set(ecm_fppc_msgType STATUS) + if(${module_name}_FIND_REQUIRED) + set(ecm_fppc_msgType FATAL_ERROR) + endif() + if(NOT ${module_name}_FIND_QUIETLY) + message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}") + endif() + return() + endif() + else() + set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS}) + endif() +endmacro() + +macro(ecm_find_package_handle_library_components module_name) + set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING) + set(ecm_fpwc_oneValueArgs) + set(ecm_fpwc_multiValueArgs COMPONENTS) + cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN}) + + if(ECM_FPWC_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}") + endif() + if(NOT ECM_FPWC_COMPONENTS) + message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components") + endif() + + include(FindPackageHandleStandardArgs) + find_package(PkgConfig QUIET) + foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS}) + set(ecm_fpwc_dep_vars) + set(ecm_fpwc_dep_targets) + if(NOT SKIP_DEPENDENCY_HANDLING) + foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps}) + list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND") + list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}") + endforeach() + endif() + + if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config) + pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET + ${${module_name}_${ecm_fpwc_comp}_pkg_config}) + endif() + + find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR + NAMES ${${module_name}_${ecm_fpwc_comp}_header} + HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS} + PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir} + ) + find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY + NAMES ${${module_name}_${ecm_fpwc_comp}_lib} + HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS} + ) + + set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}") + if(NOT ${module_name}_VERSION) + set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION}) + endif() + + set(FPHSA_NAME_MISMATCHED 1) + find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp} + FOUND_VAR + ${module_name}_${ecm_fpwc_comp}_FOUND + REQUIRED_VARS + ${module_name}_${ecm_fpwc_comp}_LIBRARY + ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR + ${ecm_fpwc_dep_vars} + VERSION_VAR + ${module_name}_${ecm_fpwc_comp}_VERSION + ) + unset(FPHSA_NAME_MISMATCHED) + + mark_as_advanced( + ${module_name}_${ecm_fpwc_comp}_LIBRARY + ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR + ) + + if(${module_name}_${ecm_fpwc_comp}_FOUND) + list(APPEND ${module_name}_LIBRARIES + "${${module_name}_${ecm_fpwc_comp}_LIBRARY}") + list(APPEND ${module_name}_INCLUDE_DIRS + "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}") + set(${module_name}_DEFINITIONS + ${${module_name}_DEFINITIONS} + ${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}) + if(NOT TARGET ${module_name}::${ecm_fpwc_comp}) + add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED) + set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES + IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}" + ) + endif() + list(APPEND ${module_name}_TARGETS + "${module_name}::${ecm_fpwc_comp}") + endif() + endforeach() + if(${module_name}_LIBRARIES) + list(REMOVE_DUPLICATES ${module_name}_LIBRARIES) + endif() + if(${module_name}_INCLUDE_DIRS) + list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS) + endif() + if(${module_name}_DEFINITIONS) + list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS) + endif() + if(${module_name}_TARGETS) + list(REMOVE_DUPLICATES ${module_name}_TARGETS) + endif() +endmacro() diff --git a/cmake/FindWayland.cmake b/cmake/FindWayland.cmake index e9009f5..926fd48 100644 --- a/cmake/FindWayland.cmake +++ b/cmake/FindWayland.cmake @@ -1,59 +1,137 @@ -find_path( - WAYLAND_CLIENT_INCLUDE_DIR - NAMES wayland-client.h -) +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2014 Martin Gräßlin +# +# SPDX-License-Identifier: BSD-3-Clause -find_library( - WAYLAND_CLIENT_LIBRARY - NAMES wayland-client libwayland-client -) +#[=======================================================================[.rst: +FindWayland +----------- -if(WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY) - add_library(wayland::client UNKNOWN IMPORTED) +Try to find Wayland. - set_target_properties( - wayland::client PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CLIENT_INCLUDE_DIR}" - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${WAYLAND_CLIENT_LIBRARY}" - ) -endif() +This is a component-based find module, which makes use of the COMPONENTS +and OPTIONAL_COMPONENTS arguments to find_module. The following components +are available:: -find_path( - WAYLAND_SERVER_INCLUDE_DIR - NAMES wayland-server.h -) + Client Server Cursor Egl + +If no components are specified, this module will act as though all components +were passed to OPTIONAL_COMPONENTS. + +This module will define the following variables, independently of the +components searched for or found: + +``Wayland_FOUND`` + TRUE if (the requested version of) Wayland is available +``Wayland_VERSION`` + Found Wayland version +``Wayland_TARGETS`` + A list of all targets imported by this module (note that there may be more + than the components that were requested) +``Wayland_LIBRARIES`` + This can be passed to target_link_libraries() instead of the imported + targets +``Wayland_INCLUDE_DIRS`` + This should be passed to target_include_directories() if the targets are + not used for linking +``Wayland_DEFINITIONS`` + This should be passed to target_compile_options() if the targets are not + used for linking +``Wayland_DATADIR`` + The core wayland protocols data directory + Since 5.73.0 + +For each searched-for components, ``Wayland__FOUND`` will be set to +TRUE if the corresponding Wayland library was found, and FALSE otherwise. If +``Wayland__FOUND`` is TRUE, the imported target +``Wayland::`` will be defined. This module will also attempt to +determine ``Wayland_*_VERSION`` variables for each imported target, although +``Wayland_VERSION`` should normally be sufficient. + +In general we recommend using the imported targets, as they are easier to use +and provide more control. Bear in mind, however, that if any target is in the +link interface of an exported library, it must be made available by the +package config file. -find_library( - WAYLAND_SERVER_LIBRARY - NAMES wayland-server libwayland-server +Since pre-1.0.0. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) + +ecm_find_package_version_check(Wayland) + +set(Wayland_known_components + Client + Server + Cursor + Egl ) +foreach(_comp ${Wayland_known_components}) + string(TOLOWER "${_comp}" _lc_comp) + set(Wayland_${_comp}_component_deps) + set(Wayland_${_comp}_pkg_config "wayland-${_lc_comp}") + set(Wayland_${_comp}_lib "wayland-${_lc_comp}") + set(Wayland_${_comp}_header "wayland-${_lc_comp}.h") +endforeach() +set(Wayland_Egl_component_deps Client) -if(WAYLAND_SERVER_INCLUDE_DIR AND WAYLAND_SERVER_LIBRARY) - add_library(wayland::server UNKNOWN IMPORTED) +ecm_find_package_parse_components(Wayland + RESULT_VAR Wayland_components + KNOWN_COMPONENTS ${Wayland_known_components} +) +ecm_find_package_handle_library_components(Wayland + COMPONENTS ${Wayland_components} +) - set_target_properties( - wayland::server PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_SERVER_INCLUDE_DIR}" - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${WAYLAND_SERVER_LIBRARY}" - ) +# If pkg-config didn't provide us with version information, +# try to extract it from wayland-version.h +# (Note that the version from wayland-egl.pc will probably be +# the Mesa version, rather than the Wayland version, but that +# version will be ignored as we always find wayland-client.pc +# first). +if(NOT Wayland_VERSION) + find_file(Wayland_VERSION_HEADER + NAMES wayland-version.h + HINTS ${Wayland_INCLUDE_DIRS} + ) + mark_as_advanced(Wayland_VERSION_HEADER) + if(Wayland_VERSION_HEADER) + file(READ ${Wayland_VERSION_HEADER} _wayland_version_header_contents) + string(REGEX REPLACE + "^.*[ \t]+WAYLAND_VERSION[ \t]+\"([0-9.]*)\".*$" + "\\1" + Wayland_VERSION + "${_wayland_version_header_contents}" + ) + unset(_wayland_version_header_contents) + endif() endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - WAYLAND_CLIENT - REQUIRED_VARS WAYLAND_CLIENT_LIBRARY WAYLAND_CLIENT_INCLUDE_DIR +find_package_handle_standard_args(Wayland + FOUND_VAR + Wayland_FOUND + REQUIRED_VARS + Wayland_LIBRARIES + VERSION_VAR + Wayland_VERSION + HANDLE_COMPONENTS ) -find_package_handle_standard_args( - WAYLAND_SERVER - REQUIRED_VARS WAYLAND_SERVER_LIBRARY WAYLAND_SERVER_INCLUDE_DIR -) +pkg_get_variable(Wayland_DATADIR wayland-scanner pkgdatadir) +if (CMAKE_CROSSCOMPILING AND (NOT EXISTS "${Wayland_DATADIR}/wayland.xml")) + # PKG_CONFIG_SYSROOT_DIR only applies to -I and -L flags, so pkg-config + # does not prepend CMAKE_SYSROOT when cross-compiling unless you pass + # --define-prefix explicitly. Therefore we have to manually do prepend + # it here when cross-compiling. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/16647#note_844761 + set(Wayland_DATADIR ${CMAKE_SYSROOT}${Wayland_DATADIR}) +endif() +if (NOT EXISTS "${Wayland_DATADIR}/wayland.xml") + message(WARNING "Could not find wayland.xml in ${Wayland_DATADIR}") +endif() -mark_as_advanced( - WAYLAND_CLIENT_INCLUDE_DIR - WAYLAND_CLIENT_LIBRARY - WAYLAND_SERVER_INCLUDE_DIR - WAYLAND_SERVER_LIBRARY -) \ No newline at end of file +include(FeatureSummary) +set_package_properties(Wayland PROPERTIES + URL "https://wayland.freedesktop.org/" + DESCRIPTION "C library implementation of the Wayland protocol: a protocol for a compositor to talk to its clients" +)