Compare commits

...

9 Commits

Author SHA1 Message Date
aa70815f5f Implement very basic grab pointer
This is definitely not correct with child windows but works with single
window apps (minecraft)
2026-06-01 22:24:17 +03:00
19f043744a Allow querying global cursor position
If a platform implements this, e.g. xeyes can track cursor when the
window is not receiving mouse movement events
2026-06-01 22:04:01 +03:00
af2132315d Implement WarpPointer
This allows playing games that capture the cursor
2026-06-01 22:04:01 +03:00
83af554e8c Add support for global window position
This allows proper TranslateCoords and event root coordinate reporting
2026-06-01 20:39:47 +03:00
b41d979dcb Port to SDL3 instead of SDL2
Main reason is that SDL3 allows getting keyboard mappings with modifiers
2026-06-01 20:35:56 +03:00
11ebced96a 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 20:35:56 +03:00
504eb2716b 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 20:35:56 +03:00
82edc9c76b Handle external window leave events 2026-06-01 20:35:56 +03:00
92e02dcadf Don't create platform window for small windows
These are usually internal event windows which should not be visible.
I'm not sure what the correct check for this would be...
gtk and other toolkits create a lot of dummy windows that should not be
visible.
2026-06-01 20:35:56 +03:00
9 changed files with 443 additions and 237 deletions

View File

@@ -28,6 +28,8 @@ CARD16 g_keymask { 0 };
CARD16 g_butmask { 0 }; CARD16 g_butmask { 0 };
WINDOW g_focus_window { None }; WINDOW g_focus_window { None };
static WINDOW s_pointer_grab_wid = None;
static const char* s_opcode_to_name[] { static const char* s_opcode_to_name[] {
[0] = "none", [0] = "none",
[X_CreateWindow] = "X_CreateWindow", [X_CreateWindow] = "X_CreateWindow",
@@ -374,6 +376,9 @@ void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h)
for (;;) for (;;)
{ {
if (wid == g_root.windowId)
return;
auto& object = *g_objects[wid]; auto& object = *g_objects[wid];
ASSERT(object.type == Object::Type::Window); ASSERT(object.type == Object::Type::Window);
@@ -447,7 +452,7 @@ void send_exposure_recursive(WINDOW wid)
struct PlatformWindowInfo struct PlatformWindowInfo
{ {
PlatformWindow* transient_for; PlatformWindow* parent;
WindowType type; WindowType type;
}; };
@@ -463,7 +468,7 @@ static PlatformWindowInfo get_plaform_window_info(const Object::Window& window)
static const CARD32 _NET_WM_WINDOW_TYPE_UTILITY = g_atoms_name_to_id["_NET_WM_WINDOW_TYPE_UTILITY"_sv]; static const CARD32 _NET_WM_WINDOW_TYPE_UTILITY = g_atoms_name_to_id["_NET_WM_WINDOW_TYPE_UTILITY"_sv];
PlatformWindowInfo info { PlatformWindowInfo info {
.transient_for = nullptr, .parent = nullptr,
.type = WindowType::Normal, .type = WindowType::Normal,
}; };
@@ -475,7 +480,7 @@ static PlatformWindowInfo get_plaform_window_info(const Object::Window& window)
const CARD32 type = *reinterpret_cast<const CARD32*>(it->value.data.data()); const CARD32 type = *reinterpret_cast<const CARD32*>(it->value.data.data());
if (type == _NET_WM_WINDOW_TYPE_NORMAL) if (type == _NET_WM_WINDOW_TYPE_NORMAL)
info.type = WindowType::Normal; info.type = WindowType::Normal;
else if (type == _NET_WM_WINDOW_TYPE_SPLASH || type == _NET_WM_WINDOW_TYPE_UTILITY || type == _NET_WM_WINDOW_TYPE_DIALOG) else if (type == _NET_WM_WINDOW_TYPE_SPLASH || type == _NET_WM_WINDOW_TYPE_DIALOG)
info.type = WindowType::Utility; info.type = WindowType::Utility;
else else
info.type = WindowType::Popup; info.type = WindowType::Popup;
@@ -497,7 +502,7 @@ wm_window_type_done:
// FIXME: support child windows // FIXME: support child windows
auto& window = it2->value->object.get<Object::Window>(); auto& window = it2->value->object.get<Object::Window>();
info.transient_for = window.platform_window.ptr(); info.parent = window.platform_window.ptr();
} }
transitient_for_done: transitient_for_done:
@@ -512,14 +517,21 @@ static BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
auto& window = object.object.get<Object::Window>(); auto& window = object.object.get<Object::Window>();
if (window.mapped) if (window.mapped)
return {}; return {};
window.mapped = true;
if (window.parent == g_root.windowId) if (window.parent == g_root.windowId)
{ {
ASSERT(!window.platform_window); ASSERT(!window.platform_window);
// NOTE: This is not perfect but seems to work.
// This is used to not display "dummy" event windows
// that should not be visible.
if (window.width <= 10 || window.height <= 10)
return {};
auto info = get_plaform_window_info(window); auto info = get_plaform_window_info(window);
window.platform_window = TRY(g_platform_ops.create_window( window.platform_window = TRY(g_platform_ops.create_window(
info.transient_for, info.parent,
info.type, info.type,
wid, wid,
window.x, window.x,
@@ -529,8 +541,6 @@ static BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
)); ));
} }
window.mapped = true;
for (auto& pixel : window.pixels) for (auto& pixel : window.pixels)
pixel = window.background; pixel = window.background;
invalidate_window(wid, 0, 0, window.width, window.height); invalidate_window(wid, 0, 0, window.width, window.height);
@@ -711,6 +721,21 @@ WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y)
return wid; return wid;
} }
xPoint get_window_position(WINDOW wid)
{
xPoint result { .x = 0, .y = 0 };
while (wid != None)
{
const auto& window = g_objects[wid]->object.get<Object::Window>();
result.x += window.x;
result.y += window.y;
wid = window.parent;
}
return result;
}
static PlatformWindow* get_platform_window(const Object::Window& window) static PlatformWindow* get_platform_window(const Object::Window& window)
{ {
if (window.platform_window) if (window.platform_window)
@@ -1231,18 +1256,21 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
} }
} }
bool window_changed = false; bool send_configure = false;
const int32_t min_x = BAN::Math::min(window.x, new_x);
const int32_t min_y = BAN::Math::min(window.y, new_y);
const int32_t max_x = BAN::Math::max(window.x + window.width, new_x + new_width);
const int32_t max_y = BAN::Math::max(window.y + window.height, new_y + new_height);
if (new_x != window.x || new_y != window.y) if (new_x != window.x || new_y != window.y)
{ {
window.x = new_x; if (window.platform_window)
window.y = new_y; {
window_changed = true; if (g_platform_ops.request_reposition)
g_platform_ops.request_reposition(window.platform_window.ptr(), new_x, new_y);
}
else
{
window.x = new_x;
window.y = new_y;
send_configure = true;
}
} }
if (new_width != window.width || new_height != window.height) if (new_width != window.width || new_height != window.height)
@@ -1251,22 +1279,20 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
{ {
if (g_platform_ops.request_resize) if (g_platform_ops.request_resize)
g_platform_ops.request_resize(window.platform_window.ptr(), new_width, new_height); g_platform_ops.request_resize(window.platform_window.ptr(), new_width, new_height);
window_changed = false;
} }
else else
{ {
window.width = new_width;
window.height = new_height;
TRY(window.pixels.resize(new_width * new_height)); TRY(window.pixels.resize(new_width * new_height));
for (auto& pixel : window.pixels) for (auto& pixel : window.pixels)
pixel = window.background; pixel = window.background;
window_changed = true; window.width = new_width;
window.height = new_height;
send_configure = true;
} }
} }
if (!window_changed) if (!send_configure)
break; break;
{ {
@@ -1828,13 +1854,37 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln(" cursor: {}", request.cursor); dprintln(" cursor: {}", request.cursor);
dprintln(" time: {}", request.time); dprintln(" time: {}", request.time);
xGrabPointerReply reply { auto& window = TRY_REF(get_window(client_info, request.grabWindow, X_GrabPointer));
.type = X_Reply,
.status = Success, if (s_pointer_grab_wid != None && !g_objects.contains(s_pointer_grab_wid))
.sequenceNumber = client_info.sequence, s_pointer_grab_wid = None;
.length = 0,
}; if (s_pointer_grab_wid == None)
TRY(encode(client_info.output_buffer, reply)); {
xGrabPointerReply reply {
.type = X_Reply,
.status = Success,
.sequenceNumber = client_info.sequence,
.length = 0,
};
TRY(encode(client_info.output_buffer, reply));
if (g_platform_ops.set_pointer_grab)
if (auto* platform_window = get_platform_window(window))
g_platform_ops.set_pointer_grab(platform_window, true);
s_pointer_grab_wid = request.grabWindow;
}
else
{
xGrabPointerReply reply {
.type = X_Reply,
.status = AlreadyGrabbed,
.sequenceNumber = client_info.sequence,
.length = 0,
};
TRY(encode(client_info.output_buffer, reply));
}
break; break;
} }
@@ -1845,6 +1895,16 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln("UngrabPointer"); dprintln("UngrabPointer");
dprintln(" time: {}", time); dprintln(" time: {}", time);
if (s_pointer_grab_wid == None)
break;
auto it = g_objects.find(s_pointer_grab_wid);
if (it != g_objects.end() && g_platform_ops.set_pointer_grab)
if (auto* platform_window = get_platform_window(it->value->object.get<Object::Window>()))
g_platform_ops.set_pointer_grab(platform_window, false);
s_pointer_grab_wid = None;
break; break;
} }
case X_GrabKeyboard: case X_GrabKeyboard:
@@ -1932,12 +1992,26 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
const auto& window = TRY_REF(get_window(client_info, wid, opcode)); const auto& window = TRY_REF(get_window(client_info, wid, opcode));
int32_t root_x, root_y; WINDOW child_wid { None };
int32_t event_x, event_y; for (auto wid_ : window.children)
root_x = event_x = window.cursor_x; {
root_y = event_y = window.cursor_y; const auto& child_window = g_objects[wid_]->object.get<Object::Window>();
if (!child_window.hovered)
continue;
child_wid = wid_;
break;
}
const auto child_wid = find_child_window(wid, event_x, event_y); const auto [root_x, root_y] = get_window_position(wid);
int32_t root_cursor_x, root_cursor_y;
if (g_platform_ops.query_pointer)
g_platform_ops.query_pointer(&root_cursor_x, &root_cursor_y);
else
{
root_cursor_x = root_x + window.cursor_x;
root_cursor_y = root_y + window.cursor_y;
}
xQueryPointerReply reply { xQueryPointerReply reply {
.type = X_Reply, .type = X_Reply,
@@ -1945,11 +2019,11 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
.sequenceNumber = client_info.sequence, .sequenceNumber = client_info.sequence,
.length = 0, .length = 0,
.root = g_root.windowId, .root = g_root.windowId,
.child = static_cast<CARD32>(child_wid == wid ? None : child_wid), .child = static_cast<CARD32>(child_wid),
.rootX = static_cast<INT16>(root_x), .rootX = static_cast<INT16>(root_cursor_x),
.rootY = static_cast<INT16>(root_y), .rootY = static_cast<INT16>(root_cursor_y),
.winX = static_cast<INT16>(event_x), .winX = static_cast<INT16>(root_cursor_x - root_x),
.winY = static_cast<INT16>(event_y), .winY = static_cast<INT16>(root_cursor_y - root_y),
.mask = static_cast<KeyButMask>(g_keymask | g_butmask), .mask = static_cast<KeyButMask>(g_keymask | g_butmask),
}; };
TRY(encode(client_info.output_buffer, reply)); TRY(encode(client_info.output_buffer, reply));
@@ -1966,19 +2040,55 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln(" src_x: {}", request.srcX); dprintln(" src_x: {}", request.srcX);
dprintln(" src_y: {}", request.srcY); dprintln(" src_y: {}", request.srcY);
(void)TRY_REF(get_window(client_info, request.dstWid, X_TranslateCoords));
(void)TRY_REF(get_window(client_info, request.srcWid, X_TranslateCoords));
const auto [src_x, src_y] = get_window_position(request.srcWid);
const auto [dst_x, dst_y] = get_window_position(request.dstWid);
xTranslateCoordsReply reply { xTranslateCoordsReply reply {
.type = X_Reply, .type = X_Reply,
.sameScreen = xTrue, .sameScreen = xTrue,
.sequenceNumber = client_info.sequence, .sequenceNumber = client_info.sequence,
.length = 0, .length = 0,
.child = None, .child = None, // FIXME
.dstX = request.srcX, .dstX = static_cast<INT16>(src_x + request.srcX - dst_x),
.dstY = request.srcY, .dstY = static_cast<INT16>(src_y + request.srcY - dst_y),
}; };
TRY(encode(client_info.output_buffer, reply)); TRY(encode(client_info.output_buffer, reply));
break; break;
} }
case X_WarpPointer:
{
auto request = decode<xWarpPointerReq>(packet).value();
dprintln("WarpPointer");
dprintln(" src_wid: {}", request.srcWid);
dprintln(" src_x: {}", request.srcX);
dprintln(" src_y: {}", request.srcY);
dprintln(" src_w: {}", request.srcWidth);
dprintln(" src_h: {}", request.srcHeight);
dprintln(" dst_wid: {}", request.dstWid);
dprintln(" dst_x: {}", request.dstX);
dprintln(" dst_y: {}", request.dstY);
if (g_platform_ops.warp_pointer == nullptr)
break;
// TODO: source window :)
if (request.dstWid == None)
g_platform_ops.warp_pointer(request.dstX, request.dstY, true);
else
{
(void)TRY_REF(get_window(client_info, request.dstWid, X_WarpPointer));
const auto [win_x, win_y] = get_window_position(request.dstWid);
g_platform_ops.warp_pointer(win_x + request.dstX, win_y + request.dstY, false);
}
break;
}
case X_QueryKeymap: case X_QueryKeymap:
{ {
dprintln("QueryKeymap"); dprintln("QueryKeymap");

View File

@@ -14,6 +14,8 @@ BAN::ErrorOr<void> destroy_window(Client& client_info, WINDOW wid);
WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y); WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y);
xPoint get_window_position(WINDOW wid);
static constexpr uint32_t COLOR_INVISIBLE = 0x69000000; static constexpr uint32_t COLOR_INVISIBLE = 0x69000000;
extern CARD16 g_keymask; extern CARD16 g_keymask;

View File

@@ -24,15 +24,15 @@ endif()
set(PLATFORM "banan-os" CACHE STRING "target platform") set(PLATFORM "banan-os" CACHE STRING "target platform")
set(VALID_PLATFORMS "banan-os" "SDL2") set(VALID_PLATFORMS "banan-os" "SDL3")
if(NOT PLATFORM IN_LIST VALID_PLATFORMS) if(NOT PLATFORM IN_LIST VALID_PLATFORMS)
message(FATAL_ERROR "platform \"${PLATFORM}\" not known, valid platforms are ${VALID_PLATFORMS}") message(FATAL_ERROR "platform \"${PLATFORM}\" not known, valid platforms are ${VALID_PLATFORMS}")
endif() endif()
if(PLATFORM STREQUAL "banan-os") if(PLATFORM STREQUAL "banan-os")
list(APPEND XBANAN_SOURCES banan-os/banan-os.cpp) list(APPEND XBANAN_SOURCES banan-os/banan-os.cpp)
elseif(PLATFORM STREQUAL "SDL2") elseif(PLATFORM STREQUAL "SDL3")
list(APPEND XBANAN_SOURCES SDL2/sdl2.cpp) list(APPEND XBANAN_SOURCES SDL3/sdl3.cpp)
endif() endif()
add_executable(xbanan ${XBANAN_SOURCES}) add_executable(xbanan ${XBANAN_SOURCES})
@@ -42,9 +42,9 @@ banan_link_library(xbanan libinput)
if(PLATFORM STREQUAL "banan-os") if(PLATFORM STREQUAL "banan-os")
banan_link_library(xbanan libgui) banan_link_library(xbanan libgui)
elseif(PLATFORM STREQUAL "SDL2") elseif(PLATFORM STREQUAL "SDL3")
find_package(SDL2 REQUIRED) find_package(SDL3 REQUIRED)
banan_link_library(xbanan SDL2::SDL2) banan_link_library(xbanan SDL3::SDL3)
endif() endif()
target_compile_options(xbanan PRIVATE -Wall -Wextra) target_compile_options(xbanan PRIVATE -Wall -Wextra)

View File

@@ -75,21 +75,8 @@ void on_window_close_event(WINDOW wid)
} }
} }
void on_window_resize_event(WINDOW wid, uint32_t new_width, uint32_t new_height) static void send_window_configure(WINDOW wid, Object::Window& window)
{ {
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 = { xEvent event = { .u = {
.configureNotify = { .configureNotify = {
@@ -129,12 +116,42 @@ void on_window_resize_event(WINDOW wid, uint32_t new_width, uint32_t new_height)
event.u.u.type = ConfigureNotify; event.u.u.type = ConfigureNotify;
MUST(parent_window.send_event(event, SubstructureNotifyMask)); MUST(parent_window.send_event(event, SubstructureNotifyMask));
} }
}
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;
}
send_window_configure(wid, window);
send_exposure_recursive(wid); send_exposure_recursive(wid);
invalidate_window(wid, 0, 0, window.width, window.height); invalidate_window(wid, 0, 0, window.width, window.height);
} }
void on_window_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>();
window.x = x;
window.y = y;
send_window_configure(wid, window);
}
void on_window_focus_event(WINDOW wid, bool focused) void on_window_focus_event(WINDOW wid, bool focused)
{ {
if (focused) if (focused)
@@ -219,15 +236,14 @@ void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen)
static void send_key_button_pointer_event(WINDOW root_wid, BYTE detail, uint32_t event_mask, BYTE event_type, KeyButMask state) 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; int32_t event_x, event_y;
{ {
auto& object = *g_objects[root_wid]; auto& object = *g_objects[root_wid];
ASSERT(object.type == Object::Type::Window); ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>(); auto& window = object.object.get<Object::Window>();
root_x = event_x = window.cursor_x; event_x = window.cursor_x;
root_y = event_y = window.cursor_y; event_y = window.cursor_y;
} }
const auto child_wid = find_child_window(root_wid, event_x, event_y); const auto child_wid = find_child_window(root_wid, event_x, event_y);
@@ -257,14 +273,15 @@ static void send_key_button_pointer_event(WINDOW root_wid, BYTE detail, uint32_t
ASSERT(object.type == Object::Type::Window); ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>(); auto& window = object.object.get<Object::Window>();
const auto [root_x, root_y] = get_window_position(wid);
xEvent event { .u = { xEvent event { .u = {
.keyButtonPointer = { .keyButtonPointer = {
.time = static_cast<CARD32>(time(nullptr)), .time = static_cast<CARD32>(time(nullptr)),
.root = g_root.windowId, .root = g_root.windowId,
.event = wid, .event = wid,
.child = static_cast<CARD32>(child_wid == wid ? None : child_wid), .child = static_cast<CARD32>(child_wid == wid ? None : child_wid),
.rootX = static_cast<INT16>(root_x), .rootX = static_cast<INT16>(root_x + event_x),
.rootY = static_cast<INT16>(root_y), .rootY = static_cast<INT16>(root_y + event_y),
.eventX = static_cast<INT16>(event_x), .eventX = static_cast<INT16>(event_x),
.eventY = static_cast<INT16>(event_y), .eventY = static_cast<INT16>(event_y),
.state = state, .state = state,
@@ -389,14 +406,15 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o
ASSERT(object.type == Object::Type::Window); ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>(); auto& window = object.object.get<Object::Window>();
const auto [root_x, root_y] = get_window_position(wid);
xEvent event { .u = { xEvent event { .u = {
.enterLeave = { .enterLeave = {
.time = static_cast<CARD32>(time(nullptr)), .time = static_cast<CARD32>(time(nullptr)),
.root = g_root.windowId, .root = g_root.windowId,
.event = wid, .event = wid,
.child = first ? static_cast<WINDOW>(None) : old_child_path.back(), .child = first ? static_cast<WINDOW>(None) : old_child_path.back(),
.rootX = static_cast<INT16>(old_x), .rootX = static_cast<INT16>(root_x + old_x),
.rootY = static_cast<INT16>(old_y), .rootY = static_cast<INT16>(root_y + old_y),
.eventX = static_cast<INT16>(old_x), .eventX = static_cast<INT16>(old_x),
.eventY = static_cast<INT16>(old_y), .eventY = static_cast<INT16>(old_y),
.state = static_cast<KeyButMask>(g_keymask | g_butmask), .state = static_cast<KeyButMask>(g_keymask | g_butmask),
@@ -429,14 +447,15 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o
ASSERT(object.type == Object::Type::Window); ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>(); auto& window = object.object.get<Object::Window>();
const auto [root_x, root_y] = get_window_position(wid);
xEvent event { .u = { xEvent event { .u = {
.enterLeave = { .enterLeave = {
.time = static_cast<CARD32>(time(nullptr)), .time = static_cast<CARD32>(time(nullptr)),
.root = g_root.windowId, .root = g_root.windowId,
.event = wid, .event = wid,
.child = last ? static_cast<WINDOW>(None) : new_child_path.back(), .child = last ? static_cast<WINDOW>(None) : new_child_path.back(),
.rootX = static_cast<INT16>(new_x), .rootX = static_cast<INT16>(root_x + new_x),
.rootY = static_cast<INT16>(new_y), .rootY = static_cast<INT16>(root_y + new_y),
.eventX = static_cast<INT16>(new_x), .eventX = static_cast<INT16>(new_x),
.eventY = static_cast<INT16>(new_y), .eventY = static_cast<INT16>(new_y),
.state = static_cast<KeyButMask>(g_keymask | g_butmask), .state = static_cast<KeyButMask>(g_keymask | g_butmask),
@@ -453,7 +472,22 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o
g_objects[wid]->object.get<Object::Window>().hovered = false; g_objects[wid]->object.get<Object::Window>().hovered = false;
for (const auto wid : new_child_path) for (const auto wid : new_child_path)
g_objects[wid]->object.get<Object::Window>().hovered = true; 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) void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y)
@@ -464,24 +498,17 @@ void on_mouse_move_event(WINDOW wid, int32_t x, int32_t y)
update_cursor(wid, x, y); 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
{ {
static WINDOW old_wid = g_root.windowId;
auto it = g_objects.find(old_wid);
if (it == g_objects.end())
{
old_wid = g_root.windowId;
it = g_objects.find(g_root.windowId);
}
ASSERT(it->value->type == Object::Type::Window); ASSERT(it->value->type == Object::Type::Window);
auto& old_window = it->value->object.get<Object::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);
send_enter_and_leave_events(old_wid, old_window.cursor_x, old_window.cursor_y, wid, x, y);
old_wid = wid;
} }
s_current_top_level_window = wid;
update_cursor_position_recursive(wid, x, y); update_cursor_position_recursive(wid, x, y);
// TODO: optimize with PointerMotionHint // TODO: optimize with PointerMotionHint

View File

@@ -7,8 +7,10 @@ void unregister_event_fd(int fd);
void on_window_close_event(WINDOW wid); void on_window_close_event(WINDOW wid);
void on_window_resize_event(WINDOW wid, uint32_t width, uint32_t height); void on_window_resize_event(WINDOW wid, uint32_t width, uint32_t height);
void on_window_move_event(WINDOW wid, int32_t x, int32_t y);
void on_window_focus_event(WINDOW wid, bool focused); void on_window_focus_event(WINDOW wid, bool focused);
void on_window_fullscreen_event(WINDOW wid, bool is_fullscreen); 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_move_event(WINDOW wid, int32_t x, int32_t y);
void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed); void on_mouse_button_event(WINDOW wid, uint8_t xbutton, bool pressed);

View File

@@ -16,9 +16,9 @@ struct PlatformCursor
enum class WindowType enum class WindowType
{ {
Popup,
Normal, Normal,
Utility, Utility,
Popup,
}; };
enum class SystemCursorType enum class SystemCursorType
@@ -55,8 +55,16 @@ struct PlatformOps
void (*invalidate)(PlatformWindow*, const uint32_t* pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height); 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 */ /* Request resize of a window, can be async */
void (*request_resize)(PlatformWindow*, uint32_t width, uint32_t height); void (*request_resize)(PlatformWindow*, uint32_t width, uint32_t height);
/* Request window repositioning */
void (*request_reposition)(PlatformWindow*, int32_t x, int32_t y);
/* Request new fullscreen state, can be async */ /* Request new fullscreen state, can be async */
void (*request_fullscreen)(PlatformWindow*, bool fullscreen); void (*request_fullscreen)(PlatformWindow*, bool fullscreen);
/* Warp pointer */
void (*warp_pointer)(int32_t x, int32_t y, bool relative);
/* Get global position of the pointer */
void (*query_pointer)(int32_t* x, int32_t* y);
/* Grab pointer events on window */
void (*set_pointer_grab)(PlatformWindow*, bool grabbed);
/* Create a system cursor */ /* Create a system cursor */
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_system_cursor)(SystemCursorType); BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_system_cursor)(SystemCursorType);
/* Create cursor from custom bitmap */ /* Create cursor from custom bitmap */

View File

@@ -4,7 +4,7 @@
#include <BAN/Atomic.h> #include <BAN/Atomic.h>
#include <BAN/HashMap.h> #include <BAN/HashMap.h>
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
#include <pthread.h> #include <pthread.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
@@ -44,7 +44,7 @@ struct SDLCursor final : public PlatformCursor
~SDLCursor() ~SDLCursor()
{ {
if (cursor != nullptr) if (cursor != nullptr)
SDL_FreeCursor(cursor); SDL_DestroyCursor(cursor);
} }
SDL_Cursor* cursor { nullptr }; SDL_Cursor* cursor { nullptr };
@@ -55,13 +55,13 @@ static int s_eventfd { -1 };
struct Keymap struct Keymap
{ {
consteval Keymap(); consteval Keymap();
uint8_t map[SDL_NUM_SCANCODES]; uint8_t map[SDL_SCANCODE_COUNT];
}; };
static Keymap s_sdl_keymap; static Keymap s_sdl_keymap;
static SDL_Cursor* s_default_cursor { nullptr }; static SDL_Cursor* s_default_cursor { nullptr };
static void* sdl2_thread(void*) static void* sdl3_thread(void*)
{ {
for (;;) for (;;)
{ {
@@ -73,22 +73,24 @@ static void* sdl2_thread(void*)
return nullptr; return nullptr;
} }
static bool sdl2_initialize(uint32_t* display_w, uint32_t* display_h) static bool sdl3_initialize(uint32_t* display_w, uint32_t* display_h)
{ {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == -1) if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
{ {
dwarnln("Could not initialize SDL: {}", SDL_GetError()); dwarnln("Could not initialize SDL: {}", SDL_GetError());
return false; return false;
} }
SDL_DisplayMode DM; *display_w = *display_h = 0;
if (SDL_GetCurrentDisplayMode(0, &DM) == -1)
const SDL_DisplayID* display_ids = SDL_GetDisplays(nullptr);
for (int i = 0; display_ids[i]; i++)
{ {
dwarnln("Could not get display mode: {}", SDL_GetError()); SDL_Rect rect;
return false; SDL_GetDisplayBounds(display_ids[i], &rect);
*display_w = BAN::Math::max<uint32_t>(*display_w, rect.x + rect.w);
*display_h = BAN::Math::max<uint32_t>(*display_h, rect.y + rect.h);
} }
*display_w = DM.w;
*display_h = DM.h;
s_default_cursor = SDL_GetCursor(); s_default_cursor = SDL_GetCursor();
@@ -102,12 +104,12 @@ static bool sdl2_initialize(uint32_t* display_w, uint32_t* display_h)
register_event_fd(s_eventfd, nullptr); register_event_fd(s_eventfd, nullptr);
pthread_t thread; pthread_t thread;
pthread_create(&thread, nullptr, sdl2_thread, nullptr); pthread_create(&thread, nullptr, sdl3_thread, nullptr);
return true; 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) static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl3_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()); auto window = TRY(BAN::UniqPtr<SDLWindow>::create());
@@ -115,39 +117,41 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl2_create_window(PlatformWin
window->width = width; window->width = width;
window->height = height; 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; int flags;
switch (type) 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: case WindowType::Normal:
case WindowType::Utility:
flags = SDL_WINDOW_RESIZABLE; flags = SDL_WINDOW_RESIZABLE;
break; break;
case WindowType::Popup:
flags = SDL_WINDOW_BORDERLESS;
break;
}
if (parent == nullptr || type != WindowType::Popup)
{
if (type != WindowType::Normal)
flags |= SDL_WINDOW_UTILITY;
window->window = SDL_CreateWindow("", width, height, flags);
}
else
{
auto& sdl_parent = *static_cast<SDLWindow*>(parent);
int parent_x, parent_y;
SDL_GetWindowPosition(sdl_parent.window, &parent_x, &parent_y);
window->window = SDL_CreatePopupWindow(sdl_parent.window, x - parent_x, y - parent_y, width, height, flags | SDL_WINDOW_POPUP_MENU);
} }
window->window = SDL_CreateWindow("", x, y, width, height, flags);
if (window->window == nullptr) if (window->window == nullptr)
{ {
dwarnln("Could not create SDL window: {}", SDL_GetError()); dwarnln("Could not create SDL window: {}", SDL_GetError());
return BAN::Error::from_errno(EFAULT); return BAN::Error::from_errno(EFAULT);
} }
window->renderer = SDL_CreateRenderer(window->window, -1, SDL_RENDERER_ACCELERATED); window->renderer = SDL_CreateRenderer(window->window, nullptr);
if (window->renderer == nullptr) if (window->renderer == nullptr)
{ {
dwarnln("Could not create SDL renderer: {}", SDL_GetError()); dwarnln("Could not create SDL renderer: {}", SDL_GetError());
@@ -166,13 +170,19 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl2_create_window(PlatformWin
return BAN::UniqPtr<PlatformWindow>(BAN::move(window)); return BAN::UniqPtr<PlatformWindow>(BAN::move(window));
} }
static void sdl2_request_resize(PlatformWindow* window, uint32_t width, uint32_t height) static void sdl3_request_resize(PlatformWindow* window, uint32_t width, uint32_t height)
{ {
auto& sdl_window = *static_cast<SDLWindow*>(window); auto& sdl_window = *static_cast<SDLWindow*>(window);
SDL_SetWindowSize(sdl_window.window, width, height); SDL_SetWindowSize(sdl_window.window, width, height);
} }
static void sdl2_poll_events(void*) static void sdl3_request_reposition(PlatformWindow* window, int32_t x, int32_t y)
{
auto& sdl_window = *static_cast<SDLWindow*>(window);
SDL_SetWindowPosition(sdl_window.window, x, y);
}
static void sdl3_poll_events(void*)
{ {
uint64_t dummy; uint64_t dummy;
read(s_eventfd, &dummy, sizeof(dummy)); read(s_eventfd, &dummy, sizeof(dummy));
@@ -182,51 +192,48 @@ static void sdl2_poll_events(void*)
{ {
switch (event.type) switch (event.type)
{ {
case SDL_WINDOWEVENT: case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
{ if (auto it = s_window_map.find(event.window.windowID); it != s_window_map.end())
auto it = s_window_map.find(event.window.windowID); on_window_close_event(it->value->wid);
if (it == s_window_map.end()) break;
break; case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
auto& window = *it->value; if (auto it = s_window_map.find(event.window.windowID); it != s_window_map.end())
switch (event.window.event)
{ {
case SDL_WINDOWEVENT_CLOSE: auto& window = *it->value;
on_window_close_event(window.wid); window.width = event.window.data1;
break; window.height = event.window.data2;
case SDL_WINDOWEVENT_SIZE_CHANGED:
window.width = event.window.data1;
window.height = event.window.data2;
SDL_DestroyTexture(window.texture); SDL_DestroyTexture(window.texture);
window.texture = SDL_CreateTexture(window.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, window.width, window.height); window.texture = SDL_CreateTexture(window.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, window.width, window.height);
ASSERT(window.texture); ASSERT(window.texture);
on_window_resize_event(window.wid, event.window.data1, event.window.data2); on_window_resize_event(window.wid, window.width, window.height);
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; break;
} case SDL_EVENT_WINDOW_MOVED:
case SDL_MOUSEMOTION: if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
{ on_window_move_event(it->value->wid, event.window.data1, event.window.data2);
auto it = s_window_map.find(event.window.windowID); break;
if (it != s_window_map.end()) case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST:
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
on_window_focus_event(it->value->wid, (event.type == SDL_EVENT_WINDOW_FOCUS_GAINED));
break;
case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
on_window_fullscreen_event(it->value->wid, (event.type == SDL_EVENT_WINDOW_ENTER_FULLSCREEN));
break;
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
on_window_leave_event(it->value->wid);
break;
case SDL_EVENT_MOUSE_MOTION:
if (auto it = s_window_map.find(event.motion.windowID); it != s_window_map.end())
on_mouse_move_event(it->value->wid, event.motion.x, event.motion.y); on_mouse_move_event(it->value->wid, event.motion.x, event.motion.y);
break; break;
} case SDL_EVENT_MOUSE_BUTTON_UP:
case SDL_MOUSEBUTTONUP: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_MOUSEBUTTONDOWN:
{ {
uint8_t xbutton { 0 }; uint8_t xbutton { 0 };
switch (event.button.button) switch (event.button.button)
@@ -240,10 +247,10 @@ static void sdl2_poll_events(void*)
auto it = s_window_map.find(event.window.windowID); auto it = s_window_map.find(event.window.windowID);
if (xbutton && it != s_window_map.end()) if (xbutton && it != s_window_map.end())
on_mouse_button_event(it->value->wid, xbutton, (event.type == SDL_MOUSEBUTTONDOWN)); on_mouse_button_event(it->value->wid, xbutton, (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN));
break; break;
} }
case SDL_MOUSEWHEEL: case SDL_EVENT_MOUSE_WHEEL:
{ {
uint8_t xbutton { 0 }; uint8_t xbutton { 0 };
if (event.wheel.y > 0) if (event.wheel.y > 0)
@@ -260,31 +267,31 @@ static void sdl2_poll_events(void*)
break; break;
} }
case SDL_KEYUP: case SDL_EVENT_KEY_UP:
case SDL_KEYDOWN: case SDL_EVENT_KEY_DOWN:
{ {
uint8_t scancode = s_sdl_keymap.map[event.key.keysym.scancode]; uint8_t scancode = s_sdl_keymap.map[event.key.scancode];
uint8_t xmod { 0 }; uint8_t xmod { 0 };
if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) if (event.key.mod & SDL_KMOD_SHIFT)
xmod |= (1 << 0); xmod |= (1 << 0);
if (event.key.keysym.mod & (KMOD_CAPS)) if (event.key.mod & SDL_KMOD_CAPS)
xmod |= (1 << 1); xmod |= (1 << 1);
if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) if (event.key.mod & SDL_KMOD_CTRL)
xmod |= (1 << 2); xmod |= (1 << 2);
if (event.key.keysym.mod & (KMOD_LALT | KMOD_RALT)) if (event.key.mod & SDL_KMOD_ALT)
xmod |= (1 << 3); xmod |= (1 << 3);
auto it = s_window_map.find(event.window.windowID); auto it = s_window_map.find(event.window.windowID);
if (it != s_window_map.end()) if (it != s_window_map.end())
on_key_event(it->value->wid, scancode, xmod, (event.type == SDL_KEYDOWN)); on_key_event(it->value->wid, scancode, xmod, (event.type == SDL_EVENT_KEY_DOWN));
break; break;
} }
} }
} }
} }
static void sdl2_invalidate(PlatformWindow* window, const uint32_t* src_pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height) static void sdl3_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); auto& sdl_window = *static_cast<SDLWindow*>(window);
@@ -300,7 +307,7 @@ static void sdl2_invalidate(PlatformWindow* window, const uint32_t* src_pixels,
void* dst_pixels; void* dst_pixels;
int dst_pitch; int dst_pitch;
if (SDL_LockTexture(sdl_window.texture, &rect, &dst_pixels, &dst_pitch) == -1) if (!SDL_LockTexture(sdl_window.texture, &rect, &dst_pixels, &dst_pitch))
{ {
dwarnln("Could not lock texture: {}", SDL_GetError()); dwarnln("Could not lock texture: {}", SDL_GetError());
return; return;
@@ -318,63 +325,68 @@ static void sdl2_invalidate(PlatformWindow* window, const uint32_t* src_pixels,
SDL_UnlockTexture(sdl_window.texture); SDL_UnlockTexture(sdl_window.texture);
SDL_RenderClear(sdl_window.renderer); SDL_RenderClear(sdl_window.renderer);
SDL_RenderCopy(sdl_window.renderer, sdl_window.texture, NULL, NULL); SDL_RenderTexture(sdl_window.renderer, sdl_window.texture, NULL, NULL);
SDL_RenderPresent(sdl_window.renderer); SDL_RenderPresent(sdl_window.renderer);
} }
static void sdl2_request_fullscreen(PlatformWindow* window, bool fullscreen) static void sdl3_request_fullscreen(PlatformWindow* window, bool fullscreen)
{ {
auto& sdl_window = *static_cast<SDLWindow*>(window); auto& sdl_window = *static_cast<SDLWindow*>(window);
SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); SDL_SetWindowFullscreen(sdl_window.window, fullscreen);
} }
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(SystemCursorType type) static void sdl3_warp_pointer(int32_t x, int32_t y, bool relative)
{ {
SDL_SystemCursor sdl_type; if (relative)
switch (type)
{ {
case SystemCursorType::Help: float cx, cy;
case SystemCursorType::Pointer: SDL_GetGlobalMouseState(&cx, &cy);
sdl_type = SDL_SYSTEM_CURSOR_ARROW; x += cx;
break; x += cy;
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;
} }
SDL_WarpMouseGlobal(x, y);
}
static void sdl3_query_pointer(int32_t* x, int32_t* y)
{
float cx, cy;
SDL_GetGlobalMouseState(&cx, &cy);
*x = cx;
*y = cy;
}
static void sdl3_set_pointer_grab(PlatformWindow* window, bool grabbed)
{
auto& sdl_window = *static_cast<SDLWindow*>(window);
SDL_SetWindowMouseGrab(sdl_window.window, grabbed);
}
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_create_system_cursor(SystemCursorType type)
{
static constexpr SDL_SystemCursor cursor_type_map[] {
[static_cast<size_t>(SystemCursorType::Pointer)] = SDL_SYSTEM_CURSOR_DEFAULT,
[static_cast<size_t>(SystemCursorType::Text)] = SDL_SYSTEM_CURSOR_TEXT,
[static_cast<size_t>(SystemCursorType::Wait)] = SDL_SYSTEM_CURSOR_WAIT,
[static_cast<size_t>(SystemCursorType::Hand)] = SDL_SYSTEM_CURSOR_POINTER,
[static_cast<size_t>(SystemCursorType::Help)] = SDL_SYSTEM_CURSOR_DEFAULT, // :(
[static_cast<size_t>(SystemCursorType::Move)] = SDL_SYSTEM_CURSOR_MOVE,
[static_cast<size_t>(SystemCursorType::Forbidden)] = SDL_SYSTEM_CURSOR_NOT_ALLOWED,
[static_cast<size_t>(SystemCursorType::ResizeN)] = SDL_SYSTEM_CURSOR_N_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeE)] = SDL_SYSTEM_CURSOR_E_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeS)] = SDL_SYSTEM_CURSOR_S_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeW)] = SDL_SYSTEM_CURSOR_W_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeNW)] = SDL_SYSTEM_CURSOR_NW_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeNE)] = SDL_SYSTEM_CURSOR_NE_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeSW)] = SDL_SYSTEM_CURSOR_SW_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeSE)] = SDL_SYSTEM_CURSOR_SE_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeVertical)] = SDL_SYSTEM_CURSOR_NS_RESIZE,
[static_cast<size_t>(SystemCursorType::ResizeHorizontal)] = SDL_SYSTEM_CURSOR_EW_RESIZE,
};
auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create()); auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create());
cursor->cursor = SDL_CreateSystemCursor(sdl_type); cursor->cursor = SDL_CreateSystemCursor(cursor_type_map[static_cast<size_t>(type)]);
if (cursor->cursor == nullptr) if (cursor->cursor == nullptr)
{ {
dwarnln("Could not create SDL system cursor: {}", SDL_GetError()); dwarnln("Could not create SDL system cursor: {}", SDL_GetError());
@@ -384,11 +396,11 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(Syst
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor)); 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) static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_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()); 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); SDL_Surface* surface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_ARGB8888, const_cast<uint32_t*>(pixels), width * 4);
if (surface == nullptr) if (surface == nullptr)
{ {
dwarnln("Could not create SDL surface for cursor: {}", SDL_GetError()); dwarnln("Could not create SDL surface for cursor: {}", SDL_GetError());
@@ -399,7 +411,7 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_bitmap_cursor(cons
origin_y = BAN::Math::clamp<int32_t>(origin_y, 0, height - 1); origin_y = BAN::Math::clamp<int32_t>(origin_y, 0, height - 1);
cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y); cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y);
SDL_FreeSurface(surface); SDL_DestroySurface(surface);
if (cursor->cursor == nullptr) if (cursor->cursor == nullptr)
{ {
@@ -410,7 +422,7 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_bitmap_cursor(cons
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor)); return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
} }
static void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor) static void sdl3_set_cursor(PlatformWindow*, PlatformCursor* cursor)
{ {
if (cursor == nullptr) if (cursor == nullptr)
SDL_SetCursor(s_default_cursor); SDL_SetCursor(s_default_cursor);
@@ -422,15 +434,19 @@ static void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor)
} }
PlatformOps g_platform_ops = { PlatformOps g_platform_ops = {
.initialize = sdl2_initialize, .initialize = sdl3_initialize,
.poll_events = sdl2_poll_events, .poll_events = sdl3_poll_events,
.create_window = sdl2_create_window, .create_window = sdl3_create_window,
.invalidate = sdl2_invalidate, .invalidate = sdl3_invalidate,
.request_resize = sdl2_request_resize, .request_resize = sdl3_request_resize,
.request_fullscreen = sdl2_request_fullscreen, .request_reposition = sdl3_request_reposition,
.create_system_cursor = sdl2_create_system_cursor, .request_fullscreen = sdl3_request_fullscreen,
.create_bitmap_cursor = sdl2_create_bitmap_cursor, .warp_pointer = sdl3_warp_pointer,
.set_cursor = sdl2_set_cursor, .query_pointer = sdl3_query_pointer,
.set_pointer_grab = sdl3_set_pointer_grab,
.create_system_cursor = sdl3_create_system_cursor,
.create_bitmap_cursor = sdl3_create_bitmap_cursor,
.set_cursor = sdl3_set_cursor,
}; };
#include <LibInput/KeyEvent.h> #include <LibInput/KeyEvent.h>
@@ -553,4 +569,4 @@ consteval Keymap::Keymap()
map[SDL_SCANCODE_KP_ENTER] = keycode_numpad(3, 3); map[SDL_SCANCODE_KP_ENTER] = keycode_numpad(3, 3);
map[SDL_SCANCODE_KP_0] = keycode_numpad(4, 0); map[SDL_SCANCODE_KP_0] = keycode_numpad(4, 0);
map[SDL_SCANCODE_KP_COMMA] = keycode_numpad(4, 1); map[SDL_SCANCODE_KP_COMMA] = keycode_numpad(4, 1);
}; };

View File

@@ -120,6 +120,13 @@ static void bananos_request_resize(PlatformWindow* window, uint32_t width, uint3
banan_window.window->request_resize(width, height); banan_window.window->request_resize(width, height);
} }
static void bananos_request_reposition(PlatformWindow* window, int32_t x, int32_t y)
{
(void)window;
(void)x;
(void)y;
}
static void bananos_invalidate(PlatformWindow* window, const uint32_t* pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t 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); auto& banan_window = *static_cast<BananWindow*>(window);
@@ -134,6 +141,31 @@ static void bananos_invalidate(PlatformWindow* window, const uint32_t* pixels, u
banan_window.window->invalidate(x, y, width, height); banan_window.window->invalidate(x, y, width, height);
} }
static void bananos_request_fullscreen(PlatformWindow* window, bool fullscreen)
{
auto& banan_window = *static_cast<BananWindow*>(window);
banan_window.window->set_fullscreen(fullscreen);
}
static void bananos_warp_pointer(int32_t x, int32_t y, bool relative)
{
(void)x;
(void)y;
(void)relative;
}
static void bananos_query_pointer(int32_t* x, int32_t* y)
{
(void)x;
(void)y;
}
static void bananos_set_pointer_grab(PlatformWindow* window, bool grabbed)
{
(void)window;
(void)grabbed;
}
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> bananos_create_system_cursor(SystemCursorType type) static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> bananos_create_system_cursor(SystemCursorType type)
{ {
(void)type; (void)type;
@@ -169,19 +201,17 @@ static void bananos_set_cursor(PlatformWindow* window, PlatformCursor* cursor)
} }
} }
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 = { PlatformOps g_platform_ops = {
.initialize = bananos_initialize, .initialize = bananos_initialize,
.poll_events = bananos_poll_events, .poll_events = bananos_poll_events,
.create_window = bananos_create_window, .create_window = bananos_create_window,
.invalidate = bananos_invalidate, .invalidate = bananos_invalidate,
.request_resize = bananos_request_resize, .request_resize = bananos_request_resize,
.request_reposition = bananos_request_reposition,
.request_fullscreen = bananos_request_fullscreen, .request_fullscreen = bananos_request_fullscreen,
.warp_pointer = bananos_warp_pointer,
.query_pointer = nullptr, /* bananos_query_pointer */
.set_pointer_grab = bananos_set_pointer_grab,
.create_system_cursor = bananos_create_system_cursor, .create_system_cursor = bananos_create_system_cursor,
.create_bitmap_cursor = bananos_create_bitmap_cursor, .create_bitmap_cursor = bananos_create_bitmap_cursor,
.set_cursor = bananos_set_cursor, .set_cursor = bananos_set_cursor,

View File

@@ -290,6 +290,18 @@ int main()
window.event_masks.remove(&client_info); 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) for (auto id : client_info.objects)
{ {
auto it = g_objects.find(id); auto it = g_objects.find(id);
@@ -304,14 +316,13 @@ int main()
case Object::Type::Pixmap: case Object::Type::Pixmap:
case Object::Type::GraphicsContext: case Object::Type::GraphicsContext:
case Object::Type::Font: case Object::Type::Font:
case Object::Type::Window:
break; break;
case Object::Type::Window:
ASSERT_NOT_REACHED();
case Object::Type::Extension: case Object::Type::Extension:
{
auto& extension = object.object.get<Object::Extension>(); auto& extension = object.object.get<Object::Extension>();
extension.destructor(extension); extension.destructor(extension);
break; break;
}
} }
g_objects.remove(it); g_objects.remove(it);