Last active
May 1, 2023 08:12
-
-
Save cfrank/6de45b5a4f2fc3e93dc2f4e6281380b7 to your computer and use it in GitHub Desktop.
Implementation of a container window for the CEF browser window - In the end this was not needed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: BSD-2-Clause | |
#include <iostream> | |
#include <sys/select.h> | |
// #include "ContainerWindow.hpp" | |
class ContainerWindow { | |
public: | |
struct Delegate { | |
virtual void OnContainerWindowCreate(xcb_window_t) const = 0; | |
virtual void OnContainerWindowDestroyRequest() const = 0; | |
}; | |
explicit ContainerWindow(Delegate&) noexcept; | |
~ContainerWindow(); | |
/** | |
* Creates the container window, and initializes the event listeners | |
* | |
* @return True if successful | |
*/ | |
bool Create(xcb_window_t); | |
/** | |
* Destroy the container window | |
*/ | |
void Destroy(); | |
/** | |
* Return the X window handle of the container window | |
* | |
* @return The X window handle | |
*/ | |
[[nodiscard]] xcb_window_t GetWindowHandle() const noexcept; | |
/** | |
* Initializes the connection to the X server | |
* | |
* @return True if successful | |
*/ | |
bool MakeXConnection() noexcept; | |
void StartEventLoop(); | |
private: | |
static xcb_connection_t* Connect(int* screen); | |
/** | |
* Find the preferred screen, as identified by `xcb_connect`. | |
* | |
* When calling `xcb_connect` we are passed a preferred screen number. By default this is `0` | |
* but in the case this is something else we will iterate over the available screens | |
* and return back the one which was requested. | |
*/ | |
static xcb_screen_t* FindPreferredScreen(xcb_connection_t* connection, int preferredScreen); | |
/** | |
* Event loop worker | |
*/ | |
void EventLoop(); | |
/** | |
* Handles the XCB window destroy request | |
*/ | |
void HandleDestroyRequest() const; | |
/** | |
* Process events from the container window | |
*/ | |
void ProcessXEvent(const xcb_generic_event_t&) const; | |
enum class EventLoopStatus { | |
STOPPED, | |
RUNNING, | |
}; | |
std::unique_ptr<std::thread> m_eventLoopThread; | |
Delegate& m_delegate; | |
xcb_connection_t* m_connection; | |
xcb_screen_t* m_screen; | |
EventLoopStatus m_status = EventLoopStatus::STOPPED; | |
// Container window handle | |
xcb_window_t m_window; | |
}; | |
namespace { | |
constexpr auto HOST_WINDOW_NAME = "jbw"; | |
} | |
ContainerWindow::ContainerWindow(ContainerWindow::Delegate& delegate) noexcept | |
: m_delegate(delegate) | |
, m_connection(nullptr) | |
, m_screen(nullptr) | |
, m_window(XCB_NONE) | |
{ | |
} | |
ContainerWindow::~ContainerWindow() | |
{ | |
std::cout << "~ContainerWindow" << '\n'; | |
xcb_disconnect(m_connection); | |
} | |
bool ContainerWindow::Create(xcb_window_t hostWindow) | |
{ | |
m_window = xcb_generate_id(m_connection); | |
const uint32_t values[] = { XCB_EVENT_MASK_STRUCTURE_NOTIFY }; | |
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, hostWindow, 0, 0, 1014, 96, 0, | |
XCB_WINDOW_CLASS_INPUT_OUTPUT, m_screen->root_visual, XCB_CW_EVENT_MASK, values); | |
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, | |
std::strlen(HOST_WINDOW_NAME), HOST_WINDOW_NAME); | |
xcb_map_window(m_connection, m_window); | |
xcb_flush(m_connection); | |
m_delegate.OnContainerWindowCreate(m_window); | |
return true; | |
} | |
void ContainerWindow::Destroy() | |
{ | |
xcb_destroy_window(m_connection, m_window); | |
xcb_flush(m_connection); | |
if (m_status == EventLoopStatus::RUNNING) { | |
m_status = EventLoopStatus::STOPPED; | |
} | |
if (m_eventLoopThread && m_eventLoopThread->joinable()) { | |
m_eventLoopThread->join(); | |
} | |
} | |
xcb_window_t ContainerWindow::GetWindowHandle() const noexcept { return m_window; } | |
bool ContainerWindow::MakeXConnection() noexcept | |
{ | |
int preferredScreen = 0; | |
m_connection = Connect(&preferredScreen); | |
if (!m_connection) { | |
return false; | |
} | |
m_screen = FindPreferredScreen(m_connection, preferredScreen); | |
if (!m_screen) { | |
return false; | |
} | |
StartEventLoop(); | |
return true; | |
} | |
void ContainerWindow::StartEventLoop() | |
{ | |
if (m_status == EventLoopStatus::RUNNING || m_eventLoopThread != nullptr) { | |
return; | |
} | |
m_status = EventLoopStatus::RUNNING; | |
m_eventLoopThread = std::make_unique<std::thread>(&ContainerWindow::EventLoop, this); | |
} | |
// static | |
xcb_connection_t* ContainerWindow::Connect(int* screen) | |
{ | |
xcb_connection_t* connection = xcb_connect(nullptr, screen); | |
int connection_error = xcb_connection_has_error(connection); | |
if (connection_error) { | |
// Still need to free connection | |
xcb_disconnect(connection); | |
return nullptr; | |
} | |
return connection; | |
} | |
// static | |
xcb_screen_t* ContainerWindow::FindPreferredScreen(xcb_connection_t* connection, int preferredScreen) | |
{ | |
const xcb_setup_t* setup = xcb_get_setup(connection); | |
xcb_screen_iterator_t itr = xcb_setup_roots_iterator(setup); | |
for (int i = 0; itr.rem; ++i, xcb_screen_next(&itr)) { | |
if (i == preferredScreen) { | |
return itr.data; | |
} | |
} | |
return nullptr; | |
} | |
void ContainerWindow::EventLoop() | |
{ | |
fd_set fds; | |
int xcb_fd = xcb_get_file_descriptor(m_connection); | |
// If there hasn't been an X event in 25 milliseconds then we time out | |
// and check for interrupts or errors | |
struct timespec ts = { .tv_sec = 0, .tv_nsec = 25000000 }; | |
xcb_generic_event_t* event = XCB_NONE; | |
while (m_status == EventLoopStatus::RUNNING) { | |
FD_ZERO(&fds); | |
FD_SET(xcb_fd, &fds); | |
if (xcb_connection_has_error(m_connection) && m_status == EventLoopStatus::RUNNING) { | |
// TODO: handle error | |
return; | |
} | |
int eventCount = pselect(xcb_fd + 1, &fds, nullptr, nullptr, &ts, nullptr); | |
if (eventCount < 0) { | |
// pselect failed | |
// TODO: Handle error | |
return; | |
} | |
if (eventCount == 0) { | |
// Nothing to process | |
continue; | |
} | |
while ((event = xcb_poll_for_event(m_connection))) { | |
ProcessXEvent(*event); | |
free(event); | |
xcb_flush(m_connection); | |
} | |
} | |
} | |
void ContainerWindow::HandleDestroyRequest() const { } | |
void ContainerWindow::ProcessXEvent(const xcb_generic_event_t& event) const | |
{ | |
auto code = event.response_type & ~0x80; | |
if (code == XCB_DESTROY_NOTIFY) { | |
HandleDestroyRequest(); | |
} else { | |
std::cout << "Handle X Event" << '\n'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment