From 2ddd67dedefad7a633e91b6285efe2f78c330b5a Mon Sep 17 00:00:00 2001 From: Oskari Alaranta Date: Sat, 30 May 2026 01:17:54 +0300 Subject: [PATCH] Start work on making xbanan portable This will allow usage of xbanan on non banan-os platforms. I added a "native" SDL2 port so it can be used without the window manager --- .clangd | 9 + xbanan/Base.cpp | 949 +++++++---------------------------- xbanan/Base.h | 11 + xbanan/CMakeLists.txt | 36 +- xbanan/Definitions.h | 56 +-- xbanan/Drawing.cpp | 6 + xbanan/Events.cpp | 559 +++++++++++++++++++++ xbanan/Events.h | 15 + xbanan/ExtGLX.cpp | 8 +- xbanan/ExtSHM.cpp | 2 - xbanan/Font.cpp | 2 +- xbanan/Keymap.cpp | 2 + xbanan/Keymap.h | 2 + xbanan/Platform.h | 30 ++ xbanan/SDL2/sdl2.cpp | 441 ++++++++++++++++ xbanan/SafeGetters.cpp | 18 +- xbanan/SafeGetters.h | 2 + xbanan/Types.h | 12 + xbanan/banan-os/banan-os.cpp | 141 ++++++ xbanan/main.cpp | 86 ++-- 20 files changed, 1508 insertions(+), 879 deletions(-) create mode 100644 .clangd create mode 100644 xbanan/Events.cpp create mode 100644 xbanan/Events.h create mode 100644 xbanan/Platform.h create mode 100644 xbanan/SDL2/sdl2.cpp create mode 100644 xbanan/Types.h create mode 100644 xbanan/banan-os/banan-os.cpp diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..9ca9898 --- /dev/null +++ b/.clangd @@ -0,0 +1,9 @@ +Diagnostics: + Suppress: [ + c99-designator, + ] + +CompileFlags: + Add: [ + -std=c++20, + ] diff --git a/xbanan/Base.cpp b/xbanan/Base.cpp index 08fda1a..6bf0cc0 100644 --- a/xbanan/Base.cpp +++ b/xbanan/Base.cpp @@ -1,3 +1,4 @@ +#include "Base.h" #include "Definitions.h" #include "Drawing.h" #include "Extensions.h" @@ -7,6 +8,8 @@ #include "SafeGetters.h" #include "Utils.h" +#include + #include #include @@ -21,12 +24,9 @@ struct Selection static BAN::HashMap g_selections; -static CARD16 s_keymask { 0 }; -static CARD16 s_butmask { 0 }; - -static WINDOW s_focus_window { None }; - -static BYTE s_pressed_keys[32] {}; +CARD16 g_keymask { 0 }; +CARD16 g_butmask { 0 }; +WINDOW g_focus_window { None }; static const char* s_opcode_to_name[] { [0] = "none", @@ -226,7 +226,7 @@ BAN::ErrorOr setup_client_conneciton(Client& client_info, const xConnClien }; TRY(encode(client_info.output_buffer, setup)); - TRY(encode(client_info.output_buffer, "banan!!!"_sv)); + TRY(encode(client_info.output_buffer, "_xbanan_"_sv)); TRY(encode(client_info.output_buffer, BAN::ConstByteSpan::from(g_formats))); TRY(encode(client_info.output_buffer, g_root)); TRY(encode(client_info.output_buffer, g_depth)); @@ -252,17 +252,6 @@ static bool is_visible(WINDOW wid) return is_visible(window.parent); } -static Object::Cursor& get_cursor_safe(CURSOR cid) -{ - static Object::Cursor dummy {}; - auto it = g_objects.find(cid); - if (it == g_objects.end()) - return dummy; - if (it->value->type != Object::Type::Cursor) - return dummy; - return it->value->object.get(); -} - static BAN::ErrorOr send_visibility_events_recursively(Client& client_info, WINDOW wid, bool visible) { auto& object = *g_objects[wid]; @@ -325,8 +314,8 @@ static void invalidate_window_recursive(WINDOW wid, int32_t x, int32_t y, int32_ const auto child_x = x - child_window.x; const auto child_y = y - child_window.y; - const auto child_w = BAN::Math::min(w - child_window.x, child_window.texture().width() - child_x); - const auto child_h = BAN::Math::min(h - child_window.y, child_window.texture().height() - child_y); + const auto child_w = BAN::Math::min(w - child_window.x, child_window.width - child_x); + const auto child_h = BAN::Math::min(h - child_window.y, child_window.height - child_y); invalidate_window_recursive( child_wid, @@ -335,22 +324,48 @@ static void invalidate_window_recursive(WINDOW wid, int32_t x, int32_t y, int32_ ); } - if (window.window.has>()) + if (window.platform_window || window.c_class == InputOnly) return; auto& parent_object = *g_objects[window.parent]; ASSERT(parent_object.type == Object::Type::Window); auto& parent_window = parent_object.object.get(); - parent_window.texture().copy_texture( - window.texture(), - window.x + x, - window.y + y, - x, - y, - w, - h - ); + + const uint32_t* child_pix = window.pixels.data(); + const int32_t child_w = window.width; + const int32_t child_h = window.height; + + uint32_t* parent_pix = parent_window.pixels.data(); + const int32_t parent_w = parent_window.width; + const int32_t parent_h = parent_window.height; + + // FIXME: optimize this + for (int32_t y_off = 0; y_off < h; y_off++) + { + const int32_t child_y = y + y_off; + if (child_y < 0 || child_y >= child_h) + continue; + + const int32_t parent_y = window.y + y + y_off; + if (parent_y < 0 || parent_y >= parent_h) + continue; + + for (int32_t x_off = 0; x_off < w; x_off++) + { + const int32_t child_x = x + x_off; + if (child_x < 0 || child_x >= child_w) + continue; + + const int32_t parent_x = window.x + x + x_off; + if (parent_x < 0 || parent_x >= parent_w) + continue; + + const uint32_t color = child_pix[child_y * child_w + child_x]; + if (color != COLOR_INVISIBLE) + parent_pix[parent_y * parent_w + parent_x] = color; + } + } } void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h) @@ -365,7 +380,7 @@ void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h) auto& window = object.object.get(); if (!window.mapped) return; - if (window.window.has>()) + if (window.platform_window) break; x += window.x; @@ -373,34 +388,61 @@ void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h) wid = window.parent; } - invalidate_window_recursive(wid, x, y, w, h); - auto& object = *g_objects[wid]; ASSERT(object.type == Object::Type::Window); auto& window = object.object.get(); - ASSERT(window.window.has>()); + ASSERT(window.platform_window); + + invalidate_window_recursive(wid, x, y, w, h); - auto& texture = window.texture(); const auto min_x = BAN::Math::max(x, 0); const auto min_y = BAN::Math::max(y, 0); - const auto max_x = BAN::Math::min(x + w, texture.width()); - const auto max_y = BAN::Math::min(y + h, texture.height()); + const auto max_x = BAN::Math::min(x + w, window.width); + const auto max_y = BAN::Math::min(y + h, window.height); - uint32_t* pixels = texture.pixels().data(); + if (min_x >= max_x || min_y >= max_y) + return; + + uint32_t* pixels = window.pixels.data(); for (auto y = min_y; y < max_y; y++) { for (auto x = min_x; x < max_x; x++) { - auto& pixel = pixels[y * texture.width() + x]; - if (pixel == LibGUI::Texture::color_invisible) + auto& pixel = pixels[y * window.width + x]; + if (pixel == COLOR_INVISIBLE) pixel = 0x00000000; else pixel |= 0xFF000000; } } - window.window.get>()->invalidate(min_x, min_y, max_x - min_x, max_y - min_y); + g_platform_ops.invalidate(window.platform_window.ptr(), window.pixels.data(), min_x, min_y, max_x - min_x, max_y - min_y); +} + +void send_exposure_recursive(WINDOW wid) +{ + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + + auto& window = object.object.get(); + if (!window.mapped) + return; + + for (auto child_wid : window.children) + send_exposure_recursive(child_wid); + + xEvent event = { .u = { + .expose = { + .window = wid, + .x = 0, + .y = 0, + .width = static_cast(window.width), + .height = static_cast(window.height), + } + }}; + event.u.u.type = Expose; + MUST(window.send_event(event, ExposureMask)); } static BAN::ErrorOr map_window(Client& client_info, WINDOW wid) @@ -412,23 +454,17 @@ static BAN::ErrorOr map_window(Client& client_info, WINDOW wid) if (window.mapped) return {}; - auto& texture = window.texture(); - texture.clear(); + if (window.parent == g_root.windowId) + { + ASSERT(!window.platform_window); + window.platform_window = TRY(g_platform_ops.create_window(wid, window.width, window.height)); + } window.mapped = true; - invalidate_window(wid, 0, 0, texture.width(), texture.height()); - - if (window.window.has>()) - { - auto& gui_window = window.window.get>(); - auto attributes = gui_window->get_attributes(); - attributes.shown = true; - gui_window->set_attributes(attributes); - - gui_window->texture().clear(); - gui_window->invalidate(); - } + for (auto& pixel : window.pixels) + pixel = window.background; + invalidate_window(wid, 0, 0, window.width, window.height); { xEvent event = { .u = { @@ -467,8 +503,8 @@ static BAN::ErrorOr map_window(Client& client_info, WINDOW wid) .window = wid, .x = 0, .y = 0, - .width = static_cast(texture.width()), - .height = static_cast(texture.height()), + .width = static_cast(window.width), + .height = static_cast(window.height), } }}; event.u.u.type = Expose; @@ -489,13 +525,8 @@ static BAN::ErrorOr unmap_window(Client& client_info, WINDOW wid) window.mapped = false; - if (window.window.has>()) - { - auto& gui_window = window.window.get>(); - auto attributes = gui_window->get_attributes(); - attributes.shown = false; - gui_window->set_attributes(attributes); - } + if (window.platform_window) + window.platform_window.clear(); if (is_visible(window.parent)) TRY(send_visibility_events_recursively(client_info, wid, false)); @@ -531,7 +562,7 @@ static BAN::ErrorOr unmap_window(Client& client_info, WINDOW wid) return {}; } -static BAN::ErrorOr destroy_window(Client& client_info, WINDOW wid) +BAN::ErrorOr destroy_window(Client& client_info, WINDOW wid) { TRY(unmap_window(client_info, wid)); @@ -577,39 +608,20 @@ static BAN::ErrorOr destroy_window(Client& client_info, WINDOW wid) break; } - if (window.window.has>()) - { - const auto server_fd = window.window.get>()->server_fd(); - epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, server_fd, nullptr); - g_epoll_thingies.remove(server_fd); - } - client_info.objects.remove(wid); g_objects.remove(wid); return {}; } -static WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y) +WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y) { auto& object = *g_objects[wid]; ASSERT(object.type == Object::Type::Window); auto& window = object.object.get(); - uint32_t width, height; - if (wid == g_root.windowId) - { - width = g_root.pixWidth; - height = g_root.pixHeight; - } - else - { - auto& texture = window.texture(); - width = texture.width(); - height = texture.height(); - } - if (x < 0 || y < 0 || x >= width || y >= height) + if (x < 0 || y < 0 || x >= window.width || y >= window.height) return None; for (auto child_wid : window.children) @@ -630,562 +642,11 @@ static WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y) return wid; } -static BAN::Vector get_path_to_child(WINDOW wid, int32_t x, int32_t y) -{ - BAN::Vector result; - - const auto window_contains = - [](const Object::Window& window, int32_t x, int32_t y) -> bool - { - const auto& texture = window.texture(); - return x >= 0 && y >= 0 && x < texture.width() && y < texture.height(); - }; - - for (;;) - { - const auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - - const auto& window = object.object.get(); - if (!window_contains(window, x, y)) - break; - - MUST(result.push_back(wid)); - - const WINDOW old_wid = wid; - for (auto child_wid : window.children) - { - const auto& child_object = *g_objects[child_wid]; - ASSERT(child_object.type == Object::Type::Window); - - const auto& child_window = child_object.object.get(); - if (!window_contains(child_window, x - child_window.x, y - child_window.y)) - continue; - wid = child_wid; - break; - } - - if (old_wid == wid) - break; - } - - return result; -} - -static void send_enter_and_leave_events(WINDOW wid, int32_t old_x, int32_t old_y, int32_t new_x, int32_t new_y) -{ - // FIXME: correct event_x and event_y values in events - - const auto old_child_path = get_path_to_child(wid, old_x, old_y); - const auto new_child_path = get_path_to_child(wid, new_x, new_y); - - size_t common_ancestors = 0; - while (common_ancestors < old_child_path.size() && common_ancestors < new_child_path.size()) - { - if (old_child_path[common_ancestors] != new_child_path[common_ancestors]) - break; - common_ancestors++; - } - - if (old_child_path.size() == common_ancestors && new_child_path.size() == common_ancestors) - return; - - const bool linear = (common_ancestors == old_child_path.size() || common_ancestors == new_child_path.size()); - - const auto get_flags = - [](WINDOW wid) -> BYTE - { - return ELFlagSameScreen | (s_focus_window == wid ? ELFlagFocus : 0); - }; - - size_t leave_events = old_child_path.size() - common_ancestors; - if (linear && common_ancestors && old_child_path.size() < new_child_path.size()) - leave_events++; - - size_t enter_events = new_child_path.size() - common_ancestors; - if (linear && common_ancestors && new_child_path.size() < old_child_path.size()) - enter_events++; - - for (size_t i = 0; i < leave_events; i++) - { - const bool first = (i == 0); - const BYTE detail = - [&]() -> BYTE { - if (!linear) - return first ? NotifyNonlinear : NotifyNonlinearVirtual; - if (!first) - return NotifyVirtual; - return (old_child_path.size() > new_child_path.size()) - ? NotifyAncestor - : NotifyInferior; - }(); - - const auto& wid = old_child_path[old_child_path.size() - i - 1]; - - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - xEvent event { .u = { - .enterLeave = { - .time = static_cast(time(nullptr)), - .root = g_root.windowId, - .event = wid, - .child = first ? static_cast(None) : old_child_path.back(), - .rootX = static_cast(old_x), - .rootY = static_cast(old_y), - .eventX = static_cast(old_x), - .eventY = static_cast(old_y), - .state = static_cast(s_keymask | s_butmask), - .mode = NotifyNormal, - .flags = get_flags(wid), - } - }}; - event.u.u.type = LeaveNotify, - event.u.u.detail = detail; - MUST(window.send_event(event, LeaveWindowMask)); - } - - for (size_t i = 0; i < enter_events; i++) - { - const bool last = (i == enter_events - 1); - const BYTE detail = - [&]() -> BYTE { - if (!linear) - return last ? NotifyNonlinear : NotifyNonlinearVirtual; - if (!last) - return NotifyVirtual; - return (old_child_path.size() > new_child_path.size()) - ? NotifyInferior - : NotifyAncestor; - }(); - - const auto& wid = new_child_path[new_child_path.size() - enter_events + i]; - - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - xEvent event { .u = { - .enterLeave = { - .time = static_cast(time(nullptr)), - .root = g_root.windowId, - .event = wid, - .child = last ? static_cast(None) : new_child_path.back(), - .rootX = static_cast(new_x), - .rootY = static_cast(new_y), - .eventX = static_cast(new_x), - .eventY = static_cast(new_y), - .state = static_cast(s_keymask | s_butmask), - .mode = NotifyNormal, - .flags = get_flags(wid), - } - }}; - event.u.u.type = EnterNotify, - event.u.u.detail = detail; - MUST(window.send_event(event, EnterWindowMask)); - } -} - -static void send_exposure_recursive(WINDOW wid) -{ - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - - auto& window = object.object.get(); - if (!window.mapped) - return; - - for (auto child_wid : window.children) - send_exposure_recursive(child_wid); - - window.texture().clear(); - - xEvent event = { .u = { - .expose = { - .window = wid, - .x = 0, - .y = 0, - .width = static_cast(window.texture().width()), - .height = static_cast(window.texture().height()), - } - }}; - event.u.u.type = Expose; - MUST(window.send_event(event, ExposureMask)); -} - -static void on_window_close_event(Client& client_info, WINDOW wid) -{ - static CARD32 WM_PROTOCOLS = g_atoms_name_to_id["WM_PROTOCOLS"_sv]; - static CARD32 WM_DELETE_WINDOW = g_atoms_name_to_id["WM_DELETE_WINDOW"_sv]; - - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - const bool supports_wm_delete_winow = - [&window] - { - auto wm_protocols_it = window.properties.find(WM_PROTOCOLS); - if (wm_protocols_it == window.properties.end()) - return false; - - const auto& wm_protocols = wm_protocols_it->value; - if (wm_protocols.type != XA_ATOM || wm_protocols.format != 32) - return false; - - const auto atoms = BAN::ConstByteSpan(wm_protocols.data.span()).as_span(); - for (auto atom : atoms) - if (atom == WM_DELETE_WINDOW) - return true; - - return false; - }(); - - if (!supports_wm_delete_winow) - MUST(destroy_window(client_info, wid)); - else - { - xEvent event { .u = { - .clientMessage = { - .window = wid, - .u = { .l = { - .type = WM_PROTOCOLS, - .longs0 = static_cast(WM_DELETE_WINDOW), - .longs1 = static_cast(time(nullptr)), - }} - } - }}; - event.u.u.type = ClientMessage; - event.u.u.detail = 32; - event.u.u.sequenceNumber = client_info.sequence; - MUST(encode(client_info.output_buffer, event)); - } -} - -static void on_window_resize_event(WINDOW wid) -{ - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - { - xEvent event = { .u = { - .configureNotify = { - .event = wid, - .window = wid, - .aboveSibling = xFalse, - .x = static_cast(window.x), - .y = static_cast(window.y), - .width = static_cast(window.texture().width()), - .height = static_cast(window.texture().height()), - .borderWidth = 0, - .override = xFalse, - } - }}; - event.u.u.type = ConfigureNotify; - MUST(window.send_event(event, StructureNotifyMask)); - } - - auto& parent_object = *g_objects[window.parent]; - ASSERT(parent_object.type == Object::Type::Window); - auto& parent_window = parent_object.object.get(); - - { - xEvent event = { .u = { - .configureNotify = { - .event = window.parent, - .window = wid, - .aboveSibling = xFalse, - .x = static_cast(window.x), - .y = static_cast(window.y), - .width = static_cast(window.texture().width()), - .height = static_cast(window.texture().height()), - .borderWidth = 0, - .override = xFalse, - } - }}; - event.u.u.type = ConfigureNotify; - MUST(parent_window.send_event(event, SubstructureNotifyMask)); - } - - send_exposure_recursive(wid); - - invalidate_window(wid, 0, 0, window.texture().width(), window.texture().height()); -} - -static void on_window_focus_event(WINDOW wid, bool focused) -{ - if (focused) - s_focus_window = wid; - - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - - auto& window = object.object.get(); - if (window.focused == focused) - return; - - window.focused = focused; - - // FIXME: handle childs - - xEvent event { .u = { - .focus = { - .window = wid, - .mode = NotifyNormal, - } - }}; - event.u.u.type = focused ? FocusIn : FocusOut, - event.u.u.detail = NotifyNonlinear; - MUST(window.send_event(event, FocusChangeMask)); -} - -static void send_key_button_pointer_event(WINDOW root_wid, BYTE detail, uint32_t event_mask, BYTE event_type, KeyButMask state) -{ - int32_t root_x, root_y; - int32_t event_x, event_y; - - { - auto& object = *g_objects[root_wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - root_x = event_x = window.cursor_x; - root_y = event_y = window.cursor_y; - } - - const auto child_wid = find_child_window(root_wid, event_x, event_y); - - auto wid = child_wid; - while (wid != None) - { - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - if (window.full_event_mask() & event_mask) - break; - - event_x += window.x; - event_y += window.y; - wid = window.parent; - } - - if (wid == None) - { - if (event_type == MotionNotify && s_butmask == 0) - return; - wid = root_wid; - } - - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - xEvent event { .u = { - .keyButtonPointer = { - .time = static_cast(time(nullptr)), - .root = g_root.windowId, - .event = wid, - .child = static_cast(child_wid == wid ? None : child_wid), - .rootX = static_cast(root_x), - .rootY = static_cast(root_y), - .eventX = static_cast(event_x), - .eventY = static_cast(event_y), - .state = state, - .sameScreen = xTrue, - } - }}; - event.u.u.type = event_type, - event.u.u.detail = detail; - MUST(window.send_event(event, event_mask)); -} - -static void update_cursor_position_recursive(WINDOW wid, int32_t new_x, int32_t new_y) -{ - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - window.cursor_x = new_x; - window.cursor_y = new_y; - - for (auto child_wid : window.children) - { - auto& child_object = *g_objects[child_wid]; - ASSERT(child_object.type == Object::Type::Window); - auto& child_window = child_object.object.get(); - update_cursor_position_recursive(child_wid, new_x - child_window.x, new_y - child_window.y); - } -} - -static void update_cursor(WINDOW wid, int32_t old_x, int32_t old_y, int32_t new_x, int32_t new_y) -{ - const auto old_wid = find_child_window(wid, old_x, old_y); - const auto new_wid = find_child_window(wid, new_x, new_y); - if (old_wid == new_wid || old_wid == None || new_wid == None) - return; - - const auto& old_window = g_objects[old_wid]->object.get(); - const auto& new_window = g_objects[new_wid]->object.get(); - if (old_window.cursor == new_window.cursor) - return; - - auto& window = g_objects[wid]->object.get(); - auto& gui_window = window.window.get>(); - - const auto& cursor = get_cursor_safe(window.cursor); - gui_window->set_cursor(cursor.width, cursor.height, cursor.pixels.span(), cursor.origin_x, cursor.origin_y); -} - -static void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y) -{ - auto& object = *g_objects[wid]; - ASSERT(object.type == Object::Type::Window); - auto& window = object.object.get(); - - update_cursor(wid, window.cursor_x, window.cursor_y, x, y); - - send_enter_and_leave_events(wid, window.cursor_x, window.cursor_y, x, y); - - update_cursor_position_recursive(wid, x, y); - - // TODO: optimize with PointerMotionHint - uint32_t event_mask = PointerMotionMask | PointerMotionHintMask; - if (s_butmask) event_mask |= ButtonMotionMask; - if (s_butmask & Button1Mask) event_mask |= Button1MotionMask; - if (s_butmask & Button2Mask) event_mask |= Button2MotionMask; - if (s_butmask & Button3Mask) event_mask |= Button3MotionMask; - if (s_butmask & Button4Mask) event_mask |= Button4MotionMask; - if (s_butmask & Button5Mask) event_mask |= Button5MotionMask; - send_key_button_pointer_event(wid, NotifyNormal, event_mask, MotionNotify, s_keymask | s_butmask); -} - -static void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed) -{ - uint16_t mask = 0; - switch (xbutton) - { - case Button1: mask = Button1Mask; break; - case Button2: mask = Button2Mask; break; - case Button3: mask = Button3Mask; break; - case Button4: mask = Button4Mask; break; - case Button5: mask = Button5Mask; break; - } - - const auto state = s_keymask | s_butmask; - - if (pressed) - s_butmask |= mask; - else - s_butmask &= ~mask; - - send_key_button_pointer_event( - wid, - xbutton, - pressed ? ButtonPressMask : ButtonReleaseMask, - pressed ? ButtonPress : ButtonRelease, - state - ); -} - -static void on_key_event(WINDOW wid, LibGUI::EventPacket::KeyEvent::event_t event) -{ - const uint8_t xkeycode = event.scancode + g_keymap_min_keycode; - if (xkeycode < g_keymap_min_keycode) - { - dwarnln("cannot send keycode {}", xkeycode); - return; - } - - { - const uint8_t byte = xkeycode / 8; - const uint8_t bit = xkeycode % 8; - if (event.pressed()) - s_pressed_keys[byte] |= 1 << bit; - else - s_pressed_keys[byte] &= ~(1 << bit); - } - - const auto state = s_keymask | s_butmask; - - s_keymask = 0; - if (event.shift()) - s_keymask |= ShiftMask; - if (event.caps_lock()) - s_keymask |= LockMask; - if (event.ctrl()) - s_keymask |= ControlMask; - if (event.alt()) - s_keymask |= Mod1Mask; - - send_key_button_pointer_event( - wid, - xkeycode, - event.pressed() ? KeyPressMask : KeyReleaseMask, - event.pressed() ? KeyPress : KeyRelease, - state - ); -} - -static void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen) -{ - static CARD32 _NET_WM_STATE = g_atoms_name_to_id["_NET_WM_STATE"_sv]; - static CARD32 _NET_WM_STATE_FULLSCREEN = g_atoms_name_to_id["_NET_WM_STATE_FULLSCREEN"_sv]; - - auto window_it = g_objects.find(wid); - if (window_it == g_objects.end() || window_it->value->type != Object::Type::Window) - return; - - auto& window = window_it->value->object.get(); - - window.fullscreen = is_fullscreen; - - auto it = window.properties.find(_NET_WM_STATE); - if (it == window.properties.end()) - it = MUST(window.properties.emplace(_NET_WM_STATE)); - - auto& _net_wm_state = it->value; - if (_net_wm_state.type != XA_ATOM || _net_wm_state.format != 32) - _net_wm_state = {}; - - _net_wm_state.type = XA_ATOM; - _net_wm_state.format = 32; - - for (size_t i = 0; i + 4 <= _net_wm_state.data.size(); i += 4) - { - const auto atom = *reinterpret_cast(_net_wm_state.data.data() + i); - if (atom != _NET_WM_STATE_FULLSCREEN) - continue; - _net_wm_state.data.remove(i); - _net_wm_state.data.remove(i); - _net_wm_state.data.remove(i); - _net_wm_state.data.remove(i); - i -= 4; - } - - if (is_fullscreen) - { - const size_t atom_count = _net_wm_state.data.size() / 4; - MUST(_net_wm_state.data.resize(_net_wm_state.data.size() + 4)); - reinterpret_cast(_net_wm_state.data.data())[atom_count] = _NET_WM_STATE_FULLSCREEN; - } - - xEvent event = { .u = { - .property = { - .window = wid, - .atom = _NET_WM_STATE, - .time = static_cast(time(nullptr)), - .state = PropertyNewValue, - } - }}; - event.u.u.type = PropertyNotify; - MUST(window.send_event(event, PropertyChangeMask)); -} static void on_root_client_message(const xEvent& event) { - static CARD32 _NET_WM_STATE = g_atoms_name_to_id["_NET_WM_STATE"_sv]; - static CARD32 _NET_WM_STATE_FULLSCREEN = g_atoms_name_to_id["_NET_WM_STATE_FULLSCREEN"_sv]; + static const CARD32 _NET_WM_STATE = g_atoms_name_to_id["_NET_WM_STATE"_sv]; + static const CARD32 _NET_WM_STATE_FULLSCREEN = g_atoms_name_to_id["_NET_WM_STATE_FULLSCREEN"_sv]; ASSERT(event.u.u.type == ClientMessage); @@ -1200,22 +661,20 @@ static void on_root_client_message(const xEvent& event) return; auto& window = window_it->value->object.get(); - if (!window.window.has>()) + if (!window.platform_window) return; - auto& gui_window = window.window.get>(); + bool want_fullscren; switch (action) { - case 0: - gui_window->set_fullscreen(false); - break; - case 1: - gui_window->set_fullscreen(true); - break; - case 2: - gui_window->set_fullscreen(!window.fullscreen); - break; + case 0: want_fullscren = false; break; + case 1: want_fullscren = true; break; + case 2: want_fullscren = !window.fullscreen; break; + default: return; } + + if (g_platform_ops.request_fullscreen) + g_platform_ops.request_fullscreen(window.platform_window.ptr(), want_fullscren); } BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) @@ -1327,7 +786,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) case 0: dprintln(" background-pixmap: {8h}", value); if (value == None || value == ParentRelative) - background = LibGUI::Texture::color_invisible; + background = COLOR_INVISIBLE; break; case 1: dprintln(" background-pixel: {8h}", value); @@ -1347,54 +806,27 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) } } - decltype(Object::Window::window) window; + Object::Window window { + .owner = client_info, + .depth = request.depth, + .x = request.x, + .y = request.y, + .parent = request.parent, + .cursor = cursor_id, + .c_class = request.c_class, + .width = request.width, + .height = request.height, + .background = background, + }; - LibGUI::Window* gui_window_ptr = nullptr; - - if (request.parent != g_root.windowId) - window = TRY(LibGUI::Texture::create(request.width, request.height, background)); - else - { - auto attributes = LibGUI::Window::default_attributes; - attributes.shown = false; - attributes.title_bar = false; - attributes.resizable = true; - - auto gui_window = TRY(LibGUI::Window::create(request.width, request.height, "window?"_sv, attributes)); - gui_window->texture().set_bg_color(background); - gui_window_ptr = gui_window.ptr(); - - if (cursor_id != None) - { - const auto& cursor = get_cursor_safe(cursor_id); - gui_window->set_cursor(cursor.width, cursor.height, cursor.pixels.span(), cursor.origin_x, cursor.origin_y); - } - - TRY(g_epoll_thingies.insert(gui_window->server_fd(), { - .type = EpollThingy::Type::Window, - .value = gui_window.ptr(), - })); - - epoll_event event { .events = EPOLLIN, .data = { .fd = gui_window->server_fd() } }; - ASSERT(epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, gui_window->server_fd(), &event) == 0); - - window = BAN::move(gui_window); - } + TRY(window.pixels.resize(window.width * window.height, window.background)); TRY(client_info.objects.insert(request.wid)); auto object_it = TRY(g_objects.insert( request.wid, TRY(BAN::UniqPtr::create(Object { .type = Object::Type::Window, - .object = Object::Window { - .depth = request.depth, - .x = request.x, - .y = request.y, - .parent = request.parent, - .cursor = cursor_id, - .c_class = request.c_class, - .window = BAN::move(window), - }, + .object = BAN::move(window), })) )); if (event_mask != 0) @@ -1402,46 +834,6 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) TRY(parent_window.children.push_back(request.wid)); - if (gui_window_ptr) - { - const WINDOW wid = request.wid; - gui_window_ptr->set_close_window_event_callback([&client_info, wid] { - on_window_close_event(client_info, wid); - }); - gui_window_ptr->set_resize_window_event_callback([wid]() { - on_window_resize_event(wid); - }); - gui_window_ptr->set_window_focus_event_callback([wid](auto event) { - on_window_focus_event(wid, event.focused); - }); - gui_window_ptr->set_window_fullscreen_event_callback([wid](auto event) { - on_window_fullscreen_event(wid, event.fullscreen); - }); - gui_window_ptr->set_mouse_move_event_callback([wid](auto event) { - on_mouse_move_event(wid, event.x, event.y); - }); - gui_window_ptr->set_mouse_button_event_callback([wid](auto event) { - uint8_t xbutton = 0; - switch (event.button) - { - case LibInput::MouseButton::Left: xbutton = 1; break; - case LibInput::MouseButton::Middle: xbutton = 2; break; - case LibInput::MouseButton::Right: xbutton = 3; break; - case LibInput::MouseButton::Extra1: xbutton = 8; break; - case LibInput::MouseButton::Extra2: xbutton = 9; break; - } - if (xbutton) - on_mouse_button_event(wid, xbutton, event.pressed); - }); - gui_window_ptr->set_mouse_scroll_event_callback([wid](auto event) { - on_mouse_button_event(wid, event.scroll > 0 ? 4 : 5, true); - on_mouse_button_event(wid, event.scroll > 0 ? 4 : 5, false); - }); - gui_window_ptr->set_key_event_callback([wid](auto event) { - on_key_event(wid, event); - }); - } - xEvent event = { .u = { .createNotify = { .parent = request.parent, @@ -1483,7 +875,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) case 0: dprintln(" background-pixmap: {8h}", value); if (value == None || value == ParentRelative) - background = LibGUI::Texture::color_invisible; + background = COLOR_INVISIBLE; break; case 1: dprintln(" background-pixel: {8h}", value); @@ -1510,19 +902,25 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) { // FIXME: show cursor if wid is hovered - if (window.window.has>()) + if (window.platform_window) { - auto& gui_window = window.window.get>(); - - const auto& cursor = get_cursor_safe(cursor_id); - gui_window->set_cursor(cursor.width, cursor.height, cursor.pixels.span(), cursor.origin_x, cursor.origin_y); + if (g_platform_ops.set_cursor) + { + const auto& cursor = get_cursor_safe(cursor_id); + g_platform_ops.set_cursor( + window.platform_window.ptr(), + cursor.pixels.data(), + cursor.width, cursor.height, + cursor.origin_x, cursor.origin_y + ); + } } window.cursor = cursor_id; } if (background.has_value()) - window.texture().set_bg_color(background.value()); + window.background = background.value(); break; } @@ -1704,12 +1102,11 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) dprintln(" window: {}", request.window); auto& window = TRY_REF(get_window(client_info, request.window, opcode)); - auto& texture = window.texture(); int32_t new_x = window.x; int32_t new_y = window.y; - uint32_t new_width = texture.width(); - uint32_t new_height = texture.height(); + uint32_t new_width = window.width; + uint32_t new_height = window.height; dprintln(" mask:"); for (size_t i = 0; i < 7; i++) @@ -1743,8 +1140,8 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) const int32_t min_x = BAN::Math::min(window.x, new_x); const int32_t min_y = BAN::Math::min(window.y, new_y); - const int32_t max_x = BAN::Math::max(window.x + texture.width(), new_x + new_width); - const int32_t max_y = BAN::Math::max(window.y + texture.height(), new_y + new_height); + const int32_t max_x = BAN::Math::max(window.x + window.width, new_x + new_width); + const int32_t max_y = BAN::Math::max(window.y + window.height, new_y + new_height); if (new_x != window.x || new_y != window.y) { @@ -1753,16 +1150,23 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) window_changed = true; } - if (new_width != texture.width() || new_height != texture.height()) + if (new_width != window.width || new_height != window.height) { - if (window.window.has>()) + if (window.platform_window) { - window.window.get>()->request_resize(new_width, new_height); + if (g_platform_ops.request_resize) + g_platform_ops.request_resize(window.platform_window.ptr(), new_width, new_height); window_changed = false; } else { - TRY(window.window.get().resize(new_width, new_height)); + window.width = new_width; + window.height = new_height; + + TRY(window.pixels.resize(new_width * new_height)); + for (auto& pixel : window.pixels) + pixel = window.background; + window_changed = true; } } @@ -1770,8 +1174,6 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) if (!window_changed) break; - invalidate_window(request.window, min_x, min_y, max_x - min_x, max_y + min_y); - { xEvent event = { .u = { .configureNotify = { @@ -1780,8 +1182,8 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) .aboveSibling = xFalse, .x = static_cast(window.x), .y = static_cast(window.y), - .width = static_cast(texture.width()), - .height = static_cast(texture.height()), + .width = static_cast(window.width), + .height = static_cast(window.height), .borderWidth = 0, .override = xFalse, } @@ -1802,8 +1204,8 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) .aboveSibling = xFalse, .x = static_cast(window.x), .y = static_cast(window.y), - .width = static_cast(texture.width()), - .height = static_cast(texture.height()), + .width = static_cast(window.width), + .height = static_cast(window.height), .borderWidth = 0, .override = xFalse, } @@ -1853,8 +1255,8 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) else if (drawable.type == Object::Type::Window) { const auto& window = drawable.object.get(); - width = window.texture().width(); - height = window.texture().height(); + width = window.width; + height = window.height; depth = g_root.rootDepth; x = window.x; y = window.y; @@ -1980,7 +1382,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) dprintln(" mode: {}", request.mode); dprintln(" window: {}", request.window); dprintln(" property: {}", g_atoms_id_to_name[request.property]); - dprintln(" type: {}", request.type); + dprintln(" type: {}", g_atoms_id_to_name[request.type]); dprintln(" format: {}", request.format); dprintln(" nUnits: {}", request.nUnits); @@ -2288,7 +1690,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) WINDOW wid = request.destination; if (wid == PointerWindow || wid == InputFocus) - wid = s_focus_window; + wid = g_focus_window; (void)TRY_REF(get_window(client_info, wid, X_SendEvent)); @@ -2453,7 +1855,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) .rootY = static_cast(root_y), .winX = static_cast(event_x), .winY = static_cast(event_y), - .mask = static_cast(s_keymask | s_butmask), + .mask = static_cast(g_keymask | g_butmask), }; TRY(encode(client_info.output_buffer, reply)); @@ -2491,7 +1893,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) .sequenceNumber = client_info.sequence, .length = 2, }; - memcpy(reply.map, s_pressed_keys, 32); + memcpy(reply.map, g_pressed_keys, 32); TRY(encode(client_info.output_buffer, reply)); break; @@ -2520,7 +1922,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) .revertTo = None, .sequenceNumber = client_info.sequence, .length = 0, - .focus = s_focus_window, + .focus = g_focus_window, }; TRY(encode(client_info.output_buffer, reply)); @@ -2776,15 +2178,25 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) dprintln(" height: {}", request.height); auto& window = TRY_REF(get_window(client_info, request.window, opcode)); - auto& texture = window.texture(); if (request.width == 0) - request.width = texture.width() - request.x; + request.width = window.width - request.x; if (request.height == 0) - request.height = texture.height() - request.y; + request.height = window.height - request.y; - texture.clear_rect(request.x, request.y, request.width, request.height); - invalidate_window(request.window, request.x, request.y, request.width, request.height); + const int32_t min_x = BAN::Math::max(request.x, 0); + const int32_t min_y = BAN::Math::max(request.y, 0); + const int32_t max_x = BAN::Math::min(request.x + request.width, window.width); + const int32_t max_y = BAN::Math::min(request.y + request.height, window.height); + + if (min_x >= max_x || min_y >= max_y) + break; + + for (int32_t y = min_y; y < max_y; y++) + for (int32_t x = min_x; x < max_x; x++) + window.pixels[y * window.width + x] = window.background; + + invalidate_window(request.window, min_x, min_y, max_x - min_x, max_y - min_y); break; } @@ -2887,7 +2299,6 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) { auto request = decode(packet).value(); -#if 0 dprintln("PutImage"); dprintln(" drawable: {}", request.drawable); dprintln(" gc: {}", request.gc); @@ -2897,8 +2308,6 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) dprintln(" dstY: {}", request.dstY); dprintln(" width: {}", request.width); dprintln(" height: {}", request.height); -#endif - auto [out_data, out_w, out_h, out_depth] = TRY(get_drawable_info(client_info, request.drawable, opcode)); @@ -3190,7 +2599,7 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) { auto request = decode(packet).value(); - dprintln("FreeCursor"); + dprintln("QueryBestSize"); dprintln(" class: {}", request.c_class); dprintln(" drawable: {}", request.drawable); dprintln(" width: {}", request.width); diff --git a/xbanan/Base.h b/xbanan/Base.h index edd0213..b6dc190 100644 --- a/xbanan/Base.h +++ b/xbanan/Base.h @@ -6,3 +6,14 @@ BAN::ErrorOr setup_client_conneciton(Client& client_info, const xConnClien BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet); void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h); +void send_exposure_recursive(WINDOW wid); + +BAN::ErrorOr destroy_window(Client& client_info, WINDOW wid); + +WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y); + +static constexpr uint32_t COLOR_INVISIBLE = 0x69000000; + +extern CARD16 g_keymask; +extern CARD16 g_butmask; +extern WINDOW g_focus_window; diff --git a/xbanan/CMakeLists.txt b/xbanan/CMakeLists.txt index 11a7568..ecdda6e 100644 --- a/xbanan/CMakeLists.txt +++ b/xbanan/CMakeLists.txt @@ -1,7 +1,8 @@ -set(SOURCES +set(XBANAN_SOURCES main.cpp Base.cpp Drawing.cpp + Events.cpp Extensions.cpp ExtBigReg.cpp ExtRANDR.cpp @@ -12,22 +13,40 @@ set(SOURCES ) option(ENABLE_GLX "enable glx extension" ON) -option(ENABLE_SHM "enable shm extension" ON) - if(ENABLE_GLX) - set(SOURCES ${SOURCES} ExtGLX.cpp) + list(APPEND XBANAN_SOURCES ExtGLX.cpp) endif() +option(ENABLE_SHM "enable shm extension" ON) if(ENABLE_SHM) - set(SOURCES ${SOURCES} ExtSHM.cpp) + list(APPEND XBANAN_SOURCES ExtSHM.cpp) endif() -add_executable(xbanan ${SOURCES}) +set(PLATFORM "banan-os" CACHE STRING "target platform") + +set(VALID_PLATFORMS "banan-os" "SDL2") +if(NOT PLATFORM IN_LIST VALID_PLATFORMS) + message(FATAL_ERROR "platform \"${PLATFORM}\" not known, valid platforms are ${VALID_PLATFORMS}") +endif() + +if(PLATFORM STREQUAL "banan-os") + list(APPEND XBANAN_SOURCES banan-os/banan-os.cpp) +elseif(PLATFORM STREQUAL "SDL2") + list(APPEND XBANAN_SOURCES SDL2/sdl2.cpp) +endif() + +add_executable(xbanan ${XBANAN_SOURCES}) banan_link_library(xbanan ban) banan_link_library(xbanan libdeflate) -banan_link_library(xbanan libgui) banan_link_library(xbanan libinput) +if(PLATFORM STREQUAL "banan-os") + banan_link_library(xbanan libgui) +elseif(PLATFORM STREQUAL "SDL2") + find_package(SDL2 REQUIRED) + banan_link_library(xbanan SDL2::SDL2) +endif() + target_compile_options(xbanan PRIVATE -Wall -Wextra) target_compile_options(xbanan PRIVATE -Wno-sign-compare @@ -36,4 +55,7 @@ target_compile_options(xbanan PRIVATE -Wno-unused-but-set-variable ) +#target_compile_options(xbanan PRIVATE -fsanitize=address) +#target_link_options(xbanan PRIVATE -fsanitize=address) + install(TARGETS xbanan OPTIONAL) diff --git a/xbanan/Definitions.h b/xbanan/Definitions.h index ce2c7e6..c0613c5 100644 --- a/xbanan/Definitions.h +++ b/xbanan/Definitions.h @@ -1,13 +1,14 @@ #pragma once #include "Font.h" +#include "Platform.h" +#include "Types.h" -#include -#include #include #include - -#include +#include +#include +#include #include @@ -18,15 +19,6 @@ #define dprintln(...) #endif -typedef CARD32 ATOM; -typedef CARD32 BITGRAVITY; -typedef CARD32 COLORMAP; -typedef CARD32 CURSOR; -typedef CARD32 PIXMAP; -typedef CARD32 VISUALID; -typedef CARD32 WINDOW; -typedef CARD32 WINGRAVITY; - struct Property { CARD8 format; @@ -60,6 +52,8 @@ struct Object struct Window { + Client& owner; + bool mapped { false }; bool focused { false }; bool fullscreen { false }; @@ -72,33 +66,19 @@ struct Object CURSOR cursor; CARD16 c_class; BAN::Vector children; - BAN::Variant< - BAN::UniqPtr, - LibGUI::Texture - > window; + + uint32_t width { 0 }; + uint32_t height { 0 }; + BAN::Vector pixels; + + uint32_t background { 0 }; + + BAN::UniqPtr platform_window; BAN::HashMap event_masks; BAN::HashMap properties; - LibGUI::Texture& texture() - { - if (window.has()) - return window.get(); - if (window.has>()) - return window.get>()->texture(); - ASSERT_NOT_REACHED(); - } - - const LibGUI::Texture& texture() const - { - if (window.has()) - return window.get(); - if (window.has>()) - return window.get>()->texture(); - ASSERT_NOT_REACHED(); - } - uint32_t full_event_mask() const; BAN::ErrorOr send_event(xEvent event, uint32_t event_mask); }; @@ -181,17 +161,17 @@ struct EpollThingy enum class Type { Client, - Window, + Event, }; Type type; - BAN::Variant value; + BAN::Variant value; }; extern const xPixmapFormat g_formats[6]; -extern const xWindowRoot g_root; extern const xDepth g_depth; extern const xVisualType g_visual; +extern xWindowRoot g_root; extern BAN::HashMap> g_objects; diff --git a/xbanan/Drawing.cpp b/xbanan/Drawing.cpp index 34093b1..a486972 100644 --- a/xbanan/Drawing.cpp +++ b/xbanan/Drawing.cpp @@ -300,6 +300,9 @@ BAN::ErrorOr poly_fill_rectangle(Client& client_info, BAN::ConstByteSpan p const int32_t max_x = BAN::Math::min(rect.x + rect.width, out_w); const int32_t max_y = BAN::Math::min(rect.y + rect.height, out_h); + if (min_x >= max_x || min_y >= max_y) + continue; + for (int32_t y = min_y; y < max_y; y++) for (int32_t x = min_x; x < max_x; x++) if (!gc.is_clipped(x, y)) @@ -351,6 +354,9 @@ BAN::ErrorOr poly_fill_arc(Client& client_info, BAN::ConstByteSpan packet) const int32_t max_x = BAN::Math::min(out_w, arc.x + arc.width); const int32_t max_y = BAN::Math::min(out_h, arc.y + arc.height); + if (min_x >= max_x || min_y >= max_y) + continue; + const auto rx = arc.width / 2; const auto ry = arc.height / 2; diff --git a/xbanan/Events.cpp b/xbanan/Events.cpp new file mode 100644 index 0000000..d744ab8 --- /dev/null +++ b/xbanan/Events.cpp @@ -0,0 +1,559 @@ +#include "Base.h" +#include "Definitions.h" +#include "Keymap.h" +#include "Utils.h" + +#include +#include + +#include +#include + +void register_event_fd(int fd, void* data) +{ + epoll_event event { .events = EPOLLIN, .data = { .fd = fd } }; + epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &event); + + MUST(g_epoll_thingies.insert(fd, { + .type = EpollThingy::Type::Event, + .value = data, + })); +} + +void unregister_event_fd(int fd) +{ + epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); + g_epoll_thingies.remove(fd); +} + +void on_window_close_event(WINDOW wid) +{ + static CARD32 WM_PROTOCOLS = g_atoms_name_to_id["WM_PROTOCOLS"_sv]; + static CARD32 WM_DELETE_WINDOW = g_atoms_name_to_id["WM_DELETE_WINDOW"_sv]; + + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + const bool supports_wm_delete_winow = + [&window] + { + auto wm_protocols_it = window.properties.find(WM_PROTOCOLS); + if (wm_protocols_it == window.properties.end()) + return false; + + const auto& wm_protocols = wm_protocols_it->value; + if (wm_protocols.type != XA_ATOM || wm_protocols.format != 32) + return false; + + const auto atoms = BAN::ConstByteSpan(wm_protocols.data.span()).as_span(); + for (auto atom : atoms) + if (atom == WM_DELETE_WINDOW) + return true; + + return false; + }(); + + if (!supports_wm_delete_winow) + MUST(destroy_window(window.owner, wid)); + else + { + xEvent event { .u = { + .clientMessage = { + .window = wid, + .u = { .l = { + .type = WM_PROTOCOLS, + .longs0 = static_cast(WM_DELETE_WINDOW), + .longs1 = static_cast(time(nullptr)), + }} + } + }}; + event.u.u.type = ClientMessage; + event.u.u.detail = 32; + event.u.u.sequenceNumber = window.owner.sequence; + MUST(encode(window.owner.output_buffer, event)); + } +} + +void on_window_resize_event(WINDOW wid, uint32_t new_width, uint32_t new_height) +{ + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + { + window.width = new_width; + window.height = new_height; + + MUST(window.pixels.resize(new_width * new_height)); + for (auto& pixel : window.pixels) + pixel = window.background; + } + + { + xEvent event = { .u = { + .configureNotify = { + .event = wid, + .window = wid, + .aboveSibling = xFalse, + .x = static_cast(window.x), + .y = static_cast(window.y), + .width = static_cast(window.width), + .height = static_cast(window.height), + .borderWidth = 0, + .override = xFalse, + } + }}; + event.u.u.type = ConfigureNotify; + MUST(window.send_event(event, StructureNotifyMask)); + } + + auto& parent_object = *g_objects[window.parent]; + ASSERT(parent_object.type == Object::Type::Window); + auto& parent_window = parent_object.object.get(); + + { + xEvent event = { .u = { + .configureNotify = { + .event = window.parent, + .window = wid, + .aboveSibling = xFalse, + .x = static_cast(window.x), + .y = static_cast(window.y), + .width = static_cast(window.width), + .height = static_cast(window.height), + .borderWidth = 0, + .override = xFalse, + } + }}; + event.u.u.type = ConfigureNotify; + MUST(parent_window.send_event(event, SubstructureNotifyMask)); + } + + send_exposure_recursive(wid); + + invalidate_window(wid, 0, 0, window.width, window.height); +} + +void on_window_focus_event(WINDOW wid, bool focused) +{ + if (focused) + g_focus_window = wid; + + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + + auto& window = object.object.get(); + if (window.focused == focused) + return; + + window.focused = focused; + + // FIXME: handle children + + xEvent event { .u = { + .focus = { + .window = wid, + .mode = NotifyNormal, + } + }}; + event.u.u.type = focused ? FocusIn : FocusOut, + event.u.u.detail = NotifyNonlinear; + MUST(window.send_event(event, FocusChangeMask)); +} + +void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen) +{ + static CARD32 _NET_WM_STATE = g_atoms_name_to_id["_NET_WM_STATE"_sv]; + static CARD32 _NET_WM_STATE_FULLSCREEN = g_atoms_name_to_id["_NET_WM_STATE_FULLSCREEN"_sv]; + + auto window_it = g_objects.find(wid); + if (window_it == g_objects.end() || window_it->value->type != Object::Type::Window) + return; + + auto& window = window_it->value->object.get(); + + window.fullscreen = is_fullscreen; + + auto it = window.properties.find(_NET_WM_STATE); + if (it == window.properties.end()) + it = MUST(window.properties.emplace(_NET_WM_STATE)); + + auto& _net_wm_state = it->value; + if (_net_wm_state.type != XA_ATOM || _net_wm_state.format != 32) + _net_wm_state = {}; + + _net_wm_state.type = XA_ATOM; + _net_wm_state.format = 32; + + for (size_t i = 0; i + 4 <= _net_wm_state.data.size(); i += 4) + { + const auto atom = *reinterpret_cast(_net_wm_state.data.data() + i); + if (atom != _NET_WM_STATE_FULLSCREEN) + continue; + _net_wm_state.data.remove(i); + _net_wm_state.data.remove(i); + _net_wm_state.data.remove(i); + _net_wm_state.data.remove(i); + i -= 4; + } + + if (is_fullscreen) + { + const size_t atom_count = _net_wm_state.data.size() / 4; + MUST(_net_wm_state.data.resize(_net_wm_state.data.size() + 4)); + reinterpret_cast(_net_wm_state.data.data())[atom_count] = _NET_WM_STATE_FULLSCREEN; + } + + xEvent event = { .u = { + .property = { + .window = wid, + .atom = _NET_WM_STATE, + .time = static_cast(time(nullptr)), + .state = PropertyNewValue, + } + }}; + event.u.u.type = PropertyNotify; + MUST(window.send_event(event, PropertyChangeMask)); +} + +static void send_key_button_pointer_event(WINDOW root_wid, BYTE detail, uint32_t event_mask, BYTE event_type, KeyButMask state) +{ + int32_t root_x, root_y; + int32_t event_x, event_y; + + { + auto& object = *g_objects[root_wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + root_x = event_x = window.cursor_x; + root_y = event_y = window.cursor_y; + } + + const auto child_wid = find_child_window(root_wid, event_x, event_y); + + auto wid = child_wid; + while (wid != None) + { + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + if (window.full_event_mask() & event_mask) + break; + + event_x += window.x; + event_y += window.y; + wid = window.parent; + } + + if (wid == None) + { + if (event_type == MotionNotify && g_butmask == 0) + return; + wid = root_wid; + } + + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + xEvent event { .u = { + .keyButtonPointer = { + .time = static_cast(time(nullptr)), + .root = g_root.windowId, + .event = wid, + .child = static_cast(child_wid == wid ? None : child_wid), + .rootX = static_cast(root_x), + .rootY = static_cast(root_y), + .eventX = static_cast(event_x), + .eventY = static_cast(event_y), + .state = state, + .sameScreen = xTrue, + } + }}; + event.u.u.type = event_type, + event.u.u.detail = detail; + MUST(window.send_event(event, event_mask)); +} + +static void update_cursor_position_recursive(WINDOW wid, int32_t new_x, int32_t new_y) +{ + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + window.cursor_x = new_x; + window.cursor_y = new_y; + + for (auto child_wid : window.children) + { + auto& child_object = *g_objects[child_wid]; + ASSERT(child_object.type == Object::Type::Window); + auto& child_window = child_object.object.get(); + update_cursor_position_recursive(child_wid, new_x - child_window.x, new_y - child_window.y); + } +} + +static void update_cursor(WINDOW wid, int32_t old_x, int32_t old_y, int32_t new_x, int32_t new_y) +{ + const auto old_wid = find_child_window(wid, old_x, old_y); + const auto new_wid = find_child_window(wid, new_x, new_y); + if (old_wid == new_wid || old_wid == None || new_wid == None) + return; + + const auto& old_window = g_objects[old_wid]->object.get(); + const auto& new_window = g_objects[new_wid]->object.get(); + if (old_window.cursor == new_window.cursor) + return; + + auto& window = g_objects[wid]->object.get(); + const auto& cursor = get_cursor_safe(window.cursor); + + if (g_platform_ops.set_cursor) + { + g_platform_ops.set_cursor( + window.platform_window.ptr(), + cursor.pixels.data(), + cursor.width, cursor.height, + cursor.origin_x, cursor.origin_y + ); + } +} + +static BAN::Vector get_path_to_child(WINDOW wid, int32_t x, int32_t y) +{ + BAN::Vector result; + + const auto window_contains = + [](const Object::Window& window, int32_t x, int32_t y) -> bool + { + return x >= 0 && y >= 0 && x < window.width && y < window.height; + }; + + for (;;) + { + const auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + + const auto& window = object.object.get(); + if (!window_contains(window, x, y)) + break; + + MUST(result.push_back(wid)); + + const WINDOW old_wid = wid; + for (auto child_wid : window.children) + { + const auto& child_object = *g_objects[child_wid]; + ASSERT(child_object.type == Object::Type::Window); + + const auto& child_window = child_object.object.get(); + if (!window_contains(child_window, x - child_window.x, y - child_window.y)) + continue; + wid = child_wid; + break; + } + + if (old_wid == wid) + break; + } + + return result; +} + +static void send_enter_and_leave_events(WINDOW wid, int32_t old_x, int32_t old_y, int32_t new_x, int32_t new_y) +{ + // FIXME: correct event_x and event_y values in events + + const auto old_child_path = get_path_to_child(wid, old_x, old_y); + const auto new_child_path = get_path_to_child(wid, new_x, new_y); + + size_t common_ancestors = 0; + while (common_ancestors < old_child_path.size() && common_ancestors < new_child_path.size()) + { + if (old_child_path[common_ancestors] != new_child_path[common_ancestors]) + break; + common_ancestors++; + } + + if (old_child_path.size() == common_ancestors && new_child_path.size() == common_ancestors) + return; + + const bool linear = (common_ancestors == old_child_path.size() || common_ancestors == new_child_path.size()); + + const auto get_flags = + [](WINDOW wid) -> BYTE + { + return ELFlagSameScreen | (g_focus_window == wid ? ELFlagFocus : 0); + }; + + size_t leave_events = old_child_path.size() - common_ancestors; + if (linear && common_ancestors && old_child_path.size() < new_child_path.size()) + leave_events++; + + size_t enter_events = new_child_path.size() - common_ancestors; + if (linear && common_ancestors && new_child_path.size() < old_child_path.size()) + enter_events++; + + for (size_t i = 0; i < leave_events; i++) + { + const bool first = (i == 0); + const BYTE detail = + [&]() -> BYTE { + if (!linear) + return first ? NotifyNonlinear : NotifyNonlinearVirtual; + if (!first) + return NotifyVirtual; + return (old_child_path.size() > new_child_path.size()) + ? NotifyAncestor + : NotifyInferior; + }(); + + const auto& wid = old_child_path[old_child_path.size() - i - 1]; + + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + xEvent event { .u = { + .enterLeave = { + .time = static_cast(time(nullptr)), + .root = g_root.windowId, + .event = wid, + .child = first ? static_cast(None) : old_child_path.back(), + .rootX = static_cast(old_x), + .rootY = static_cast(old_y), + .eventX = static_cast(old_x), + .eventY = static_cast(old_y), + .state = static_cast(g_keymask | g_butmask), + .mode = NotifyNormal, + .flags = get_flags(wid), + } + }}; + event.u.u.type = LeaveNotify, + event.u.u.detail = detail; + MUST(window.send_event(event, LeaveWindowMask)); + } + + for (size_t i = 0; i < enter_events; i++) + { + const bool last = (i == enter_events - 1); + const BYTE detail = + [&]() -> BYTE { + if (!linear) + return last ? NotifyNonlinear : NotifyNonlinearVirtual; + if (!last) + return NotifyVirtual; + return (old_child_path.size() > new_child_path.size()) + ? NotifyInferior + : NotifyAncestor; + }(); + + const auto& wid = new_child_path[new_child_path.size() - enter_events + i]; + + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + xEvent event { .u = { + .enterLeave = { + .time = static_cast(time(nullptr)), + .root = g_root.windowId, + .event = wid, + .child = last ? static_cast(None) : new_child_path.back(), + .rootX = static_cast(new_x), + .rootY = static_cast(new_y), + .eventX = static_cast(new_x), + .eventY = static_cast(new_y), + .state = static_cast(g_keymask | g_butmask), + .mode = NotifyNormal, + .flags = get_flags(wid), + } + }}; + event.u.u.type = EnterNotify, + event.u.u.detail = detail; + MUST(window.send_event(event, EnterWindowMask)); + } +} + +void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y) +{ + auto& object = *g_objects[wid]; + ASSERT(object.type == Object::Type::Window); + auto& window = object.object.get(); + + update_cursor(wid, window.cursor_x, window.cursor_y, x, y); + + send_enter_and_leave_events(wid, window.cursor_x, window.cursor_y, x, y); + + update_cursor_position_recursive(wid, x, y); + + // TODO: optimize with PointerMotionHint + uint32_t event_mask = PointerMotionMask | PointerMotionHintMask; + if (g_butmask) event_mask |= ButtonMotionMask; + if (g_butmask & Button1Mask) event_mask |= Button1MotionMask; + if (g_butmask & Button2Mask) event_mask |= Button2MotionMask; + if (g_butmask & Button3Mask) event_mask |= Button3MotionMask; + if (g_butmask & Button4Mask) event_mask |= Button4MotionMask; + if (g_butmask & Button5Mask) event_mask |= Button5MotionMask; + send_key_button_pointer_event(wid, NotifyNormal, event_mask, MotionNotify, g_keymask | g_butmask); +} + +void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed) +{ + uint16_t mask = 0; + switch (xbutton) + { + case Button1: mask = Button1Mask; break; + case Button2: mask = Button2Mask; break; + case Button3: mask = Button3Mask; break; + case Button4: mask = Button4Mask; break; + case Button5: mask = Button5Mask; break; + } + + const auto old_state = g_keymask | g_butmask; + + if (pressed) + g_butmask |= mask; + else + g_butmask &= ~mask; + + send_key_button_pointer_event( + wid, + xbutton, + pressed ? ButtonPressMask : ButtonReleaseMask, + pressed ? ButtonPress : ButtonRelease, + old_state + ); +} + +void on_key_event(WINDOW wid, uint8_t scancode, uint8_t xmod, bool pressed) +{ + const uint8_t xkeycode = scancode + g_keymap_min_keycode; + if (xkeycode < g_keymap_min_keycode) + { + dwarnln("cannot send keycode {}", xkeycode); + return; + } + + { + const uint8_t byte = xkeycode / 8; + const uint8_t bit = xkeycode % 8; + if (pressed) + g_pressed_keys[byte] |= 1 << bit; + else + g_pressed_keys[byte] &= ~(1 << bit); + } + + const auto old_state = g_keymask | g_butmask; + + g_keymask = xmod; + + send_key_button_pointer_event( + wid, + xkeycode, + pressed ? KeyPressMask : KeyReleaseMask, + pressed ? KeyPress : KeyRelease, + old_state + ); +} diff --git a/xbanan/Events.h b/xbanan/Events.h new file mode 100644 index 0000000..4926d5f --- /dev/null +++ b/xbanan/Events.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Types.h" + +void register_event_fd(int fd, void* data); +void unregister_event_fd(int fd); + +void on_window_close_event(WINDOW wid); +void on_window_resize_event(WINDOW wid, uint32_t width, uint32_t height); +void on_window_focus_event(WINDOW wid, bool focused); +void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen); + +void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y); +void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed); +void on_key_event(WINDOW wid, uint8_t scancode, uint8_t xmod, bool pressed); diff --git a/xbanan/ExtGLX.cpp b/xbanan/ExtGLX.cpp index effa659..7c09e85 100644 --- a/xbanan/ExtGLX.cpp +++ b/xbanan/ExtGLX.cpp @@ -337,7 +337,7 @@ BAN::ErrorOr extension_glx(Client& client_info, BAN::ConstByteSpan packet) const auto& object = g_objects[request.drawable]; ASSERT(object->type == Object::Type::Window); - const auto& texture = object->object.get().texture(); + const auto& window = object->object.get(); xGLXGetDrawableAttributesReply reply { .type = X_Reply, @@ -348,16 +348,16 @@ BAN::ErrorOr extension_glx(Client& client_info, BAN::ConstByteSpan packet) TRY(encode(client_info.output_buffer, reply)); TRY(encode(client_info.output_buffer, GLX_WIDTH)); - TRY(encode(client_info.output_buffer, texture.width())); + TRY(encode(client_info.output_buffer, window.width)); TRY(encode(client_info.output_buffer, GLX_HEIGHT)); - TRY(encode(client_info.output_buffer, texture.height())); + TRY(encode(client_info.output_buffer, window.height)); TRY(encode(client_info.output_buffer, GLX_PRESERVED_CONTENTS)); TRY(encode(client_info.output_buffer, xTrue)); TRY(encode(client_info.output_buffer, GLX_LARGEST_PBUFFER)); - TRY(encode(client_info.output_buffer, texture.width() * texture.height())); + TRY(encode(client_info.output_buffer, window.width * window.height)); TRY(encode(client_info.output_buffer, GLX_FBCONFIG_ID)); TRY(encode(client_info.output_buffer, 1)); diff --git a/xbanan/ExtSHM.cpp b/xbanan/ExtSHM.cpp index b086694..721d2a0 100644 --- a/xbanan/ExtSHM.cpp +++ b/xbanan/ExtSHM.cpp @@ -159,7 +159,6 @@ static BAN::ErrorOr extension_shm(Client& client_info, BAN::ConstByteSpan { auto request = decode(packet).value(); -#if 0 dprintln("ShmPutImage"); dprintln(" drawable: {}", request.drawable); dprintln(" gc: {}", request.gc); @@ -176,7 +175,6 @@ static BAN::ErrorOr extension_shm(Client& client_info, BAN::ConstByteSpan dprintln(" sendEvent: {}", request.sendEvent); dprintln(" shmseg: {}", request.shmseg); dprintln(" offset: {}", request.offset); -#endif void* shm_segment = TRY(get_shmseg(client_info, request.shmseg, op_major, op_minor)); diff --git a/xbanan/Font.cpp b/xbanan/Font.cpp index dafff21..e37d5a9 100644 --- a/xbanan/Font.cpp +++ b/xbanan/Font.cpp @@ -784,7 +784,7 @@ static void write_text(WriteTextInfo& info) ? info.gc.foreground : info.gc.background; - if (color != LibGUI::Texture::color_invisible) + if (color != COLOR_INVISIBLE) info.out_data_u32[out_y * info.out_w + out_x] = color; } } diff --git a/xbanan/Keymap.cpp b/xbanan/Keymap.cpp index 2d618f9..82e607f 100644 --- a/xbanan/Keymap.cpp +++ b/xbanan/Keymap.cpp @@ -14,6 +14,8 @@ uint32_t g_keymap[0x100][g_keymap_layers]; +uint8_t g_pressed_keys[32] {}; + static constexpr uint32_t my_key_to_x_keysym(LibInput::Key key) { using LibInput::Key; diff --git a/xbanan/Keymap.h b/xbanan/Keymap.h index 4a5ad9a..6f73cb4 100644 --- a/xbanan/Keymap.h +++ b/xbanan/Keymap.h @@ -11,3 +11,5 @@ constexpr size_t g_keymap_min_keycode = 8; constexpr size_t g_keymap_max_keycode = 255; constexpr size_t g_keymap_layers = 4; extern uint32_t g_keymap[0x100][g_keymap_layers]; + +extern uint8_t g_pressed_keys[32]; diff --git a/xbanan/Platform.h b/xbanan/Platform.h new file mode 100644 index 0000000..b73e692 --- /dev/null +++ b/xbanan/Platform.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Types.h" + +#include + +struct PlatformWindow +{ + virtual ~PlatformWindow() = default; +}; + +// initialize, poll_events, create_window and invalidate are required +struct PlatformOps +{ + /* Do platform initialization */ + bool (*initialize)(uint32_t* width, uint32_t* height); + /* Handle pending events */ + void (*poll_events)(void*); + /* Create a window with given size */ + BAN::ErrorOr> (*create_window)(WINDOW wid, uint32_t width, uint32_t height); + /* Invaldate part of a window */ + void (*invalidate)(PlatformWindow*, const uint32_t* pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height); + /* Request resize of a window, can be async */ + void (*request_resize)(PlatformWindow*, uint32_t width, uint32_t height); + /* Request new fullscreen state, can be async */ + void (*request_fullscreen)(PlatformWindow*, bool fullscreen); + /* Set custom cursor */ + void (*set_cursor)(PlatformWindow*, const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y); +}; +extern PlatformOps g_platform_ops; diff --git a/xbanan/SDL2/sdl2.cpp b/xbanan/SDL2/sdl2.cpp new file mode 100644 index 0000000..023c182 --- /dev/null +++ b/xbanan/SDL2/sdl2.cpp @@ -0,0 +1,441 @@ +#include "../Events.h" +#include "../Platform.h" + +#include +#include + +#include + +#include +#include +#include + +BAN::HashMap s_window_map; + +struct SDLWindow final : public PlatformWindow +{ + ~SDLWindow() + { + if (texture != nullptr) + SDL_DestroyTexture(texture); + + if (renderer != nullptr) + SDL_DestroyRenderer(renderer); + + if (window != nullptr) + { + s_window_map.remove(SDL_GetWindowID(window)); + SDL_DestroyWindow(window); + } + } + + WINDOW wid; + + uint32_t width; + uint32_t height; + + SDL_Window* window { nullptr }; + SDL_Renderer* renderer { nullptr }; + SDL_Texture* texture { nullptr }; +}; + +static int s_eventfd { -1 }; + +struct Keymap +{ + consteval Keymap(); + uint8_t map[SDL_NUM_SCANCODES]; +}; +static Keymap s_sdl_keymap; + +static void* sdl2_thread(void*) +{ + for (;;) + { + const uint64_t value { 1 }; + write(s_eventfd, &value, sizeof(value)); + usleep(16'666); + } + + return nullptr; +} + +static bool sdl2_initialize(uint32_t* display_w, uint32_t* display_h) +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == -1) + { + dwarnln("Could not initialize SDL: {}", SDL_GetError()); + return false; + } + + SDL_DisplayMode DM; + if (SDL_GetCurrentDisplayMode(0, &DM) == -1) + { + dwarnln("Could not get display mode: {}", SDL_GetError()); + return false; + } + *display_w = DM.w; + *display_h = DM.h; + + s_eventfd = eventfd(0, 0); + if (s_eventfd == -1) + { + dwarnln("Could not create eventfd: {}", strerror(errno)); + return false; + } + + register_event_fd(s_eventfd, nullptr); + + pthread_t thread; + pthread_create(&thread, nullptr, sdl2_thread, nullptr); + + return true; +} + +static bool sdl2_init_window(SDLWindow& window, uint32_t width, uint32_t height) +{ + window.renderer = SDL_CreateRenderer(window.window, -1, SDL_RENDERER_ACCELERATED); + if (window.renderer == nullptr) + { + dwarnln("Could not create SDL renderer: {}\n", SDL_GetError()); + return false; + } + + window.texture = SDL_CreateTexture(window.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + if (window.texture == nullptr) + { + dwarnln("Could not create SDL texture: {}\n", SDL_GetError()); + return false; + } + + return true; +} + +static BAN::ErrorOr> sdl2_create_window(WINDOW wid, uint32_t width, uint32_t height) +{ + auto window = TRY(BAN::UniqPtr::create()); + + window->wid = wid; + window->width = width; + window->height = height; + + const auto flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_UTILITY; + window->window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags); + if (window->window == nullptr) + { + dwarnln("Could not create SDL window: {}", SDL_GetError()); + return BAN::Error::from_errno(EFAULT); + } + + if (!sdl2_init_window(*window, width, height)) + return BAN::Error::from_errno(EFAULT); + + TRY(s_window_map.insert(SDL_GetWindowID(window->window), window.ptr())); + + return BAN::UniqPtr(BAN::move(window)); +} + +static void sdl2_request_resize(PlatformWindow* window, uint32_t width, uint32_t height) +{ + auto& sdl_window = *static_cast(window); + SDL_SetWindowSize(sdl_window.window, width, height); +} + +static void sdl2_poll_events(void*) +{ + uint64_t dummy; + read(s_eventfd, &dummy, sizeof(dummy)); + + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_WINDOWEVENT: + { + auto it = s_window_map.find(event.window.windowID); + if (it == s_window_map.end()) + break; + auto& window = *it->value; + switch (event.window.event) + { + case SDL_WINDOWEVENT_CLOSE: + on_window_close_event(window.wid); + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + window.width = event.window.data1; + window.height = event.window.data2; + + SDL_DestroyTexture(window.texture); + window.texture = SDL_CreateTexture(window.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, window.width, window.height); + ASSERT(window.texture); + + on_window_resize_event(window.wid, event.window.data1, event.window.data2); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + on_window_focus_event(window.wid, true); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + on_window_focus_event(window.wid, false); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + on_window_fullscreen_event(window.wid, true); + break; + case SDL_WINDOWEVENT_RESTORED: + on_window_fullscreen_event(window.wid, false); + break; + } + break; + } + case SDL_MOUSEMOTION: + { + auto it = s_window_map.find(event.window.windowID); + if (it != s_window_map.end()) + on_mouse_move_event(it->value->wid, event.motion.x, event.motion.y); + break; + } + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + { + uint8_t xbutton { 0 }; + switch (event.button.button) + { + case SDL_BUTTON_LEFT: xbutton = 1; break; + case SDL_BUTTON_MIDDLE: xbutton = 2; break; + case SDL_BUTTON_RIGHT: xbutton = 3; break; + case SDL_BUTTON_X1: xbutton = 8; break; + case SDL_BUTTON_X2: xbutton = 9; break; + } + + auto it = s_window_map.find(event.window.windowID); + if (xbutton && it != s_window_map.end()) + on_mouse_button_event(it->value->wid, xbutton, (event.type == SDL_MOUSEBUTTONDOWN)); + break; + } + case SDL_MOUSEWHEEL: + { + uint8_t xbutton { 0 }; + if (event.wheel.y > 0) + xbutton = 4; + if (event.wheel.y < 0) + xbutton = 5; + + auto it = s_window_map.find(event.window.windowID); + if (xbutton && it != s_window_map.end()) + { + on_mouse_button_event(it->value->wid, xbutton, true); + on_mouse_button_event(it->value->wid, xbutton, false); + } + + break; + } + case SDL_KEYUP: + case SDL_KEYDOWN: + { + uint8_t scancode = s_sdl_keymap.map[event.key.keysym.scancode]; + + uint8_t xmod { 0 }; + if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) + xmod |= (1 << 0); + if (event.key.keysym.mod & (KMOD_CAPS)) + xmod |= (1 << 1); + if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) + xmod |= (1 << 2); + if (event.key.keysym.mod & (KMOD_LALT | KMOD_RALT)) + xmod |= (1 << 3); + + auto it = s_window_map.find(event.window.windowID); + if (it != s_window_map.end()) + on_key_event(it->value->wid, scancode, xmod, (event.type == SDL_KEYDOWN)); + break; + } + } + } +} + +static void sdl2_invalidate(PlatformWindow* window, const uint32_t* src_pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ + auto& sdl_window = *static_cast(window); + + ASSERT(x < sdl_window.width && width <= sdl_window.width - x); + ASSERT(y < sdl_window.height && height <= sdl_window.height - y); + + const SDL_Rect rect { + .x = static_cast(x), + .y = static_cast(y), + .w = static_cast(width), + .h = static_cast(height), + }; + + void* dst_pixels; + int dst_pitch; + if (SDL_LockTexture(sdl_window.texture, &rect, &dst_pixels, &dst_pitch) == -1) + { + dwarnln("Could not lock texture: {}", SDL_GetError()); + return; + } + + for (int32_t y_off = 0; y_off < rect.h; y_off++) + { + memcpy( + static_cast(dst_pixels) + y_off * dst_pitch, + &src_pixels[(rect.y + y_off) * sdl_window.width + rect.x], + rect.w * sizeof(uint32_t) + ); + } + + SDL_UnlockTexture(sdl_window.texture); + + SDL_RenderClear(sdl_window.renderer); + SDL_RenderCopy(sdl_window.renderer, sdl_window.texture, NULL, NULL); + SDL_RenderPresent(sdl_window.renderer); +} + +static void sdl2_set_cursor(PlatformWindow* window, const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y) +{ + auto& sdl_window = *static_cast(window); + (void)sdl_window; + (void)pixels; + (void)width; + (void)height; + (void)origin_x; + (void)origin_y; +} + +static void sdl2_request_fullscreen(PlatformWindow* window, bool fullscreen) +{ + auto& sdl_window = *static_cast(window); + SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); +} + +PlatformOps g_platform_ops = { + .initialize = sdl2_initialize, + .poll_events = sdl2_poll_events, + .create_window = sdl2_create_window, + .invalidate = sdl2_invalidate, + .request_resize = sdl2_request_resize, + .request_fullscreen = sdl2_request_fullscreen, + .set_cursor = sdl2_set_cursor, +}; + +#include + +consteval Keymap::Keymap() +{ + for (auto& scancode : map) + scancode = 0; + + using LibInput::keycode_normal; + using LibInput::keycode_function; + using LibInput::keycode_numpad; + + map[SDL_SCANCODE_GRAVE] = keycode_normal(0, 0); + map[SDL_SCANCODE_1] = keycode_normal(0, 1); + map[SDL_SCANCODE_2] = keycode_normal(0, 2); + map[SDL_SCANCODE_3] = keycode_normal(0, 3); + map[SDL_SCANCODE_4] = keycode_normal(0, 4); + map[SDL_SCANCODE_5] = keycode_normal(0, 5); + map[SDL_SCANCODE_6] = keycode_normal(0, 6); + map[SDL_SCANCODE_7] = keycode_normal(0, 7); + map[SDL_SCANCODE_8] = keycode_normal(0, 8); + map[SDL_SCANCODE_9] = keycode_normal(0, 9); + map[SDL_SCANCODE_0] = keycode_normal(0, 10); + map[SDL_SCANCODE_MINUS] = keycode_normal(0, 11); + map[SDL_SCANCODE_EQUALS] = keycode_normal(0, 12); + map[SDL_SCANCODE_BACKSPACE] = keycode_normal(0, 13); + + map[SDL_SCANCODE_TAB] = keycode_normal(1, 0); + map[SDL_SCANCODE_Q] = keycode_normal(1, 1); + map[SDL_SCANCODE_W] = keycode_normal(1, 2); + map[SDL_SCANCODE_E] = keycode_normal(1, 3); + map[SDL_SCANCODE_R] = keycode_normal(1, 4); + map[SDL_SCANCODE_T] = keycode_normal(1, 5); + map[SDL_SCANCODE_Y] = keycode_normal(1, 6); + map[SDL_SCANCODE_U] = keycode_normal(1, 7); + map[SDL_SCANCODE_I] = keycode_normal(1, 8); + map[SDL_SCANCODE_O] = keycode_normal(1, 9); + map[SDL_SCANCODE_P] = keycode_normal(1, 10); + map[SDL_SCANCODE_LEFTBRACKET] = keycode_normal(1, 11); + map[SDL_SCANCODE_RIGHTBRACKET] = keycode_normal(1, 12); + + map[SDL_SCANCODE_CAPSLOCK] = keycode_normal(2, 0); + map[SDL_SCANCODE_A] = keycode_normal(2, 1); + map[SDL_SCANCODE_S] = keycode_normal(2, 2); + map[SDL_SCANCODE_D] = keycode_normal(2, 3); + map[SDL_SCANCODE_F] = keycode_normal(2, 4); + map[SDL_SCANCODE_G] = keycode_normal(2, 5); + map[SDL_SCANCODE_H] = keycode_normal(2, 6); + map[SDL_SCANCODE_J] = keycode_normal(2, 7); + map[SDL_SCANCODE_K] = keycode_normal(2, 8); + map[SDL_SCANCODE_L] = keycode_normal(2, 9); + map[SDL_SCANCODE_SEMICOLON] = keycode_normal(2, 10); + map[SDL_SCANCODE_APOSTROPHE] = keycode_normal(2, 11); + map[SDL_SCANCODE_BACKSLASH] = keycode_normal(2, 12); + map[SDL_SCANCODE_RETURN] = keycode_normal(2, 13); + + map[SDL_SCANCODE_LSHIFT] = keycode_normal(3, 0); + map[SDL_SCANCODE_NONUSBACKSLASH] = keycode_normal(3, 1); + map[SDL_SCANCODE_Z] = keycode_normal(3, 2); + map[SDL_SCANCODE_X] = keycode_normal(3, 3); + map[SDL_SCANCODE_C] = keycode_normal(3, 4); + map[SDL_SCANCODE_V] = keycode_normal(3, 5); + map[SDL_SCANCODE_B] = keycode_normal(3, 6); + map[SDL_SCANCODE_N] = keycode_normal(3, 7); + map[SDL_SCANCODE_M] = keycode_normal(3, 8); + map[SDL_SCANCODE_COMMA] = keycode_normal(3, 9); + map[SDL_SCANCODE_PERIOD] = keycode_normal(3, 10); + map[SDL_SCANCODE_SLASH] = keycode_normal(3, 11); + map[SDL_SCANCODE_RSHIFT] = keycode_normal(3, 12); + + map[SDL_SCANCODE_LCTRL] = keycode_normal(4, 0); + map[SDL_SCANCODE_LGUI] = keycode_normal(4, 1); + map[SDL_SCANCODE_LALT] = keycode_normal(4, 2); + map[SDL_SCANCODE_SPACE] = keycode_normal(4, 3); + map[SDL_SCANCODE_RALT] = keycode_normal(4, 4); + map[SDL_SCANCODE_RCTRL] = keycode_normal(4, 5); + + map[SDL_SCANCODE_UP] = keycode_normal(5, 0); + map[SDL_SCANCODE_LEFT] = keycode_normal(5, 1); + map[SDL_SCANCODE_DOWN] = keycode_normal(5, 2); + map[SDL_SCANCODE_RIGHT] = keycode_normal(5, 3); + + map[SDL_SCANCODE_ESCAPE] = keycode_function(0); + map[SDL_SCANCODE_F1] = keycode_function(1); + map[SDL_SCANCODE_F2] = keycode_function(2); + map[SDL_SCANCODE_F3] = keycode_function(3); + map[SDL_SCANCODE_F4] = keycode_function(4); + map[SDL_SCANCODE_F5] = keycode_function(5); + map[SDL_SCANCODE_F6] = keycode_function(6); + map[SDL_SCANCODE_F7] = keycode_function(7); + map[SDL_SCANCODE_F8] = keycode_function(8); + map[SDL_SCANCODE_F9] = keycode_function(9); + map[SDL_SCANCODE_F10] = keycode_function(10); + map[SDL_SCANCODE_F11] = keycode_function(11); + map[SDL_SCANCODE_F12] = keycode_function(12); + map[SDL_SCANCODE_INSERT] = keycode_function(13); + map[SDL_SCANCODE_PRINTSCREEN] = keycode_function(14); + map[SDL_SCANCODE_DELETE] = keycode_function(15); + map[SDL_SCANCODE_HOME] = keycode_function(16); + map[SDL_SCANCODE_END] = keycode_function(17); + map[SDL_SCANCODE_PAGEUP] = keycode_function(18); + map[SDL_SCANCODE_PAGEDOWN] = keycode_function(19); + map[SDL_SCANCODE_SCROLLLOCK] = keycode_function(20); + + map[SDL_SCANCODE_NUMLOCKCLEAR] = keycode_numpad(0, 0); + map[SDL_SCANCODE_KP_DIVIDE] = keycode_numpad(0, 1); + map[SDL_SCANCODE_KP_MULTIPLY] = keycode_numpad(0, 2); + map[SDL_SCANCODE_KP_MINUS] = keycode_numpad(0, 3); + map[SDL_SCANCODE_KP_7] = keycode_numpad(1, 0); + map[SDL_SCANCODE_KP_8] = keycode_numpad(1, 1); + map[SDL_SCANCODE_KP_9] = keycode_numpad(1, 2); + map[SDL_SCANCODE_KP_PLUS] = keycode_numpad(1, 3); + map[SDL_SCANCODE_KP_4] = keycode_numpad(2, 0); + map[SDL_SCANCODE_KP_5] = keycode_numpad(2, 1); + map[SDL_SCANCODE_KP_6] = keycode_numpad(2, 2); + map[SDL_SCANCODE_KP_1] = keycode_numpad(3, 0); + map[SDL_SCANCODE_KP_2] = keycode_numpad(3, 1); + map[SDL_SCANCODE_KP_3] = keycode_numpad(3, 2); + map[SDL_SCANCODE_KP_ENTER] = keycode_numpad(3, 3); + map[SDL_SCANCODE_KP_0] = keycode_numpad(4, 0); + map[SDL_SCANCODE_KP_COMMA] = keycode_numpad(4, 1); +}; \ No newline at end of file diff --git a/xbanan/SafeGetters.cpp b/xbanan/SafeGetters.cpp index cffab82..0ec6d0e 100644 --- a/xbanan/SafeGetters.cpp +++ b/xbanan/SafeGetters.cpp @@ -87,10 +87,9 @@ BAN::ErrorOr get_drawable_info(Client& client_info, CARD32 drawabl case Object::Type::Window: { auto& window = drawable_it->value->object.get(); - auto& texture = window.texture(); - info.data_u32 = texture.pixels().data(); - info.w = texture.width(); - info.h = texture.height(); + info.data_u32 = window.pixels.data(); + info.w = window.width; + info.h = window.height; info.depth = window.depth; break; } @@ -109,3 +108,14 @@ BAN::ErrorOr get_drawable_info(Client& client_info, CARD32 drawabl return info; } + +Object::Cursor& get_cursor_safe(CURSOR cid) +{ + static Object::Cursor dummy {}; + auto it = g_objects.find(cid); + if (it == g_objects.end()) + return dummy; + if (it->value->type != Object::Type::Cursor) + return dummy; + return it->value->object.get(); +} diff --git a/xbanan/SafeGetters.h b/xbanan/SafeGetters.h index 1649e99..9553a36 100644 --- a/xbanan/SafeGetters.h +++ b/xbanan/SafeGetters.h @@ -13,3 +13,5 @@ BAN::ErrorOr get_window(Client& client_info, CARD32 wid, BYTE o BAN::ErrorOr get_pixmap(Client& client_info, CARD32 pid, BYTE op_major, BYTE op_minor = 0); BAN::ErrorOr get_gc(Client& client_info, CARD32 gc, BYTE op_major, BYTE op_minor = 0); BAN::ErrorOr get_drawable_info(Client& client_info, CARD32 drawable, BYTE op_major, BYTE op_minor = 0); + +Object::Cursor& get_cursor_safe(CURSOR cid); diff --git a/xbanan/Types.h b/xbanan/Types.h new file mode 100644 index 0000000..13b83bb --- /dev/null +++ b/xbanan/Types.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +using ATOM = uint32_t; +using BITGRAVITY = uint32_t; +using COLORMAP = uint32_t; +using CURSOR = uint32_t; +using PIXMAP = uint32_t; +using VISUALID = uint32_t; +using WINDOW = uint32_t; +using WINGRAVITY = uint32_t; diff --git a/xbanan/banan-os/banan-os.cpp b/xbanan/banan-os/banan-os.cpp new file mode 100644 index 0000000..d0d222b --- /dev/null +++ b/xbanan/banan-os/banan-os.cpp @@ -0,0 +1,141 @@ +#include "../Events.h" +#include "../Platform.h" + +#include + +struct BananWindow final : public PlatformWindow +{ + ~BananWindow() + { + if (window) + unregister_event_fd(window->server_fd()); + } + + BAN::UniqPtr window; +}; + +static bool bananos_initialize(uint32_t* display_w, uint32_t* display_h) +{ + auto attributes = LibGUI::Window::default_attributes; + attributes.shown = false; + + auto dummy_or_error = LibGUI::Window::create(0, 0, ""_sv, attributes); + if (dummy_or_error.is_error()) + { + dwarnln("Could not get display size: {}", dummy_or_error.error()); + return false; + } + + *display_w = dummy_or_error.value()->width(); + *display_h = dummy_or_error.value()->height(); + + return true; +} + +static void bananos_poll_events(void* window) +{ + auto& banan_window = *static_cast(window); + banan_window.window->poll_events(); +} + +static BAN::ErrorOr> bananos_create_window(WINDOW wid, uint32_t width, uint32_t height) +{ + auto window = TRY(BAN::UniqPtr::create()); + + auto attributes = LibGUI::Window::default_attributes; + attributes.shown = true; + attributes.title_bar = false; + attributes.resizable = true; + + auto gui_window = TRY(LibGUI::Window::create(width, height, ""_sv, attributes)); + auto* winp = gui_window.ptr(); + + gui_window->set_close_window_event_callback([wid] { + on_window_close_event(wid); + }); + gui_window->set_resize_window_event_callback([wid, winp]() { + on_window_resize_event(wid, winp->width(), winp->height()); + }); + gui_window->set_window_focus_event_callback([wid](auto event) { + on_window_focus_event(wid, event.focused); + }); + gui_window->set_window_fullscreen_event_callback([wid](auto event) { + on_window_fullscreen_event(wid, event.fullscreen); + }); + gui_window->set_mouse_move_event_callback([wid](auto event) { + on_mouse_move_event(wid, event.x, event.y); + }); + gui_window->set_mouse_button_event_callback([wid](auto event) { + uint8_t xbutton = 0; + switch (event.button) + { + case LibInput::MouseButton::Left: xbutton = 1; break; + case LibInput::MouseButton::Middle: xbutton = 2; break; + case LibInput::MouseButton::Right: xbutton = 3; break; + case LibInput::MouseButton::Extra1: xbutton = 8; break; + case LibInput::MouseButton::Extra2: xbutton = 9; break; + } + if (xbutton) + on_mouse_button_event(wid, xbutton, event.pressed); + }); + gui_window->set_mouse_scroll_event_callback([wid](auto event) { + on_mouse_button_event(wid, event.scroll > 0 ? 4 : 5, true); + on_mouse_button_event(wid, event.scroll > 0 ? 4 : 5, false); + }); + gui_window->set_key_event_callback([wid](auto event) { + const uint8_t xmod = + (event.shift() ? (1 << 0) : 0) | + (event.caps_lock() ? (1 << 1) : 0) | + (event.ctrl() ? (1 << 2) : 0) | + (event.alt() ? (1 << 3) : 0); + on_key_event(wid, event.scancode, xmod, event.pressed()); + }); + + window->window = BAN::move(gui_window); + + register_event_fd(window->window->server_fd(), window.ptr()); + + return BAN::UniqPtr(BAN::move(window)); +} + +static void bananos_request_resize(PlatformWindow* window, uint32_t width, uint32_t height) +{ + auto& banan_window = *static_cast(window); + banan_window.window->request_resize(width, height); +} + +static void bananos_invalidate(PlatformWindow* window, const uint32_t* pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ + auto& banan_window = *static_cast(window); + + const uint32_t win_width = banan_window.window->width(); + + auto* out_pixels = banan_window.window->texture().pixels().data(); + for (uint32_t y_off = 0; y_off < height; y_off++) + for (uint32_t x_off = 0; x_off < width; x_off++) + out_pixels[(y + y_off) * win_width + (x + x_off)] = pixels[(y + y_off) * win_width + (x + x_off)]; + + banan_window.window->invalidate(x, y, width, height); +} + +static void bananos_set_cursor(PlatformWindow* window, const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y) +{ + auto& banan_window = *static_cast(window); + banan_window.window->set_cursor(width, height, { pixels, width * height }, origin_x, origin_y); +} + +static void bananos_request_fullscreen(PlatformWindow* window, bool fullscreen) +{ + auto& banan_window = *static_cast(window); + banan_window.window->set_fullscreen(fullscreen); +} + +PlatformOps g_platform_ops = { + .initialize = bananos_initialize, + .poll_events = bananos_poll_events, + .create_window = bananos_create_window, + .invalidate = bananos_invalidate, + .request_resize = bananos_request_resize, + .request_fullscreen = bananos_request_fullscreen, + .set_cursor = bananos_set_cursor, +}; diff --git a/xbanan/main.cpp b/xbanan/main.cpp index afd0144..b630666 100644 --- a/xbanan/main.cpp +++ b/xbanan/main.cpp @@ -11,7 +11,7 @@ #include #include -#define USE_UNIX_SOCKET 0 +#define USE_UNIX_SOCKET 1 #if USE_UNIX_SOCKET #include @@ -19,20 +19,6 @@ #include #endif -static BAN::UniqPtr s_dummy_window = - []() { - auto attributes = LibGUI::Window::default_attributes; - attributes.shown = false; - return MUST(LibGUI::Window::create(0, 0, ""_sv, attributes)); - }(); - -static const xRectangle s_screen_bounds = { - .x = 0, - .y = 0, - .width = static_cast(s_dummy_window->width()), - .height = static_cast(s_dummy_window->height()), -}; - const xPixmapFormat g_formats[6] { { .depth = 1, @@ -81,16 +67,16 @@ const xVisualType g_visual { .blueMask = 0x0000FF, }; -const xWindowRoot g_root { +xWindowRoot g_root { .windowId = 2, .defaultColormap = 0, .whitePixel = 0xFFFFFF, .blackPixel = 0x000000, .currentInputMask = 0, - .pixWidth = s_screen_bounds.width, - .pixHeight = s_screen_bounds.height, - .mmWidth = static_cast(s_screen_bounds.width * 254 / 960), // 96 DPI - .mmHeight = static_cast(s_screen_bounds.height * 254 / 960), // 96 DPI + .pixWidth = 0, + .pixHeight = 0, + .mmWidth = 0, + .mmHeight = 0, .minInstalledMaps = 1, .maxInstalledMaps = 1, .rootVisualID = g_visual.visualID, @@ -183,17 +169,6 @@ int main() } } - { - epoll_event event { .events = EPOLLIN, .data = { .ptr = (void*)1 } }; - if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, s_dummy_window->server_fd(), &event) == -1) - { - perror("xbanan: epoll_ctl"); - return 1; - } - - s_dummy_window->request_resize(1, 1); - } - #define APPEND_ATOM(name) do { \ MUST(g_atoms_id_to_name.insert(name, #name##_sv.substring(3))); \ MUST(g_atoms_name_to_id.insert(#name##_sv.substring(3), name)); \ @@ -281,6 +256,14 @@ int main() MUST(initialize_keymap()); + uint32_t display_w, display_h; + if (!g_platform_ops.initialize(&display_w, &display_h)) + return 1; + g_root.pixWidth = display_w; + g_root.pixHeight = display_h; + g_root.mmWidth = static_cast(display_w * 254 / 960); // 96 DPI + g_root.mmHeight = static_cast(display_h * 254 / 960); // 96 DPI + printf("xbanan started\n"); const auto close_client = @@ -309,28 +292,29 @@ int main() continue; auto& object = *it->value; - if (object.type == Object::Type::Window) + switch (object.type) { - auto& window = object.object.get(); - if (window.window.has>()) + case Object::Type::Visual: + case Object::Type::Cursor: + case Object::Type::Pixmap: + case Object::Type::GraphicsContext: + case Object::Type::Font: + case Object::Type::Window: + break; + case Object::Type::Extension: { - auto& gui_window = window.window.get>(); - epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, gui_window->server_fd(), nullptr); - g_epoll_thingies.remove(gui_window->server_fd()); + auto& extension = object.object.get(); + extension.destructor(extension); + break; } } - if (object.type == Object::Type::Extension) - { - auto& extension = object.object.get(); - extension.destructor(extension); - } g_objects.remove(it); } epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr); - close(client_fd); g_epoll_thingies.remove(client_fd); + close(client_fd); if (client_fd == g_server_grabber_fd) { @@ -353,15 +337,18 @@ int main() } }; + Client dummy_client {}; MUST(g_objects.insert(g_root.windowId, MUST(BAN::UniqPtr::create(Object { .type = Object::Type::Window, .object = Object::Window { + .owner = dummy_client, .mapped = true, .depth = g_root.rootDepth, .parent = None, .c_class = InputOutput, - .window = {}, + .width = g_root.pixWidth, + .height = g_root.pixHeight, } })) )); @@ -412,21 +399,14 @@ int main() continue; } - if (events[i].data.ptr == (void*)1) - { - s_dummy_window->poll_events(); - continue; - } - auto it = g_epoll_thingies.find(events[i].data.fd); if (it == g_epoll_thingies.end()) continue; auto& epoll_thingy = it->value; - if (epoll_thingy.type == EpollThingy::Type::Window) + if (epoll_thingy.type == EpollThingy::Type::Event) { - auto* window = epoll_thingy.value.get(); - window->poll_events(); + g_platform_ops.poll_events(epoll_thingy.value.get()); continue; }