From bd44d5ec8cf07fbf1767acebc769f5604cfdd760 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 22 Apr 2023 16:32:19 +0100 Subject: [PATCH] client,native: run on dedicated thread --- src/browser/client.hxx | 9 ++-- src/native/client_x11.cxx | 88 ++++++++++++++++++++++++++++++++------- src/native/native.hxx | 3 ++ 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/browser/client.hxx b/src/browser/client.hxx index 49e5672..cfaf753 100644 --- a/src/browser/client.hxx +++ b/src/browser/client.hxx @@ -45,15 +45,12 @@ namespace Browser { /* Functions implemented in native */ - /// Initialises the Native::Connection owned by this struct. Called on the main thread only. - /// Returns true on success, false on failure. - bool SetupNative(); - - /// Runs a platform-native event loop, blocking until it exits. Called on the main thread only. + /// Runs a platform-native event loop, blocking until it exits. Called in its own dedicated thread. /// This function assumes SetupNative has been called and that its return value indicated success. void Run(); - /// Closes the Native::Connection owned by this struct. Called on the main thread only. + /// Closes the Native::Connection owned by this struct. Called on the main thread only, so should fire an + /// event to the event loop indicating to stop. This function must be non-blocking. /// This function will not be called until all owned browsers have been destroyed (i.e. OnBeforeClose /// has been called for all Browsers), and this will be the last function call made to the Client. void CloseNative(); diff --git a/src/native/client_x11.cxx b/src/native/client_x11.cxx index cc025b1..1586876 100644 --- a/src/native/client_x11.cxx +++ b/src/native/client_x11.cxx @@ -8,37 +8,74 @@ // Uses the xcb-record extension to report mouse button events to main event loop void Record(xcb_connection_t*); -bool Browser::Client::SetupNative() { +void Browser::Client::Run() { this->native.connection = xcb_connect(nullptr, nullptr); if (!this->native.connection) { fmt::print("[native] connection is null\n"); - return false; + return; } if (xcb_connection_has_error(this->native.connection)) { fmt::print("[native] connection has error\n"); - return false; + return; } xcb_screen_iterator_t screens = xcb_setup_roots_iterator(xcb_get_setup(this->native.connection)); if (screens.rem != 1) { fmt::print("[native] {} root screens found; expected exactly 1\n", screens.rem); xcb_disconnect(this->native.connection); - return false; + return; } this->native.root_window = screens.data->root; + this->native.event_window = xcb_generate_id(this->native.connection); + if (this->native.event_window == 0xFFFFFFFF) { + fmt::print("[native] failed to create event window - xcb_generate_id failed\n"); + xcb_disconnect(this->native.connection); + return; + } + auto event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY; + auto create_error = xcb_request_check(this->native.connection, xcb_create_window( + this->native.connection, + XCB_COPY_FROM_PARENT, + this->native.event_window, + this->native.root_window, + 0, + 0, + 1, + 1, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + XCB_COPY_FROM_PARENT, + XCB_CW_EVENT_MASK, + &event_mask + )); + if (create_error) { + fmt::print("[native] failed to create event window - error {}\n", create_error->error_code); + free(create_error); + xcb_disconnect(this->native.connection); + return; + } + this->native.record_thread = std::thread(Record, this->native.connection); - return true; -} + xcb_flush(this->native.connection); -void Browser::Client::Run() { + bool run = true; xcb_generic_event_t* event; - while (true) { + while (run) { event = xcb_wait_for_event(this->native.connection); if (event) { // Top bit indicates whether the event came from SendEvent uint8_t type = event->response_type & 0b01111111; switch(type) { + case XCB_CLIENT_MESSAGE: { + auto message = reinterpret_cast(event); + if (message->window == this->native.event_window) { + fmt::print("[native] closing\n"); + run = false; + } + break; + } + // type=0 is Error, although this isn't documented anywhere nor defined with any name case 0: { xcb_generic_error_t* error = reinterpret_cast(event); @@ -62,11 +99,26 @@ void Browser::Client::Run() { break; } } + + xcb_disconnect(this->native.connection); + this->native.record_thread.join(); + fmt::print("[native] stopped\n"); } void Browser::Client::CloseNative() { - xcb_disconnect(this->native.connection); - this->native.record_thread.join(); + xcb_client_message_event_t* event = new xcb_client_message_event_t; + event->response_type = XCB_CLIENT_MESSAGE; + event->window = this->native.event_window; + event->format = 8; + auto cookie = xcb_send_event_checked( + this->native.connection, + false, + this->native.event_window, + XCB_EVENT_MASK_STRUCTURE_NOTIFY, + reinterpret_cast(event) + ); + xcb_discard_reply(this->native.connection, cookie.sequence); + xcb_flush(this->native.connection); } void Record(xcb_connection_t* connection) { @@ -127,7 +179,17 @@ void Record(xcb_connection_t* connection) { switch (reply->category) { case 0: { // XRecordFromServer - fmt::print("[native] record\n"); + uint8_t* data = xcb_record_enable_context_data(reply); + int data_len = xcb_record_enable_context_data_length(reply); + if (data_len == sizeof(xcb_button_press_event_t)) { + xcb_generic_event_t* ev = reinterpret_cast(data); + switch (ev->response_type) { + case XCB_BUTTON_PRESS: { + xcb_button_press_event_t* event = reinterpret_cast(ev); + fmt::print("[native] record {}\n", event->detail); + } + } + } break; } case 4: { // XRecordStartOfData @@ -144,7 +206,5 @@ void Record(xcb_connection_t* connection) { free(reply); } - xcb_record_disable_context (connection, id); - xcb_record_free_context (connection, id); - xcb_disconnect (record_connection); + xcb_disconnect(record_connection); } diff --git a/src/native/native.hxx b/src/native/native.hxx index ee082a8..1d9fba6 100644 --- a/src/native/native.hxx +++ b/src/native/native.hxx @@ -2,6 +2,8 @@ #define _BOLT_NATIVE_HXX_ #include "include/views/cef_window.h" +#include + #ifdef __linux__ #include #include @@ -16,6 +18,7 @@ namespace Native { #ifdef __linux__ xcb_connection_t* connection; xcb_window_t root_window; + xcb_window_t event_window; std::thread record_thread; #endif