diff --git a/README.md b/README.md index b47a1bb..b382d4b 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ sudo apt-get install libpng-dev #### Window rendering ```bash -sudo apt-get install libwayland-dev wayland-protocols libx11-dev libxkbcommon-dev libx11-xcb-dev libxcb-image0-dev +sudo apt-get install libwayland-dev wayland-protocols libx11-dev libxkbcommon-dev libxkbcommon-x11-dev libx11-xcb-dev libxcb-image0-dev ``` We need to generate suitable xdg shell c code for wayland: diff --git a/apps/sample-gui/MediaTool.cpp b/apps/sample-gui/MediaTool.cpp index 55a9ff5..2f83e4c 100644 --- a/apps/sample-gui/MediaTool.cpp +++ b/apps/sample-gui/MediaTool.cpp @@ -23,6 +23,7 @@ void MediaTool::initializeViews() { auto mainWindow = mDesktopManager->getWindowManager()->getMainWindow(); mainWindow->setSize(800, 600); + mainWindow->setTitle("Media Tool"); auto tabbedPanel = TabbedPanelWidget::Create(); diff --git a/src/ui_elements/desktop_elements/Keyboard.cpp b/src/ui_elements/desktop_elements/Keyboard.cpp index 8f50236..457f988 100644 --- a/src/ui_elements/desktop_elements/Keyboard.cpp +++ b/src/ui_elements/desktop_elements/Keyboard.cpp @@ -1,7 +1,6 @@ #include "Keyboard.h" Keyboard::Keyboard() - :mKeyMap() { } diff --git a/src/ui_elements/desktop_elements/Keyboard.h b/src/ui_elements/desktop_elements/Keyboard.h index de12d6b..bebdc8e 100644 --- a/src/ui_elements/desktop_elements/Keyboard.h +++ b/src/ui_elements/desktop_elements/Keyboard.h @@ -7,9 +7,7 @@ class Keyboard { public: using KeyCode = unsigned; - -protected: - std::map mKeyMap; + using KeyState = unsigned; public: Keyboard(); @@ -18,7 +16,7 @@ public: static std::unique_ptr Create(); - virtual std::string GetKeyString(KeyCode code) + virtual std::string getKeyString(KeyCode code) { return ""; } diff --git a/src/ui_elements/desktop_elements/Window.h b/src/ui_elements/desktop_elements/Window.h index a006334..540a573 100644 --- a/src/ui_elements/desktop_elements/Window.h +++ b/src/ui_elements/desktop_elements/Window.h @@ -5,6 +5,7 @@ #include #include #include +#include class PaintEvent; class MouseEvent; @@ -61,8 +62,19 @@ public: bool isDirty() const; + void setTitle(const std::string& title) + { + mTitle = title; + } + + const std::string& getTitle() const + { + return mTitle; + } + private: WidgetPtr mWidget {nullptr}; + std::string mTitle; IPlatformWindowPtr mPlatformWindow {nullptr}; std::unique_ptr mDrawingContext; }; diff --git a/src/windows/CMakeLists.txt b/src/windows/CMakeLists.txt index e31634d..170be88 100644 --- a/src/windows/CMakeLists.txt +++ b/src/windows/CMakeLists.txt @@ -15,9 +15,10 @@ if(UNIX) ui_interfaces/x11/XcbTextInterface.cpp ui_interfaces/x11/XcbKeyboard.cpp ui_interfaces/x11/XcbGlInterface.cpp + ui_interfaces/x11/XcbExtensionInterface.cpp ui_interfaces/x11/XcbGlWindowInterface.cpp ) - list(APPEND platform_LIBS ${X11_LIBRARIES} ${X11_xcb_LIB} ${X11_X11_xcb_LIB} ${X11_xkbcommon_LIB} libxcb-image.so) + list(APPEND platform_LIBS ${X11_LIBRARIES} ${X11_xcb_LIB} ${X11_X11_xcb_LIB} ${X11_xkbcommon_LIB} ${X11_xkbcommon_X11_LIB} libxcb-image.so) list(APPEND X11_INCLUDE_DIRS ${X11_xkbcommon_INCLUDE_PATH}) else() message(STATUS "x11 development headers not found - skipping support") diff --git a/src/windows/ui_interfaces/wayland/WaylandKeyboard.h b/src/windows/ui_interfaces/wayland/WaylandKeyboard.h index ee310b3..4fd6ea5 100644 --- a/src/windows/ui_interfaces/wayland/WaylandKeyboard.h +++ b/src/windows/ui_interfaces/wayland/WaylandKeyboard.h @@ -1,16 +1,20 @@ #pragma once +#include "Keyboard.h" + #include "wayland-client.h" #include -class WaylandKeyboard +class WaylandKeyboard : public Keyboard { public: WaylandKeyboard(wl_keyboard* keyboard); ~WaylandKeyboard(); + std::string getKeyString(KeyCode keyCode) override { return "";}; + private: static void keyboardKeymapEvent(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size); static void keyboardEnterEvent(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys); diff --git a/src/windows/ui_interfaces/x11/XcbEventInterface.cpp b/src/windows/ui_interfaces/x11/XcbEventInterface.cpp index c7ae938..afa0a55 100644 --- a/src/windows/ui_interfaces/x11/XcbEventInterface.cpp +++ b/src/windows/ui_interfaces/x11/XcbEventInterface.cpp @@ -17,7 +17,7 @@ std::unique_ptr XcbEventInterface::ConvertKeyPress(xcb_key_press_ { auto ui_event = KeyboardEvent::Create(); ui_event->SetAction(KeyboardEvent::Action::Pressed); - ui_event->SetKeyString(keyboard->GetKeyString(event->detail)); + ui_event->SetKeyString(keyboard->getKeyString(event->detail)); return ui_event; } @@ -25,7 +25,7 @@ std::unique_ptr XcbEventInterface::ConvertKeyRelease(xcb_key_pres { auto ui_event = KeyboardEvent::Create(); ui_event->SetAction(KeyboardEvent::Action::Released); - ui_event->SetKeyString(keyboard->GetKeyString(event->detail)); + ui_event->SetKeyString(keyboard->getKeyString(event->detail)); return ui_event; } diff --git a/src/windows/ui_interfaces/x11/XcbExtensionInterface.cpp b/src/windows/ui_interfaces/x11/XcbExtensionInterface.cpp new file mode 100644 index 0000000..789cec0 --- /dev/null +++ b/src/windows/ui_interfaces/x11/XcbExtensionInterface.cpp @@ -0,0 +1,41 @@ +#include "XcbExtensionInterface.h" + +#include "FileLogger.h" + +// Header is c-like, uses explicit as a var +#define explicit explicit_ +#include +#undef explicit + +void XcbExtensionInterface::initialize(xcb_connection_t* connection) +{ + xcb_prefetch_extension_data(connection, &xcb_xkb_id); + + const auto reply = xcb_get_extension_data(connection, &xcb_xkb_id); + if (!reply || !reply->present) + { + MLOG_ERROR("XKB extension not found on server"); + return; + } + + auto cookie = xcb_xkb_use_extension(connection, 1, 0); + auto extension_reply = xcb_xkb_use_extension_reply(connection, cookie, nullptr); + if (!extension_reply) + { + MLOG_ERROR("XKB extension not found on server"); + return; + } + if (!extension_reply->supported) + { + MLOG_ERROR("XKB extension version not found on server"); + return; + } + + mXkbEventFlag = reply->first_event; + mHasXkb = true; +} + +bool XcbExtensionInterface::isXkBEvent(unsigned responseType) const +{ + return mHasXkb && mXkbEventFlag == responseType; +} diff --git a/src/windows/ui_interfaces/x11/XcbExtensionInterface.h b/src/windows/ui_interfaces/x11/XcbExtensionInterface.h new file mode 100644 index 0000000..941e7a6 --- /dev/null +++ b/src/windows/ui_interfaces/x11/XcbExtensionInterface.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include + +class XcbExtensionInterface +{ +public: + + void initialize(xcb_connection_t* connection); + + bool isXkBEvent(unsigned responseType) const; + +private: + bool mHasXkb{false}; + uint8_t mXkbEventFlag{0}; + //std::unordered_map mExtensions; +}; diff --git a/src/windows/ui_interfaces/x11/XcbInterface.cpp b/src/windows/ui_interfaces/x11/XcbInterface.cpp index 9a59771..80cd3b4 100644 --- a/src/windows/ui_interfaces/x11/XcbInterface.cpp +++ b/src/windows/ui_interfaces/x11/XcbInterface.cpp @@ -4,8 +4,14 @@ #include #include /* for XGetXCBConnection, link with libX11-xcb */ +#include #include +// Header is c-like, uses explicit as a var +#define explicit explicit_ +#include +#undef explicit + #include "PaintEvent.h" #include "ResizeEvent.h" #include "Screen.h" @@ -14,17 +20,35 @@ #include "UiEvent.h" #include "XcbKeyboard.h" #include "XcbWindow.h" + #include "XcbEventInterface.h" #include "XcbGlInterface.h" +#include "XcbExtensionInterface.h" + #include "FileLogger.h" #include "FontsManager.h" #include +namespace { + typedef union { + struct { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + } any; + xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify; + xcb_xkb_map_notify_event_t map_notify; + xcb_xkb_state_notify_event_t state_notify; + } _xkb_event; +} XcbInterface::XcbInterface(DesktopManager* desktopManager, std::unique_ptr fontsManager, bool useHardware) : AbstractUIInterface(desktopManager, std::move(fontsManager), useHardware), - mEventInterface(XcbEventInterface::Create()) + mEventInterface(XcbEventInterface::Create()), + mExtensionInterface(std::make_unique()) { } @@ -43,7 +67,9 @@ void XcbInterface::initialize() { initializeHardwareRendering(); } - mDesktopManager->setKeyboard(XcbKeyboard::Create()); + + mExtensionInterface->initialize(mConnection); + mDesktopManager->setKeyboard(XcbKeyboard::Create(mConnection)); const auto num_windows = mDesktopManager->getWindowManager()->getNumWindows(); for(std::size_t idx=0; idx< num_windows; idx++) @@ -178,6 +204,7 @@ void XcbInterface::loop() xcb_generic_event_t *event; while ((event = xcb_wait_for_event(mConnection))) { + auto handled = true; switch (event->response_type & ~0x80) { case XCB_EXPOSE:{ @@ -235,9 +262,23 @@ void XcbInterface::loop() break; } default: - /* Unknown event type, ignore it */ + handled = false; break; } + + if (!handled) + { + if (mExtensionInterface->isXkBEvent(event->response_type)) + { + auto xkb_event = reinterpret_cast<_xkb_event *>(event); + if (xkb_event->any.xkbType == XCB_XKB_STATE_NOTIFY) + { + dynamic_cast(mDesktopManager->getKeyboard())->updateState(&xkb_event->state_notify); + } + } + } + + free(event); mDesktopManager->onLoopIteration(); } diff --git a/src/windows/ui_interfaces/x11/XcbInterface.h b/src/windows/ui_interfaces/x11/XcbInterface.h index c6513d8..d8ef107 100644 --- a/src/windows/ui_interfaces/x11/XcbInterface.h +++ b/src/windows/ui_interfaces/x11/XcbInterface.h @@ -13,6 +13,8 @@ using XcbEventInterfacePtr = std::unique_ptr; class XcbWindowInterface; using XcbWindowInterfacePtr = std::unique_ptr; +class XcbExtensionInterface; + struct xcb_connection_t; struct xcb_expose_event_t; struct _XDisplay; @@ -55,4 +57,5 @@ private: _XDisplay* mX11Display{nullptr}; XcbGlInterfacePtr mGlInterface; XcbEventInterfacePtr mEventInterface; + std::unique_ptr mExtensionInterface; }; diff --git a/src/windows/ui_interfaces/x11/XcbKeyboard.cpp b/src/windows/ui_interfaces/x11/XcbKeyboard.cpp index 8505cff..a698ea3 100644 --- a/src/windows/ui_interfaces/x11/XcbKeyboard.cpp +++ b/src/windows/ui_interfaces/x11/XcbKeyboard.cpp @@ -1,25 +1,83 @@ #include "XcbKeyboard.h" -XcbKeyboard::XcbKeyboard() - : Keyboard() +// Header is c-like, uses explicit as a var +#define explicit explicit_ +#include +#undef explicit +#include + +#include "FileLogger.h" + +XcbKeyboard::XcbKeyboard(xcb_connection_t* connection) + : Keyboard(), + mConnection(connection) { - mKeyMap = {{10, "1"}, {11, "2"}, {12, "3"}, {13, "4"}, {14, "5"}, {15, "6"}, - {16, "7"}, {17, "8"}, {18, "9"}, {19, "0"}, {20, "-"}, {21, "+"}, {22, "KEY_BACK"}, - {24, "q"}, {25, "w"}, {26, "e"}, {27, "r"}, {28, "t"}, {29, "y"}, - {30, "u"}, {31, "i"}, {32, "o"}, {33, "p"}, {34, "["}, {35, "]"}, {36, "KEY_RETURN"}, - {38, "a"}, {39, "s"}, {40, "d"}, {41, "f"}, {42, "g"}, {43, "h"}, - {44, "j"}, {45, "k"}, {46, "l"}, {47, ":"}, {48, "'"}, {49, "#"}, - {52, "z"}, {53, "x"}, {54, "c"}, {55, "v"}, {56, "b"}, - {57, "n"}, {58, "m"}, {59, ","}, {60, "."}, {61, "/"}, - {65, "KEY_SPACE"}, {66, "KEY_CAPS"}}; + mXkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!mXkbContext) + { + MLOG_ERROR("Failed to get XKB context"); + } } -std::unique_ptr XcbKeyboard::Create() +std::unique_ptr XcbKeyboard::Create(xcb_connection_t* connection) { - return std::make_unique(); + return std::make_unique(connection); } -std::string XcbKeyboard::GetKeyString(KeyCode keyCode) +void XcbKeyboard::updateState(xcb_xkb_state_notify_event_t *state) { - return mKeyMap[keyCode]; + xkb_state_update_mask(mXkbState, state->baseMods, + state->latchedMods, + state->lockedMods, + state->baseGroup, + state->latchedGroup, + state->lockedGroup); +} + +std::string XcbKeyboard::getKeyString(KeyCode keyCode) +{ + if (!mXkbState) + { + getKeyMap(); + } + + auto size = xkb_state_key_get_utf8(mXkbState, keyCode, nullptr, 0) + 1; + if (size <= 1) + { + return {}; + } + + char buffer[size]; + xkb_state_key_get_utf8(mXkbState, keyCode, buffer, size); + + std::string ret(buffer); + return ret; +} + +void XcbKeyboard::getKeyMap() +{ + auto device_id = xkb_x11_get_core_keyboard_device_id(mConnection); + if (device_id == -1) + { + MLOG_ERROR("Failed to get connection for keymap"); + } + + auto xkb_keymap = xkb_x11_keymap_new_from_device(mXkbContext, mConnection, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS); + + struct xkb_state* xkb_state = xkb_x11_state_new_from_device(xkb_keymap, mConnection, device_id); + xkb_keymap_unref(mXkbKeymap); + xkb_state_unref(mXkbState); + mXkbKeymap = xkb_keymap; + mXkbState = xkb_state; + + xcb_xkb_select_events_details_t details; + auto cookie = xcb_xkb_select_events(mConnection, XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY, 0, XCB_XKB_EVENT_TYPE_STATE_NOTIFY, 0, 0, &details); + + // Check errors + auto error = xcb_request_check(mConnection, cookie); + if (error) + { + MLOG_ERROR("Failed to select XKB events"); + } } diff --git a/src/windows/ui_interfaces/x11/XcbKeyboard.h b/src/windows/ui_interfaces/x11/XcbKeyboard.h index 3e7b7b1..38c010a 100644 --- a/src/windows/ui_interfaces/x11/XcbKeyboard.h +++ b/src/windows/ui_interfaces/x11/XcbKeyboard.h @@ -1,14 +1,29 @@ #pragma once #include "Keyboard.h" +#include +#include + +struct xcb_xkb_state_notify_event_t; + class XcbKeyboard : public Keyboard { public: - XcbKeyboard(); + XcbKeyboard(xcb_connection_t* connection); - static std::unique_ptr Create(); + static std::unique_ptr Create(xcb_connection_t* connection); - std::string GetKeyString(KeyCode keyCode) override; + std::string getKeyString(KeyCode keyCode) override; + + void updateState(xcb_xkb_state_notify_event_t *state); + +private: + void getKeyMap(); + + xcb_connection_t* mConnection{nullptr}; + xkb_state* mXkbState{nullptr}; + xkb_context* mXkbContext{nullptr}; + xkb_keymap* mXkbKeymap{nullptr}; }; using XcbKeyboardUPtr = std::unique_ptr; diff --git a/src/windows/ui_interfaces/x11/XcbWindow.cpp b/src/windows/ui_interfaces/x11/XcbWindow.cpp index 905240e..3163b53 100644 --- a/src/windows/ui_interfaces/x11/XcbWindow.cpp +++ b/src/windows/ui_interfaces/x11/XcbWindow.cpp @@ -11,6 +11,7 @@ #include "FontsManager.h" #include +#include XcbWindow::XcbWindow(mt::Window* window, int hwnd, xcb_connection_t* connection, XcbGlInterface* xcbGlInterface) : IPlatformWindow(window), @@ -50,6 +51,13 @@ void XcbWindow::add(mt::Window* window, xcb_connection_t* connection, mt::Screen xcb_screen->GetNativeScreen()->root_visual, /* visual */ mask, values ); /* masks */ + if (!window->getTitle().empty()) + { + auto title = window->getTitle(); + xcb_change_property (connection, XCB_PROP_MODE_REPLACE, hwnd, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, title.size(), title.c_str()); + //xcb_change_property (connection, XCB_PROP_MODE_REPLACE, hwnd, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, title.size(), title.c_str()); + } + auto xcb_window = std::make_unique(window, hwnd, connection, xcbGlInterface); const auto drawing_mode = xcbGlInterface ? DrawingMode::GRAPH : DrawingMode::RASTER;