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
This commit is contained in:
2026-05-30 01:17:54 +03:00
parent b2c642f03d
commit 2ddd67dede
20 changed files with 1508 additions and 879 deletions

9
.clangd Normal file
View File

@@ -0,0 +1,9 @@
Diagnostics:
Suppress: [
c99-designator,
]
CompileFlags:
Add: [
-std=c++20,
]

File diff suppressed because it is too large Load Diff

View File

@@ -6,3 +6,14 @@ BAN::ErrorOr<void> setup_client_conneciton(Client& client_info, const xConnClien
BAN::ErrorOr<void> 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<void> 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;

View File

@@ -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)

View File

@@ -1,13 +1,14 @@
#pragma once
#include "Font.h"
#include "Platform.h"
#include "Types.h"
#include <BAN/Vector.h>
#include <BAN/UniqPtr.h>
#include <BAN/HashMap.h>
#include <BAN/HashSet.h>
#include <LibGUI/Window.h>
#include <BAN/String.h>
#include <BAN/UniqPtr.h>
#include <BAN/Vector.h>
#include <X11/Xproto.h>
@@ -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<WINDOW> children;
BAN::Variant<
BAN::UniqPtr<LibGUI::Window>,
LibGUI::Texture
> window;
uint32_t width { 0 };
uint32_t height { 0 };
BAN::Vector<uint32_t> pixels;
uint32_t background { 0 };
BAN::UniqPtr<PlatformWindow> platform_window;
BAN::HashMap<Client*, uint32_t> event_masks;
BAN::HashMap<ATOM, Property> properties;
LibGUI::Texture& texture()
{
if (window.has<LibGUI::Texture>())
return window.get<LibGUI::Texture>();
if (window.has<BAN::UniqPtr<LibGUI::Window>>())
return window.get<BAN::UniqPtr<LibGUI::Window>>()->texture();
ASSERT_NOT_REACHED();
}
const LibGUI::Texture& texture() const
{
if (window.has<LibGUI::Texture>())
return window.get<LibGUI::Texture>();
if (window.has<BAN::UniqPtr<LibGUI::Window>>())
return window.get<BAN::UniqPtr<LibGUI::Window>>()->texture();
ASSERT_NOT_REACHED();
}
uint32_t full_event_mask() const;
BAN::ErrorOr<void> send_event(xEvent event, uint32_t event_mask);
};
@@ -181,17 +161,17 @@ struct EpollThingy
enum class Type
{
Client,
Window,
Event,
};
Type type;
BAN::Variant<Client, LibGUI::Window*> value;
BAN::Variant<Client, void*> 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<CARD32, BAN::UniqPtr<Object>> g_objects;

View File

@@ -300,6 +300,9 @@ BAN::ErrorOr<void> poly_fill_rectangle(Client& client_info, BAN::ConstByteSpan p
const int32_t max_x = BAN::Math::min<int32_t>(rect.x + rect.width, out_w);
const int32_t max_y = BAN::Math::min<int32_t>(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<void> poly_fill_arc(Client& client_info, BAN::ConstByteSpan packet)
const int32_t max_x = BAN::Math::min<int32_t>(out_w, arc.x + arc.width);
const int32_t max_y = BAN::Math::min<int32_t>(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;

559
xbanan/Events.cpp Normal file
View File

@@ -0,0 +1,559 @@
#include "Base.h"
#include "Definitions.h"
#include "Keymap.h"
#include "Utils.h"
#include <X11/X.h>
#include <X11/Xatom.h>
#include <time.h>
#include <sys/epoll.h>
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<Object::Window>();
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<const CARD32>();
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<INT32>(WM_DELETE_WINDOW),
.longs1 = static_cast<INT32>(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<Object::Window>();
{
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<INT16>(window.x),
.y = static_cast<INT16>(window.y),
.width = static_cast<CARD16>(window.width),
.height = static_cast<CARD16>(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<Object::Window>();
{
xEvent event = { .u = {
.configureNotify = {
.event = window.parent,
.window = wid,
.aboveSibling = xFalse,
.x = static_cast<INT16>(window.x),
.y = static_cast<INT16>(window.y),
.width = static_cast<CARD16>(window.width),
.height = static_cast<CARD16>(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<Object::Window>();
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<Object::Window>();
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<const CARD32*>(_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<CARD32*>(_net_wm_state.data.data())[atom_count] = _NET_WM_STATE_FULLSCREEN;
}
xEvent event = { .u = {
.property = {
.window = wid,
.atom = _NET_WM_STATE,
.time = static_cast<CARD32>(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<Object::Window>();
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<Object::Window>();
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<Object::Window>();
xEvent event { .u = {
.keyButtonPointer = {
.time = static_cast<CARD32>(time(nullptr)),
.root = g_root.windowId,
.event = wid,
.child = static_cast<CARD32>(child_wid == wid ? None : child_wid),
.rootX = static_cast<INT16>(root_x),
.rootY = static_cast<INT16>(root_y),
.eventX = static_cast<INT16>(event_x),
.eventY = static_cast<INT16>(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<Object::Window>();
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<Object::Window>();
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<Object::Window>();
const auto& new_window = g_objects[new_wid]->object.get<Object::Window>();
if (old_window.cursor == new_window.cursor)
return;
auto& window = g_objects[wid]->object.get<Object::Window>();
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<WINDOW> get_path_to_child(WINDOW wid, int32_t x, int32_t y)
{
BAN::Vector<WINDOW> 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<Object::Window>();
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<Object::Window>();
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<Object::Window>();
xEvent event { .u = {
.enterLeave = {
.time = static_cast<CARD32>(time(nullptr)),
.root = g_root.windowId,
.event = wid,
.child = first ? static_cast<WINDOW>(None) : old_child_path.back(),
.rootX = static_cast<INT16>(old_x),
.rootY = static_cast<INT16>(old_y),
.eventX = static_cast<INT16>(old_x),
.eventY = static_cast<INT16>(old_y),
.state = static_cast<KeyButMask>(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<Object::Window>();
xEvent event { .u = {
.enterLeave = {
.time = static_cast<CARD32>(time(nullptr)),
.root = g_root.windowId,
.event = wid,
.child = last ? static_cast<WINDOW>(None) : new_child_path.back(),
.rootX = static_cast<INT16>(new_x),
.rootY = static_cast<INT16>(new_y),
.eventX = static_cast<INT16>(new_x),
.eventY = static_cast<INT16>(new_y),
.state = static_cast<KeyButMask>(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<Object::Window>();
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
);
}

15
xbanan/Events.h Normal file
View File

@@ -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);

View File

@@ -337,7 +337,7 @@ BAN::ErrorOr<void> 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<Object::Window>().texture();
const auto& window = object->object.get<Object::Window>();
xGLXGetDrawableAttributesReply reply {
.type = X_Reply,
@@ -348,16 +348,16 @@ BAN::ErrorOr<void> extension_glx(Client& client_info, BAN::ConstByteSpan packet)
TRY(encode(client_info.output_buffer, reply));
TRY(encode<CARD32>(client_info.output_buffer, GLX_WIDTH));
TRY(encode<CARD32>(client_info.output_buffer, texture.width()));
TRY(encode<CARD32>(client_info.output_buffer, window.width));
TRY(encode<CARD32>(client_info.output_buffer, GLX_HEIGHT));
TRY(encode<CARD32>(client_info.output_buffer, texture.height()));
TRY(encode<CARD32>(client_info.output_buffer, window.height));
TRY(encode<CARD32>(client_info.output_buffer, GLX_PRESERVED_CONTENTS));
TRY(encode<CARD32>(client_info.output_buffer, xTrue));
TRY(encode<CARD32>(client_info.output_buffer, GLX_LARGEST_PBUFFER));
TRY(encode<CARD32>(client_info.output_buffer, texture.width() * texture.height()));
TRY(encode<CARD32>(client_info.output_buffer, window.width * window.height));
TRY(encode<CARD32>(client_info.output_buffer, GLX_FBCONFIG_ID));
TRY(encode<CARD32>(client_info.output_buffer, 1));

View File

@@ -159,7 +159,6 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
{
auto request = decode<xShmPutImageReq>(packet).value();
#if 0
dprintln("ShmPutImage");
dprintln(" drawable: {}", request.drawable);
dprintln(" gc: {}", request.gc);
@@ -176,7 +175,6 @@ static BAN::ErrorOr<void> 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));

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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];

30
xbanan/Platform.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include "Types.h"
#include <BAN/UniqPtr.h>
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<BAN::UniqPtr<PlatformWindow>> (*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;

441
xbanan/SDL2/sdl2.cpp Normal file
View File

@@ -0,0 +1,441 @@
#include "../Events.h"
#include "../Platform.h"
#include <BAN/Atomic.h>
#include <BAN/HashMap.h>
#include <SDL2/SDL.h>
#include <pthread.h>
#include <sys/eventfd.h>
#include <unistd.h>
BAN::HashMap<uint32_t, struct SDLWindow*> 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<BAN::UniqPtr<PlatformWindow>> sdl2_create_window(WINDOW wid, uint32_t width, uint32_t height)
{
auto window = TRY(BAN::UniqPtr<SDLWindow>::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<PlatformWindow>(BAN::move(window));
}
static void sdl2_request_resize(PlatformWindow* window, uint32_t width, uint32_t height)
{
auto& sdl_window = *static_cast<SDLWindow*>(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<SDLWindow*>(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<int>(x),
.y = static_cast<int>(y),
.w = static_cast<int>(width),
.h = static_cast<int>(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<uint8_t*>(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<SDLWindow*>(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<SDLWindow*>(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 <LibInput/KeyEvent.h>
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);
};

View File

@@ -87,10 +87,9 @@ BAN::ErrorOr<DrawableInfo> get_drawable_info(Client& client_info, CARD32 drawabl
case Object::Type::Window:
{
auto& window = drawable_it->value->object.get<Object::Window>();
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<DrawableInfo> 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<Object::Cursor>();
}

View File

@@ -13,3 +13,5 @@ BAN::ErrorOr<Object::Window&> get_window(Client& client_info, CARD32 wid, BYTE o
BAN::ErrorOr<Object::Pixmap&> get_pixmap(Client& client_info, CARD32 pid, BYTE op_major, BYTE op_minor = 0);
BAN::ErrorOr<Object::GraphicsContext&> get_gc(Client& client_info, CARD32 gc, BYTE op_major, BYTE op_minor = 0);
BAN::ErrorOr<DrawableInfo> get_drawable_info(Client& client_info, CARD32 drawable, BYTE op_major, BYTE op_minor = 0);
Object::Cursor& get_cursor_safe(CURSOR cid);

12
xbanan/Types.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
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;

View File

@@ -0,0 +1,141 @@
#include "../Events.h"
#include "../Platform.h"
#include <LibGUI/Window.h>
struct BananWindow final : public PlatformWindow
{
~BananWindow()
{
if (window)
unregister_event_fd(window->server_fd());
}
BAN::UniqPtr<LibGUI::Window> 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<BananWindow*>(window);
banan_window.window->poll_events();
}
static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> bananos_create_window(WINDOW wid, uint32_t width, uint32_t height)
{
auto window = TRY(BAN::UniqPtr<BananWindow>::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<PlatformWindow>(BAN::move(window));
}
static void bananos_request_resize(PlatformWindow* window, uint32_t width, uint32_t height)
{
auto& banan_window = *static_cast<BananWindow*>(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<BananWindow*>(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<BananWindow*>(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<BananWindow*>(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,
};

View File

@@ -11,7 +11,7 @@
#include <sys/stat.h>
#include <unistd.h>
#define USE_UNIX_SOCKET 0
#define USE_UNIX_SOCKET 1
#if USE_UNIX_SOCKET
#include <sys/un.h>
@@ -19,20 +19,6 @@
#include <netinet/in.h>
#endif
static BAN::UniqPtr<LibGUI::Window> 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<CARD16>(s_dummy_window->width()),
.height = static_cast<CARD16>(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<CARD16>(s_screen_bounds.width * 254 / 960), // 96 DPI
.mmHeight = static_cast<CARD16>(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<CARD16>(display_w * 254 / 960); // 96 DPI
g_root.mmHeight = static_cast<CARD16>(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<Object::Window>();
if (window.window.has<BAN::UniqPtr<LibGUI::Window>>())
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<BAN::UniqPtr<LibGUI::Window>>();
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<Object::Extension>();
extension.destructor(extension);
break;
}
}
if (object.type == Object::Type::Extension)
{
auto& extension = object.object.get<Object::Extension>();
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<Object>::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<LibGUI::Window*>();
window->poll_events();
g_platform_ops.poll_events(epoll_thingy.value.get<void*>());
continue;
}