Compare commits

...

10 Commits

Author SHA1 Message Date
16d57d0194 Destroy client's windows on disconnect
This makes sure client's windows are removed from the root window and
all of the destroy events are sent.
2026-06-01 05:01:11 +03:00
f1a0738d13 Fix QueryPointer
QueryPointer is supposed to return first child of the window containing
the pointer, not the deepest child. This fixes gtk getting into an
invalid state when pressing a mouse button
2026-06-01 04:55:43 +03:00
b81ff29e00 Handle external window leave events 2026-06-01 04:55:43 +03:00
0dc88d09bf Don't create platform window for windows without WM_CLASS
gtk and other toolkits create a lot of dummy windows that should not be
visible.
2026-06-01 04:55:43 +03:00
97e82afd4d Optimize {,Shm}PutImage
Don't calculate clipping when its not needed. This can still be improved
by a ton, but it can wait :)
2026-06-01 04:55:43 +03:00
d787afb7e3 Disable shared pixmaps on MIT-SHM
Our internal pixmap representation does not match what we advertice in
out pixmap formats, and there is no way to only enable 24 and 32 depth
pixmaps for SHM so just disable them all together. MIT-SHM can still be
used through Shm{Get,Put}Image. We probably should store pixmaps as we
say we do, but its too much work for now :^)
2026-06-01 04:55:43 +03:00
f6a87f064f Add support for system cursors
When a client creates a cursor from the default cursor font, we try to
detect which cursor it is and create a native system cursor based on
that.
2026-06-01 04:55:43 +03:00
7fe5b0174f Allow sending enter/leave events between different native windows
This fixes hovering on popups
2026-06-01 03:01:31 +03:00
6450fd9a2f Add initial support for window types
This makes popup windows work much better
2026-06-01 03:01:31 +03:00
2ddd67dede 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
2026-06-01 01:27:49 +03:00
22 changed files with 1950 additions and 983 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,16 @@ 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);
void update_cursor(WINDOW wid, int32_t x, int32_t y);
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;
@@ -49,20 +41,15 @@ struct Object
Type type;
struct Cursor
{
uint32_t width;
uint32_t height;
int32_t origin_x;
int32_t origin_y;
BAN::Vector<uint32_t> pixels;
};
struct Window
{
Client& owner;
bool mapped { false };
bool focused { false };
bool fullscreen { false };
bool hovered { false };
uint8_t depth { 0 };
int32_t x { 0 };
int32_t y { 0 };
@@ -72,33 +59,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);
};
@@ -108,8 +81,7 @@ struct Object
CARD8 depth;
CARD32 width;
CARD32 height;
BAN::ByteSpan data;
BAN::Vector<uint8_t> owned_data;
BAN::Vector<uint8_t> data;
};
struct GraphicsContext
@@ -156,7 +128,7 @@ struct Object
void (*destructor)(Extension&);
};
BAN::Variant<Cursor, Window, Pixmap, GraphicsContext, BAN::RefPtr<PCFFont>, Extension> object;
BAN::Variant<Window, Pixmap, GraphicsContext, BAN::RefPtr<PCFFont>, BAN::UniqPtr<PlatformCursor>, Extension> object;
};
struct Client
@@ -181,17 +153,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;

563
xbanan/Events.cpp Normal file
View File

@@ -0,0 +1,563 @@
#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 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 old_wid, int32_t old_x, int32_t old_y, WINDOW new_wid, 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(old_wid, old_x, old_y);
const auto new_child_path = get_path_to_child(new_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));
}
for (const auto wid : old_child_path)
g_objects[wid]->object.get<Object::Window>().hovered = false;
for (const auto wid : new_child_path)
g_objects[wid]->object.get<Object::Window>().hovered = true;
}
static WINDOW s_current_top_level_window = None;
void on_window_leave_event(WINDOW wid)
{
if (s_current_top_level_window != wid)
return;
auto& object = *g_objects[wid];
ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>();
send_enter_and_leave_events(wid, window.cursor_x, window.cursor_y, wid, -1, -1);
s_current_top_level_window = None;
}
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, x, y);
if (auto it = g_objects.find(s_current_top_level_window); it == g_objects.end())
send_enter_and_leave_events(wid, -1, -1, wid, x, y);
else
{
ASSERT(it->value->type == Object::Type::Window);
auto& old_window = it->value->object.get<Object::Window>();
send_enter_and_leave_events(s_current_top_level_window, old_window.cursor_x, old_window.cursor_y, wid, x, y);
}
s_current_top_level_window = wid;
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
);
}

16
xbanan/Events.h Normal file
View File

@@ -0,0 +1,16 @@
#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_window_leave_event(WINDOW wid);
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

@@ -7,42 +7,13 @@
#include <X11/X.h>
#include <X11/extensions/shmproto.h>
#include <netinet/in.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <unistd.h>
static BYTE s_shm_event_base;
static BYTE s_shm_error_base;
static BYTE s_shm_major_opcode;
static bool is_local_socket(int socket)
{
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (getpeername(socket, reinterpret_cast<sockaddr*>(&addr), &addr_len) == -1)
return false;
switch (addr.ss_family)
{
case AF_UNIX:
return true;
case AF_INET:
{
const auto* addr_in = reinterpret_cast<const sockaddr_in*>(&addr);
const auto ipv4 = ntohl(addr_in->sin_addr.s_addr);
return (ipv4 & IN_CLASSA_NET) == IN_LOOPBACKNET;
}
case AF_INET6:
{
const auto* addr_in6 = reinterpret_cast<const sockaddr_in6*>(&addr);
return IN6_IS_ADDR_LOOPBACK(&addr_in6->sin6_addr);
}
}
return false;
}
static BAN::ErrorOr<void*> get_shmseg(Client& client_info, CARD32 shmseg, BYTE op_major, BYTE op_minor)
{
auto it = g_objects.find(shmseg);
@@ -78,7 +49,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
xShmQueryVersionReply reply {
.type = X_Reply,
.sharedPixmaps = is_local_socket(client_info.fd),
.sharedPixmaps = false,
.sequenceNumber = client_info.sequence,
.length = 0,
.majorVersion = 1,
@@ -159,7 +130,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 +146,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));
@@ -267,40 +236,6 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
break;
}
case X_ShmCreatePixmap:
{
auto request = decode<xShmCreatePixmapReq>(packet).value();
dprintln("ShmCreatePixmap");
dprintln(" depth: {}", request.depth);
dprintln(" pid: {}", request.pid);
dprintln(" drawable: {}", request.drawable);
dprintln(" width: {}", request.width);
dprintln(" height: {}", request.height);
dprintln(" shmseg: {}", request.shmseg);
dprintln(" offset: {}", request.offset);
ASSERT(request.depth == 24 || request.depth == 32);
void* shm_segment = TRY(get_shmseg(client_info, request.shmseg, op_major, op_minor));
TRY(client_info.objects.insert(request.pid));
TRY(g_objects.insert(
request.pid,
TRY(BAN::UniqPtr<Object>::create(Object {
.type = Object::Type::Pixmap,
.object = Object::Pixmap {
.depth = request.depth,
.width = request.width,
.height = request.height,
.data = BAN::ByteSpan(static_cast<uint8_t*>(shm_segment) + request.offset, request.width * request.height * 4),
.owned_data = {},
}
}))
));
break;
}
default:
dwarnln("unsupported shm minor opcode {}", packet[1]);
break;

View File

@@ -385,6 +385,8 @@ static BAN::ErrorOr<BAN::RefPtr<PCFFont>> parse_font(const BAN::String& path)
font->font_ascent = font->max_bounds.ascent;
font->font_descent = font->max_bounds.descent;
font->is_cursor_font = (path == "fonts/misc/cursor.pcf.gz"_sv);
return font;
}
@@ -784,7 +786,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;
}
}
@@ -938,6 +940,49 @@ BAN::ErrorOr<void> create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p
const auto& source_font = TRY(get_fontable(client_info, request.source, X_CreateGlyphCursor));
// Try to use system cursor for known cursors
if (source_font->is_cursor_font && request.mask == request.source && request.sourceChar + 1 == request.maskChar && g_platform_ops.create_system_cursor)
{
BAN::Optional<SystemCursorType> type;
switch (request.sourceChar)
{
case 68: type = SystemCursorType::Pointer; break;
case 152: type = SystemCursorType::Text; break;
case 150: type = SystemCursorType::Wait; break;
case 60: type = SystemCursorType::Hand; break;
case 92: type = SystemCursorType::Help; break;
case 52: type = SystemCursorType::Move; break;
case 88: type = SystemCursorType::Forbidden; break;
case 138: type = SystemCursorType::ResizeN; break;
case 96: type = SystemCursorType::ResizeE; break;
case 16: type = SystemCursorType::ResizeS; break;
case 70: type = SystemCursorType::ResizeW; break;
case 134: type = SystemCursorType::ResizeNW; break;
case 136: type = SystemCursorType::ResizeNE; break;
case 12: type = SystemCursorType::ResizeSW; break;
case 14: type = SystemCursorType::ResizeSE; break;
case 108: type = SystemCursorType::ResizeHorizontal; break;
case 116: type = SystemCursorType::ResizeVertical; break;
}
if (type.has_value())
{
auto cursor_or_error = g_platform_ops.create_system_cursor(type.value());
if (!cursor_or_error.is_error())
{
TRY(client_info.objects.insert(request.cid));
TRY(g_objects.insert(
request.cid,
TRY(BAN::UniqPtr<Object>::create(Object {
.type = Object::Type::Cursor,
.object = BAN::move(cursor_or_error.value()),
}))
));
return {};
}
}
}
auto source_glyph_index = source_font->find_glyph(request.sourceChar);
if (!source_glyph_index.has_value())
{
@@ -958,19 +1003,14 @@ BAN::ErrorOr<void> create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p
const uint32_t source_width = source_ci.rightSideBearing - source_ci.leftSideBearing;
const uint32_t source_height = source_ci.ascent + source_ci.descent;
Object::Cursor cursor {
.width = source_width,
.height = source_height,
.origin_x = -source_ci.leftSideBearing,
.origin_y = source_ci.ascent,
};
TRY(cursor.pixels.resize(cursor.width * cursor.height));
BAN::Vector<uint32_t> pixels;
TRY(pixels.resize(source_width * source_height));
for (uint32_t y = 0; y < source_height; y++)
{
const uint8_t* row_base = source_font->bitmap.data() + source_glyph.bitmap_offset + (source_width + 7) / 8 * y;
for (uint32_t x = 0; x < source_width; x++)
cursor.pixels[y * source_width + x] = 0xFF000000 | ((row_base[x / 8] & (1 << (x % 8))) ? foreground : background);
pixels[y * source_width + x] = 0xFF000000 | ((row_base[x / 8] & (1 << (x % 8))) ? foreground : background);
}
if (request.mask != None)
@@ -1008,11 +1048,19 @@ BAN::ErrorOr<void> create_glyph_cursor(Client& client_info, BAN::ConstByteSpan p
const uint8_t* row_base = mask_font->bitmap.data() + mask_glyph.bitmap_offset + (mask_width + 7) / 8 * mask_y;
if (!(row_base[mask_x / 8] & (1 << (mask_x % 8))))
cursor.pixels[src_y * source_width + src_x] = 0;
pixels[src_y * source_width + src_x] = 0;
}
}
}
BAN::UniqPtr<PlatformCursor> cursor;
if (g_platform_ops.create_bitmap_cursor)
{
auto cursor_or_error = g_platform_ops.create_bitmap_cursor(pixels.data(), source_width, source_height, -source_ci.leftSideBearing, source_ci.ascent);
if (!cursor_or_error.is_error())
cursor = cursor_or_error.release_value();
}
TRY(client_info.objects.insert(request.cid));
TRY(g_objects.insert(
request.cid,

View File

@@ -40,6 +40,8 @@ struct PCFFont : public BAN::RefCounted<PCFFont>, public BAN::Weakable<PCFFont>
BAN::Vector<MapEntry> map;
BAN::Vector<uint8_t> bitmap;
bool is_cursor_font;
BAN::Optional<uint16_t> find_glyph(uint16_t codepoint) const
{
size_t l = 0;

View File

@@ -60,8 +60,33 @@ void put_image(const PutImageInfo& info)
}
case ZPixmap:
{
bool needs_clipping = true;
if (info.gc.clip_mask == None)
{
needs_clipping = false;
}
else if (info.gc.clip_mask == UINT32_MAX)
{
const uint32_t clip_min_x = min_x - info.gc.clip_origin_x;
const uint32_t clip_min_y = min_y - info.gc.clip_origin_y;
const uint32_t clip_max_x = max_x - info.gc.clip_origin_x;
const uint32_t clip_max_y = max_y - info.gc.clip_origin_y;
for (const auto& rect : info.gc.clip_rects)
{
if (clip_min_x < rect.x || clip_max_x > rect.x + rect.width)
continue;
if (clip_min_y < rect.y || clip_max_y > rect.y + rect.height)
continue;
needs_clipping = false;
break;
}
}
ASSERT(info.left_pad == 0);
if (in_bpp == 32 && info.gc.clip_mask == None)
if (in_bpp == 32 && !needs_clipping)
{
const auto bytes_per_row = (max_x - min_x) * 4;
for (int32_t y = min_y; y < max_y; y++)

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

67
xbanan/Platform.h Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include "Types.h"
#include <BAN/UniqPtr.h>
struct PlatformWindow
{
virtual ~PlatformWindow() = default;
};
struct PlatformCursor
{
virtual ~PlatformCursor() = default;
};
enum class WindowType
{
Popup,
Normal,
Utility,
};
enum class SystemCursorType
{
Pointer,
Text,
Wait,
Hand,
Help,
Move,
Forbidden,
ResizeN,
ResizeE,
ResizeS,
ResizeW,
ResizeNW,
ResizeNE,
ResizeSW,
ResizeSE,
ResizeVertical,
ResizeHorizontal,
};
// 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)(PlatformWindow* parent, WindowType, WINDOW wid, int32_t x, int32_t y, 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);
/* Create a system cursor */
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_system_cursor)(SystemCursorType);
/* Create cursor from custom bitmap */
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_bitmap_cursor)(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y);
/* Set custom cursor */
void (*set_cursor)(PlatformWindow*, PlatformCursor*);
};
extern PlatformOps g_platform_ops;

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

@@ -0,0 +1,559 @@
#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 };
};
struct SDLCursor final : public PlatformCursor
{
~SDLCursor()
{
if (cursor != nullptr)
SDL_FreeCursor(cursor);
}
SDL_Cursor* cursor { nullptr };
};
static int s_eventfd { -1 };
struct Keymap
{
consteval Keymap();
uint8_t map[SDL_NUM_SCANCODES];
};
static Keymap s_sdl_keymap;
static SDL_Cursor* s_default_cursor { nullptr };
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_default_cursor = SDL_GetCursor();
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 BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl2_create_window(PlatformWindow* parent, WindowType type, WINDOW wid, int32_t x, int32_t y, uint32_t width, uint32_t height)
{
auto window = TRY(BAN::UniqPtr<SDLWindow>::create());
window->wid = wid;
window->width = width;
window->height = height;
if (parent == nullptr)
x = y = SDL_WINDOWPOS_UNDEFINED;
else
{
auto& sdl_parent = *static_cast<SDLWindow*>(parent);
int parent_x, parent_y;
SDL_GetWindowPosition(sdl_parent.window, &parent_x, &parent_y);
x += parent_x;
y += parent_y;
}
int flags;
switch (type)
{
case WindowType::Popup:
flags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_UTILITY;
break;
case WindowType::Utility:
flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_UTILITY;
break;
case WindowType::Normal:
flags = SDL_WINDOW_RESIZABLE;
break;
}
window->window = SDL_CreateWindow("", x, y, width, height, flags);
if (window->window == nullptr)
{
dwarnln("Could not create SDL window: {}", SDL_GetError());
return BAN::Error::from_errno(EFAULT);
}
window->renderer = SDL_CreateRenderer(window->window, -1, SDL_RENDERER_ACCELERATED);
if (window->renderer == nullptr)
{
dwarnln("Could not create SDL renderer: {}", SDL_GetError());
return BAN::Error::from_errno(EFAULT);
}
window->texture = SDL_CreateTexture(window->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
if (window->texture == nullptr)
{
dwarnln("Could not create SDL texture: {}", SDL_GetError());
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;
case SDL_WINDOWEVENT_LEAVE:
on_window_leave_event(window.wid);
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_request_fullscreen(PlatformWindow* window, bool fullscreen)
{
auto& sdl_window = *static_cast<SDLWindow*>(window);
SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(SystemCursorType type)
{
SDL_SystemCursor sdl_type;
switch (type)
{
case SystemCursorType::Help:
case SystemCursorType::Pointer:
sdl_type = SDL_SYSTEM_CURSOR_ARROW;
break;
case SystemCursorType::Text:
sdl_type = SDL_SYSTEM_CURSOR_IBEAM;
break;
case SystemCursorType::Wait:
sdl_type = SDL_SYSTEM_CURSOR_WAIT;
break;
case SystemCursorType::Hand:
sdl_type = SDL_SYSTEM_CURSOR_HAND;
break;
case SystemCursorType::Move:
sdl_type = SDL_SYSTEM_CURSOR_SIZEALL;
break;
case SystemCursorType::Forbidden:
sdl_type = SDL_SYSTEM_CURSOR_NO;
break;
case SystemCursorType::ResizeN:
case SystemCursorType::ResizeS:
case SystemCursorType::ResizeVertical:
sdl_type = SDL_SYSTEM_CURSOR_SIZENS;
break;
case SystemCursorType::ResizeE:
case SystemCursorType::ResizeW:
case SystemCursorType::ResizeHorizontal:
sdl_type = SDL_SYSTEM_CURSOR_SIZEWE;
break;
case SystemCursorType::ResizeNW:
case SystemCursorType::ResizeSE:
sdl_type = SDL_SYSTEM_CURSOR_SIZENWSE;
break;
case SystemCursorType::ResizeNE:
case SystemCursorType::ResizeSW:
sdl_type = SDL_SYSTEM_CURSOR_SIZENESW;
break;
}
auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create());
cursor->cursor = SDL_CreateSystemCursor(sdl_type);
if (cursor->cursor == nullptr)
{
dwarnln("Could not create SDL system cursor: {}", SDL_GetError());
return BAN::Error::from_errno(EFAULT);
}
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
}
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_bitmap_cursor(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y)
{
auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create());
SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormatFrom(const_cast<uint32_t*>(pixels), width, height, 32, width * 4, SDL_PIXELFORMAT_ARGB8888);
if (surface == nullptr)
{
dwarnln("Could not create SDL surface for cursor: {}", SDL_GetError());
return BAN::Error::from_errno(EFAULT);
}
origin_x = BAN::Math::clamp<int32_t>(origin_x, 0, width - 1);
origin_y = BAN::Math::clamp<int32_t>(origin_y, 0, height - 1);
cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y);
SDL_FreeSurface(surface);
if (cursor->cursor == nullptr)
{
dwarnln("Could not create SDL color cursor: {}", SDL_GetError());
return BAN::Error::from_errno(EFAULT);
}
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
}
static void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor)
{
if (cursor == nullptr)
SDL_SetCursor(s_default_cursor);
else
{
auto& sdl_cursor = *static_cast<SDLCursor*>(cursor);
SDL_SetCursor(sdl_cursor.cursor);
}
}
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,
.create_system_cursor = sdl2_create_system_cursor,
.create_bitmap_cursor = sdl2_create_bitmap_cursor,
.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,13 @@ BAN::ErrorOr<DrawableInfo> get_drawable_info(Client& client_info, CARD32 drawabl
return info;
}
PlatformCursor* get_cursor_safe(CURSOR cid)
{
auto it = g_objects.find(cid);
if (it == g_objects.end())
return nullptr;
if (it->value->type != Object::Type::Cursor)
return nullptr;
return it->value->object.get<BAN::UniqPtr<PlatformCursor>>().ptr();
}

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);
PlatformCursor* 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,188 @@
#include "../Events.h"
#include "../Platform.h"
#include <BAN/Vector.h>
#include <LibGUI/Window.h>
struct BananWindow final : public PlatformWindow
{
~BananWindow()
{
if (window)
unregister_event_fd(window->server_fd());
}
BAN::UniqPtr<LibGUI::Window> window;
};
struct BananCursor final : public PlatformCursor
{
BAN::Vector<uint32_t> pixels;
uint32_t width;
uint32_t height;
int32_t origin_x;
int32_t origin_y;
};
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(PlatformWindow* parent, WindowType type, WINDOW wid, int32_t x, int32_t y, uint32_t width, uint32_t height)
{
(void)parent;
(void)type;
(void)x;
(void)y;
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 BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> bananos_create_system_cursor(SystemCursorType type)
{
(void)type;
return BAN::Error::from_errno(ENOTSUP);
}
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> bananos_create_bitmap_cursor(const uint32_t* pixels, uint32_t width, uint32_t height, int32_t origin_x, int32_t origin_y)
{
auto cursor = TRY(BAN::UniqPtr<BananCursor>::create());
cursor->width = width;
cursor->height = height;
cursor->origin_x = origin_x;
cursor->origin_y = origin_y;
TRY(cursor->pixels.resize(width * height));
memcpy(cursor->pixels.data(), pixels, cursor->pixels.size() * 4);
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
}
static void bananos_set_cursor(PlatformWindow* window, PlatformCursor* cursor)
{
if (window == nullptr)
return;
auto& banan_window = *static_cast<BananWindow*>(window);
if (cursor == nullptr)
banan_window.window->set_cursor(0, 0, {}, 0, 0);
else
{
auto& banan_cursor = *static_cast<BananCursor*>(cursor);
banan_window.window->set_cursor(banan_cursor.width, banan_cursor.height, banan_cursor.pixels.span(), banan_cursor.origin_x, banan_cursor.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,
.create_system_cursor = bananos_create_system_cursor,
.create_bitmap_cursor = bananos_create_bitmap_cursor,
.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)); \
@@ -277,10 +252,23 @@ int main()
APPEND_ATOM_CUSTOM(WM_DELETE_WINDOW);
APPEND_ATOM_CUSTOM(_NET_WM_STATE);
APPEND_ATOM_CUSTOM(_NET_WM_STATE_FULLSCREEN);
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE);
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_DIALOG);
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_NORMAL);
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_SPLASH);
APPEND_ATOM_CUSTOM(_NET_WM_WINDOW_TYPE_UTILITY);
#undef APPEND_ATOM_CUSTOM
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 =
@@ -302,6 +290,18 @@ int main()
window.event_masks.remove(&client_info);
}
for (auto it = client_info.objects.begin(); it != client_info.objects.end();)
{
auto obj_it = g_objects.find(*it);
if (obj_it == g_objects.end() || obj_it->value->type != Object::Type::Window)
it++;
else
{
(void)destroy_window(client_info, *it);
it = client_info.objects.begin();
}
}
for (auto id : client_info.objects)
{
auto it = g_objects.find(id);
@@ -309,28 +309,28 @@ 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>>())
{
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());
}
}
if (object.type == Object::Type::Extension)
{
auto& extension = object.object.get<Object::Extension>();
extension.destructor(extension);
case Object::Type::Visual:
case Object::Type::Cursor:
case Object::Type::Pixmap:
case Object::Type::GraphicsContext:
case Object::Type::Font:
break;
case Object::Type::Window:
ASSERT_NOT_REACHED();
case Object::Type::Extension:
auto& extension = object.object.get<Object::Extension>();
extension.destructor(extension);
break;
}
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 +353,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 +415,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;
}