diff options
| author | srdusr <trevorgray@srdusr.com> | 2025-09-26 12:23:19 +0200 |
|---|---|---|
| committer | srdusr <trevorgray@srdusr.com> | 2025-09-26 12:23:19 +0200 |
| commit | e4a0432383331e013808a97b7c24707e4ddc4726 (patch) | |
| tree | 3ef4465be03bc7b92a0b048f02f76475045404b6 /docs/PLATFORM_IMPLEMENTATION.md | |
| parent | 105732dde10b317a81d5a10a3f66b315d6f85015 (diff) | |
| download | srdwm-e4a0432383331e013808a97b7c24707e4ddc4726.tar.gz srdwm-e4a0432383331e013808a97b7c24707e4ddc4726.zip | |
Initial Commit
Diffstat (limited to 'docs/PLATFORM_IMPLEMENTATION.md')
| -rw-r--r-- | docs/PLATFORM_IMPLEMENTATION.md | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/docs/PLATFORM_IMPLEMENTATION.md b/docs/PLATFORM_IMPLEMENTATION.md new file mode 100644 index 0000000..4e70767 --- /dev/null +++ b/docs/PLATFORM_IMPLEMENTATION.md @@ -0,0 +1,636 @@ +# SRDWM Platform Implementation Guide + +## Overview +This document outlines the proper implementation approach for each platform, recognizing that **Wayland/XWayland is fundamentally different from X11** and requires completely different technologies and approaches. + +## Platform Architecture Differences + +### Linux: X11 vs Wayland +- **X11**: Traditional X11 window management with Xlib/XCB +- **Wayland**: Modern display protocol requiring wlroots or similar compositor framework +- **XWayland**: X11 applications running on Wayland (requires special handling) + +### Windows vs macOS vs Linux +- **Windows**: Win32 API with global hooks and window subclassing +- **macOS**: Core Graphics/AppKit with accessibility APIs and event taps +- **Linux**: X11 or Wayland with different event systems + +## Linux Implementation + +### X11 Backend +```cpp +// X11-specific implementation using Xlib/XCB +class X11Platform : public Platform { +private: + Display* display_; + Window root_; + std::map<Window, Window*> window_map_; + +public: + bool initialize() override { + display_ = XOpenDisplay(nullptr); + if (!display_) return false; + + root_ = DefaultRootWindow(display_); + setup_event_handling(); + return true; + } + + void setup_event_handling() { + // X11 event masks and handlers + XSelectInput(display_, root_, + SubstructureRedirectMask | SubstructureNotifyMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask); + } + + bool poll_events(std::vector<Event>& events) override { + XEvent xevent; + while (XPending(display_)) { + XNextEvent(display_, &xevent); + convert_x11_event(xevent, events); + } + return true; + } + + void convert_x11_event(const XEvent& xevent, std::vector<Event>& events) { + switch (xevent.type) { + case MapRequest: + handle_map_request(xevent.xmaprequest); + break; + case ConfigureRequest: + handle_configure_request(xevent.xconfigurerequest); + break; + case KeyPress: + handle_key_press(xevent.xkey); + break; + // ... other event types + } + } +}; +``` + +### Wayland Backend (using wlroots) +```cpp +// Wayland implementation using wlroots +class WaylandPlatform : public Platform { +private: + struct wl_display* display_; + struct wlroots_backend* backend_; + struct wlroots_compositor* compositor_; + struct wlroots_output* output_; + struct wlroots_input_device* input_device_; + +public: + bool initialize() override { + // Initialize wlroots backend + backend_ = wlroots_backend_create(); + if (!backend_) return false; + + // Create compositor + compositor_ = wlroots_compositor_create(backend_); + if (!compositor_) return false; + + // Setup output and input + setup_output(); + setup_input(); + + return true; + } + + void setup_output() { + // Create and configure output + output_ = wlroots_output_create(compositor_); + wlroots_output_set_mode(output_, 1920, 1080, 60); + wlroots_output_commit(output_); + } + + void setup_input() { + // Setup input devices + input_device_ = wlroots_input_device_create(compositor_); + wlroots_input_device_set_capabilities(input_device_, + WLROOTS_INPUT_DEVICE_CAP_KEYBOARD | + WLROOTS_INPUT_DEVICE_CAP_POINTER); + } + + bool poll_events(std::vector<Event>& events) override { + // wlroots event loop + wlroots_backend_dispatch(backend_); + + // Process wlroots events + struct wlroots_event* event; + while ((event = wlroots_backend_get_event(backend_))) { + convert_wlroots_event(event, events); + wlroots_event_destroy(event); + } + + return true; + } + + void convert_wlroots_event(struct wlroots_event* event, std::vector<Event>& events) { + switch (wlroots_event_get_type(event)) { + case WLROOTS_EVENT_NEW_SURFACE: + handle_new_surface(event); + break; + case WLROOTS_EVENT_SURFACE_COMMIT: + handle_surface_commit(event); + break; + case WLROOTS_EVENT_KEYBOARD_KEY: + handle_keyboard_key(event); + break; + // ... other event types + } + } +}; +``` + +### XWayland Support +```cpp +// XWayland support for running X11 apps on Wayland +class XWaylandManager { +private: + struct wlroots_xwayland* xwayland_; + struct wlroots_xwayland_server* xwayland_server_; + +public: + bool initialize(struct wlroots_compositor* compositor) { + // Create XWayland server + xwayland_server_ = wlroots_xwayland_server_create(compositor); + if (!xwayland_server_) return false; + + // Setup XWayland + xwayland_ = wlroots_xwayland_create(xwayland_server_); + if (!xwayland_) return false; + + return true; + } + + void handle_xwayland_surface(struct wlroots_surface* surface) { + // Handle X11 windows running on Wayland + // These need special treatment for proper integration + } +}; +``` + +## Windows Implementation + +### Win32 API Integration +```cpp +// Windows implementation using Win32 API +class WindowsPlatform : public Platform { +private: + HINSTANCE h_instance_; + std::map<HWND, Window*> window_map_; + HHOOK keyboard_hook_; + HHOOK mouse_hook_; + +public: + bool initialize() override { + h_instance_ = GetModuleHandle(nullptr); + + // Register window class + if (!register_window_class()) return false; + + // Setup global hooks + setup_global_hooks(); + + return true; + } + + bool register_window_class() { + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = window_proc; + wc.hInstance = h_instance_; + wc.lpszClassName = L"SRDWM_Window"; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + + return RegisterClassEx(&wc) != 0; + } + + void setup_global_hooks() { + // Global keyboard hook + keyboard_hook_ = SetWindowsHookEx(WH_KEYBOARD_LL, + keyboard_proc, h_instance_, 0); + + // Global mouse hook + mouse_hook_ = SetWindowsHookEx(WH_MOUSE_LL, + mouse_proc, h_instance_, 0); + } + + static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, + WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_CREATE: + // Handle window creation + break; + case WM_DESTROY: + // Handle window destruction + break; + case WM_SIZE: + // Handle window resizing + break; + // ... other messages + } + return DefWindowProc(hwnd, msg, wparam, lparam); + } + + static LRESULT CALLBACK keyboard_proc(int nCode, WPARAM wparam, LPARAM lparam) { + if (nCode >= 0) { + KBDLLHOOKSTRUCT* kbhs = (KBDLLHOOKSTRUCT*)lparam; + // Handle global keyboard events + handle_global_keyboard(wparam, kbhs); + } + return CallNextHookEx(nullptr, nCode, wparam, lparam); + } + + static LRESULT CALLBACK mouse_proc(int nCode, WPARAM wparam, LPARAM lparam) { + if (nCode >= 0) { + MSLLHOOKSTRUCT* mhs = (MSLLHOOKSTRUCT*)lparam; + // Handle global mouse events + handle_global_mouse(wparam, mhs); + } + return CallNextHookEx(nullptr, nCode, wparam, lparam); + } +}; +``` + +## macOS Implementation + +### Core Graphics/AppKit Integration +```cpp +// macOS implementation using Core Graphics and AppKit +class MacOSPlatform : public Platform { +private: + CGEventTap event_tap_; + std::map<CGWindowID, Window*> window_map_; + +public: + bool initialize() override { + // Request accessibility permissions + if (!request_accessibility_permissions()) return false; + + // Setup event tap + setup_event_tap(); + + // Setup window monitoring + setup_window_monitoring(); + + return true; + } + + bool request_accessibility_permissions() { + // Check if accessibility is enabled + const void* keys[] = { kAXTrustedCheckOptionPrompt }; + const void* values[] = { kCFBooleanTrue }; + + CFDictionaryRef options = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, 1, nullptr, nullptr); + + bool trusted = AXIsProcessTrustedWithOptions(options); + CFRelease(options); + + return trusted; + } + + void setup_event_tap() { + // Create event tap for global events + event_tap_ = CGEventTapCreate( + kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(kCGEventKeyDown) | + CGEventMaskBit(kCGEventKeyUp) | + CGEventMaskBit(kCGEventLeftMouseDown) | + CGEventMaskBit(kCGEventLeftMouseUp) | + CGEventMaskBit(kCGEventMouseMoved), + event_tap_callback, + this); + + if (event_tap_) { + CFRunLoopSourceRef run_loop_source = + CFMachPortCreateRunLoopSource(kCFAllocatorDefault, event_tap_, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source, kCFRunLoopCommonModes); + CGEventTapEnable(event_tap_, true); + } + } + + static CGEventRef event_tap_callback(CGEventTapProxy proxy, CGEventType type, + CGEventRef event, void* user_info) { + MacOSPlatform* platform = static_cast<MacOSPlatform*>(user_info); + return platform->handle_event_tap(proxy, type, event); + } + + CGEventRef handle_event_tap(CGEventTapProxy proxy, CGEventType type, CGEventRef event) { + switch (type) { + case kCGEventKeyDown: + handle_key_event(event, true); + break; + case kCGEventKeyUp: + handle_key_event(event, false); + break; + case kCGEventLeftMouseDown: + handle_mouse_event(event, true); + break; + case kCGEventLeftMouseUp: + handle_mouse_event(event, false); + break; + case kCGEventMouseMoved: + handle_mouse_motion(event); + break; + } + return event; + } + + void setup_window_monitoring() { + // Monitor window creation/destruction + CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | + kCGWindowListExcludeDesktopElements, + kCGNullWindowID); + } +}; +``` + +## Platform Detection and Selection + +### Automatic Platform Detection +```cpp +// Platform factory with automatic detection +class PlatformFactory { +public: + static std::unique_ptr<Platform> create_platform() { + #ifdef _WIN32 + return std::make_unique<WindowsPlatform>(); + #elif defined(__APPLE__) + return std::make_unique<MacOSPlatform>(); + #else + // Linux: detect X11 vs Wayland + return detect_linux_platform(); + #endif + } + +private: + static std::unique_ptr<Platform> detect_linux_platform() { + // Check environment variables + const char* wayland_display = std::getenv("WAYLAND_DISPLAY"); + const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE"); + + if (wayland_display || (xdg_session_type && strcmp(xdg_session_type, "wayland") == 0)) { + // Try Wayland first + auto wayland_platform = std::make_unique<WaylandPlatform>(); + if (wayland_platform->initialize()) { + std::cout << "Using Wayland backend" << std::endl; + return wayland_platform; + } + std::cout << "Wayland initialization failed, falling back to X11" << std::endl; + } + + // Fall back to X11 + auto x11_platform = std::make_unique<X11Platform>(); + if (x11_platform->initialize()) { + std::cout << "Using X11 backend" << std::endl; + return x11_platform; + } + + std::cerr << "Failed to initialize any platform backend" << std::endl; + return nullptr; + } +}; +``` + +## Dependencies and Build System + +### CMake Configuration +```cmake +# Platform-specific dependencies +if(WIN32) + # Windows dependencies + find_package(PkgConfig REQUIRED) + set(PLATFORM_LIBS user32 gdi32) + +elseif(APPLE) + # macOS dependencies + find_library(COCOA_LIBRARY Cocoa) + find_library(CARBON_LIBRARY Carbon) + find_library(IOKIT_LIBRARY IOKit) + set(PLATFORM_LIBS ${COCOA_LIBRARY} ${CARBON_LIBRARY} ${IOKIT_LIBRARY}) + +else() + # Linux dependencies + find_package(PkgConfig REQUIRED) + + # X11 dependencies + pkg_check_modules(X11 REQUIRED x11 xcb xcb-keysyms) + + # Wayland dependencies (optional) + pkg_check_modules(WAYLAND wayland-client wayland-cursor) + pkg_check_modules(WLROOTS wlroots) + + if(WAYLAND_FOUND AND WLROOTS_FOUND) + add_definitions(-DWAYLAND_ENABLED) + set(PLATFORM_LIBS ${PLATFORM_LIBS} ${WAYLAND_LIBRARIES} ${WLROOTS_LIBRARIES}) + endif() + + set(PLATFORM_LIBS ${PLATFORM_LIBS} ${X11_LIBRARIES}) +endif() +``` + +### Package Dependencies +```bash +# Ubuntu/Debian +sudo apt install libx11-dev libxcb1-dev libxcb-keysyms1-dev +sudo apt install libwayland-dev libwlroots-dev + +# Arch Linux +sudo pacman -S xorg-server-devel wayland wlroots + +# Fedora +sudo dnf install libX11-devel libxcb-devel wayland-devel wlroots-devel +``` + +## Event Handling Differences + +### X11 Event System +```cpp +// X11 events are synchronous and direct +void X11Platform::handle_map_request(const XMapRequestEvent& event) { + Window* window = create_window(event.window); + if (window) { + window_map_[event.window] = window; + // X11 window is now managed + } +} +``` + +### Wayland Event System +```cpp +// Wayland events are asynchronous and callback-based +void WaylandPlatform::handle_new_surface(struct wlroots_event* event) { + struct wlroots_surface* surface = wlroots_event_get_surface(event); + + // Create window for new surface + Window* window = create_window_from_surface(surface); + if (window) { + surface_window_map_[surface] = window; + } +} +``` + +### Windows Event System +```cpp +// Windows uses message-based event system +LRESULT WindowsPlatform::window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_CREATE: + // Window creation + break; + case WM_DESTROY: + // Window destruction + break; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} +``` + +### macOS Event System +```cpp +// macOS uses event taps and accessibility APIs +CGEventRef MacOSPlatform::handle_event_tap(CGEventTapProxy proxy, CGEventType type, CGEventRef event) { + switch (type) { + case kCGEventKeyDown: + // Handle key press + break; + case kCGEventMouseMoved: + // Handle mouse movement + break; + } + return event; +} +``` + +## Window Management Differences + +### X11 Window Management +```cpp +// X11: Direct window manipulation +void X11Platform::set_window_position(Window* window, int x, int y) { + XMoveWindow(display_, window->get_x11_handle(), x, y); +} + +void X11Platform::set_window_size(Window* window, int width, int height) { + XResizeWindow(display_, window->get_x11_handle(), width, height); +} +``` + +### Wayland Window Management +```cpp +// Wayland: Surface-based management +void WaylandPlatform::set_window_position(Window* window, int x, int y) { + struct wlroots_surface* surface = window->get_wayland_surface(); + wlroots_surface_set_position(surface, x, y); +} + +void WaylandPlatform::set_window_size(Window* window, int width, int height) { + struct wlroots_surface* surface = window->get_wayland_surface(); + wlroots_surface_set_size(surface, width, height); +} +``` + +### Windows Window Management +```cpp +// Windows: Win32 API calls +void WindowsPlatform::set_window_position(Window* window, int x, int y) { + HWND hwnd = window->get_win32_handle(); + SetWindowPos(hwnd, nullptr, x, y, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); +} + +void WindowsPlatform::set_window_size(Window* window, int width, int height) { + HWND hwnd = window->get_win32_handle(); + SetWindowPos(hwnd, nullptr, 0, 0, width, height, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); +} +``` + +### macOS Window Management +```cpp +// macOS: Core Graphics API calls +void MacOSPlatform::set_window_position(Window* window, int x, int y) { + CGWindowID window_id = window->get_macos_window_id(); + CGPoint position = CGPointMake(x, y); + + // Use accessibility APIs to move window + AXUIElementRef element = AXUIElementCreateApplication( + window->get_macos_pid()); + + if (element) { + AXUIElementSetAttributeValue(element, kAXPositionAttribute, &position); + CFRelease(element); + } +} +``` + +## Testing and Validation + +### Platform-Specific Testing +```cpp +// Test each platform independently +class PlatformTest { +public: + static void test_x11_platform() { + auto platform = std::make_unique<X11Platform>(); + assert(platform->initialize()); + // Test X11-specific functionality + } + + static void test_wayland_platform() { + auto platform = std::make_unique<WaylandPlatform>(); + assert(platform->initialize()); + // Test Wayland-specific functionality + } + + static void test_windows_platform() { + auto platform = std::make_unique<WindowsPlatform>(); + assert(platform->initialize()); + // Test Windows-specific functionality + } + + static void test_macos_platform() { + auto platform = std::make_unique<MacOSPlatform>(); + assert(platform->initialize()); + // Test macOS-specific functionality + } +}; +``` + +## Best Practices + +### 1. **Platform Abstraction** +- Keep platform-specific code isolated +- Use common interfaces for cross-platform functionality +- Implement platform detection automatically + +### 2. **Wayland vs X11** +- **Never mix X11 and Wayland APIs** +- Use wlroots for Wayland (don't implement from scratch) +- Handle XWayland as a special case within Wayland + +### 3. **Event Handling** +- Respect each platform's event model +- Don't force synchronous behavior on asynchronous platforms +- Handle platform-specific quirks gracefully + +### 4. **Window Management** +- Use platform-native APIs for best performance +- Don't try to emulate one platform's behavior on another +- Handle platform-specific window states properly + +### 5. **Testing** +- Test each platform independently +- Use CI/CD with multiple platform targets +- Validate platform-specific features thoroughly + +This implementation approach ensures that SRDWM works correctly on each platform while respecting the fundamental differences between X11, Wayland, Windows, and macOS. + + |
