LibGUI/WindowServer: Implement per-window custom cursors
This commit is contained in:
parent
273e9bbc92
commit
cf07b747fe
|
@ -211,6 +211,7 @@ namespace LibGUI
|
|||
WindowSetMaxSize,
|
||||
WindowSetFullscreen,
|
||||
WindowSetTitle,
|
||||
WindowSetCursor,
|
||||
|
||||
DestroyWindowEvent,
|
||||
CloseWindowEvent,
|
||||
|
@ -297,6 +298,13 @@ namespace LibGUI
|
|||
BAN::String, title
|
||||
);
|
||||
|
||||
DEFINE_PACKET(
|
||||
WindowSetCursor,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
BAN::Vector<uint32_t>, pixels
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
namespace EventPacket
|
||||
|
|
|
@ -50,6 +50,7 @@ namespace LibGUI
|
|||
void set_position(int32_t x, int32_t y);
|
||||
|
||||
void set_cursor_visible(bool visible);
|
||||
void set_cursor(uint32_t width, uint32_t height, BAN::Span<uint32_t> pixels);
|
||||
|
||||
Attributes get_attributes() const { return m_attributes; }
|
||||
void set_attributes(Attributes attributes);
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
static int32_t s_cursor_width = 17;
|
||||
static int32_t s_cursor_height = 26;
|
||||
static const char* s_cursor_data =
|
||||
static int32_t s_default_cursor_width = 17;
|
||||
static int32_t s_default_cursor_height = 26;
|
||||
static const char* s_default_cursor_data =
|
||||
"!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
|
||||
"`Q$`!!!!!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
|
||||
"`Q$``Q$`!!!!````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
|
||||
|
|
|
@ -11,6 +11,14 @@
|
|||
|
||||
class Window : public BAN::RefCounted<Window>
|
||||
{
|
||||
public:
|
||||
struct Cursor
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
BAN::Vector<uint32_t> pixels;
|
||||
};
|
||||
|
||||
public:
|
||||
Window(int fd, const LibFont::Font& font)
|
||||
: m_font(font)
|
||||
|
@ -49,6 +57,11 @@ public:
|
|||
Rectangle full_size() const { return { 0, 0, full_width(), full_height() }; }
|
||||
Rectangle full_area() const { return { full_x(), full_y(), full_width(), full_height() }; }
|
||||
|
||||
bool has_cursor() const { return m_cursor.has_value(); }
|
||||
const Cursor& cursor() const { return m_cursor.value(); }
|
||||
void set_cursor(Cursor&& cursor) { m_cursor = BAN::move(cursor); }
|
||||
void remove_cursor() { m_cursor.clear(); }
|
||||
|
||||
LibGUI::Window::Attributes get_attributes() const { return m_attributes; };
|
||||
void set_attributes(LibGUI::Window::Attributes attributes) { m_attributes = attributes; };
|
||||
|
||||
|
@ -96,6 +109,8 @@ private:
|
|||
uint32_t* m_fb_addr { nullptr };
|
||||
BAN::String m_title;
|
||||
|
||||
BAN::Optional<Cursor> m_cursor;
|
||||
|
||||
BAN::Vector<uint32_t> m_title_bar_data;
|
||||
|
||||
LibGUI::Window::Attributes m_attributes { LibGUI::Window::default_attributes };
|
||||
|
|
|
@ -350,6 +350,50 @@ void WindowServer::on_window_set_title(int fd, const LibGUI::WindowPacket::Windo
|
|||
invalidate(target_window->title_bar_area());
|
||||
}
|
||||
|
||||
void WindowServer::on_window_set_cursor(int fd, const LibGUI::WindowPacket::WindowSetCursor& packet)
|
||||
{
|
||||
auto target_window = find_window_with_fd(fd);
|
||||
if (!target_window)
|
||||
{
|
||||
dwarnln("client tried to set cursor while not owning a window");
|
||||
return;
|
||||
}
|
||||
|
||||
if (BAN::Math::will_multiplication_overflow(packet.width, packet.height))
|
||||
{
|
||||
dwarnln("client tried to set cursor with invalid size {}x{}", packet.width, packet.height);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.width * packet.height != packet.pixels.size())
|
||||
{
|
||||
dwarnln("client tried to set cursor with buffer size mismatch {}x{}, {} pixels", packet.width, packet.height, packet.pixels.size());
|
||||
return;
|
||||
}
|
||||
|
||||
auto old_cursor = cursor_area();
|
||||
|
||||
if (packet.width == 0 || packet.height == 0)
|
||||
target_window->remove_cursor();
|
||||
else
|
||||
{
|
||||
Window::Cursor cursor;
|
||||
cursor.width = packet.width;
|
||||
cursor.height = packet.height;
|
||||
if (auto ret = cursor.pixels.resize(packet.pixels.size()); ret.is_error())
|
||||
{
|
||||
dwarnln("failed to set cursor: {}", ret.error());
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < cursor.pixels.size(); i++)
|
||||
cursor.pixels[i] = packet.pixels[i];
|
||||
target_window->set_cursor(BAN::move(cursor));
|
||||
}
|
||||
|
||||
if (find_hovered_window() == target_window)
|
||||
invalidate(cursor_area().get_bounding_box(old_cursor));
|
||||
}
|
||||
|
||||
void WindowServer::on_key_event(LibInput::KeyEvent event)
|
||||
{
|
||||
// Mod key is not passed to clients
|
||||
|
@ -725,13 +769,25 @@ void WindowServer::invalidate(Rectangle area)
|
|||
ASSERT(m_background_image->width() == (uint64_t)m_framebuffer.width);
|
||||
ASSERT(m_background_image->height() == (uint64_t)m_framebuffer.height);
|
||||
|
||||
const Window::Cursor* window_cursor = nullptr;
|
||||
if (auto window = this->find_hovered_window(); window && window->has_cursor())
|
||||
window_cursor = &window->cursor();
|
||||
|
||||
const auto get_cursor_pixel =
|
||||
[](int32_t rel_x, int32_t rel_y) -> BAN::Optional<uint32_t>
|
||||
[window_cursor](int32_t rel_x, int32_t rel_y) -> BAN::Optional<uint32_t>
|
||||
{
|
||||
const uint32_t offset = (rel_y * s_cursor_width + rel_x) * 4;
|
||||
uint32_t r = (((s_cursor_data[offset + 0] - 33) << 2) | ((s_cursor_data[offset + 1] - 33) >> 4));
|
||||
uint32_t g = ((((s_cursor_data[offset + 1] - 33) & 0xF) << 4) | ((s_cursor_data[offset + 2] - 33) >> 2));
|
||||
uint32_t b = ((((s_cursor_data[offset + 2] - 33) & 0x3) << 6) | ((s_cursor_data[offset + 3] - 33)));
|
||||
if (window_cursor)
|
||||
{
|
||||
const auto pixel = window_cursor->pixels[rel_y * window_cursor->width + rel_x];
|
||||
if ((pixel >> 24) == 0)
|
||||
return {};
|
||||
return pixel & 0xFFFFFF;
|
||||
}
|
||||
|
||||
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));
|
||||
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)));
|
||||
uint32_t color = (r << 16) | (g << 8) | b;
|
||||
if (color == 0xFF00FF)
|
||||
return {};
|
||||
|
@ -812,12 +868,9 @@ void WindowServer::invalidate(Rectangle area)
|
|||
|
||||
if (!m_is_mouse_captured)
|
||||
{
|
||||
const Rectangle cursor_area {
|
||||
.x = m_cursor.x - m_focused_window->client_x(),
|
||||
.y = m_cursor.y - m_focused_window->client_y(),
|
||||
.width = s_cursor_width,
|
||||
.height = s_cursor_height,
|
||||
};
|
||||
auto cursor_area = this->cursor_area();
|
||||
cursor_area.x -= m_focused_window->client_x();
|
||||
cursor_area.y -= m_focused_window->client_y();
|
||||
|
||||
if (!area.get_overlap(cursor_area).has_value())
|
||||
return;
|
||||
|
@ -825,9 +878,9 @@ void WindowServer::invalidate(Rectangle area)
|
|||
const int32_t cursor_tl_dst_x = cursor_area.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();
|
||||
|
||||
for (int32_t rel_y = 0; rel_y < s_cursor_height; rel_y++)
|
||||
for (int32_t rel_y = 0; rel_y < cursor_area.height; rel_y++)
|
||||
{
|
||||
for (int32_t rel_x = 0; rel_x < s_cursor_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);
|
||||
if (!pixel.has_value())
|
||||
|
@ -1201,9 +1254,21 @@ void WindowServer::sync()
|
|||
|
||||
Rectangle WindowServer::cursor_area() const
|
||||
{
|
||||
if (auto window = find_hovered_window(); window && !window->get_attributes().cursor_visible)
|
||||
return { m_cursor.x, m_cursor.y, 0, 0 };
|
||||
return { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height };
|
||||
int32_t width = s_default_cursor_width;
|
||||
int32_t height = s_default_cursor_height;
|
||||
|
||||
if (auto window = find_hovered_window())
|
||||
{
|
||||
if (!window->get_attributes().cursor_visible)
|
||||
width = height = 0;
|
||||
else if (window->has_cursor())
|
||||
{
|
||||
width = window->cursor().width;
|
||||
height = window->cursor().height;
|
||||
}
|
||||
}
|
||||
|
||||
return { m_cursor.x, m_cursor.y, width, height };
|
||||
}
|
||||
|
||||
Rectangle WindowServer::resize_area(Position cursor) const
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
void on_window_set_max_size(int fd, const LibGUI::WindowPacket::WindowSetMaxSize&);
|
||||
void on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::WindowSetFullscreen&);
|
||||
void on_window_set_title(int fd, const LibGUI::WindowPacket::WindowSetTitle&);
|
||||
void on_window_set_cursor(int fd, const LibGUI::WindowPacket::WindowSetCursor&);
|
||||
|
||||
void on_key_event(LibInput::KeyEvent event);
|
||||
void on_mouse_button(LibInput::MouseButtonEvent event);
|
||||
|
|
|
@ -371,6 +371,7 @@ int main()
|
|||
WINDOW_PACKET_CASE(WindowSetMaxSize, on_window_set_max_size);
|
||||
WINDOW_PACKET_CASE(WindowSetFullscreen, on_window_set_fullscreen);
|
||||
WINDOW_PACKET_CASE(WindowSetTitle, on_window_set_title);
|
||||
WINDOW_PACKET_CASE(WindowSetCursor, on_window_set_cursor);
|
||||
#undef WINDOW_PACKET_CASE
|
||||
default:
|
||||
dprintln("unhandled packet type: {}", *reinterpret_cast<uint32_t*>(client_data.packet_buffer.data()));
|
||||
|
|
Loading…
Reference in New Issue