Compare commits

..

4 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
8 changed files with 220 additions and 390 deletions

View File

@@ -28,8 +28,6 @@ CARD16 g_keymask { 0 };
CARD16 g_butmask { 0 };
WINDOW g_focus_window { None };
static WINDOW s_pointer_grab_wid = None;
static const char* s_opcode_to_name[] {
[0] = "none",
[X_CreateWindow] = "X_CreateWindow",
@@ -452,7 +450,7 @@ void send_exposure_recursive(WINDOW wid)
struct PlatformWindowInfo
{
PlatformWindow* parent;
PlatformWindow* transient_for;
WindowType type;
};
@@ -468,7 +466,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];
PlatformWindowInfo info {
.parent = nullptr,
.transient_for = nullptr,
.type = WindowType::Normal,
};
@@ -480,7 +478,7 @@ static PlatformWindowInfo get_plaform_window_info(const Object::Window& window)
const CARD32 type = *reinterpret_cast<const CARD32*>(it->value.data.data());
if (type == _NET_WM_WINDOW_TYPE_NORMAL)
info.type = WindowType::Normal;
else if (type == _NET_WM_WINDOW_TYPE_SPLASH || type == _NET_WM_WINDOW_TYPE_DIALOG)
else if (type == _NET_WM_WINDOW_TYPE_SPLASH || type == _NET_WM_WINDOW_TYPE_UTILITY || type == _NET_WM_WINDOW_TYPE_DIALOG)
info.type = WindowType::Utility;
else
info.type = WindowType::Popup;
@@ -502,7 +500,7 @@ wm_window_type_done:
// FIXME: support child windows
auto& window = it2->value->object.get<Object::Window>();
info.parent = window.platform_window.ptr();
info.transient_for = window.platform_window.ptr();
}
transitient_for_done:
@@ -511,6 +509,8 @@ transitient_for_done:
static BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
{
static const CARD32 WM_CLASS = g_atoms_name_to_id["WM_CLASS"_sv];
auto& object = *g_objects[wid];
ASSERT(object.type == Object::Type::Window);
@@ -526,12 +526,12 @@ static BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
// 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)
if (!window.properties.contains(WM_CLASS))
return {};
auto info = get_plaform_window_info(window);
window.platform_window = TRY(g_platform_ops.create_window(
info.parent,
info.transient_for,
info.type,
wid,
window.x,
@@ -721,21 +721,6 @@ WINDOW find_child_window(WINDOW wid, int32_t& x, int32_t& y)
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)
{
if (window.platform_window)
@@ -1256,21 +1241,18 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
}
}
bool send_configure = false;
bool window_changed = 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 (window.platform_window)
{
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;
}
window.x = new_x;
window.y = new_y;
window_changed = true;
}
if (new_width != window.width || new_height != window.height)
@@ -1279,20 +1261,22 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
{
if (g_platform_ops.request_resize)
g_platform_ops.request_resize(window.platform_window.ptr(), new_width, new_height);
window_changed = false;
}
else
{
window.width = new_width;
window.height = new_height;
TRY(window.pixels.resize(new_width * new_height));
for (auto& pixel : window.pixels)
pixel = window.background;
window.width = new_width;
window.height = new_height;
send_configure = true;
window_changed = true;
}
}
if (!send_configure)
if (!window_changed)
break;
{
@@ -1854,37 +1838,13 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln(" cursor: {}", request.cursor);
dprintln(" time: {}", request.time);
auto& window = TRY_REF(get_window(client_info, request.grabWindow, X_GrabPointer));
if (s_pointer_grab_wid != None && !g_objects.contains(s_pointer_grab_wid))
s_pointer_grab_wid = None;
if (s_pointer_grab_wid == None)
{
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));
}
xGrabPointerReply reply {
.type = X_Reply,
.status = Success,
.sequenceNumber = client_info.sequence,
.length = 0,
};
TRY(encode(client_info.output_buffer, reply));
break;
}
@@ -1895,16 +1855,6 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln("UngrabPointer");
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;
}
case X_GrabKeyboard:
@@ -2002,17 +1952,6 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
break;
}
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 {
.type = X_Reply,
.sameScreen = xTrue,
@@ -2020,10 +1959,10 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
.length = 0,
.root = g_root.windowId,
.child = static_cast<CARD32>(child_wid),
.rootX = static_cast<INT16>(root_cursor_x),
.rootY = static_cast<INT16>(root_cursor_y),
.winX = static_cast<INT16>(root_cursor_x - root_x),
.winY = static_cast<INT16>(root_cursor_y - root_y),
.rootX = static_cast<INT16>(window.cursor_x), // FIXME
.rootY = static_cast<INT16>(window.cursor_y), // FIXME
.winX = static_cast<INT16>(window.cursor_x),
.winY = static_cast<INT16>(window.cursor_y),
.mask = static_cast<KeyButMask>(g_keymask | g_butmask),
};
TRY(encode(client_info.output_buffer, reply));
@@ -2040,55 +1979,19 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln(" src_x: {}", request.srcX);
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 {
.type = X_Reply,
.sameScreen = xTrue,
.sequenceNumber = client_info.sequence,
.length = 0,
.child = None, // FIXME
.dstX = static_cast<INT16>(src_x + request.srcX - dst_x),
.dstY = static_cast<INT16>(src_y + request.srcY - dst_y),
.child = None,
.dstX = request.srcX,
.dstY = request.srcY,
};
TRY(encode(client_info.output_buffer, reply));
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:
{
dprintln("QueryKeymap");

View File

@@ -14,8 +14,6 @@ BAN::ErrorOr<void> destroy_window(Client& client_info, WINDOW wid);
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;
extern CARD16 g_keymask;

View File

@@ -24,15 +24,15 @@ endif()
set(PLATFORM "banan-os" CACHE STRING "target platform")
set(VALID_PLATFORMS "banan-os" "SDL3")
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 "SDL3")
list(APPEND XBANAN_SOURCES SDL3/sdl3.cpp)
elseif(PLATFORM STREQUAL "SDL2")
list(APPEND XBANAN_SOURCES SDL2/sdl2.cpp)
endif()
add_executable(xbanan ${XBANAN_SOURCES})
@@ -42,9 +42,9 @@ banan_link_library(xbanan libinput)
if(PLATFORM STREQUAL "banan-os")
banan_link_library(xbanan libgui)
elseif(PLATFORM STREQUAL "SDL3")
find_package(SDL3 REQUIRED)
banan_link_library(xbanan SDL3::SDL3)
elseif(PLATFORM STREQUAL "SDL2")
find_package(SDL2 REQUIRED)
banan_link_library(xbanan SDL2::SDL2)
endif()
target_compile_options(xbanan PRIVATE -Wall -Wextra)

View File

@@ -75,8 +75,21 @@ void on_window_close_event(WINDOW wid)
}
}
static void send_window_configure(WINDOW wid, Object::Window& window)
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 = {
@@ -116,42 +129,12 @@ static void send_window_configure(WINDOW wid, Object::Window& window)
event.u.u.type = ConfigureNotify;
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);
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)
{
if (focused)
@@ -236,14 +219,15 @@ 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)
{
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>();
event_x = window.cursor_x;
event_y = window.cursor_y;
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);
@@ -273,15 +257,14 @@ static void send_key_button_pointer_event(WINDOW root_wid, BYTE detail, uint32_t
ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>();
const auto [root_x, root_y] = get_window_position(wid);
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 + event_x),
.rootY = static_cast<INT16>(root_y + event_y),
.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,
@@ -406,15 +389,14 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o
ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>();
const auto [root_x, root_y] = get_window_position(wid);
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>(root_x + old_x),
.rootY = static_cast<INT16>(root_y + old_y),
.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),
@@ -447,15 +429,14 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o
ASSERT(object.type == Object::Type::Window);
auto& window = object.object.get<Object::Window>();
const auto [root_x, root_y] = get_window_position(wid);
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>(root_x + new_x),
.rootY = static_cast<INT16>(root_y + new_y),
.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),

View File

@@ -7,7 +7,6 @@ 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_move_event(WINDOW wid, int32_t x, int32_t y);
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);

View File

@@ -16,9 +16,9 @@ struct PlatformCursor
enum class WindowType
{
Popup,
Normal,
Utility,
Popup,
};
enum class SystemCursorType
@@ -55,16 +55,8 @@ struct PlatformOps
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 window repositioning */
void (*request_reposition)(PlatformWindow*, int32_t x, int32_t y);
/* Request new fullscreen state, can be async */
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 */
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> (*create_system_cursor)(SystemCursorType);
/* Create cursor from custom bitmap */

View File

@@ -4,7 +4,7 @@
#include <BAN/Atomic.h>
#include <BAN/HashMap.h>
#include <SDL3/SDL.h>
#include <SDL2/SDL.h>
#include <pthread.h>
#include <sys/eventfd.h>
@@ -44,7 +44,7 @@ struct SDLCursor final : public PlatformCursor
~SDLCursor()
{
if (cursor != nullptr)
SDL_DestroyCursor(cursor);
SDL_FreeCursor(cursor);
}
SDL_Cursor* cursor { nullptr };
@@ -55,13 +55,13 @@ static int s_eventfd { -1 };
struct Keymap
{
consteval Keymap();
uint8_t map[SDL_SCANCODE_COUNT];
uint8_t map[SDL_NUM_SCANCODES];
};
static Keymap s_sdl_keymap;
static SDL_Cursor* s_default_cursor { nullptr };
static void* sdl3_thread(void*)
static void* sdl2_thread(void*)
{
for (;;)
{
@@ -73,24 +73,22 @@ static void* sdl3_thread(void*)
return nullptr;
}
static bool sdl3_initialize(uint32_t* display_w, uint32_t* display_h)
static bool sdl2_initialize(uint32_t* display_w, uint32_t* display_h)
{
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) == -1)
{
dwarnln("Could not initialize SDL: {}", SDL_GetError());
return false;
}
*display_w = *display_h = 0;
const SDL_DisplayID* display_ids = SDL_GetDisplays(nullptr);
for (int i = 0; display_ids[i]; i++)
SDL_DisplayMode DM;
if (SDL_GetCurrentDisplayMode(0, &DM) == -1)
{
SDL_Rect rect;
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);
dwarnln("Could not get display mode: {}", SDL_GetError());
return false;
}
*display_w = DM.w;
*display_h = DM.h;
s_default_cursor = SDL_GetCursor();
@@ -104,12 +102,12 @@ static bool sdl3_initialize(uint32_t* display_w, uint32_t* display_h)
register_event_fd(s_eventfd, nullptr);
pthread_t thread;
pthread_create(&thread, nullptr, sdl3_thread, nullptr);
pthread_create(&thread, nullptr, sdl2_thread, nullptr);
return true;
}
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)
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());
@@ -117,41 +115,39 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl3_create_window(PlatformWin
window->width = width;
window->height = height;
int flags;
switch (type)
{
case WindowType::Normal:
case WindowType::Utility:
flags = SDL_WINDOW_RESIZABLE;
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);
}
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);
window->window = SDL_CreatePopupWindow(sdl_parent.window, x - parent_x, y - parent_y, width, height, flags | SDL_WINDOW_POPUP_MENU);
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, nullptr);
window->renderer = SDL_CreateRenderer(window->window, -1, SDL_RENDERER_ACCELERATED);
if (window->renderer == nullptr)
{
dwarnln("Could not create SDL renderer: {}", SDL_GetError());
@@ -170,19 +166,13 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformWindow>> sdl3_create_window(PlatformWin
return BAN::UniqPtr<PlatformWindow>(BAN::move(window));
}
static void sdl3_request_resize(PlatformWindow* window, uint32_t width, uint32_t height)
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 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*)
static void sdl2_poll_events(void*)
{
uint64_t dummy;
read(s_eventfd, &dummy, sizeof(dummy));
@@ -192,48 +182,54 @@ static void sdl3_poll_events(void*)
{
switch (event.type)
{
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
if (auto it = s_window_map.find(event.window.windowID); it != s_window_map.end())
on_window_close_event(it->value->wid);
break;
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
if (auto it = s_window_map.find(event.window.windowID); it != s_window_map.end())
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)
{
auto& window = *it->value;
window.width = event.window.data1;
window.height = event.window.data2;
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);
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, window.width, window.height);
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_EVENT_WINDOW_MOVED:
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);
break;
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())
}
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_EVENT_MOUSE_BUTTON_UP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
}
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
{
uint8_t xbutton { 0 };
switch (event.button.button)
@@ -247,10 +243,10 @@ static void sdl3_poll_events(void*)
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_EVENT_MOUSE_BUTTON_DOWN));
on_mouse_button_event(it->value->wid, xbutton, (event.type == SDL_MOUSEBUTTONDOWN));
break;
}
case SDL_EVENT_MOUSE_WHEEL:
case SDL_MOUSEWHEEL:
{
uint8_t xbutton { 0 };
if (event.wheel.y > 0)
@@ -267,31 +263,31 @@ static void sdl3_poll_events(void*)
break;
}
case SDL_EVENT_KEY_UP:
case SDL_EVENT_KEY_DOWN:
case SDL_KEYUP:
case SDL_KEYDOWN:
{
uint8_t scancode = s_sdl_keymap.map[event.key.scancode];
uint8_t scancode = s_sdl_keymap.map[event.key.keysym.scancode];
uint8_t xmod { 0 };
if (event.key.mod & SDL_KMOD_SHIFT)
if (event.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT))
xmod |= (1 << 0);
if (event.key.mod & SDL_KMOD_CAPS)
if (event.key.keysym.mod & (KMOD_CAPS))
xmod |= (1 << 1);
if (event.key.mod & SDL_KMOD_CTRL)
if (event.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL))
xmod |= (1 << 2);
if (event.key.mod & SDL_KMOD_ALT)
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_EVENT_KEY_DOWN));
on_key_event(it->value->wid, scancode, xmod, (event.type == SDL_KEYDOWN));
break;
}
}
}
}
static void sdl3_invalidate(PlatformWindow* window, const uint32_t* src_pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height)
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);
@@ -307,7 +303,7 @@ static void sdl3_invalidate(PlatformWindow* window, const uint32_t* src_pixels,
void* dst_pixels;
int dst_pitch;
if (!SDL_LockTexture(sdl_window.texture, &rect, &dst_pixels, &dst_pitch))
if (SDL_LockTexture(sdl_window.texture, &rect, &dst_pixels, &dst_pitch) == -1)
{
dwarnln("Could not lock texture: {}", SDL_GetError());
return;
@@ -325,68 +321,63 @@ static void sdl3_invalidate(PlatformWindow* window, const uint32_t* src_pixels,
SDL_UnlockTexture(sdl_window.texture);
SDL_RenderClear(sdl_window.renderer);
SDL_RenderTexture(sdl_window.renderer, sdl_window.texture, NULL, NULL);
SDL_RenderCopy(sdl_window.renderer, sdl_window.texture, NULL, NULL);
SDL_RenderPresent(sdl_window.renderer);
}
static void sdl3_request_fullscreen(PlatformWindow* window, bool fullscreen)
static void sdl2_request_fullscreen(PlatformWindow* window, bool fullscreen)
{
auto& sdl_window = *static_cast<SDLWindow*>(window);
SDL_SetWindowFullscreen(sdl_window.window, fullscreen);
SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
static void sdl3_warp_pointer(int32_t x, int32_t y, bool relative)
static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(SystemCursorType type)
{
if (relative)
SDL_SystemCursor sdl_type;
switch (type)
{
float cx, cy;
SDL_GetGlobalMouseState(&cx, &cy);
x += cx;
x += cy;
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;
}
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());
cursor->cursor = SDL_CreateSystemCursor(cursor_type_map[static_cast<size_t>(type)]);
cursor->cursor = SDL_CreateSystemCursor(sdl_type);
if (cursor->cursor == nullptr)
{
dwarnln("Could not create SDL system cursor: {}", SDL_GetError());
@@ -396,11 +387,11 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_create_system_cursor(Syst
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
}
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)
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_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_ARGB8888, const_cast<uint32_t*>(pixels), width * 4);
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());
@@ -411,7 +402,7 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_create_bitmap_cursor(cons
origin_y = BAN::Math::clamp<int32_t>(origin_y, 0, height - 1);
cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y);
SDL_DestroySurface(surface);
SDL_FreeSurface(surface);
if (cursor->cursor == nullptr)
{
@@ -422,7 +413,7 @@ static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl3_create_bitmap_cursor(cons
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
}
static void sdl3_set_cursor(PlatformWindow*, PlatformCursor* cursor)
static void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor)
{
if (cursor == nullptr)
SDL_SetCursor(s_default_cursor);
@@ -434,19 +425,15 @@ static void sdl3_set_cursor(PlatformWindow*, PlatformCursor* cursor)
}
PlatformOps g_platform_ops = {
.initialize = sdl3_initialize,
.poll_events = sdl3_poll_events,
.create_window = sdl3_create_window,
.invalidate = sdl3_invalidate,
.request_resize = sdl3_request_resize,
.request_reposition = sdl3_request_reposition,
.request_fullscreen = sdl3_request_fullscreen,
.warp_pointer = sdl3_warp_pointer,
.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,
.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>
@@ -569,4 +556,4 @@ consteval Keymap::Keymap()
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

@@ -120,13 +120,6 @@ static void bananos_request_resize(PlatformWindow* window, uint32_t width, uint3
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)
{
auto& banan_window = *static_cast<BananWindow*>(window);
@@ -141,31 +134,6 @@ static void bananos_invalidate(PlatformWindow* window, const uint32_t* pixels, u
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)
{
(void)type;
@@ -201,17 +169,19 @@ 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 = {
.initialize = bananos_initialize,
.poll_events = bananos_poll_events,
.create_window = bananos_create_window,
.invalidate = bananos_invalidate,
.request_resize = bananos_request_resize,
.request_reposition = bananos_request_reposition,
.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_bitmap_cursor = bananos_create_bitmap_cursor,
.set_cursor = bananos_set_cursor,