wind+gclient: Add SetWindowTitle and support shm buffers
This commit is contained in:
parent
0127068177
commit
0fb47d90a7
@ -41,3 +41,4 @@ luna_app(gol.cpp gol)
|
||||
luna_app(touch.cpp touch)
|
||||
luna_app(free.cpp free)
|
||||
luna_app(gclient.cpp gclient)
|
||||
target_link_libraries(gclient PUBLIC ui)
|
||||
|
@ -1,22 +1,67 @@
|
||||
#include <errno.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/LocalClient.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ui/Canvas.h>
|
||||
#include <ui/ipc/Server.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct Window
|
||||
{
|
||||
ui::Canvas canvas;
|
||||
int id;
|
||||
|
||||
void set_title(os::LocalClient& client, const char* title)
|
||||
{
|
||||
ui::SetWindowTitleRequest request;
|
||||
request.window = id;
|
||||
SET_IPC_STRING(request.title, title);
|
||||
os::IPC::send_async(client, request);
|
||||
}
|
||||
|
||||
void redraw(os::LocalClient& client)
|
||||
{
|
||||
ui::InvalidateRequest request;
|
||||
request.window = id;
|
||||
os::IPC::send_async(client, request);
|
||||
}
|
||||
};
|
||||
|
||||
Result<void> handle_ipc_client_event(os::LocalClient&, u8)
|
||||
{
|
||||
todo();
|
||||
}
|
||||
|
||||
Result<int> create_window(os::LocalClient& client, ui::Rect rect, StringView name, ui::Color color)
|
||||
static Result<u32*> create_shm_region(const char* path, int* outfd, ui::Rect rect)
|
||||
{
|
||||
int fd = shm_open(path, O_RDWR, 0600);
|
||||
shm_unlink(path);
|
||||
if (fd < 0) return err(errno);
|
||||
|
||||
usize size = rect.width * rect.height * 4; // 4 bytes per pixel
|
||||
|
||||
void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (p == MAP_FAILED)
|
||||
{
|
||||
shm_unlink(path);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (outfd) *outfd = fd;
|
||||
else
|
||||
close(fd);
|
||||
return (u32*)p;
|
||||
}
|
||||
|
||||
Result<Window> create_window(os::LocalClient& client, ui::Rect rect)
|
||||
{
|
||||
ui::CreateWindowRequest request;
|
||||
request.rect = rect;
|
||||
SET_IPC_STRING(request.name, name.chars());
|
||||
request.color = color;
|
||||
auto response = TRY(os::IPC::send_sync<ui::CreateWindowResponse>(client, request));
|
||||
return response.window;
|
||||
u32* pixels = TRY(create_shm_region(response.shm_path, nullptr, rect));
|
||||
return Window { ui::Canvas { rect.width, rect.height, rect.width, (u8*)pixels }, response.window };
|
||||
}
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
@ -31,16 +76,19 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
auto client = TRY(os::LocalClient::connect(socket_path, false));
|
||||
|
||||
int id = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 }, "My Window", ui::CYAN));
|
||||
os::println("Created new window with id %d!", id);
|
||||
Window window = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 }));
|
||||
os::println("Created new window with id %d!", window.id);
|
||||
|
||||
sleep(3);
|
||||
|
||||
id =
|
||||
TRY(create_window(*client, ui::Rect { 100, 100, 200, 150 }, "Super Long Name that is Way Too Long ", ui::BLUE));
|
||||
os::println("Created new window with id %d!", id);
|
||||
window.set_title(*client, "Example Window");
|
||||
|
||||
sleep(6);
|
||||
sleep(3);
|
||||
|
||||
window.canvas.fill(ui::CYAN);
|
||||
window.redraw(*client);
|
||||
|
||||
sleep(3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,5 +23,6 @@ namespace ui
|
||||
static constexpr u8 id = CREATE_WINDOW_RESPONSE_ID;
|
||||
|
||||
int window;
|
||||
IPC_STRING(shm_path);
|
||||
};
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ namespace ui
|
||||
{
|
||||
IPC_ENUM_SERVER(ui),
|
||||
CREATE_WINDOW_ID,
|
||||
SET_WINDOW_TITLE_ID,
|
||||
INVALIDATE_ID,
|
||||
};
|
||||
|
||||
struct CreateWindowRequest
|
||||
@ -27,7 +29,20 @@ namespace ui
|
||||
static constexpr u8 id = CREATE_WINDOW_ID;
|
||||
|
||||
ui::Rect rect;
|
||||
IPC_STRING(name);
|
||||
ui::Color color;
|
||||
};
|
||||
|
||||
struct SetWindowTitleRequest
|
||||
{
|
||||
static constexpr u8 id = SET_WINDOW_TITLE_ID;
|
||||
|
||||
int window;
|
||||
IPC_STRING(title);
|
||||
};
|
||||
|
||||
struct InvalidateRequest
|
||||
{
|
||||
static constexpr u8 id = INVALIDATE_ID;
|
||||
|
||||
int window;
|
||||
};
|
||||
}
|
||||
|
140
wind/IPC.cpp
140
wind/IPC.cpp
@ -1,50 +1,144 @@
|
||||
#include "IPC.h"
|
||||
#include <errno.h>
|
||||
#include <luna/Alignment.h>
|
||||
#include <luna/String.h>
|
||||
#include <os/File.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TRY_OR_IPC_ERROR(expr) \
|
||||
({ \
|
||||
auto _expr_rc = (expr); \
|
||||
if (!_expr_rc.has_value()) \
|
||||
{ \
|
||||
delete window; \
|
||||
os::IPC::send_error(client.conn, _expr_rc.error()); \
|
||||
return {}; \
|
||||
} \
|
||||
_expr_rc.release_value(); \
|
||||
})
|
||||
|
||||
static Result<u32*> create_shm_region(const char* path, int* outfd, ui::Rect rect)
|
||||
{
|
||||
int fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0)
|
||||
{
|
||||
os::eprintln("wind: could not create shared memory region: shm_open failed (%s) - %s", path, strerror(errno));
|
||||
return err(errno);
|
||||
}
|
||||
|
||||
usize size = align_up<PAGE_SIZE>(rect.width * rect.height * 4); // 4 bytes per pixel
|
||||
|
||||
if (ftruncate(fd, size) < 0)
|
||||
{
|
||||
os::eprintln("wind: could not create shared memory region: ftruncate failed (%d, %zu) - %s", fd, size,
|
||||
strerror(errno));
|
||||
shm_unlink(path);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* p = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (p == MAP_FAILED)
|
||||
{
|
||||
os::eprintln("wind: could not create shared memory region: mmap failed (%zu, %d) - %s", size, fd,
|
||||
strerror(errno));
|
||||
shm_unlink(path);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (outfd) *outfd = fd;
|
||||
else
|
||||
close(fd);
|
||||
return (u32*)p;
|
||||
}
|
||||
|
||||
#define READ_MESSAGE(request) \
|
||||
do { \
|
||||
auto rc = client.conn.recv_typed(request); \
|
||||
if (rc.has_error()) \
|
||||
{ \
|
||||
if (rc.error() == EAGAIN) \
|
||||
{ \
|
||||
client.rpc_in_progress = true; \
|
||||
client.rpc_id = decltype(request)::id; \
|
||||
return {}; \
|
||||
} \
|
||||
else \
|
||||
return rc.release_error(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static Result<void> handle_create_window_message(Client& client)
|
||||
{
|
||||
ui::CreateWindowRequest request;
|
||||
auto rc = client.conn.recv_typed(request);
|
||||
if (rc.has_error())
|
||||
{
|
||||
if (rc.error() == EAGAIN)
|
||||
{
|
||||
client.rpc_in_progress = true;
|
||||
client.rpc_id = ui::CREATE_WINDOW_ID;
|
||||
return {};
|
||||
}
|
||||
else
|
||||
return rc.release_error();
|
||||
}
|
||||
READ_MESSAGE(request);
|
||||
|
||||
request.rect = request.rect.normalized();
|
||||
request.rect.height += Window::titlebar_height(); // Make sure we provide the full contents rect that was asked for.
|
||||
|
||||
auto name = COPY_IPC_STRING(request.name);
|
||||
auto name = TRY(String::from_cstring("Window"));
|
||||
|
||||
auto* window = new (std::nothrow) Window(request.rect, request.color, move(name));
|
||||
auto* window = new (std::nothrow) Window(request.rect, move(name));
|
||||
if (!window)
|
||||
{
|
||||
os::IPC::send_error(client.conn, ENOMEM);
|
||||
return {};
|
||||
}
|
||||
|
||||
int id = static_cast<int>(client.windows.size());
|
||||
rc = client.windows.try_append(window);
|
||||
if (rc.has_error())
|
||||
{
|
||||
delete window;
|
||||
os::IPC::send_error(client.conn, rc.error());
|
||||
return {};
|
||||
}
|
||||
auto shm_path = TRY_OR_IPC_ERROR(String::format("/wind-shm-%d-%lu"_sv, client.conn.fd(), time(NULL)));
|
||||
|
||||
window->pixels = TRY_OR_IPC_ERROR(create_shm_region(shm_path.chars(), nullptr, window->contents));
|
||||
|
||||
TRY_OR_IPC_ERROR(client.windows.try_append(window));
|
||||
int id = static_cast<int>(client.windows.size() - 1);
|
||||
|
||||
ui::CreateWindowResponse response;
|
||||
response.window = id;
|
||||
SET_IPC_STRING(response.shm_path, shm_path.chars());
|
||||
os::IPC::send_async(client.conn, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_set_window_title_message(Client& client)
|
||||
{
|
||||
ui::SetWindowTitleRequest request;
|
||||
READ_MESSAGE(request);
|
||||
|
||||
auto name = COPY_IPC_STRING(request.title);
|
||||
|
||||
os::println("wind: SetWindowTitle(\"%s\") for window %d", name.chars(), request.window);
|
||||
|
||||
if ((usize)request.window >= client.windows.size())
|
||||
{
|
||||
os::eprintln("wind: Window id out of range!");
|
||||
return {};
|
||||
}
|
||||
|
||||
client.windows[request.window]->name = move(name);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Result<void> handle_invalidate_message(Client& client)
|
||||
{
|
||||
ui::InvalidateRequest request;
|
||||
READ_MESSAGE(request);
|
||||
|
||||
if ((usize)request.window >= client.windows.size())
|
||||
{
|
||||
os::eprintln("wind: Window id out of range!");
|
||||
return {};
|
||||
}
|
||||
|
||||
client.windows[request.window]->dirty = true;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace wind
|
||||
{
|
||||
Result<void> handle_ipc_message(Client& client, u8 id)
|
||||
@ -53,6 +147,8 @@ namespace wind
|
||||
switch (id)
|
||||
{
|
||||
case ui::CREATE_WINDOW_ID: return handle_create_window_message(client);
|
||||
case ui::SET_WINDOW_TITLE_ID: return handle_set_window_title_message(client);
|
||||
case ui::INVALIDATE_ID: return handle_invalidate_message(client);
|
||||
default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "Window.h"
|
||||
#include <luna/Utf8.h>
|
||||
#include <os/File.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ui/Font.h>
|
||||
#include <ui/Image.h>
|
||||
|
||||
@ -8,8 +9,10 @@ LinkedList<Window> g_windows;
|
||||
|
||||
void Window::draw(ui::Canvas& screen)
|
||||
{
|
||||
dirty = false;
|
||||
|
||||
auto window = screen.subcanvas(surface);
|
||||
window.subcanvas(contents).fill(color);
|
||||
window.subcanvas(contents).fill(pixels, contents.width);
|
||||
|
||||
wchar_t buffer[4096];
|
||||
Utf8StringDecoder decoder(name.chars());
|
||||
@ -38,7 +41,7 @@ void Window::focus()
|
||||
g_windows.append(this);
|
||||
}
|
||||
|
||||
Window::Window(ui::Rect r, ui::Color c, String&& n) : surface(r), color(c), name(move(n))
|
||||
Window::Window(ui::Rect r, String&& n) : surface(r), name(move(n))
|
||||
{
|
||||
auto font = ui::Font::default_font();
|
||||
if (surface.width < 36) surface.width = 36;
|
||||
@ -54,3 +57,10 @@ int Window::titlebar_height()
|
||||
auto font = ui::Font::default_font();
|
||||
return font->height() + 20;
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
usize size = contents.width * contents.height * 4;
|
||||
|
||||
munmap(pixels, size);
|
||||
}
|
||||
|
@ -11,12 +11,14 @@ struct Window : public LinkedListNode<Window>
|
||||
ui::Rect titlebar;
|
||||
ui::Rect close_button;
|
||||
ui::Rect contents;
|
||||
ui::Color color;
|
||||
u32* pixels;
|
||||
String name;
|
||||
bool dirty { false };
|
||||
|
||||
static int titlebar_height();
|
||||
|
||||
Window(ui::Rect, ui::Color, String&&);
|
||||
Window(ui::Rect, String&&);
|
||||
~Window();
|
||||
|
||||
void focus();
|
||||
|
||||
|
@ -90,7 +90,7 @@ Result<int> luna_main(int argc, char** argv)
|
||||
TRY(fds.try_append({ .fd = keyboard->fd(), .events = POLLIN, .revents = 0 }));
|
||||
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
|
||||
|
||||
TRY(os::Security::pledge("stdio rpath unix signal proc", NULL));
|
||||
TRY(os::Security::pledge("stdio rpath wpath cpath unix signal proc", NULL));
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user