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
8 changed files with 132 additions and 141 deletions

View File

@@ -374,6 +374,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);
@@ -506,17 +509,26 @@ transitient_for_done:
static BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid) 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]; auto& object = *g_objects[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>();
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.properties.contains(WM_CLASS))
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.transient_for,
@@ -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);
@@ -715,7 +725,7 @@ static PlatformWindow* get_platform_window(const Object::Window& window)
{ {
if (window.platform_window) if (window.platform_window)
return const_cast<PlatformWindow*>(window.platform_window.ptr()); return const_cast<PlatformWindow*>(window.platform_window.ptr());
if (window.parent == g_root.windowId) if (window.parent == None)
return nullptr; return nullptr;
auto& object = *g_objects[window.parent]; auto& object = *g_objects[window.parent];
@@ -728,30 +738,28 @@ void update_cursor(WINDOW wid, int32_t x, int32_t y)
if (g_platform_ops.set_cursor == nullptr) if (g_platform_ops.set_cursor == nullptr)
return; return;
const auto child_wid = find_child_window(wid, x, y); wid = find_child_window(wid, x, y);
if (child_wid == None) if (wid == None)
return; return;
CURSOR cursor = None; const CURSOR cid = [&wid]() -> CURSOR {
for (;;)
{
const auto& window = g_objects[wid]->object.get<Object::Window>();
if (window.cursor != None || window.parent == None)
return window.cursor;
wid = window.parent;
}
}();
if (child_wid != None) static CURSOR active_cid = None;
{ if (cid == active_cid)
auto& object = *g_objects[child_wid];
ASSERT(object.type == Object::Type::Window);
cursor = object.object.get<Object::Window>().cursor;
}
static CURSOR active_cursor = None;
if (cursor == active_cursor)
return; return;
auto& object = *g_objects[wid]; const auto& window = g_objects[wid]->object.get<Object::Window>();
ASSERT(object.type == Object::Type::Window); g_platform_ops.set_cursor(get_platform_window(window), get_cursor_safe(cid));
auto& window = object.object.get<Object::Window>();
g_platform_ops.set_cursor(get_platform_window(window), get_cursor_safe(cursor)); active_cid = cid;
active_cursor = window.cursor;
} }
static void on_root_client_message(const xEvent& event) static void on_root_client_message(const xEvent& event)
@@ -1010,9 +1018,11 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
} }
if (cursor_id.has_value()) if (cursor_id.has_value())
{
window.cursor = cursor_id.value(); window.cursor = cursor_id.value();
if (window.hovered) if (window.hovered)
update_cursor(request.window, window.cursor_x, window.cursor_y); update_cursor(request.window, window.cursor_x, window.cursor_y);
}
if (background.has_value()) if (background.has_value())
window.background = background.value(); window.background = background.value();
@@ -1932,12 +1942,15 @@ 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)
const auto child_wid = find_child_window(wid, event_x, event_y); continue;
child_wid = wid_;
break;
}
xQueryPointerReply reply { xQueryPointerReply reply {
.type = X_Reply, .type = X_Reply,
@@ -1945,11 +1958,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>(window.cursor_x), // FIXME
.rootY = static_cast<INT16>(root_y), .rootY = static_cast<INT16>(window.cursor_y), // FIXME
.winX = static_cast<INT16>(event_x), .winX = static_cast<INT16>(window.cursor_x),
.winY = static_cast<INT16>(event_y), .winY = static_cast<INT16>(window.cursor_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));
@@ -2052,8 +2065,7 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
.depth = request.depth, .depth = request.depth,
.width = request.width, .width = request.width,
.height = request.height, .height = request.height,
.data = data.span(), .data = BAN::move(data),
.owned_data = BAN::move(data),
} }
})) }))
)); ));
@@ -2697,7 +2709,7 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
{ {
auto request = decode<xQueryBestSizeReq>(packet).value(); auto request = decode<xQueryBestSizeReq>(packet).value();
dprintln("FreeCursor"); dprintln("QueryBestSize");
dprintln(" class: {}", request.c_class); dprintln(" class: {}", request.c_class);
dprintln(" drawable: {}", request.drawable); dprintln(" drawable: {}", request.drawable);
dprintln(" width: {}", request.width); dprintln(" width: {}", request.width);

View File

@@ -81,8 +81,7 @@ struct Object
CARD8 depth; CARD8 depth;
CARD32 width; CARD32 width;
CARD32 height; CARD32 height;
BAN::ByteSpan data; BAN::Vector<uint8_t> data;
BAN::Vector<uint8_t> owned_data;
}; };
struct GraphicsContext struct GraphicsContext

View File

@@ -1,8 +1,6 @@
#include "Base.h" #include "Base.h"
#include "Definitions.h" #include "Definitions.h"
#include "Keymap.h" #include "Keymap.h"
#include "Platform.h"
#include "SafeGetters.h"
#include "Utils.h" #include "Utils.h"
#include <X11/X.h> #include <X11/X.h>
@@ -451,14 +449,26 @@ static void send_enter_and_leave_events(WINDOW old_wid, int32_t old_x, int32_t o
MUST(window.send_event(event, EnterWindowMask)); MUST(window.send_event(event, EnterWindowMask));
} }
const auto set_last_entry_hovered = [](const BAN::Vector<WINDOW>& path, bool value) { for (const auto wid : old_child_path)
if (path.empty()) g_objects[wid]->object.get<Object::Window>().hovered = false;
return; for (const auto wid : new_child_path)
g_objects[path.back()]->object.get<Object::Window>().hovered = value; g_objects[wid]->object.get<Object::Window>().hovered = true;
}; }
set_last_entry_hovered(old_child_path, false); static WINDOW s_current_top_level_window = None;
set_last_entry_hovered(new_child_path, true);
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)
@@ -469,21 +479,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())
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

@@ -9,6 +9,7 @@ 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_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

@@ -7,42 +7,13 @@
#include <X11/X.h> #include <X11/X.h>
#include <X11/extensions/shmproto.h> #include <X11/extensions/shmproto.h>
#include <netinet/in.h>
#include <sys/shm.h> #include <sys/shm.h>
#include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
static BYTE s_shm_event_base; static BYTE s_shm_event_base;
static BYTE s_shm_error_base; static BYTE s_shm_error_base;
static BYTE s_shm_major_opcode; 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) static BAN::ErrorOr<void*> get_shmseg(Client& client_info, CARD32 shmseg, BYTE op_major, BYTE op_minor)
{ {
auto it = g_objects.find(shmseg); auto it = g_objects.find(shmseg);
@@ -74,11 +45,11 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
{ {
case X_ShmQueryVersion: case X_ShmQueryVersion:
{ {
dwarnln("ShmQueryVersion"); dprintln("ShmQueryVersion");
xShmQueryVersionReply reply { xShmQueryVersionReply reply {
.type = X_Reply, .type = X_Reply,
.sharedPixmaps = is_local_socket(client_info.fd), .sharedPixmaps = false,
.sequenceNumber = client_info.sequence, .sequenceNumber = client_info.sequence,
.length = 0, .length = 0,
.majorVersion = 1, .majorVersion = 1,
@@ -95,7 +66,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
{ {
auto request = decode<xShmAttachReq>(packet).value(); auto request = decode<xShmAttachReq>(packet).value();
dwarnln("ShmAttach"); dprintln("ShmAttach");
dprintln(" shmseg: {}", request.shmseg); dprintln(" shmseg: {}", request.shmseg);
dprintln(" shmid: {}", request.shmid); dprintln(" shmid: {}", request.shmid);
dprintln(" readOnly: {}", request.readOnly); dprintln(" readOnly: {}", request.readOnly);
@@ -144,7 +115,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
{ {
auto request = decode<xShmDetachReq>(packet).value(); auto request = decode<xShmDetachReq>(packet).value();
dwarnln("ShmDetach"); dprintln("ShmDetach");
dprintln(" shmseg: {}", request.shmseg); dprintln(" shmseg: {}", request.shmseg);
void* shm_segment = TRY(get_shmseg(client_info, request.shmseg, op_major, op_minor)); void* shm_segment = TRY(get_shmseg(client_info, request.shmseg, op_major, op_minor));
@@ -159,7 +130,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
{ {
auto request = decode<xShmPutImageReq>(packet).value(); auto request = decode<xShmPutImageReq>(packet).value();
dwarnln("ShmPutImage"); dprintln("ShmPutImage");
dprintln(" drawable: {}", request.drawable); dprintln(" drawable: {}", request.drawable);
dprintln(" gc: {}", request.gc); dprintln(" gc: {}", request.gc);
dprintln(" totalWidth: {}", request.totalWidth); dprintln(" totalWidth: {}", request.totalWidth);
@@ -223,7 +194,7 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
{ {
auto request = decode<xShmGetImageReq>(packet).value(); auto request = decode<xShmGetImageReq>(packet).value();
dwarnln("ShmGetImage"); dprintln("ShmGetImage");
dprintln(" drawable: {}", request.drawable); dprintln(" drawable: {}", request.drawable);
dprintln(" x: {}", request.x); dprintln(" x: {}", request.x);
dprintln(" y: {}", request.y); dprintln(" y: {}", request.y);
@@ -265,44 +236,6 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
break; break;
} }
case X_ShmCreatePixmap:
{
auto request = decode<xShmCreatePixmapReq>(packet).value();
dwarnln("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);
uint8_t bpp = 0;
for (auto format : g_formats)
if (format.depth == request.depth)
bpp = format.bitsPerPixel;
ASSERT(bpp == 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: default:
dwarnln("unsupported shm minor opcode {}", packet[1]); dwarnln("unsupported shm minor opcode {}", packet[1]);
break; break;

View File

@@ -60,8 +60,33 @@ void put_image(const PutImageInfo& info)
} }
case ZPixmap: 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); 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; const auto bytes_per_row = (max_x - min_x) * 4;
for (int32_t y = min_y; y < max_y; y++) for (int32_t y = min_y; y < max_y; y++)

View File

@@ -115,7 +115,7 @@ 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 || type != WindowType::Popup) if (parent == nullptr)
x = y = SDL_WINDOWPOS_UNDEFINED; x = y = SDL_WINDOWPOS_UNDEFINED;
else else
{ {
@@ -215,6 +215,9 @@ static void sdl2_poll_events(void*)
case SDL_WINDOWEVENT_RESTORED: case SDL_WINDOWEVENT_RESTORED:
on_window_fullscreen_event(window.wid, false); on_window_fullscreen_event(window.wid, false);
break; break;
case SDL_WINDOWEVENT_LEAVE:
on_window_leave_event(window.wid);
break;
} }
break; break;
} }
@@ -325,11 +328,10 @@ static void sdl2_invalidate(PlatformWindow* window, const uint32_t* src_pixels,
static void sdl2_request_fullscreen(PlatformWindow* window, bool fullscreen) static void sdl2_request_fullscreen(PlatformWindow* window, bool fullscreen)
{ {
auto& sdl_window = *static_cast<SDLWindow*>(window); auto& sdl_window = *static_cast<SDLWindow*>(window);
(void)sdl_window; SDL_SetWindowFullscreen(sdl_window.window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
(void)fullscreen;
} }
BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(SystemCursorType type) static BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(SystemCursorType type)
{ {
SDL_SystemCursor sdl_type; SDL_SystemCursor sdl_type;
switch (type) switch (type)
@@ -385,7 +387,7 @@ BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_system_cursor(SystemCurso
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor)); return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
} }
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>> 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()); auto cursor = TRY(BAN::UniqPtr<SDLCursor>::create());
@@ -396,6 +398,8 @@ BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_bitmap_cursor(const uint3
return BAN::Error::from_errno(EFAULT); 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); cursor->cursor = SDL_CreateColorCursor(surface, origin_x, origin_y);
SDL_FreeSurface(surface); SDL_FreeSurface(surface);
@@ -409,7 +413,7 @@ BAN::ErrorOr<BAN::UniqPtr<PlatformCursor>> sdl2_create_bitmap_cursor(const uint3
return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor)); return BAN::UniqPtr<PlatformCursor>(BAN::move(cursor));
} }
void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor) static void sdl2_set_cursor(PlatformWindow*, PlatformCursor* cursor)
{ {
if (cursor == nullptr) if (cursor == nullptr)
SDL_SetCursor(s_default_cursor); SDL_SetCursor(s_default_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);