stuff-from-scratch/src/windows/ui_interfaces/x11/XcbInterface.cpp
2022-12-02 13:44:52 +00:00

299 lines
8.5 KiB
C++

#include "XcbInterface.h"
#include "DesktopManager.h"
#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"
#include "XcbScreen.h"
#include "Color.h"
#include "Widget.h"
#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()),
mExtensionInterface(std::make_unique<XcbExtensionInterface>())
{
}
XcbInterface::~XcbInterface()
{
shutDown();
}
void XcbInterface::initialize()
{
connect();
updateScreen();
createGraphicsContext();
if (mUseHardwareRendering)
{
initializeHardwareRendering();
}
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++)
{
addWindow(mDesktopManager->getWindowManager()->getWindow(idx));
}
mDesktopManager->getWindowManager()->getMainWindow()->show();
}
void XcbInterface::connect()
{
if (mUseHardwareRendering)
{
mX11Display = XOpenDisplay(0);
if (!mX11Display)
{
MLOG_ERROR("Can't open X11 display");
return;
}
mConnection = XGetXCBConnection(mX11Display);
if (!mConnection)
{
XCloseDisplay(mX11Display);
MLOG_ERROR("Can't get xcb connection from display");
return;
}
XSetEventQueueOwner(mX11Display, XCBOwnsEventQueue);
}
else
{
mConnection = xcb_connect(nullptr, nullptr);
}
}
void XcbInterface::updateScreen()
{
if (!mConnection)
{
return;
}
auto screen_iter = xcb_setup_roots_iterator(xcb_get_setup(mConnection));
if (mUseHardwareRendering)
{
if (!mX11Display)
{
return;
}
const auto default_screen = DefaultScreen(mX11Display);
for(int screen_num = default_screen; screen_iter.rem && screen_num > 0;
--screen_num, xcb_screen_next(&screen_iter));
}
auto xcb_screen = XcbScreen::Create(screen_iter.data);
auto screen = mt::Screen::Create();
screen->SetPlatformScreen(std::move(xcb_screen));
mDesktopManager->addScreen(std::move(screen));
}
void XcbInterface::initializeHardwareRendering()
{
const auto default_screen = DefaultScreen(mX11Display);
mGlInterface = XcbGlInterface::Create(mX11Display, default_screen);
}
void XcbInterface::createGraphicsContext()
{
if (!mConnection || mDesktopManager->getDefaultScreen())
{
return;
}
auto xcb_screen = dynamic_cast<XcbScreen*>(mDesktopManager->getDefaultScreen()->GetPlatformScreen());
if (!xcb_screen)
{
return;
}
auto gc = xcb_generate_id(mConnection);
xcb_drawable_t window = xcb_screen->GetNativeScreen()->root;
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
auto color = Color(240, 240, 240);
uint32_t values[2] = {color.getAsUInt32(), 0};
xcb_create_gc(mConnection, gc, window, mask, values);
xcb_screen->SetGraphicsContext(gc);
}
void XcbInterface::mapWindow(mt::Window* window)
{
window->map();
}
void XcbInterface::showWindow(mt::Window* window)
{
window->show();
}
uint32_t XcbInterface::getEventMask()
{
return XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
}
void XcbInterface::addWindow(mt::Window* window)
{
auto screen = mDesktopManager->getDefaultScreen();
XcbWindow::add(window, mConnection, screen, getEventMask(), mFontsManager.get(), mGlInterface.get());
}
void XcbInterface::onPaint()
{
mDesktopManager->onUiEvent(PaintEvent::Create());
auto mainWindow = mDesktopManager->getWindowManager()->getMainWindow();
auto defaultScreen = mDesktopManager->getDefaultScreen();
mainWindow->doPaint(defaultScreen);
}
void XcbInterface::onExposeEvent(xcb_expose_event_t* event)
{
//auto window = mWindows[event->window];
//window->SetSize(event->width, event->height);
onPaint();
}
void XcbInterface::loop()
{
initialize();
xcb_generic_event_t *event;
while ((event = xcb_wait_for_event(mConnection)))
{
auto handled = true;
switch (event->response_type & ~0x80)
{
case XCB_EXPOSE:{
auto expose_event = reinterpret_cast<xcb_expose_event_t*>(event);
onExposeEvent(expose_event);
break;
}
case XCB_KEY_PRESS: {
auto kp = reinterpret_cast<xcb_key_press_event_t*>(event);
auto ui_event = mEventInterface->ConvertKeyPress(kp, mDesktopManager->getKeyboard());
mDesktopManager->onUiEvent(std::move(ui_event));
break;
}
case XCB_KEY_RELEASE: {
auto kr = reinterpret_cast<xcb_key_release_event_t*>(event);
auto ui_event = mEventInterface->ConvertKeyRelease(kr, mDesktopManager->getKeyboard());
mDesktopManager->onUiEvent(std::move(ui_event));
break;
}
case XCB_BUTTON_PRESS: {
auto press = reinterpret_cast<xcb_button_press_event_t*>(event);
auto ui_event = mEventInterface->ConvertButtonPress(press);
mDesktopManager->onUiEvent(std::move(ui_event));
break;
}
case XCB_BUTTON_RELEASE: {
auto release = reinterpret_cast<xcb_button_release_event_t*>(event);
auto ui_event = mEventInterface->ConvertButtonRelease(release);
mDesktopManager->onUiEvent(std::move(ui_event));
break;
}
case XCB_CONFIGURE_NOTIFY:
{
auto config_event = (xcb_configure_notify_event_t*) event;
auto mainWindow = mDesktopManager->getWindowManager()->getMainWindow();
auto width = mainWindow->getWidth();
auto height = mainWindow->getHeight();
bool changed = false;
if (config_event->width != width)
{
width = config_event->width;
changed = true;
}
if (config_event->height != height)
{
height = config_event->height;
changed = true;
}
if (changed)
{
auto ui_event = std::make_unique<ResizeEvent>(width, height);
mDesktopManager->onUiEvent(std::move(ui_event));
}
break;
}
default:
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();
}
}
void XcbInterface::shutDown()
{
mDesktopManager->getWindowManager()->clearPlatformWindows();
if (!mConnection)
{
return;
}
MLOG_INFO("starting shutdown");
xcb_disconnect(mConnection);
MLOG_INFO("Ending shutdown");
}