Add XKB interface for x11

This commit is contained in:
James Grogan 2022-11-17 11:28:18 +00:00
parent 7ad237edc1
commit cf9bace272
15 changed files with 232 additions and 31 deletions

View file

@ -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:

View file

@ -23,6 +23,7 @@ void MediaTool::initializeViews()
{
auto mainWindow = mDesktopManager->getWindowManager()->getMainWindow();
mainWindow->setSize(800, 600);
mainWindow->setTitle("Media Tool");
auto tabbedPanel = TabbedPanelWidget::Create();

View file

@ -1,7 +1,6 @@
#include "Keyboard.h"
Keyboard::Keyboard()
:mKeyMap()
{
}

View file

@ -7,9 +7,7 @@ class Keyboard
{
public:
using KeyCode = unsigned;
protected:
std::map<KeyCode, std::string> mKeyMap;
using KeyState = unsigned;
public:
Keyboard();
@ -18,7 +16,7 @@ public:
static std::unique_ptr<Keyboard> Create();
virtual std::string GetKeyString(KeyCode code)
virtual std::string getKeyString(KeyCode code)
{
return "";
}

View file

@ -5,6 +5,7 @@
#include <memory>
#include <vector>
#include <cstdint>
#include <string>
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<DrawingContext> mDrawingContext;
};

View file

@ -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")

View file

@ -1,16 +1,20 @@
#pragma once
#include "Keyboard.h"
#include "wayland-client.h"
#include <xkbcommon/xkbcommon.h>
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);

View file

@ -17,7 +17,7 @@ std::unique_ptr<KeyboardEvent> 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<KeyboardEvent> 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;
}

View file

@ -0,0 +1,41 @@
#include "XcbExtensionInterface.h"
#include "FileLogger.h"
// Header is c-like, uses explicit as a var
#define explicit explicit_
#include <xcb/xkb.h>
#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;
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <unordered_map>
#include <string>
#include <xcb/xcb.h>
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<std::string, xcb_extension_t> mExtensions;
};

View file

@ -4,8 +4,14 @@
#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h> /* for XGetXCBConnection, link with libX11-xcb */
#include <xkbcommon/xkbcommon-x11.h>
#include <xcb/xcb.h>
// Header is c-like, uses explicit as a var
#define explicit explicit_
#include <xcb/xkb.h>
#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 <iostream>
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> fontsManager, bool useHardware)
: AbstractUIInterface(desktopManager, std::move(fontsManager), useHardware),
mEventInterface(XcbEventInterface::Create())
mEventInterface(XcbEventInterface::Create()),
mExtensionInterface(std::make_unique<XcbExtensionInterface>())
{
}
@ -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<XcbKeyboard*>(mDesktopManager->getKeyboard())->updateState(&xkb_event->state_notify);
}
}
}
free(event);
mDesktopManager->onLoopIteration();
}

View file

@ -13,6 +13,8 @@ using XcbEventInterfacePtr = std::unique_ptr<XcbEventInterface>;
class XcbWindowInterface;
using XcbWindowInterfacePtr = std::unique_ptr<XcbWindowInterface>;
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<XcbExtensionInterface> mExtensionInterface;
};

View file

@ -1,25 +1,83 @@
#include "XcbKeyboard.h"
XcbKeyboard::XcbKeyboard()
: Keyboard()
// Header is c-like, uses explicit as a var
#define explicit explicit_
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon-x11.h>
#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> XcbKeyboard::Create()
std::unique_ptr<XcbKeyboard> XcbKeyboard::Create(xcb_connection_t* connection)
{
return std::make_unique<XcbKeyboard>();
return std::make_unique<XcbKeyboard>(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");
}
}

View file

@ -1,14 +1,29 @@
#pragma once
#include "Keyboard.h"
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
struct xcb_xkb_state_notify_event_t;
class XcbKeyboard : public Keyboard
{
public:
XcbKeyboard();
XcbKeyboard(xcb_connection_t* connection);
static std::unique_ptr<XcbKeyboard> Create();
static std::unique_ptr<XcbKeyboard> 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<XcbKeyboard>;

View file

@ -11,6 +11,7 @@
#include "FontsManager.h"
#include <xcb/xcb.h>
#include <xcb/xcb_atom.h>
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<XcbWindow>(window, hwnd, connection, xcbGlInterface);
const auto drawing_mode = xcbGlInterface ? DrawingMode::GRAPH : DrawingMode::RASTER;