#include "XcbInterface.h" #include "DesktopManager.h" #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" #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 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()), mExtensionInterface(std::make_unique()) { } 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(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(event); onExposeEvent(expose_event); break; } case XCB_KEY_PRESS: { auto kp = reinterpret_cast(event); auto ui_event = mEventInterface->ConvertKeyPress(kp, mDesktopManager->getKeyboard()); mDesktopManager->onUiEvent(std::move(ui_event)); break; } case XCB_KEY_RELEASE: { auto kr = reinterpret_cast(event); auto ui_event = mEventInterface->ConvertKeyRelease(kr, mDesktopManager->getKeyboard()); mDesktopManager->onUiEvent(std::move(ui_event)); break; } case XCB_BUTTON_PRESS: { auto press = reinterpret_cast(event); auto ui_event = mEventInterface->ConvertButtonPress(press); mDesktopManager->onUiEvent(std::move(ui_event)); break; } case XCB_BUTTON_RELEASE: { auto release = reinterpret_cast(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(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(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"); }