Compare commits

..

9 Commits

32 changed files with 566 additions and 26 deletions

View File

@ -1,5 +1,23 @@
#include <os/ArgumentParser.h> #include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/LocalClient.h> #include <os/LocalClient.h>
#include <ui/ipc/Server.h>
#include <unistd.h>
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)
{
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;
}
Result<int> luna_main(int argc, char** argv) Result<int> luna_main(int argc, char** argv)
{ {
@ -13,8 +31,16 @@ Result<int> luna_main(int argc, char** argv)
auto client = TRY(os::LocalClient::connect(socket_path, false)); auto client = TRY(os::LocalClient::connect(socket_path, false));
StringView message = "hello"; int id = TRY(create_window(*client, ui::Rect { 200, 200, 400, 300 }, "My Window", ui::CYAN));
TRY(client->send((const u8*)message.chars(), message.length())); os::println("Created new window with id %d!", 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);
sleep(6);
return 0; return 0;
} }

View File

@ -69,6 +69,8 @@ class Socket : public VFS::FileInode
virtual bool can_read_data() const = 0; virtual bool can_read_data() const = 0;
virtual bool peer_disconnected() const = 0;
virtual ~Socket() = default; virtual ~Socket() = default;
protected: protected:

View File

@ -27,6 +27,11 @@ class UnixSocket : public Socket
return !m_listen_queue.is_empty(); return !m_listen_queue.is_empty();
} }
bool peer_disconnected() const override
{
return m_state == Reset;
}
Result<usize> send(const u8*, usize, int) override; Result<usize> send(const u8*, usize, int) override;
Result<usize> recv(u8*, usize, int) const override; Result<usize> recv(u8*, usize, int) const override;

View File

@ -56,6 +56,11 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
fds_with_events++; fds_with_events++;
kfds[i].revents |= POLLIN; kfds[i].revents |= POLLIN;
} }
if (socket->peer_disconnected())
{
fds_with_events++;
kfds[i].revents |= POLLHUP;
}
} }
else else
{ {

View File

@ -8,6 +8,7 @@
#define POLLIN (1 << 0) #define POLLIN (1 << 0)
#define POLLERR (1 << 1) #define POLLERR (1 << 1)
#define POLLNVAL (1 << 2) #define POLLNVAL (1 << 2)
#define POLLHUP (1 << 3)
typedef __u64_t nfds_t; typedef __u64_t nfds_t;

View File

@ -16,6 +16,7 @@ set(SOURCES
src/Security.cpp src/Security.cpp
src/LocalServer.cpp src/LocalServer.cpp
src/LocalClient.cpp src/LocalClient.cpp
src/IPC.cpp
) )
add_library(os ${SOURCES}) add_library(os ${SOURCES})

157
libos/include/os/IPC.h Normal file
View File

@ -0,0 +1,157 @@
/**
* @file IPC.h
* @author apio (cloudapio.eu)
* @brief Inter-process communication primitives.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <os/LocalClient.h>
#include <os/LocalServer.h>
#define IPC_ENUM_SERVER(name) __##name##_SERVER_ERROR = 0
#define IPC_ENUM_CLIENT(name) __##name##_CLIENT_ERROR = 0
/**
* @brief Called to handle IPC events (client-side).
*
* @param conn The connection object being used.
* @param id The ID of the message.
* @return Result<void> Whether the operation succeded.
*/
extern Result<void> handle_ipc_client_event(os::LocalClient& conn, u8 id);
/**
* @brief Called to handle IPC events (server-side).
*
* @param conn The connection object being used.
* @param id The ID of the message.
* @return Result<void> Whether the operation succeded.
*/
extern Result<void> handle_ipc_server_event(os::LocalServer::Client& conn, u8 id);
namespace os
{
namespace IPC
{
static constexpr usize IPC_STRING_LENGTH = 256;
#define IPC_STRING(name) char name[os::IPC::IPC_STRING_LENGTH];
#define COPY_IPC_STRING(name) \
TRY(String::from_string_view(StringView::from_fixed_size_cstring(name, os::IPC::IPC_STRING_LENGTH)))
#define SET_IPC_STRING(name, value) strlcpy(name, value, os::IPC::IPC_STRING_LENGTH)
/**
* @brief Sends an IPC message without waiting for a reply.
*
* @tparam Client The type of the client interface being used to communicate.
* @tparam T The type of the message.
* @param client The connection object being used to communicate.
* @param message The IPC message.
* @return Result<void> Whether the operation succeded.
*/
template <typename Client, typename T> Result<void> send_async(Client& client, const T& message)
{
u8 id = T::id;
TRY(client.send_typed(id));
TRY(client.send_typed(message));
return {};
}
/**
* @brief Sends an error result to the IPC connection, indicating that an operation could not be performed.
*
* @tparam Client The type of the client interface being used to communicate.
* @param client The connection object being used to communicate.
* @param error The error code.
* @return Result<void> Whether the operation succeded.
*/
template <typename Client> Result<void> send_error(Client& client, int error)
{
u8 id = 0;
TRY(client.send_typed(id));
TRY(client.send_typed(error));
return {};
}
/**
* @brief Sends an IPC message and waits for a reply (client-only).
*
* @tparam ResponseType The type of the response.
* @tparam T The type of the message.
* @param client The connection object being used to communicate.
* @param message The IPC message.
* @param handler The function used to handle messages that do not match the reply.
* @return Result<ResponseType> An error, or the response.
*/
template <typename ResponseType, typename T>
Result<ResponseType> send_sync(os::LocalClient& client, const T& message,
decltype(handle_ipc_client_event) handler = handle_ipc_client_event)
{
u8 id = T::id;
TRY(client.send_typed(id));
TRY(client.send_typed(message));
// We allow receiving 5 messages of different types, but if those have passed and we still don't have a
// reply, fail with ENOMSG.
int max_other_messages = 5;
while (max_other_messages)
{
u8 response_id;
auto rc = client.recv_typed(response_id);
if (rc.has_error() && rc.error() == EAGAIN) continue;
if (response_id == 0) // Error result
{
while (1)
{
int code;
rc = client.recv_typed(code);
if (rc.has_error() && rc.error() == EAGAIN) continue;
return err(code);
}
}
if (response_id != ResponseType::id)
{
TRY(handler(client, response_id));
max_other_messages--;
continue;
}
while (1)
{
ResponseType response;
rc = client.recv_typed(response);
if (rc.has_error() && rc.error() == EAGAIN) continue;
return response;
}
}
return err(ENOMSG);
}
/**
* @brief Check for new IPC messages on a connection and handle them appropriately.
*
* @param client The client connection.
* @param handler The function used to handle messages.
* @return Result<void> Whether the operation succeded.
*/
Result<void> check_for_messages(os::LocalClient& client,
decltype(handle_ipc_client_event) handler = handle_ipc_client_event);
/**
* @brief Check for new IPC messages on a connection and handle them appropriately.
*
* @param server The server connection.
* @param handler The function used to handle messages.
* @return Result<void> Whether the operation succeded.
*/
Result<void> check_for_messages(os::LocalServer::Client& server,
decltype(handle_ipc_server_event) handler = handle_ipc_server_event);
}
}

View File

@ -1,3 +1,12 @@
/**
* @file LocalClient.h
* @author apio (cloudapio.eu)
* @brief UNIX local domain client class.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/OwnedPtr.h> #include <luna/OwnedPtr.h>
#include <luna/StringView.h> #include <luna/StringView.h>

View File

@ -1,3 +1,12 @@
/**
* @file LocalServer.h
* @author apio (cloudapio.eu)
* @brief UNIX local domain server class.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/OwnedPtr.h> #include <luna/OwnedPtr.h>
#include <luna/Result.h> #include <luna/Result.h>

39
libos/src/IPC.cpp Normal file
View File

@ -0,0 +1,39 @@
/**
* @file IPC.cpp
* @author apio (cloudapio.eu)
* @brief Inter-process communication primitives.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <os/IPC.h>
namespace os::IPC
{
Result<void> check_for_messages(os::LocalClient& client, decltype(handle_ipc_client_event) handler)
{
u8 id;
auto rc = client.recv_typed(id);
if (rc.has_error())
{
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
return rc.release_error();
}
return handler(client, id);
}
Result<void> check_for_messages(os::LocalServer::Client& client, decltype(handle_ipc_server_event) handler)
{
u8 id;
auto rc = client.recv_typed(id);
if (rc.has_error())
{
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
return rc.release_error();
}
return handler(client, id);
}
}

View File

@ -1,3 +1,12 @@
/**
* @file LocalClient.cpp
* @author apio (cloudapio.eu)
* @brief UNIX local domain client class.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <os/LocalClient.h> #include <os/LocalClient.h>

View File

@ -1,3 +1,12 @@
/**
* @file LocalServer.cpp
* @author apio (cloudapio.eu)
* @brief UNIX local domain server class.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <os/FileSystem.h> #include <os/FileSystem.h>

View File

@ -4,6 +4,8 @@ file(GLOB HEADERS include/ui/*.h)
set(SOURCES set(SOURCES
${HEADERS} ${HEADERS}
include/ui/ipc/Server.h
include/ui/ipc/Client.h
src/Canvas.cpp src/Canvas.cpp
src/Rect.cpp src/Rect.cpp
src/Font.cpp src/Font.cpp

View File

@ -1,3 +1,12 @@
/**
* @file Canvas.h
* @author apio (cloudapio.eu)
* @brief Drawable surfaces.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/Types.h> #include <luna/Types.h>

View File

@ -1,3 +1,12 @@
/**
* @file Color.h
* @author apio (cloudapio.eu)
* @brief RGBA colors.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/Types.h> #include <luna/Types.h>

View File

@ -1,3 +1,12 @@
/**
* @file Font.h
* @author apio (cloudapio.eu)
* @brief PSF font loading and rendering.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/Buffer.h> #include <luna/Buffer.h>
#include <luna/SharedPtr.h> #include <luna/SharedPtr.h>

View File

@ -1,3 +1,12 @@
/**
* @file Image.h
* @author apio (cloudapio.eu)
* @brief TGA image loading and rendering.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <luna/Buffer.h> #include <luna/Buffer.h>
#include <luna/SharedPtr.h> #include <luna/SharedPtr.h>

View File

@ -1,3 +1,12 @@
/**
* @file Point.h
* @author apio (cloudapio.eu)
* @brief 2D space points.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
namespace ui namespace ui

View File

@ -1,3 +1,12 @@
/**
* @file Rect.h
* @author apio (cloudapio.eu)
* @brief A simple 2D rectangle representation.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once #pragma once
#include <ui/Point.h> #include <ui/Point.h>

View File

@ -0,0 +1,27 @@
/**
* @file ipc/Client.h
* @author apio (cloudapio.eu)
* @brief IPC message definitions for UI messages sent to the client.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <os/IPC.h>
namespace ui
{
enum ClientMessages : u8
{
IPC_ENUM_CLIENT(ui),
CREATE_WINDOW_RESPONSE_ID,
};
struct CreateWindowResponse
{
static constexpr u8 id = CREATE_WINDOW_RESPONSE_ID;
int window;
};
}

View File

@ -0,0 +1,33 @@
/**
* @file ipc/Server.h
* @author apio (cloudapio.eu)
* @brief IPC message definitions for UI messages sent to the server.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <os/IPC.h>
#include <ui/Color.h>
#include <ui/Rect.h>
#include <ui/ipc/Client.h>
namespace ui
{
enum ServerMessages : u8
{
IPC_ENUM_SERVER(ui),
CREATE_WINDOW_ID,
};
struct CreateWindowRequest
{
using ResponseType = CreateWindowResponse;
static constexpr u8 id = CREATE_WINDOW_ID;
ui::Rect rect;
IPC_STRING(name);
ui::Color color;
};
}

View File

@ -1,3 +1,12 @@
/**
* @file Canvas.cpp
* @author apio (cloudapio.eu)
* @brief Drawable surfaces.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <ui/Canvas.h> #include <ui/Canvas.h>
namespace ui namespace ui

View File

@ -1,3 +1,12 @@
/**
* @file Font.cpp
* @author apio (cloudapio.eu)
* @brief PSF font loading and rendering.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/String.h> #include <luna/String.h>
#include <os/File.h> #include <os/File.h>
#include <ui/Font.h> #include <ui/Font.h>

View File

@ -1,3 +1,12 @@
/**
* @file Image.cpp
* @author apio (cloudapio.eu)
* @brief TGA image loading and rendering.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <os/File.h> #include <os/File.h>
#include <ui/Image.h> #include <ui/Image.h>

View File

@ -1,3 +1,12 @@
/**
* @file Rect.cpp
* @author apio (cloudapio.eu)
* @brief A simple 2D rectangle representation.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <ui/Rect.h> #include <ui/Rect.h>
namespace ui namespace ui

View File

@ -6,6 +6,9 @@ set(SOURCES
Mouse.cpp Mouse.cpp
Window.h Window.h
Window.cpp Window.cpp
IPC.cpp
IPC.h
Client.h
) )
add_executable(wind ${SOURCES}) add_executable(wind ${SOURCES})

11
wind/Client.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "Window.h"
#include <os/LocalServer.h>
struct Client
{
os::LocalServer::Client conn;
Vector<Window*> windows;
bool rpc_in_progress { false };
u8 rpc_id { 0 };
};

75
wind/IPC.cpp Normal file
View File

@ -0,0 +1,75 @@
#include "IPC.h"
#include <luna/String.h>
#include <os/File.h>
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();
}
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* window = new (std::nothrow) Window(request.rect, request.color, 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 {};
}
ui::CreateWindowResponse response;
response.window = id;
os::IPC::send_async(client.conn, response);
return {};
}
namespace wind
{
Result<void> handle_ipc_message(Client& client, u8 id)
{
client.rpc_in_progress = false;
switch (id)
{
case ui::CREATE_WINDOW_ID: return handle_create_window_message(client);
default: os::eprintln("wind: Invalid IPC message from client!"); return err(EINVAL);
}
}
Result<void> handle_ipc(Client& client)
{
if (client.rpc_in_progress) return handle_ipc_message(client, client.rpc_id);
u8 id;
auto rc = client.conn.recv_typed(id);
if (rc.has_error())
{
if (rc.error() == EAGAIN) { return {}; }
else
return rc.release_error();
}
return handle_ipc_message(client, id);
}
}

10
wind/IPC.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "Client.h"
#include <ui/ipc/Server.h>
namespace wind
{
Result<void> handle_ipc_message(Client& client, u8 id);
Result<void> handle_ipc(Client& client);
}

View File

@ -38,7 +38,7 @@ void Window::focus()
g_windows.append(this); g_windows.append(this);
} }
Window::Window(ui::Rect r, ui::Color c, StringView n) : surface(r), color(c), name(n) Window::Window(ui::Rect r, ui::Color c, String&& n) : surface(r), color(c), name(move(n))
{ {
auto font = ui::Font::default_font(); auto font = ui::Font::default_font();
if (surface.width < 36) surface.width = 36; if (surface.width < 36) surface.width = 36;
@ -48,3 +48,9 @@ Window::Window(ui::Rect r, ui::Color c, StringView n) : surface(r), color(c), na
contents = ui::Rect { 0, font->height() + 20, surface.width, surface.height - (font->height() + 20) }; contents = ui::Rect { 0, font->height() + 20, surface.width, surface.height - (font->height() + 20) };
g_windows.append(this); g_windows.append(this);
} }
int Window::titlebar_height()
{
auto font = ui::Font::default_font();
return font->height() + 20;
}

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <luna/LinkedList.h> #include <luna/LinkedList.h>
#include <luna/StringView.h> #include <luna/String.h>
#include <ui/Canvas.h> #include <ui/Canvas.h>
#include <ui/Color.h> #include <ui/Color.h>
#include <ui/Rect.h> #include <ui/Rect.h>
@ -12,9 +12,11 @@ struct Window : public LinkedListNode<Window>
ui::Rect close_button; ui::Rect close_button;
ui::Rect contents; ui::Rect contents;
ui::Color color; ui::Color color;
StringView name; String name;
Window(ui::Rect, ui::Color, StringView); static int titlebar_height();
Window(ui::Rect, ui::Color, String&&);
void focus(); void focus();

View File

@ -1,3 +1,5 @@
#include "Client.h"
#include "IPC.h"
#include "Mouse.h" #include "Mouse.h"
#include "Screen.h" #include "Screen.h"
#include "Window.h" #include "Window.h"
@ -20,7 +22,7 @@ Result<int> luna_main(int argc, char** argv)
{ {
srand((unsigned)time(NULL)); srand((unsigned)time(NULL));
TRY(os::Security::pledge("stdio rpath wpath cpath unix proc exec tty id", NULL)); TRY(os::Security::pledge("stdio rpath wpath cpath unix proc exec tty signal id", NULL));
StringView socket_path = "/tmp/wind.sock"; StringView socket_path = "/tmp/wind.sock";
StringView user; StringView user;
@ -82,13 +84,13 @@ Result<int> luna_main(int argc, char** argv)
ui::Color background = ui::BLACK; ui::Color background = ui::BLACK;
TRY(make<Window>(ui::Rect { 200, 200, 600, 400 }, ui::GREEN, "Calculator"_sv)); Vector<Client> clients;
TRY(make<Window>(ui::Rect { 100, 100, 300, 200 }, ui::RED, "Settings"_sv)); Vector<struct pollfd> fds;
TRY(make<Window>(ui::Rect { 600, 130, 350, 250 }, ui::CYAN, "File Manager"_sv)); TRY(fds.try_append({ .fd = mouse->fd(), .events = POLLIN, .revents = 0 }));
TRY(fds.try_append({ .fd = keyboard->fd(), .events = POLLIN, .revents = 0 }));
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
Vector<os::LocalServer::Client> clients; TRY(os::Security::pledge("stdio rpath unix signal proc", NULL));
TRY(os::Security::pledge("stdio rpath unix", NULL));
while (1) while (1)
{ {
@ -97,13 +99,9 @@ Result<int> luna_main(int argc, char** argv)
mouse_pointer.draw(screen.canvas()); mouse_pointer.draw(screen.canvas());
screen.sync(); screen.sync();
struct pollfd fds[] = { for (auto& pfd : fds) { pfd.revents = 0; }
{ .fd = mouse->fd(), .events = POLLIN, .revents = 0 },
{ .fd = keyboard->fd(), .events = POLLIN, .revents = 0 },
{ .fd = server->fd(), .events = POLLIN, .revents = 0 },
};
int rc = poll(fds, 3, 1000); int rc = poll(fds.data(), fds.size(), 1000);
if (!rc) continue; if (!rc) continue;
if (rc < 0 && errno != EINTR) { os::println("poll: error: %s", strerror(errno)); } if (rc < 0 && errno != EINTR) { os::println("poll: error: %s", strerror(errno)); }
@ -117,20 +115,31 @@ Result<int> luna_main(int argc, char** argv)
{ {
moon::KeyboardPacket packet; moon::KeyboardPacket packet;
TRY(keyboard->read_typed(packet)); TRY(keyboard->read_typed(packet));
if (!packet.released) os::println("%s key %d", packet.released ? "Released" : "Pressed", packet.key);
}
for (usize i = 0; i < clients.size(); i++)
{ {
TRY(make<Window>(ui::Rect { rand() % screen.canvas().width, rand() % screen.canvas().height, if (fds[i + 3].revents & POLLIN) wind::handle_ipc(clients[i]);
rand() % screen.canvas().width, rand() % screen.canvas().height }, if (fds[i + 3].revents & POLLHUP)
ui::Color::from_rgb(static_cast<u8>(rand() % 256), static_cast<u8>(rand() % 256), {
static_cast<u8>(rand() % 256)), os::println("wind: Client %d disconnected", i);
strerror(packet.key))); fds.remove_at(i + 3);
auto client = clients.remove_at(i);
client.conn.disconnect();
for (auto& window : client.windows)
{
g_windows.remove(window);
delete window;
}
} }
} }
if (fds[2].revents & POLLIN) if (fds[2].revents & POLLIN)
{ {
auto client = TRY(server->accept()); auto client = TRY(server->accept());
os::println("wind: New client connected!"); os::println("wind: New client connected!");
TRY(clients.try_append(move(client))); TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
Client c { move(client), {} };
TRY(clients.try_append(move(c)));
} }
} }
} }