WindowServer: Store rectangles as min and max bounds

This makes some math easier than x,y and w,h
This commit is contained in:
2026-04-11 06:30:21 +03:00
parent 2a9dad2dd8
commit 4bde088b28
4 changed files with 328 additions and 284 deletions

View File

@@ -13,55 +13,70 @@ struct Position
struct Rectangle struct Rectangle
{ {
int32_t x; int32_t min_x;
int32_t y; int32_t min_y;
int32_t width; int32_t max_x;
int32_t height; int32_t max_y;
int32_t width() const
{
return max_x - min_x;
}
int32_t height() const
{
return max_y - min_y;
}
int32_t area() const
{
return width() * height();
}
bool contains(Position position) const bool contains(Position position) const
{ {
if (position.x < x || position.x >= x + width) if (position.x < min_x || position.x >= max_x)
return false; return false;
if (position.y < y || position.y >= y + height) if (position.y < min_y || position.y >= max_y)
return false; return false;
return true; return true;
} }
BAN::Optional<Rectangle> get_overlap(Rectangle other) const BAN::Optional<Rectangle> get_overlap(Rectangle other) const
{ {
if (height == 0 || width == 0 || other.width == 0 || other.height == 0) if (width() == 0 || height() == 0 || other.width() == 0 || other.height() == 0)
return {}; return {};
const auto min_x = BAN::Math::max(x, other.x); const auto min_x = BAN::Math::max(this->min_x, other.min_x);
const auto min_y = BAN::Math::max(y, other.y); const auto min_y = BAN::Math::max(this->min_y, other.min_y);
const auto max_x = BAN::Math::min(x + width, other.x + other.width); const auto max_x = BAN::Math::min(this->max_x, other.max_x);
const auto max_y = BAN::Math::min(y + height, other.y + other.height); const auto max_y = BAN::Math::min(this->max_y, other.max_y);
if (min_x >= max_x || min_y >= max_y) if (min_x >= max_x || min_y >= max_y)
return {}; return {};
return Rectangle { return Rectangle {
.x = min_x, .min_x = min_x,
.y = min_y, .min_y = min_y,
.width = max_x - min_x, .max_x = max_x,
.height = max_y - min_y, .max_y = max_y,
}; };
} }
Rectangle get_bounding_box(Rectangle other) const Rectangle get_bounding_box(Rectangle other) const
{ {
const auto min_x = BAN::Math::min(x, other.x); const auto min_x = BAN::Math::min(this->min_x, other.min_x);
const auto min_y = BAN::Math::min(y, other.y); const auto min_y = BAN::Math::min(this->min_y, other.min_y);
const auto max_x = BAN::Math::max(x + width, other.x + other.width); const auto max_x = BAN::Math::max(this->max_x, other.max_x);
const auto max_y = BAN::Math::max(y + height, other.y + other.height); const auto max_y = BAN::Math::max(this->max_y, other.max_y);
return Rectangle { return Rectangle {
.x = min_x, .min_x = min_x,
.y = min_y, .min_y = min_y,
.width = max_x - min_x, .max_x = max_x,
.height = max_y - min_y, .max_y = max_y,
}; };
} }
bool operator==(const Rectangle& other) const bool operator==(const Rectangle& other) const
{ {
return x == other.x && y == other.y && width == other.width && height == other.height; return min_x == other.min_x && min_y == other.min_y && max_x == other.max_x && max_y == other.max_y;
} }
}; };

View File

@@ -61,8 +61,8 @@ BAN::ErrorOr<void> Window::resize(uint32_t width, uint32_t height)
{ {
const auto old_area = m_client_area; const auto old_area = m_client_area;
m_client_area.width = width; m_client_area.max_x = m_client_area.min_x + width;
m_client_area.height = height; m_client_area.max_y = m_client_area.min_y + height;
auto title_bar_ret = prepare_title_bar(); auto title_bar_ret = prepare_title_bar();
m_client_area = old_area; m_client_area = old_area;
@@ -80,8 +80,9 @@ BAN::ErrorOr<void> Window::resize(uint32_t width, uint32_t height)
m_fb_addr = fb_addr; m_fb_addr = fb_addr;
m_smo_key = smo_key; m_smo_key = smo_key;
m_client_area.width = width;
m_client_area.height = height; m_client_area.max_x = m_client_area.min_x + width;
m_client_area.max_y = m_client_area.min_y + height;
return {}; return {};
} }
@@ -98,7 +99,7 @@ BAN::ErrorOr<void> Window::prepare_title_bar()
const auto text_area = title_text_area(); const auto text_area = title_text_area();
for (size_t i = 0; i < m_title.size() && (i + 1) * font_w < static_cast<uint32_t>(text_area.width); i++) for (size_t i = 0; i < m_title.size() && (i + 1) * font_w < static_cast<uint32_t>(text_area.width()); i++)
{ {
const auto* glyph = m_font.glyph(m_title[i]); const auto* glyph = m_font.glyph(m_title[i]);
if (glyph == nullptr) if (glyph == nullptr)
@@ -112,7 +113,7 @@ BAN::ErrorOr<void> Window::prepare_title_bar()
break; break;
for (int32_t x = 0; (uint32_t)x < font_w; x++) for (int32_t x = 0; (uint32_t)x < font_w; x++)
{ {
if (x + x_off >= text_area.width) if (x + x_off >= text_area.width())
break; break;
const uint8_t bitmask = 1 << (font_w - x - 1); const uint8_t bitmask = 1 << (font_w - x - 1);
if (glyph[y * font_p] & bitmask) if (glyph[y * font_p] & bitmask)

View File

@@ -31,17 +31,23 @@ public:
void set_position(Position position) void set_position(Position position)
{ {
m_client_area.x = position.x; const auto width = m_client_area.width();
m_client_area.y = position.y; const auto height = m_client_area.height();
m_client_area = {
.min_x = position.x,
.min_y = position.y,
.max_x = position.x + width,
.max_y = position.y + height,
};
} }
int client_fd() const { return m_client_fd; } int client_fd() const { return m_client_fd; }
long smo_key() const { return m_smo_key; } long smo_key() const { return m_smo_key; }
int32_t client_x() const { return m_client_area.x; } int32_t client_x() const { return m_client_area.min_x; }
int32_t client_y() const { return m_client_area.y; } int32_t client_y() const { return m_client_area.min_y; }
int32_t client_width() const { return m_client_area.width; } int32_t client_width() const { return m_client_area.width(); }
int32_t client_height() const { return m_client_area.height; } int32_t client_height() const { return m_client_area.height(); }
Rectangle client_size() const { return { 0, 0, client_width(), client_height() }; } Rectangle client_size() const { return { 0, 0, client_width(), client_height() }; }
Rectangle client_area() const { return m_client_area; } Rectangle client_area() const { return m_client_area; }
@@ -50,14 +56,14 @@ public:
int32_t title_bar_width() const { return client_width(); } int32_t title_bar_width() const { return client_width(); }
int32_t title_bar_height() const { return m_attributes.title_bar ? m_title_bar_height : 0; } int32_t title_bar_height() const { return m_attributes.title_bar ? m_title_bar_height : 0; }
Rectangle title_bar_size() const { return { 0, 0, title_bar_width(), title_bar_height() }; } Rectangle title_bar_size() const { return { 0, 0, title_bar_width(), title_bar_height() }; }
Rectangle title_bar_area() const { return { title_bar_x(), title_bar_y(), title_bar_width(), title_bar_height() }; } Rectangle title_bar_area() const { return { title_bar_x(), title_bar_y(), title_bar_x() + title_bar_width(), title_bar_y() + title_bar_height() }; }
int32_t full_x() const { return title_bar_x(); } int32_t full_x() const { return title_bar_x(); }
int32_t full_y() const { return title_bar_y(); } int32_t full_y() const { return title_bar_y(); }
int32_t full_width() const { return client_width(); } int32_t full_width() const { return client_width(); }
int32_t full_height() const { return client_height() + title_bar_height(); } int32_t full_height() const { return client_height() + title_bar_height(); }
Rectangle full_size() const { return { 0, 0, full_width(), full_height() }; } Rectangle full_size() const { return { 0, 0, full_width(), full_height() }; }
Rectangle full_area() const { return { full_x(), full_y(), full_width(), full_height() }; } Rectangle full_area() const { return { full_x(), full_y(), full_x() + full_width(), full_y() + full_height() }; }
bool has_cursor() const { return m_cursor.has_value(); } bool has_cursor() const { return m_cursor.has_value(); }
const Cursor& cursor() const { return m_cursor.value(); } const Cursor& cursor() const { return m_cursor.value(); }
@@ -90,7 +96,7 @@ public:
} }
Circle close_button_area() const { return { title_bar_x() + title_bar_width() - title_bar_height() / 2, title_bar_y() + title_bar_height() / 2, title_bar_height() * 3 / 8 }; } Circle close_button_area() const { return { title_bar_x() + title_bar_width() - title_bar_height() / 2, title_bar_y() + title_bar_height() / 2, title_bar_height() * 3 / 8 }; }
Rectangle title_text_area() const { return { title_bar_x(), title_bar_y(), title_bar_width() - title_bar_height(), title_bar_height() }; } Rectangle title_text_area() const { return { title_bar_x(), title_bar_y(), title_bar_x() + title_bar_width() - title_bar_height(), title_bar_y() + title_bar_height() }; }
BAN::ErrorOr<void> initialize(BAN::StringView title, uint32_t width, uint32_t height); BAN::ErrorOr<void> initialize(BAN::StringView title, uint32_t width, uint32_t height);
BAN::ErrorOr<void> resize(uint32_t width, uint32_t height); BAN::ErrorOr<void> resize(uint32_t width, uint32_t height);

View File

@@ -82,11 +82,12 @@ void WindowServer::on_window_create(int fd, const LibGUI::WindowPacket::WindowCr
static_cast<int32_t>((m_framebuffer.height - window->client_height()) / 2), static_cast<int32_t>((m_framebuffer.height - window->client_height()) / 2),
}); });
LibGUI::EventPacket::ResizeWindowEvent response; const LibGUI::EventPacket::ResizeWindowEvent event_packet {
response.width = window->client_width(); .width = static_cast<uint32_t>(window->client_width()),
response.height = window->client_height(); .height = static_cast<uint32_t>(window->client_height()),
response.smo_key = window->smo_key(); .smo_key = window->smo_key(),
if (auto ret = append_serialized_packet(response, fd); ret.is_error()) };
if (auto ret = append_serialized_packet(event_packet, fd); ret.is_error())
{ {
dwarnln("could not respond to window create request: {}", ret.error()); dwarnln("could not respond to window create request: {}", ret.error());
return; return;
@@ -122,12 +123,17 @@ void WindowServer::on_window_invalidate(int fd, const LibGUI::WindowPacket::Wind
if (!target_window->get_attributes().shown) if (!target_window->get_attributes().shown)
return; return;
invalidate({ const auto client_area = target_window->client_area();
target_window->client_x() + static_cast<int32_t>(packet.x),
target_window->client_y() + static_cast<int32_t>(packet.y), const Rectangle invalidate_area {
BAN::Math::min<int32_t>(packet.width, target_window->client_width()), .min_x = client_area.min_x + static_cast<int32_t>(packet.x),
BAN::Math::min<int32_t>(packet.height, target_window->client_height()) .min_y = client_area.min_y + static_cast<int32_t>(packet.y),
}); .max_x = client_area.min_x + static_cast<int32_t>(packet.x + packet.width),
.max_y = client_area.min_y + static_cast<int32_t>(packet.y + packet.height),
};
if (auto opt_overlap = invalidate_area.get_overlap(client_area); opt_overlap.has_value())
invalidate(opt_overlap.release_value());
} }
void WindowServer::on_window_set_position(int fd, const LibGUI::WindowPacket::WindowSetPosition& packet) void WindowServer::on_window_set_position(int fd, const LibGUI::WindowPacket::WindowSetPosition& packet)
@@ -155,8 +161,8 @@ void WindowServer::on_window_set_position(int fd, const LibGUI::WindowPacket::Wi
if (!target_window->get_attributes().shown) if (!target_window->get_attributes().shown)
return; return;
const auto new_client_area = target_window->full_area(); invalidate(old_client_area);
invalidate(new_client_area.get_bounding_box(old_client_area)); invalidate(target_window->full_area());
} }
void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket::WindowSetAttributes& packet) void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket::WindowSetAttributes& packet)
@@ -172,16 +178,17 @@ void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket::
const auto old_client_area = target_window->full_area(); const auto old_client_area = target_window->full_area();
target_window->set_attributes(packet.attributes); target_window->set_attributes(packet.attributes);
const auto new_client_area = target_window->full_area();
invalidate(new_client_area.get_bounding_box(old_client_area)); invalidate(old_client_area);
invalidate(target_window->full_area());
if ((!packet.attributes.focusable || !packet.attributes.shown) && m_focused_window == target_window) if ((!packet.attributes.focusable || !packet.attributes.shown) && m_focused_window == target_window)
{ {
if (m_state == State::Fullscreen && m_focused_window->get_attributes().resizable) if (m_state == State::Fullscreen && m_focused_window->get_attributes().resizable)
{ {
if (!resize_window(m_focused_window, m_non_full_screen_rect.width, m_non_full_screen_rect.height)) if (!resize_window(m_focused_window, m_non_full_screen_rect.width(), m_non_full_screen_rect.height()))
return; return;
m_focused_window->set_position({ m_non_full_screen_rect.x, m_non_full_screen_rect.y }); m_focused_window->set_position({ m_non_full_screen_rect.min_x, m_non_full_screen_rect.min_y });
} }
m_focused_window = nullptr; m_focused_window = nullptr;
@@ -200,11 +207,9 @@ void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket::
if (!send_shown_event) if (!send_shown_event)
return; return;
auto event_packet = LibGUI::EventPacket::WindowShownEvent { const LibGUI::EventPacket::WindowShownEvent event_packet { .event = {
.event = {
.shown = target_window->get_attributes().shown, .shown = target_window->get_attributes().shown,
}, }};
};
if (auto ret = append_serialized_packet(event_packet, target_window->client_fd()); ret.is_error()) if (auto ret = append_serialized_packet(event_packet, target_window->client_fd()); ret.is_error())
dwarnln("could not send window shown event: {}", ret.error()); dwarnln("could not send window shown event: {}", ret.error());
@@ -262,7 +267,8 @@ void WindowServer::on_window_set_size(int fd, const LibGUI::WindowPacket::Window
if (!target_window->get_attributes().shown) if (!target_window->get_attributes().shown)
return; return;
invalidate(target_window->full_area().get_bounding_box(old_area)); invalidate(old_area);
invalidate(target_window->full_area());
} }
void WindowServer::on_window_set_min_size(int fd, const LibGUI::WindowPacket::WindowSetMinSize& packet) void WindowServer::on_window_set_min_size(int fd, const LibGUI::WindowPacket::WindowSetMinSize& packet)
@@ -304,14 +310,14 @@ void WindowServer::on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::
return; return;
if (m_focused_window->get_attributes().resizable) if (m_focused_window->get_attributes().resizable)
{ {
if (!resize_window(m_focused_window, m_non_full_screen_rect.width, m_non_full_screen_rect.height)) if (!resize_window(m_focused_window, m_non_full_screen_rect.width(), m_non_full_screen_rect.height()))
return; return;
m_focused_window->set_position({ m_non_full_screen_rect.x, m_non_full_screen_rect.y }); m_focused_window->set_position({ m_non_full_screen_rect.min_x, m_non_full_screen_rect.min_y });
} }
auto event_packet = LibGUI::EventPacket::WindowFullscreenEvent { const LibGUI::EventPacket::WindowFullscreenEvent event_packet { .event = {
.event = { .fullscreen = false } .fullscreen = false,
}; }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error()) if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send window fullscreen event: {}", ret.error()); dwarnln("could not send window fullscreen event: {}", ret.error());
@@ -344,9 +350,9 @@ void WindowServer::on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::
m_non_full_screen_rect = old_area; m_non_full_screen_rect = old_area;
} }
auto event_packet = LibGUI::EventPacket::WindowFullscreenEvent { const LibGUI::EventPacket::WindowFullscreenEvent event_packet { .event = {
.event = { .fullscreen = true } .fullscreen = true,
}; }};
if (auto ret = append_serialized_packet(event_packet, target_window->client_fd()); ret.is_error()) if (auto ret = append_serialized_packet(event_packet, target_window->client_fd()); ret.is_error())
dwarnln("could not send window fullscreen event: {}", ret.error()); dwarnln("could not send window fullscreen event: {}", ret.error());
@@ -373,7 +379,7 @@ void WindowServer::on_window_set_title(int fd, const LibGUI::WindowPacket::Windo
if (!target_window->get_attributes().shown) if (!target_window->get_attributes().shown)
return; return;
invalidate(target_window->title_bar_area()); invalidate(target_window->title_text_area());
} }
void WindowServer::on_window_set_cursor(int fd, const LibGUI::WindowPacket::WindowSetCursor& packet) void WindowServer::on_window_set_cursor(int fd, const LibGUI::WindowPacket::WindowSetCursor& packet)
@@ -397,17 +403,19 @@ void WindowServer::on_window_set_cursor(int fd, const LibGUI::WindowPacket::Wind
return; return;
} }
auto old_cursor = cursor_area(); const auto old_cursor_area = cursor_area();
if (packet.width == 0 || packet.height == 0) if (packet.width == 0 || packet.height == 0)
target_window->remove_cursor(); target_window->remove_cursor();
else else
{ {
Window::Cursor cursor; Window::Cursor cursor {
cursor.width = packet.width; .width = packet.width,
cursor.height = packet.height; .height = packet.height,
cursor.origin_x = packet.origin_x; .origin_x = packet.origin_x,
cursor.origin_y = packet.origin_y; .origin_y = packet.origin_y,
.pixels = {},
};
if (auto ret = cursor.pixels.resize(packet.pixels.size()); ret.is_error()) if (auto ret = cursor.pixels.resize(packet.pixels.size()); ret.is_error())
{ {
dwarnln("failed to set cursor: {}", ret.error()); dwarnln("failed to set cursor: {}", ret.error());
@@ -419,7 +427,10 @@ void WindowServer::on_window_set_cursor(int fd, const LibGUI::WindowPacket::Wind
} }
if (find_hovered_window() == target_window) if (find_hovered_window() == target_window)
invalidate(cursor_area().get_bounding_box(old_cursor)); {
invalidate(old_cursor_area);
invalidate(cursor_area());
}
} }
static void update_volume(const char* new_volume) static void update_volume(const char* new_volume)
@@ -487,8 +498,8 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
// Kill window with mod+Q // Kill window with mod+Q
if (m_is_mod_key_held && event.pressed() && event.key == LibInput::Key::Q) if (m_is_mod_key_held && event.pressed() && event.key == LibInput::Key::Q)
{ {
LibGUI::EventPacket::CloseWindowEvent packet; const LibGUI::EventPacket::CloseWindowEvent event_packet {};
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send window close event: {}", ret.error()); dwarnln("could not send window close event: {}", ret.error());
return; return;
} }
@@ -499,9 +510,9 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
{ {
if (m_focused_window->get_attributes().resizable) if (m_focused_window->get_attributes().resizable)
{ {
if (!resize_window(m_focused_window, m_non_full_screen_rect.width, m_non_full_screen_rect.height)) if (!resize_window(m_focused_window, m_non_full_screen_rect.width(), m_non_full_screen_rect.height()))
return; return;
m_focused_window->set_position({ m_non_full_screen_rect.x, m_non_full_screen_rect.y }); m_focused_window->set_position({ m_non_full_screen_rect.min_x, m_non_full_screen_rect.min_y });
} }
m_state = State::Normal; m_state = State::Normal;
} }
@@ -518,9 +529,9 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
m_state = State::Fullscreen; m_state = State::Fullscreen;
} }
auto event_packet = LibGUI::EventPacket::WindowFullscreenEvent { const LibGUI::EventPacket::WindowFullscreenEvent event_packet { .event = {
.event = { .fullscreen = (m_state == State::Fullscreen) } .fullscreen = (m_state == State::Fullscreen),
}; }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error()) if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send window fullscreen event: {}", ret.error()); dwarnln("could not send window fullscreen event: {}", ret.error());
@@ -528,9 +539,10 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
return; return;
} }
LibGUI::EventPacket::KeyEvent packet; const LibGUI::EventPacket::KeyEvent event_packet {
packet.event = event; .event = event,
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) };
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send key event: {}", ret.error()); dwarnln("could not send key event: {}", ret.error());
} }
@@ -540,12 +552,13 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
{ {
ASSERT(m_focused_window); ASSERT(m_focused_window);
LibGUI::EventPacket::MouseButtonEvent packet; const LibGUI::EventPacket::MouseButtonEvent event_packet { .event = {
packet.event.button = event.button; .button = event.button,
packet.event.pressed = event.pressed; .pressed = event.pressed,
packet.event.x = 0; .x = 0,
packet.event.y = 0; .y = 0,
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send mouse button event: {}", ret.error()); dwarnln("could not send mouse button event: {}", ret.error());
return; return;
} }
@@ -603,8 +616,8 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
if (event.button == LibInput::MouseButton::Left && !event.pressed && target_window->close_button_area().contains(m_cursor)) if (event.button == LibInput::MouseButton::Left && !event.pressed && target_window->close_button_area().contains(m_cursor))
{ {
LibGUI::EventPacket::CloseWindowEvent packet; const LibGUI::EventPacket::CloseWindowEvent event_packet {};
if (auto ret = append_serialized_packet(packet, target_window->client_fd()); ret.is_error()) if (auto ret = append_serialized_packet(event_packet, target_window->client_fd()); ret.is_error())
dwarnln("could not send close window event: {}", ret.error()); dwarnln("could not send close window event: {}", ret.error());
break; break;
} }
@@ -613,12 +626,13 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
case State::Fullscreen: case State::Fullscreen:
if (target_window && (!event.pressed || target_window->client_area().contains(m_cursor))) if (target_window && (!event.pressed || target_window->client_area().contains(m_cursor)))
{ {
LibGUI::EventPacket::MouseButtonEvent packet; const LibGUI::EventPacket::MouseButtonEvent event_packet { .event = {
packet.event.button = event.button; .button = event.button,
packet.event.pressed = event.pressed; .pressed = event.pressed,
packet.event.x = m_cursor.x - target_window->client_x(); .x = m_cursor.x - target_window->client_x(),
packet.event.y = m_cursor.y - target_window->client_y(); .y = m_cursor.y - target_window->client_y(),
if (auto ret = append_serialized_packet(packet, target_window->client_fd()); ret.is_error()) }};
if (auto ret = append_serialized_packet(event_packet, target_window->client_fd()); ret.is_error())
{ {
dwarnln("could not send mouse button event: {}", ret.error()); dwarnln("could not send mouse button event: {}", ret.error());
return; return;
@@ -635,27 +649,29 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
{ {
const auto resize_area = this->resize_area(m_cursor); const auto resize_area = this->resize_area(m_cursor);
m_state = State::Normal; m_state = State::Normal;
invalidate(resize_area.get_bounding_box(m_focused_window->full_area()));
const auto old_area = m_focused_window->full_area(); invalidate(resize_area);
if (auto ret = m_focused_window->resize(resize_area.width, resize_area.height - m_focused_window->title_bar_height()); ret.is_error()) invalidate(m_focused_window->full_area());
if (auto ret = m_focused_window->resize(resize_area.width(), resize_area.height() - m_focused_window->title_bar_height()); ret.is_error())
{ {
dwarnln("could not resize client window {}", ret.error()); dwarnln("could not resize client window {}", ret.error());
return; return;
} }
m_focused_window->set_position({ resize_area.x, resize_area.y + m_focused_window->title_bar_height() }); m_focused_window->set_position({ resize_area.min_x, resize_area.min_y + m_focused_window->title_bar_height() });
LibGUI::EventPacket::ResizeWindowEvent event; const LibGUI::EventPacket::ResizeWindowEvent event_packet {
event.width = m_focused_window->client_width(); .width = static_cast<uint32_t>(m_focused_window->client_width()),
event.height = m_focused_window->client_height(); .height = static_cast<uint32_t>(m_focused_window->client_height()),
event.smo_key = m_focused_window->smo_key(); .smo_key = m_focused_window->smo_key(),
if (auto ret = append_serialized_packet(event, m_focused_window->client_fd()); ret.is_error()) };
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
{ {
dwarnln("could not respond to window resize request: {}", ret.error()); dwarnln("could not respond to window resize request: {}", ret.error());
return; return;
} }
invalidate(m_focused_window->full_area().get_bounding_box(old_area)); invalidate(m_focused_window->full_area());
} }
break; break;
} }
@@ -663,28 +679,28 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
void WindowServer::on_mouse_move_impl(int32_t new_x, int32_t new_y) void WindowServer::on_mouse_move_impl(int32_t new_x, int32_t new_y)
{ {
LibInput::MouseMoveEvent event; const LibInput::MouseMoveEvent event {
event.rel_x = new_x - m_cursor.x; .rel_x = new_x - m_cursor.x,
event.rel_y = new_y - m_cursor.y; .rel_y = new_y - m_cursor.y,
};
if (event.rel_x == 0 && event.rel_y == 0) if (event.rel_x == 0 && event.rel_y == 0)
return; return;
auto old_cursor = cursor_area(); const auto old_cursor_area = cursor_area();
m_cursor.x = new_x; m_cursor.x = new_x;
m_cursor.y = new_y; m_cursor.y = new_y;
auto new_cursor = cursor_area();
invalidate(old_cursor); invalidate(old_cursor_area);
invalidate(new_cursor); invalidate(cursor_area());
// TODO: Really no need to loop over every window // TODO: Really no need to loop over every window
for (auto& window : m_client_windows) for (auto& window : m_client_windows)
{ {
if (!window->get_attributes().shown) if (!window->get_attributes().shown)
continue; continue;
auto title_bar = window->title_bar_area(); const auto title_bar_area = window->title_bar_area();
if (title_bar.get_overlap(old_cursor).has_value() || title_bar.get_overlap(new_cursor).has_value()) if (title_bar_area.get_overlap(old_cursor_area).has_value() || title_bar_area.get_overlap(cursor_area()).has_value())
invalidate(title_bar); invalidate(title_bar_area);
} }
if (!m_focused_window) if (!m_focused_window)
@@ -695,10 +711,11 @@ void WindowServer::on_mouse_move_impl(int32_t new_x, int32_t new_y)
case State::Normal: case State::Normal:
case State::Fullscreen: case State::Fullscreen:
{ {
LibGUI::EventPacket::MouseMoveEvent packet; const LibGUI::EventPacket::MouseMoveEvent event_packet { .event = {
packet.event.x = m_cursor.x - m_focused_window->client_x(); .x = m_cursor.x - m_focused_window->client_x(),
packet.event.y = m_cursor.y - m_focused_window->client_y(); .y = m_cursor.y - m_focused_window->client_y(),
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
{ {
dwarnln("could not send mouse move event: {}", ret.error()); dwarnln("could not send mouse move event: {}", ret.error());
return; return;
@@ -707,21 +724,19 @@ void WindowServer::on_mouse_move_impl(int32_t new_x, int32_t new_y)
} }
case State::Moving: case State::Moving:
{ {
auto old_window = m_focused_window->full_area(); const auto old_window_area = m_focused_window->full_area();
m_focused_window->set_position({ m_focused_window->set_position({
m_focused_window->client_x() + event.rel_x, m_focused_window->client_x() + event.rel_x,
m_focused_window->client_y() + event.rel_y, m_focused_window->client_y() + event.rel_y,
}); });
auto new_window = m_focused_window->full_area(); invalidate(old_window_area);
invalidate(old_window); invalidate(m_focused_window->full_area());
invalidate(new_window);
break; break;
} }
case State::Resizing: case State::Resizing:
{ {
const auto old_resize_area = resize_area({ old_cursor.x, old_cursor.y }); invalidate(resize_area({ .x = old_cursor_area.min_x, .y = old_cursor_area.min_y }));
const auto new_resize_area = resize_area({ new_cursor.x, new_cursor.y }); invalidate(resize_area(m_cursor));
invalidate(old_resize_area.get_bounding_box(new_resize_area));
break; break;
} }
} }
@@ -733,10 +748,11 @@ void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
{ {
ASSERT(m_focused_window); ASSERT(m_focused_window);
LibGUI::EventPacket::MouseMoveEvent packet; const LibGUI::EventPacket::MouseMoveEvent event_packet { .event = {
packet.event.x = event.rel_x; .x = event.rel_x,
packet.event.y = -event.rel_y; .y = -event.rel_y,
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send mouse move event: {}", ret.error()); dwarnln("could not send mouse move event: {}", ret.error());
return; return;
} }
@@ -803,16 +819,17 @@ void WindowServer::on_mouse_move_abs(LibInput::MouseMoveAbsEvent event)
void WindowServer::on_mouse_scroll(LibInput::MouseScrollEvent event) void WindowServer::on_mouse_scroll(LibInput::MouseScrollEvent event)
{ {
if (m_focused_window) if (!m_focused_window)
{ return;
LibGUI::EventPacket::MouseScrollEvent packet;
packet.event.scroll = event.scroll; const LibGUI::EventPacket::MouseScrollEvent event_packet { .event = {
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) .scroll = event.scroll,
}};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
{ {
dwarnln("could not send mouse scroll event: {}", ret.error()); dwarnln("could not send mouse scroll event: {}", ret.error());
return; return;
} }
}
} }
void WindowServer::set_focused_window(BAN::RefPtr<Window> window) void WindowServer::set_focused_window(BAN::RefPtr<Window> window)
@@ -829,9 +846,10 @@ void WindowServer::set_focused_window(BAN::RefPtr<Window> window)
if (m_focused_window) if (m_focused_window)
{ {
LibGUI::EventPacket::WindowFocusEvent packet; const LibGUI::EventPacket::WindowFocusEvent event_packet { .event = {
packet.event.focused = false; .focused = false,
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send window focus event: {}", ret.error()); dwarnln("could not send window focus event: {}", ret.error());
} }
@@ -849,9 +867,10 @@ void WindowServer::set_focused_window(BAN::RefPtr<Window> window)
if (m_focused_window) if (m_focused_window)
{ {
LibGUI::EventPacket::WindowFocusEvent packet; const LibGUI::EventPacket::WindowFocusEvent event_packet { .event = {
packet.event.focused = true; .focused = true,
if (auto ret = append_serialized_packet(packet, m_focused_window->client_fd()); ret.is_error()) }};
if (auto ret = append_serialized_packet(event_packet, m_focused_window->client_fd()); ret.is_error())
dwarnln("could not send window focus event: {}", ret.error()); dwarnln("could not send window focus event: {}", ret.error());
} }
} }
@@ -928,10 +947,10 @@ void WindowServer::invalidate(Rectangle area)
} }
const uint32_t offset = (rel_y * s_default_cursor_width + rel_x) * 4; const uint32_t offset = (rel_y * s_default_cursor_width + rel_x) * 4;
uint32_t r = (((s_default_cursor_data[offset + 0] - 33) << 2) | ((s_default_cursor_data[offset + 1] - 33) >> 4)); const uint32_t r = ((( s_default_cursor_data[offset + 0] - 33 ) << 2) | ((s_default_cursor_data[offset + 1] - 33) >> 4));
uint32_t g = ((((s_default_cursor_data[offset + 1] - 33) & 0xF) << 4) | ((s_default_cursor_data[offset + 2] - 33) >> 2)); const uint32_t g = ((((s_default_cursor_data[offset + 1] - 33) & 0xF) << 4) | ((s_default_cursor_data[offset + 2] - 33) >> 2));
uint32_t b = ((((s_default_cursor_data[offset + 2] - 33) & 0x3) << 6) | ((s_default_cursor_data[offset + 3] - 33))); const uint32_t b = ((((s_default_cursor_data[offset + 2] - 33) & 0x3) << 6) | ((s_default_cursor_data[offset + 3] - 33) ));
uint32_t color = (r << 16) | (g << 8) | b; const uint32_t color = (r << 16) | (g << 8) | b;
if (color == 0xFF00FF) if (color == 0xFF00FF)
return {}; return {};
return color; return color;
@@ -940,14 +959,16 @@ void WindowServer::invalidate(Rectangle area)
if (m_state == State::Fullscreen) if (m_state == State::Fullscreen)
{ {
ASSERT(m_focused_window); ASSERT(m_focused_window);
area.x -= m_focused_window->client_x(); area.min_x -= m_focused_window->client_x();
area.y -= m_focused_window->client_y(); area.max_x -= m_focused_window->client_x();
area.min_y -= m_focused_window->client_y();
area.max_y -= m_focused_window->client_y();
const Rectangle client_area { const Rectangle client_area {
.x = 0, .min_x = 0,
.y = 0, .min_y = 0,
.width = m_focused_window->client_width(), .max_x = m_focused_window->client_width(),
.height = m_focused_window->client_height(), .max_y = m_focused_window->client_height(),
}; };
auto focused_overlap = area.get_overlap(client_area); auto focused_overlap = area.get_overlap(client_area);
@@ -964,24 +985,24 @@ void WindowServer::invalidate(Rectangle area)
if (!should_alpha_blend) if (!should_alpha_blend)
{ {
for (int32_t y = area.y; y < area.y + area.height; y++) for (int32_t y = area.min_y; y < area.max_y; y++)
{ {
memcpy( memcpy(
&m_framebuffer.mmap[y * m_framebuffer.width + area.x], &m_framebuffer.mmap[y * m_framebuffer.width + area.min_x],
&client_ptr[y * client_width + area.x], &client_ptr[y * client_width + area.min_x],
area.width * sizeof(uint32_t) area.width() * sizeof(uint32_t)
); );
} }
} }
else else
{ {
for (int32_t y = area.y; y < area.y + area.height; y++) for (int32_t y = area.min_y; y < area.max_y; y++)
{ {
const uint32_t* window_row = &client_ptr[y * client_width + area.x]; const uint32_t* window_row = &client_ptr[y * client_width + area.min_x];
const uint32_t* image_row = &m_background_image[y * m_framebuffer.width + area.x]; const uint32_t* image_row = &m_background_image[y * m_framebuffer.width + area.min_x];
uint32_t* frameb_row = &m_framebuffer.mmap[y * m_framebuffer.width + area.x]; uint32_t* frameb_row = &m_framebuffer.mmap[y * m_framebuffer.width + area.min_x];
int32_t pixels = area.width; int32_t pixels = area.width();
for (; pixels >= 4; pixels -= 4) for (; pixels >= 4; pixels -= 4)
{ {
alpha_blend4(window_row, image_row, frameb_row); alpha_blend4(window_row, image_row, frameb_row);
@@ -1004,18 +1025,18 @@ void WindowServer::invalidate(Rectangle area)
else else
{ {
auto opt_dst_area = Rectangle { auto opt_dst_area = Rectangle {
.x = area.x * m_framebuffer.width / m_focused_window->client_width(), .min_x = area.min_x * m_framebuffer.width / m_focused_window->client_width(),
.y = area.y * m_framebuffer.height / m_focused_window->client_height(), .min_y = area.min_y * m_framebuffer.height / m_focused_window->client_height(),
.width = BAN::Math::div_round_up(area.width * m_framebuffer.width, m_focused_window->client_width()), .max_x = BAN::Math::div_round_up(area.max_x * m_framebuffer.width, m_focused_window->client_width()),
.height = BAN::Math::div_round_up(area.height * m_framebuffer.height, m_focused_window->client_height()) .max_y = BAN::Math::div_round_up(area.max_y * m_framebuffer.height, m_focused_window->client_height())
}.get_overlap(m_framebuffer.area()); }.get_overlap(m_framebuffer.area());
if (!opt_dst_area.has_value()) if (!opt_dst_area.has_value())
return; return;
const auto dst_area = opt_dst_area.release_value(); const auto dst_area = opt_dst_area.release_value();
for (int32_t dst_y = dst_area.y; dst_y < dst_area.y + dst_area.height; dst_y++) for (int32_t dst_y = dst_area.min_y; dst_y < dst_area.max_y; dst_y++)
{ {
for (int32_t dst_x = dst_area.x; dst_x < dst_area.x + dst_area.width; dst_x++) for (int32_t dst_x = dst_area.min_x; dst_x < dst_area.max_x; dst_x++)
{ {
const int32_t src_x = BAN::Math::clamp<int32_t>(dst_x * m_focused_window->client_width() / m_framebuffer.width, 0, m_focused_window->client_width()); const int32_t src_x = BAN::Math::clamp<int32_t>(dst_x * m_focused_window->client_width() / m_framebuffer.width, 0, m_focused_window->client_width());
const int32_t src_y = BAN::Math::clamp<int32_t>(dst_y * m_focused_window->client_height() / m_framebuffer.height, 0, m_focused_window->client_height()); const int32_t src_y = BAN::Math::clamp<int32_t>(dst_y * m_focused_window->client_height() / m_framebuffer.height, 0, m_focused_window->client_height());
@@ -1034,18 +1055,19 @@ void WindowServer::invalidate(Rectangle area)
if (!m_is_mouse_relative) if (!m_is_mouse_relative)
{ {
auto cursor_area = this->cursor_area(); auto cursor_area = this->cursor_area();
cursor_area.x -= m_focused_window->client_x(); cursor_area.min_x -= m_focused_window->client_x();
cursor_area.y -= m_focused_window->client_y(); cursor_area.max_x -= m_focused_window->client_x();
cursor_area.min_y -= m_focused_window->client_y();
cursor_area.max_y -= m_focused_window->client_y();
if (!area.get_overlap(cursor_area).has_value()) if (!area.get_overlap(cursor_area).has_value())
return; return;
const int32_t cursor_tl_dst_x = cursor_area.x * m_framebuffer.width / m_focused_window->client_width(); const int32_t cursor_tl_dst_x = cursor_area.min_x * m_framebuffer.width / m_focused_window->client_width();
const int32_t cursor_tl_dst_y = cursor_area.y * m_framebuffer.height / m_focused_window->client_height(); const int32_t cursor_tl_dst_y = cursor_area.min_y * m_framebuffer.height / m_focused_window->client_height();
for (int32_t rel_y = 0; rel_y < cursor_area.height; rel_y++) for (int32_t rel_y = 0; rel_y < cursor_area.height(); rel_y++)
{ {
for (int32_t rel_x = 0; rel_x < cursor_area.width; rel_x++) for (int32_t rel_x = 0; rel_x < cursor_area.width(); rel_x++)
{ {
const auto pixel = get_cursor_pixel(rel_x, rel_y); const auto pixel = get_cursor_pixel(rel_x, rel_y);
if (!pixel.has_value()) if (!pixel.has_value())
@@ -1074,12 +1096,12 @@ void WindowServer::invalidate(Rectangle area)
return; return;
area = fb_overlap.release_value(); area = fb_overlap.release_value();
for (int32_t y = area.y; y < area.y + area.height; y++) for (int32_t y = area.min_y; y < area.max_y; y++)
{ {
memcpy( memcpy(
&m_framebuffer.mmap[y * m_framebuffer.width + area.x], &m_framebuffer.mmap[y * m_framebuffer.width + area.min_x],
&m_background_image[y * m_framebuffer.width + area.x], &m_background_image[y * m_framebuffer.width + area.min_x],
area.width * sizeof(uint32_t) area.width() * sizeof(uint32_t)
); );
} }
@@ -1091,71 +1113,65 @@ void WindowServer::invalidate(Rectangle area)
if (!window.get_attributes().shown) if (!window.get_attributes().shown)
continue; continue;
const auto window_full_area = window.full_area();
const Rectangle fast_areas[] { const Rectangle fast_areas[] {
{ {
window.full_x() + m_corner_radius, .min_x = window_full_area.min_x,
window.full_y(), .min_y = window_full_area.min_y + m_corner_radius,
window.full_width() - 2 * m_corner_radius, .max_x = window_full_area.max_x,
m_corner_radius .max_y = window_full_area.max_y - m_corner_radius,
}, {
.min_x = window_full_area.min_x + m_corner_radius,
.min_y = window_full_area.min_y,
.max_x = window_full_area.max_x - m_corner_radius,
.max_y = window_full_area.min_y + m_corner_radius,
}, {
.min_x = window_full_area.min_x + m_corner_radius,
.min_y = window_full_area.max_y - m_corner_radius,
.max_x = window_full_area.max_x - m_corner_radius,
.max_y = window_full_area.max_y,
}, },
{
window.full_x(),
window.full_y() + m_corner_radius,
window.full_width(),
window.full_height() - 2 * m_corner_radius
},
{
window.full_x() + m_corner_radius,
window.full_y() + window.full_height() - m_corner_radius,
window.full_width() - 2 * m_corner_radius,
m_corner_radius
}
}; };
const Position corner_centers[] { const Position corner_centers[] {
{ {
window.full_x() + m_corner_radius, .x = window_full_area.min_x + m_corner_radius,
window.full_y() + m_corner_radius, .y = window_full_area.min_y + m_corner_radius,
}, }, {
{ .x = window_full_area.max_x - m_corner_radius - 1,
window.full_x() + (window.full_width() - 1) - m_corner_radius, .y = window_full_area.min_y + m_corner_radius,
window.full_y() + m_corner_radius, }, {
}, .x = window_full_area.min_x + m_corner_radius,
{ .y = window_full_area.max_y - m_corner_radius - 1,
window.full_x() + m_corner_radius, }, {
window.full_y() + (window.full_height() - 1) - m_corner_radius, .x = window_full_area.max_x - m_corner_radius - 1,
}, .y = window_full_area.max_y - m_corner_radius - 1,
{
window.full_x() + (window.full_width() - 1) - m_corner_radius,
window.full_y() + (window.full_height() - 1) - m_corner_radius,
}, },
}; };
const Rectangle corner_areas[] { const Rectangle corner_areas[] {
{ {
window.full_x(), .min_x = window_full_area.min_x,
window.full_y(), .min_y = window_full_area.min_y,
m_corner_radius, .max_x = window_full_area.min_x + m_corner_radius,
m_corner_radius .max_y = window_full_area.min_y + m_corner_radius,
}, {
.min_x = window_full_area.max_x - m_corner_radius,
.min_y = window_full_area.min_y,
.max_x = window_full_area.max_x,
.max_y = window_full_area.min_y + m_corner_radius,
}, {
.min_x = window_full_area.min_x,
.min_y = window_full_area.max_y - m_corner_radius,
.max_x = window_full_area.min_x + m_corner_radius,
.max_y = window_full_area.max_y,
}, {
.min_x = window_full_area.max_x - m_corner_radius,
.min_y = window_full_area.max_y - m_corner_radius,
.max_x = window_full_area.max_x,
.max_y = window_full_area.max_y,
}, },
{
window.full_x() + window.full_width() - m_corner_radius,
window.full_y(),
m_corner_radius,
m_corner_radius
},
{
window.full_x(),
window.full_y() + window.full_height() - m_corner_radius,
m_corner_radius,
m_corner_radius
},
{
window.full_x() + window.full_width() - m_corner_radius,
window.full_y() + window.full_height() - m_corner_radius,
m_corner_radius,
m_corner_radius
}
}; };
const auto is_rounded_off = const auto is_rounded_off =
@@ -1178,14 +1194,13 @@ void WindowServer::invalidate(Rectangle area)
}; };
// window title bar // window title bar
if (auto title_overlap = window.title_bar_area().get_overlap(area); title_overlap.has_value()) if (auto opt_title_overlap = window.title_bar_area().get_overlap(area); opt_title_overlap.has_value())
{ {
for (int32_t y_off = 0; y_off < title_overlap->height; y_off++) const auto title_overlap = opt_title_overlap.release_value();
for (int32_t abs_y = title_overlap.min_y; abs_y < title_overlap.max_y; abs_y++)
{ {
for (int32_t x_off = 0; x_off < title_overlap->width; x_off++) for (int32_t abs_x = title_overlap.min_x; abs_x < title_overlap.max_x; abs_x++)
{ {
const int32_t abs_x = title_overlap->x + x_off;
const int32_t abs_y = title_overlap->y + y_off;
if (is_rounded_off(window, { abs_x, abs_y })) if (is_rounded_off(window, { abs_x, abs_y }))
continue; continue;
@@ -1196,21 +1211,21 @@ void WindowServer::invalidate(Rectangle area)
} }
// window client area // window client area
if (auto client_overlap = window.client_area().get_overlap(area); client_overlap.has_value()) if (auto opt_client_overlap = window.client_area().get_overlap(area); opt_client_overlap.has_value())
{ {
const bool should_alpha_blend = window.get_attributes().alpha_channel; const bool should_alpha_blend = window.get_attributes().alpha_channel;
const auto client_overlap = opt_client_overlap.release_value();
for (const auto& fast_area : fast_areas) for (const auto& fast_area : fast_areas)
{ {
auto opt_fast_overlap = client_overlap->get_overlap(fast_area); auto opt_fast_overlap = client_overlap.get_overlap(fast_area);
if (!opt_fast_overlap.has_value()) if (!opt_fast_overlap.has_value())
continue; continue;
const auto fast_overlap = opt_fast_overlap.release_value(); const auto fast_overlap = opt_fast_overlap.release_value();
for (int32_t y_off = 0; y_off < fast_overlap.height; y_off++) for (int32_t abs_row_y = fast_overlap.min_y; abs_row_y < fast_overlap.max_y; abs_row_y++)
{ {
const int32_t abs_row_y = fast_overlap.y + y_off; const int32_t abs_row_x = fast_overlap.min_x;
const int32_t abs_row_x = fast_overlap.x;
const int32_t src_row_y = abs_row_y - window.client_y(); const int32_t src_row_y = abs_row_y - window.client_y();
const int32_t src_row_x = abs_row_x - window.client_x(); const int32_t src_row_x = abs_row_x - window.client_x();
@@ -1219,10 +1234,10 @@ void WindowServer::invalidate(Rectangle area)
auto* frameb_row = &m_framebuffer.mmap[abs_row_y * m_framebuffer.width + abs_row_x]; auto* frameb_row = &m_framebuffer.mmap[abs_row_y * m_framebuffer.width + abs_row_x];
if (!should_alpha_blend) if (!should_alpha_blend)
memcpy(frameb_row, window_row, fast_overlap.width * sizeof(uint32_t)); memcpy(frameb_row, window_row, fast_overlap.width() * sizeof(uint32_t));
else else
{ {
int32_t pixels = fast_overlap.width; int32_t pixels = fast_overlap.width();
for (; pixels >= 4; pixels -= 4) for (; pixels >= 4; pixels -= 4)
{ {
alpha_blend4(window_row, frameb_row, frameb_row); alpha_blend4(window_row, frameb_row, frameb_row);
@@ -1241,17 +1256,15 @@ void WindowServer::invalidate(Rectangle area)
for (const auto& corner_area : corner_areas) for (const auto& corner_area : corner_areas)
{ {
auto opt_corner_overlap = client_overlap->get_overlap(corner_area); auto opt_corner_overlap = client_overlap.get_overlap(corner_area);
if (!opt_corner_overlap.has_value()) if (!opt_corner_overlap.has_value())
continue; continue;
const auto corner_overlap = opt_corner_overlap.release_value(); const auto corner_overlap = opt_corner_overlap.release_value();
for (int32_t y_off = 0; y_off < corner_overlap.height; y_off++) for (int32_t abs_y = corner_overlap.min_y; abs_y < corner_overlap.max_y; abs_y++)
{ {
for (int32_t x_off = 0; x_off < corner_overlap.width; x_off++) for (int32_t abs_x = corner_overlap.min_x; abs_x < corner_overlap.max_x; abs_x++)
{ {
const int32_t abs_x = corner_overlap.x + x_off;
const int32_t abs_y = corner_overlap.y + y_off;
if (is_rounded_off(window, { abs_x, abs_y })) if (is_rounded_off(window, { abs_x, abs_y }))
continue; continue;
@@ -1278,11 +1291,11 @@ void WindowServer::invalidate(Rectangle area)
constexpr uint32_t blend_color = 0x80000000; constexpr uint32_t blend_color = 0x80000000;
const auto overlap = opt_overlap.release_value(); const auto overlap = opt_overlap.release_value();
for (int32_t y = overlap.y; y < overlap.y + overlap.height; y++) for (int32_t y = overlap.min_y; y < overlap.max_y; y++)
{ {
uint32_t* frameb_row = &m_framebuffer.mmap[y * m_framebuffer.width + overlap.x]; uint32_t* frameb_row = &m_framebuffer.mmap[y * m_framebuffer.width + overlap.min_x];
int32_t pixels = overlap.width; int32_t pixels = overlap.width();
for (; pixels >= 4; pixels -= 4) for (; pixels >= 4; pixels -= 4)
{ {
const uint32_t blend_colors[] { blend_color, blend_color, blend_color, blend_color }; const uint32_t blend_colors[] { blend_color, blend_color, blend_color, blend_color };
@@ -1300,19 +1313,20 @@ void WindowServer::invalidate(Rectangle area)
if (!m_is_mouse_relative) if (!m_is_mouse_relative)
{ {
if (const auto overlap = cursor_area().get_overlap(area); overlap.has_value()) if (auto opt_overlap = cursor_area().get_overlap(area); opt_overlap.has_value())
{ {
const int32_t origin_x = window_cursor ? window_cursor->origin_x : 0; const int32_t origin_x = window_cursor ? window_cursor->origin_x : 0;
const int32_t origin_y = window_cursor ? window_cursor->origin_y : 0; const int32_t origin_y = window_cursor ? window_cursor->origin_y : 0;
for (int32_t y_off = 0; y_off < overlap->height; y_off++) const auto overlap = opt_overlap.release_value();
for (int32_t abs_y = overlap.min_y; abs_y < overlap.max_y; abs_y++)
{ {
for (int32_t x_off = 0; x_off < overlap->width; x_off++) for (int32_t abs_x = overlap.min_x; abs_x < overlap.max_x; abs_x++)
{ {
const int32_t rel_x = overlap->x - m_cursor.x + x_off + origin_x; const int32_t rel_x = abs_x - m_cursor.x + origin_x;
const int32_t rel_y = overlap->y - m_cursor.y + y_off + origin_y; const int32_t rel_y = abs_y - m_cursor.y + origin_y;
const auto pixel = get_cursor_pixel(rel_x, rel_y); const auto pixel = get_cursor_pixel(rel_x, rel_y);
if (pixel.has_value()) if (pixel.has_value())
m_framebuffer.mmap[(overlap->y + y_off) * m_framebuffer.width + (overlap->x + x_off)] = pixel.value(); m_framebuffer.mmap[abs_y * m_framebuffer.width + abs_x] = pixel.value();
} }
} }
} }
@@ -1384,8 +1398,8 @@ void WindowServer::RangeList::add_range(const Range& range)
void WindowServer::mark_pending_sync(Rectangle to_sync) void WindowServer::mark_pending_sync(Rectangle to_sync)
{ {
ASSERT(to_sync == to_sync.get_overlap(m_framebuffer.area()).value()); ASSERT(to_sync == to_sync.get_overlap(m_framebuffer.area()).value());
for (int32_t y_off = 0; y_off < to_sync.height; y_off++) for (int32_t abs_y = to_sync.min_y; abs_y < to_sync.max_y; abs_y++)
m_pending_syncs[to_sync.y + y_off].add_range({ static_cast<uint32_t>(to_sync.x), static_cast<uint32_t>(to_sync.width) }); m_pending_syncs[abs_y].add_range({ static_cast<uint32_t>(to_sync.min_x), static_cast<uint32_t>(to_sync.width()) });
} }
void WindowServer::sync() void WindowServer::sync()
@@ -1399,9 +1413,9 @@ void WindowServer::sync()
m_focused_window->client_x() + dir_x, m_focused_window->client_x() + dir_x,
m_focused_window->client_y() + dir_y, m_focused_window->client_y() + dir_y,
}); });
auto new_window = m_focused_window->full_area();
invalidate(old_window); invalidate(old_window);
invalidate(new_window); invalidate(m_focused_window->full_area());
if ((m_focused_window->full_x() < 0 && dir_x < 0) || (m_focused_window->full_x() + m_focused_window->full_width() >= m_framebuffer.width && dir_x > 0)) if ((m_focused_window->full_x() < 0 && dir_x < 0) || (m_focused_window->full_x() + m_focused_window->full_width() >= m_framebuffer.width && dir_x > 0))
dir_x = -dir_x; dir_x = -dir_x;
@@ -1470,7 +1484,12 @@ Rectangle WindowServer::cursor_area() const
} }
} }
return { m_cursor.x - origin_x, m_cursor.y - origin_y, width, height }; return Rectangle {
.min_x = m_cursor.x - origin_x,
.min_y = m_cursor.y - origin_y,
.max_x = m_cursor.x - origin_x + width,
.max_y = m_cursor.y - origin_y + height,
};
} }
Rectangle WindowServer::resize_area(Position cursor) const Rectangle WindowServer::resize_area(Position cursor) const
@@ -1482,16 +1501,16 @@ Rectangle WindowServer::resize_area(Position cursor) const
if (m_resize_quadrant % 2) if (m_resize_quadrant % 2)
diff_x = -diff_x; diff_x = -diff_x;
diff_x = BAN::Math::clamp(diff_x, diff_x = BAN::Math::clamp(diff_x,
-m_focused_window->client_width() + min_size.width, -m_focused_window->client_width() + min_size.width(),
-m_focused_window->client_width() + max_size.width -m_focused_window->client_width() + max_size.width()
); );
int32_t diff_y = m_resize_start.y - cursor.y; int32_t diff_y = m_resize_start.y - cursor.y;
if (m_resize_quadrant / 2) if (m_resize_quadrant / 2)
diff_y = -diff_y; diff_y = -diff_y;
diff_y = BAN::Math::clamp(diff_y, diff_y = BAN::Math::clamp(diff_y,
-m_focused_window->client_height() + min_size.height, -m_focused_window->client_height() + min_size.height(),
-m_focused_window->client_height() + max_size.height -m_focused_window->client_height() + max_size.height()
); );
int32_t off_x = 0; int32_t off_x = 0;
@@ -1502,11 +1521,13 @@ Rectangle WindowServer::resize_area(Position cursor) const
if (m_resize_quadrant / 2 == 0) if (m_resize_quadrant / 2 == 0)
off_y = -diff_y; off_y = -diff_y;
return { const int min_x = off_x + m_focused_window->full_x();
.x = off_x + m_focused_window->full_x(), const int min_y = off_y + m_focused_window->full_y();
.y = off_y + m_focused_window->full_y(), return Rectangle {
.width = diff_x + m_focused_window->full_width(), .min_x = min_x,
.height = diff_y + m_focused_window->full_height(), .min_y = min_y,
.max_x = min_x + diff_x + m_focused_window->full_width(),
.max_y = min_y + diff_y + m_focused_window->full_height(),
}; };
} }
@@ -1534,11 +1555,12 @@ bool WindowServer::resize_window(BAN::RefPtr<Window> window, uint32_t width, uin
return false; return false;
} }
LibGUI::EventPacket::ResizeWindowEvent response; const LibGUI::EventPacket::ResizeWindowEvent event_packet {
response.width = window->client_width(); .width = static_cast<uint32_t>(window->client_width()),
response.height = window->client_height(); .height = static_cast<uint32_t>(window->client_height()),
response.smo_key = window->smo_key(); .smo_key = window->smo_key(),
if (auto ret = append_serialized_packet(response, window->client_fd()); ret.is_error()) };
if (auto ret = append_serialized_packet(event_packet, window->client_fd()); ret.is_error())
{ {
dwarnln("could not respond to window resize request: {}", ret.error()); dwarnln("could not respond to window resize request: {}", ret.error());
return false; return false;