Add a display server and graphical user interface #38
@ -1,94 +1,24 @@
|
||||
#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);
|
||||
}
|
||||
};
|
||||
#include <ui/App.h>
|
||||
#include <ui/Color.h>
|
||||
#include <ui/Window.h>
|
||||
|
||||
Result<void> handle_ipc_client_event(os::LocalClient&, u8)
|
||||
{
|
||||
todo();
|
||||
}
|
||||
|
||||
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;
|
||||
auto response = TRY(os::IPC::send_sync<ui::CreateWindowResponse>(client, request));
|
||||
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)
|
||||
{
|
||||
StringView socket_path = "/tmp/wind.sock";
|
||||
ui::App app;
|
||||
TRY(app.init(argc, argv));
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("A graphical user interface client."_sv);
|
||||
parser.add_system_program_info("gclient"_sv);
|
||||
parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv);
|
||||
parser.parse(argc, argv);
|
||||
auto window = TRY(ui::Window::create(ui::Rect { 200, 200, 400, 300 }));
|
||||
os::println("gclient: Created new window!");
|
||||
|
||||
auto client = TRY(os::LocalClient::connect(socket_path, false));
|
||||
window->set_title("Example Window");
|
||||
window->canvas().fill(ui::CYAN);
|
||||
window->update();
|
||||
|
||||
Window window = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 }));
|
||||
os::println("Created new window with id %d!", window.id);
|
||||
|
||||
sleep(3);
|
||||
|
||||
window.set_title(*client, "Example Window");
|
||||
|
||||
sleep(3);
|
||||
|
||||
window.canvas.fill(ui::CYAN);
|
||||
window.redraw(*client);
|
||||
|
||||
sleep(3);
|
||||
|
||||
return 0;
|
||||
return app.run();
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ set(SOURCES
|
||||
src/Rect.cpp
|
||||
src/Font.cpp
|
||||
src/Image.cpp
|
||||
src/App.cpp
|
||||
src/Window.cpp
|
||||
)
|
||||
|
||||
add_library(ui ${SOURCES})
|
||||
|
41
libui/include/ui/App.h
Normal file
41
libui/include/ui/App.h
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @file App.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief UI application event loop.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <os/LocalClient.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class App
|
||||
{
|
||||
public:
|
||||
App();
|
||||
~App();
|
||||
|
||||
Result<void> init(int, char**);
|
||||
Result<int> run();
|
||||
|
||||
os::LocalClient& client()
|
||||
{
|
||||
return *m_client;
|
||||
}
|
||||
|
||||
void set_should_close(bool b)
|
||||
{
|
||||
m_should_close = b;
|
||||
}
|
||||
|
||||
static App& the();
|
||||
|
||||
private:
|
||||
static App* s_app;
|
||||
OwnedPtr<os::LocalClient> m_client;
|
||||
bool m_should_close { false };
|
||||
};
|
||||
}
|
38
libui/include/ui/Window.h
Normal file
38
libui/include/ui/Window.h
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file Window.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief UI windows.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <luna/OwnedPtr.h>
|
||||
#include <luna/StringView.h>
|
||||
#include <ui/Canvas.h>
|
||||
#include <ui/Rect.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
static Result<OwnedPtr<Window>> create(Rect rect);
|
||||
|
||||
void set_title(StringView title);
|
||||
|
||||
Canvas& canvas()
|
||||
{
|
||||
return m_canvas;
|
||||
}
|
||||
|
||||
void update();
|
||||
|
||||
~Window();
|
||||
|
||||
private:
|
||||
int m_id;
|
||||
Canvas m_canvas;
|
||||
};
|
||||
}
|
54
libui/src/App.cpp
Normal file
54
libui/src/App.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @file App.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief UI application event loop.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/IPC.h>
|
||||
#include <ui/App.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
App* App::s_app { nullptr };
|
||||
|
||||
App::App()
|
||||
{
|
||||
s_app = this;
|
||||
}
|
||||
|
||||
App::~App()
|
||||
{
|
||||
s_app = nullptr;
|
||||
}
|
||||
|
||||
Result<void> App::init(int argc, char** argv)
|
||||
{
|
||||
StringView socket_path = "/tmp/wind.sock";
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("A UI application."_sv);
|
||||
parser.add_system_program_info(argv[0]);
|
||||
parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
m_client = TRY(os::LocalClient::connect(socket_path, true));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<int> App::run()
|
||||
{
|
||||
while (!m_should_close) { TRY(os::IPC::check_for_messages(*m_client)); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
App& App::the()
|
||||
{
|
||||
check(s_app);
|
||||
return *s_app;
|
||||
}
|
||||
}
|
77
libui/src/Window.cpp
Normal file
77
libui/src/Window.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @file Window.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief UI windows.
|
||||
*
|
||||
* @copyright Copyright (c) 2023, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Window.h>
|
||||
#include <ui/ipc/Server.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
namespace ui
|
||||
{
|
||||
Result<OwnedPtr<Window>> Window::create(Rect rect)
|
||||
{
|
||||
auto window = TRY(make_owned<Window>());
|
||||
|
||||
ui::CreateWindowRequest request;
|
||||
request.rect = rect;
|
||||
auto response = TRY(os::IPC::send_sync<ui::CreateWindowResponse>(App::the().client(), request));
|
||||
|
||||
u32* pixels = TRY(create_shm_region(response.shm_path, nullptr, rect));
|
||||
|
||||
window->m_canvas = ui::Canvas { rect.width, rect.height, rect.width, (u8*)pixels };
|
||||
window->m_id = response.window;
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
if (m_canvas.ptr) munmap(m_canvas.ptr, ((usize)m_canvas.width) * ((usize)m_canvas.height) * 4);
|
||||
}
|
||||
|
||||
void Window::set_title(StringView title)
|
||||
{
|
||||
ui::SetWindowTitleRequest request;
|
||||
request.window = m_id;
|
||||
SET_IPC_STRING(request.title, title.chars());
|
||||
os::IPC::send_async(App::the().client(), request);
|
||||
}
|
||||
|
||||
void Window::update()
|
||||
{
|
||||
ui::InvalidateRequest request;
|
||||
request.window = m_id;
|
||||
os::IPC::send_async(App::the().client(), request);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user