Compare commits
96 Commits
1de85aa4be
...
1d4055411d
Author | SHA1 | Date | |
---|---|---|---|
1d4055411d | |||
033b4c3db8 | |||
744ca07799 | |||
8f0356700c | |||
dad573f1fe | |||
870cb867ee | |||
11d47c3982 | |||
2eb973ca2b | |||
a227578107 | |||
8e28d45b9c | |||
f8e6b45dd2 | |||
ff68f574ff | |||
da6daff8c7 | |||
09cb378a7a | |||
d58a56a1d1 | |||
096a4e7953 | |||
9c30641587 | |||
75b3e78d34 | |||
d801fe6bb8 | |||
41f9d8cadd | |||
36cc66c2e9 | |||
d6b75f5981 | |||
c6bff6f8b4 | |||
03bde69c65 | |||
9752ab29d3 | |||
2445c2f6aa | |||
7aaa05ded6 | |||
3253151cb7 | |||
e28f8f7520 | |||
65aa931931 | |||
2192399ca2 | |||
0df32b92b3 | |||
baab157431 | |||
0fcc5b9a5e | |||
92a4174f02 | |||
48e97de59c | |||
f092e41247 | |||
c5ffbae6a0 | |||
790d9d119b | |||
554a8ee300 | |||
1caf80bde2 | |||
5b496791c5 | |||
fd99fc88ee | |||
4ac0adee57 | |||
432a5c27cb | |||
8e01e2cd77 | |||
ee7671a8cb | |||
efa96655f3 | |||
a7c4e8900c | |||
f7763ca2bc | |||
f104d491e0 | |||
786bec0b18 | |||
57e1f49a32 | |||
b49d9d55b7 | |||
99080b8c50 | |||
9ecf259c71 | |||
dc107d14d7 | |||
d39f0387fe | |||
1fa0bbfa37 | |||
39296773d2 | |||
d328c722f0 | |||
2177828456 | |||
3252612d90 | |||
48a7ffcce8 | |||
57a05b73f3 | |||
60dc10d4fc | |||
a8379b6c73 | |||
64f9e9dcde | |||
9c912ddc51 | |||
419604a4d2 | |||
97037b06cb | |||
c2f173f584 | |||
a772d92e6f | |||
6f3ed70363 | |||
d48142f163 | |||
cbea66c533 | |||
c6d817a0fd | |||
55c362eecf | |||
9fd8b10b3f | |||
516d6bc65e | |||
4becb2e427 | |||
3e174337ab | |||
0f377e7289 | |||
798a6d63aa | |||
3f1e6fc2ff | |||
b8ae61b7c7 | |||
1449e966ab | |||
24f9dd22ec | |||
b8e70996c3 | |||
6058a69182 | |||
e247310ded | |||
a35ca0b367 | |||
d144a818d8 | |||
33c1a9c92b | |||
65834ff491 | |||
5c2718545f |
10
.gitignore
vendored
10
.gitignore
vendored
@ -4,7 +4,15 @@ build/
|
|||||||
initrd/boot/moon
|
initrd/boot/moon
|
||||||
env-local.sh
|
env-local.sh
|
||||||
initrd/bin/**
|
initrd/bin/**
|
||||||
base/usr/**
|
base/usr/bin/**
|
||||||
|
base/usr/include/**
|
||||||
|
base/usr/lib/**
|
||||||
|
base/usr/share/pkgdb/**
|
||||||
|
!base/usr/share/fonts/*
|
||||||
|
!base/usr/share/icons/*
|
||||||
|
base/usr/share/**
|
||||||
|
base/usr/x86_64-luna/**
|
||||||
|
base/etc/skel/LICENSE
|
||||||
.fakeroot
|
.fakeroot
|
||||||
kernel/config.cmake
|
kernel/config.cmake
|
||||||
ports/out/
|
ports/out/
|
||||||
|
@ -45,8 +45,10 @@ endif()
|
|||||||
|
|
||||||
add_subdirectory(libluna)
|
add_subdirectory(libluna)
|
||||||
add_subdirectory(libos)
|
add_subdirectory(libos)
|
||||||
|
add_subdirectory(libui)
|
||||||
add_subdirectory(libc)
|
add_subdirectory(libc)
|
||||||
add_subdirectory(kernel)
|
add_subdirectory(kernel)
|
||||||
add_subdirectory(apps)
|
add_subdirectory(apps)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
add_subdirectory(shell)
|
add_subdirectory(shell)
|
||||||
|
add_subdirectory(wind)
|
||||||
|
@ -41,3 +41,7 @@ luna_app(kill.cpp kill)
|
|||||||
luna_app(gol.cpp gol)
|
luna_app(gol.cpp gol)
|
||||||
luna_app(touch.cpp touch)
|
luna_app(touch.cpp touch)
|
||||||
luna_app(free.cpp free)
|
luna_app(free.cpp free)
|
||||||
|
luna_app(gclient.cpp gclient)
|
||||||
|
target_link_libraries(gclient PUBLIC ui)
|
||||||
|
luna_app(taskbar.cpp taskbar)
|
||||||
|
target_link_libraries(taskbar PUBLIC ui)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
|
#include <os/FileSystem.h>
|
||||||
|
#include <os/Prompt.h>
|
||||||
|
|
||||||
using os::File;
|
using os::File;
|
||||||
|
|
||||||
@ -14,10 +16,20 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
parser.add_positional_argument(pathname, "path"_sv, true);
|
parser.add_positional_argument(pathname, "path"_sv, true);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (os::FileSystem::exists(pathname))
|
||||||
|
{
|
||||||
|
String prompt = TRY(String::format("File %s already exists. Overwrite?"_sv, pathname.chars()));
|
||||||
|
bool overwrite = os::conditional_prompt(prompt.chars(), os::DefaultNo);
|
||||||
|
if (!overwrite) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
auto file = TRY(File::open_or_create(pathname, File::WriteOnly));
|
auto file = TRY(File::open_or_create(pathname, File::WriteOnly));
|
||||||
|
|
||||||
auto input = File::standard_input();
|
auto input = File::standard_input();
|
||||||
|
|
||||||
|
os::println("- Editing %s. Press Enter to start a new line, or Ctrl+D at the start of a line to save and exit. -",
|
||||||
|
pathname.chars());
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
String line = TRY(input->read_line());
|
String line = TRY(input->read_line());
|
||||||
|
75
apps/gclient.cpp
Normal file
75
apps/gclient.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <ui/App.h>
|
||||||
|
#include <ui/Layout.h>
|
||||||
|
|
||||||
|
struct ColorWidget : public ui::Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ColorWidget(ui::Color first, ui::Color second) : m_color(first), m_first_color(first), m_second_color(second)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<ui::EventResult> handle_mouse_move(ui::Point) override
|
||||||
|
{
|
||||||
|
m_color = m_second_color;
|
||||||
|
return ui::EventResult::DidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<ui::EventResult> handle_mouse_leave(ui::Point) override
|
||||||
|
{
|
||||||
|
m_color = m_first_color;
|
||||||
|
return ui::EventResult::DidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<ui::EventResult> handle_mouse_down(ui::Point, int) override
|
||||||
|
{
|
||||||
|
return ui::EventResult::DidNotHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<ui::EventResult> handle_mouse_up(ui::Point, int) override
|
||||||
|
{
|
||||||
|
return ui::EventResult::DidNotHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> draw(ui::Canvas& canvas) override
|
||||||
|
{
|
||||||
|
canvas.fill(m_color);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ui::Color m_color;
|
||||||
|
ui::Color m_first_color;
|
||||||
|
ui::Color m_second_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
ui::App app;
|
||||||
|
TRY(app.init(argc, argv));
|
||||||
|
|
||||||
|
auto* window = TRY(ui::Window::create(ui::Rect { 200, 200, 400, 300 }));
|
||||||
|
app.set_main_window(window);
|
||||||
|
|
||||||
|
window->set_title("Main Window");
|
||||||
|
window->set_background(ui::CYAN);
|
||||||
|
|
||||||
|
ui::HorizontalLayout layout;
|
||||||
|
window->set_main_widget(layout);
|
||||||
|
|
||||||
|
ColorWidget green(ui::GREEN, ui::WHITE);
|
||||||
|
layout.add_widget(green);
|
||||||
|
ColorWidget blue(ui::BLUE, ui::GRAY);
|
||||||
|
layout.add_widget(blue);
|
||||||
|
|
||||||
|
ui::VerticalLayout sublayout;
|
||||||
|
layout.add_widget(sublayout);
|
||||||
|
|
||||||
|
ColorWidget red(ui::RED, ui::CYAN);
|
||||||
|
sublayout.add_widget(red);
|
||||||
|
ColorWidget white(ui::WHITE, ui::GREEN);
|
||||||
|
sublayout.add_widget(white);
|
||||||
|
|
||||||
|
window->draw();
|
||||||
|
|
||||||
|
return app.run();
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
#include <luna/PathParser.h>
|
#include <luna/PathParser.h>
|
||||||
#include <luna/Sort.h>
|
#include <luna/Sort.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
|
#include <luna/Utf8.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <os/Directory.h>
|
#include <os/Directory.h>
|
||||||
@ -24,18 +25,6 @@ static bool g_is_system = false;
|
|||||||
|
|
||||||
FILE* g_init_log = nullptr;
|
FILE* g_init_log = nullptr;
|
||||||
|
|
||||||
// Request a successful exit from the system (for tests)
|
|
||||||
void sigterm_handler(int)
|
|
||||||
{
|
|
||||||
_exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request a failure exit from the system (for tests)
|
|
||||||
void sigquit_handler(int)
|
|
||||||
{
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Service
|
struct Service
|
||||||
{
|
{
|
||||||
String name;
|
String name;
|
||||||
@ -63,6 +52,20 @@ static void do_log(const char* format, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request a successful exit from the system (for tests)
|
||||||
|
void sigterm_handler(int)
|
||||||
|
{
|
||||||
|
do_log("[init] successful exit requested, complying\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request a failure exit from the system (for tests)
|
||||||
|
void sigquit_handler(int)
|
||||||
|
{
|
||||||
|
do_log("[init] failure exit requested, complying\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
|
static Result<void> service_child(const Service& service, SharedPtr<os::File> output, SharedPtr<os::File> error,
|
||||||
SharedPtr<os::File> input)
|
SharedPtr<os::File> input)
|
||||||
{
|
{
|
||||||
@ -300,6 +303,19 @@ static Result<void> set_hostname()
|
|||||||
auto hostname = TRY(file->read_line());
|
auto hostname = TRY(file->read_line());
|
||||||
hostname.trim("\n");
|
hostname.trim("\n");
|
||||||
|
|
||||||
|
if (hostname.is_empty())
|
||||||
|
{
|
||||||
|
do_log("[init] /etc/hostname is empty or invalid, keeping the default hostname\n");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Utf8StringDecoder decoder(hostname.chars());
|
||||||
|
if (decoder.code_points().has_error())
|
||||||
|
{
|
||||||
|
do_log("[init] /etc/hostname is not valid UTF-8, keeping the default hostname\n");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (sethostname(hostname.chars(), hostname.length()) < 0) return {};
|
if (sethostname(hostname.chars(), hostname.length()) < 0) return {};
|
||||||
|
|
||||||
do_log("[init] successfully set system hostname to '%s'\n", hostname.chars());
|
do_log("[init] successfully set system hostname to '%s'\n", hostname.chars());
|
||||||
@ -323,7 +339,7 @@ static void mount_shmfs()
|
|||||||
if (chmod("/dev/shm", 01777) < 0) exit(255);
|
if (chmod("/dev/shm", 01777) < 0) exit(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> sysinit()
|
Result<int> sysinit(StringView path)
|
||||||
{
|
{
|
||||||
if (getpid() != 1)
|
if (getpid() != 1)
|
||||||
{
|
{
|
||||||
@ -358,7 +374,8 @@ Result<int> sysinit()
|
|||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec id", nullptr));
|
||||||
|
|
||||||
start_services("/etc/init");
|
if (path.is_empty()) path = "/etc/init";
|
||||||
|
start_services(path);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@ -394,7 +411,7 @@ Result<int> sysinit()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<int> user_init()
|
Result<int> user_init(StringView path)
|
||||||
{
|
{
|
||||||
setpgid(0, 0);
|
setpgid(0, 0);
|
||||||
|
|
||||||
@ -405,7 +422,8 @@ Result<int> user_init()
|
|||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath cpath proc exec", nullptr));
|
||||||
|
|
||||||
start_services("/etc/user");
|
if (path.is_empty()) path = "/etc/user";
|
||||||
|
start_services(path);
|
||||||
|
|
||||||
TRY(os::Security::pledge("stdio rpath wpath proc exec", nullptr));
|
TRY(os::Security::pledge("stdio rpath wpath proc exec", nullptr));
|
||||||
|
|
||||||
@ -446,13 +464,16 @@ Result<int> user_init()
|
|||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
bool user;
|
bool user;
|
||||||
|
StringView service_path;
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("The init system for Luna.");
|
parser.add_description("The init system for Luna.");
|
||||||
parser.add_system_program_info("init"_sv);
|
parser.add_system_program_info("init"_sv);
|
||||||
parser.add_switch_argument(user, 'u', "user"_sv, "initialize a user session instead of the system");
|
parser.add_switch_argument(user, 'u', "user"_sv, "initialize a user session instead of the system");
|
||||||
|
parser.add_value_argument(service_path, 's', "service-path"_sv,
|
||||||
|
"change the default service path (/etc/init or /etc/user)");
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
if (user) return user_init();
|
if (user) return user_init(service_path);
|
||||||
return sysinit();
|
return sysinit(service_path);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <luna/Syscall.h>
|
#include <moon/Syscall.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
49
apps/taskbar.cpp
Normal file
49
apps/taskbar.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include <os/Process.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <ui/App.h>
|
||||||
|
#include <ui/Button.h>
|
||||||
|
#include <ui/Container.h>
|
||||||
|
#include <ui/Image.h>
|
||||||
|
#include <ui/Layout.h>
|
||||||
|
|
||||||
|
void sigchld_handler(int)
|
||||||
|
{
|
||||||
|
wait(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
ui::App app;
|
||||||
|
TRY(app.init(argc, argv));
|
||||||
|
|
||||||
|
signal(SIGCHLD, sigchld_handler);
|
||||||
|
|
||||||
|
ui::Rect screen = app.screen_rect();
|
||||||
|
|
||||||
|
ui::Rect bar = ui::Rect { ui::Point { 0, screen.height - 50 }, screen.width, 50 };
|
||||||
|
|
||||||
|
auto window = TRY(ui::Window::create(bar, false));
|
||||||
|
app.set_main_window(window);
|
||||||
|
window->set_background(ui::GRAY);
|
||||||
|
|
||||||
|
ui::HorizontalLayout layout(ui::AdjustHeight::Yes, ui::AdjustWidth::No);
|
||||||
|
window->set_main_widget(layout);
|
||||||
|
|
||||||
|
ui::Button button({ 0, 0, 50, 50 });
|
||||||
|
layout.add_widget(button);
|
||||||
|
|
||||||
|
ui::Container container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
|
||||||
|
button.set_widget(container);
|
||||||
|
button.set_action([] {
|
||||||
|
StringView args[] = { "/usr/bin/gclient" };
|
||||||
|
os::Process::spawn("/usr/bin/gclient", Slice<StringView> { args, 1 }, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto image = TRY(ui::ImageWidget::load("/usr/share/icons/32x32/start-icon.tga"));
|
||||||
|
container.set_widget(*image);
|
||||||
|
|
||||||
|
window->draw();
|
||||||
|
|
||||||
|
return app.run();
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
Name=motd
|
|
||||||
Description=Show the message of the day to the user.
|
|
||||||
Command=/usr/bin/cat /etc/motd
|
|
||||||
Wait=true
|
|
@ -1,4 +1,6 @@
|
|||||||
Name=login
|
Name=login
|
||||||
Description=Start the command-line login program.
|
Description=Start the display server.
|
||||||
Command=/usr/bin/login
|
Command=/usr/bin/wind --user=selene
|
||||||
|
StandardOutput=/dev/uart0
|
||||||
|
StandardError=/dev/uart0
|
||||||
Restart=true
|
Restart=true
|
||||||
|
14
base/etc/skel/welcome
Normal file
14
base/etc/skel/welcome
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Welcome to the Luna operating system!
|
||||||
|
You are running on the default user account, selene.
|
||||||
|
|
||||||
|
If you are familiar with Unix-style operating systems (like Linux or *BSD), you should be able to use the Luna terminal without much problems.
|
||||||
|
|
||||||
|
Following the traditional Unix filesystem structure, programs are installed in /usr/bin (/bin is a symlink to /usr/bin). The command `ls /bin` will show all commands available on your current Luna installation.
|
||||||
|
|
||||||
|
Currently, because of driver limitations, the root file system is mounted read-only. Your home folder is writable, but volatile; it is created and populated on boot, and its contents will vanish after a reboot.
|
||||||
|
|
||||||
|
The system is booted using the 'init' program. You can read its configuration files in the /etc/init directory to learn more about the boot process.
|
||||||
|
|
||||||
|
Luna is free software, released under the BSD-2-Clause license. The license is included in the LICENSE file in your home directory.
|
||||||
|
|
||||||
|
View the source code and read more about Luna at https://git.cloudapio.eu/apio/Luna.
|
@ -1,3 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
# Create and populate a volatile home directory.
|
||||||
mount -t tmpfs tmpfs /home/selene
|
mount -t tmpfs tmpfs /home/selene
|
||||||
chown selene:selene /home/selene
|
chown selene:selene /home/selene
|
||||||
|
cp /etc/skel/welcome /home/selene/
|
||||||
|
cp /etc/skel/LICENSE /home/selene/
|
||||||
|
4
base/etc/user/00-taskbar
Normal file
4
base/etc/user/00-taskbar
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Name=taskbar
|
||||||
|
Description=Start the taskbar.
|
||||||
|
Command=/usr/bin/taskbar
|
||||||
|
Restart=true
|
3
base/etc/user/01-gclient
Normal file
3
base/etc/user/01-gclient
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Name=gclient
|
||||||
|
Description=Sample user application.
|
||||||
|
Command=/usr/bin/gclient
|
BIN
base/usr/share/cursors/default.tga
Normal file
BIN
base/usr/share/cursors/default.tga
Normal file
Binary file not shown.
After Width: | Height: | Size: 1004 B |
BIN
base/usr/share/fonts/Tamsyn-Bold.psf
Normal file
BIN
base/usr/share/fonts/Tamsyn-Bold.psf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/Tamsyn-Regular.psf
Normal file
BIN
base/usr/share/fonts/Tamsyn-Regular.psf
Normal file
Binary file not shown.
BIN
base/usr/share/icons/16x16/app-close.tga
Normal file
BIN
base/usr/share/icons/16x16/app-close.tga
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
base/usr/share/icons/32x32/start-icon.tga
Normal file
BIN
base/usr/share/icons/32x32/start-icon.tga
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
@ -24,13 +24,16 @@ Result<void> check_pledge(Thread* thread, Promise promise)
|
|||||||
// Thread::process_pending_signals().
|
// Thread::process_pending_signals().
|
||||||
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
|
thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL;
|
||||||
|
|
||||||
|
// Unblock SIGABRT.
|
||||||
|
thread->signal_mask.set(SIGABRT - 1, false);
|
||||||
|
|
||||||
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting the
|
// If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting the
|
||||||
// thread's pending signals.
|
// thread's pending signals.
|
||||||
thread->pending_signals = 0;
|
thread->pending_signals.clear();
|
||||||
|
|
||||||
thread->send_signal(SIGABRT);
|
thread->send_signal(SIGABRT);
|
||||||
|
|
||||||
// This should never arrive to userspace.
|
// This should never arrive to userspace, unless we're init and have ignored SIGABRT.
|
||||||
return err(ENOSYS);
|
return err(ENOSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +43,8 @@ static void process_mouse_event(u8 data)
|
|||||||
packet.buttons = 0;
|
packet.buttons = 0;
|
||||||
|
|
||||||
u8 flags = g_mouse_packet[0];
|
u8 flags = g_mouse_packet[0];
|
||||||
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -packet.xdelta;
|
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -(256 - packet.xdelta);
|
||||||
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -packet.ydelta;
|
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -(256 - packet.ydelta);
|
||||||
|
|
||||||
if (flags & PS2_MOUSE_MIDDLE_BTN) packet.buttons |= moon::MouseButton::Middle;
|
if (flags & PS2_MOUSE_MIDDLE_BTN) packet.buttons |= moon::MouseButton::Middle;
|
||||||
if (flags & PS2_MOUSE_RIGHT_BTN) packet.buttons |= moon::MouseButton::Right;
|
if (flags & PS2_MOUSE_RIGHT_BTN) packet.buttons |= moon::MouseButton::Right;
|
||||||
|
@ -124,7 +124,7 @@ bool Thread::deliver_signal(int signo, Registers* current_regs)
|
|||||||
if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false;
|
if (push_mem_on_stack((u8*)&handler.__sa_sigreturn, sizeof(void*)).has_error()) return false;
|
||||||
|
|
||||||
signal_mask = handler.sa_mask;
|
signal_mask = handler.sa_mask;
|
||||||
if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask |= (1 << (signo - 1));
|
if ((handler.sa_flags & SA_NODEFER) == 0) signal_mask.set(signo - 1, true);
|
||||||
|
|
||||||
rsp = regs.rsp;
|
rsp = regs.rsp;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ Result<void> ConsoleDevice::handle_background_process_group(bool can_succeed, in
|
|||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
if (current->pgid == foreground_pgrp) return {};
|
if (current->pgid == foreground_pgrp) return {};
|
||||||
|
|
||||||
if ((current->signal_mask & (1 << (signo - 1))) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
|
||||||
{
|
{
|
||||||
if (can_succeed) return {};
|
if (can_succeed) return {};
|
||||||
return err(EIO);
|
return err(EIO);
|
||||||
|
@ -65,6 +65,12 @@ class Socket : public VFS::FileInode
|
|||||||
m_metadata.nlinks--;
|
m_metadata.nlinks--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool can_accept_connections() const = 0;
|
||||||
|
|
||||||
|
virtual bool can_read_data() const = 0;
|
||||||
|
|
||||||
|
virtual bool peer_disconnected() const = 0;
|
||||||
|
|
||||||
virtual ~Socket() = default;
|
virtual ~Socket() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -17,6 +17,21 @@ class UnixSocket : public Socket
|
|||||||
return (m_state == Connected || m_state == Reset) && !m_data.size();
|
return (m_state == Connected || m_state == Reset) && !m_data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_read_data() const override
|
||||||
|
{
|
||||||
|
return (m_state == Connected || m_state == Reset) && m_data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_accept_connections() const override
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "api/Syscall.h"
|
||||||
#include "arch/CPU.h"
|
#include "arch/CPU.h"
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/Syscall.h>
|
|
||||||
|
|
||||||
typedef u64 SyscallArgs[6];
|
typedef u64 SyscallArgs[6];
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "Pledge.h"
|
#include "Pledge.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "net/Socket.h"
|
||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include <bits/poll.h>
|
#include <bits/poll.h>
|
||||||
@ -12,7 +13,7 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
|
|||||||
nfds_t nfds = (nfds_t)args[1];
|
nfds_t nfds = (nfds_t)args[1];
|
||||||
int timeout = (int)args[2];
|
int timeout = (int)args[2];
|
||||||
|
|
||||||
struct pollfd* kfds = (struct pollfd*)TRY(malloc_impl(nfds * sizeof(pollfd)));
|
struct pollfd* kfds = (struct pollfd*)TRY(malloc_impl(nfds * sizeof(pollfd), false, false));
|
||||||
auto guard = make_scope_guard([kfds] { free_impl(kfds); });
|
auto guard = make_scope_guard([kfds] { free_impl(kfds); });
|
||||||
|
|
||||||
if (!MemoryManager::copy_from_user(fds, kfds, nfds * sizeof(pollfd))) return err(EFAULT);
|
if (!MemoryManager::copy_from_user(fds, kfds, nfds * sizeof(pollfd))) return err(EFAULT);
|
||||||
@ -47,10 +48,30 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
|
|||||||
auto& inode = inodes[i];
|
auto& inode = inodes[i];
|
||||||
if (!inode) continue;
|
if (!inode) continue;
|
||||||
|
|
||||||
if (kfds[i].events & POLLIN && !inode->will_block_if_read())
|
if (kfds[i].events & POLLIN)
|
||||||
{
|
{
|
||||||
fds_with_events++;
|
if (inode->type() == VFS::InodeType::Socket)
|
||||||
kfds[i].revents |= POLLIN;
|
{
|
||||||
|
auto socket = (Socket*)inode.ptr();
|
||||||
|
if (socket->can_read_data() || socket->can_accept_connections())
|
||||||
|
{
|
||||||
|
fds_with_events++;
|
||||||
|
kfds[i].revents |= POLLIN;
|
||||||
|
}
|
||||||
|
if (socket->peer_disconnected())
|
||||||
|
{
|
||||||
|
fds_with_events++;
|
||||||
|
kfds[i].revents |= POLLHUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!inode->will_block_if_read())
|
||||||
|
{
|
||||||
|
fds_with_events++;
|
||||||
|
kfds[i].revents |= POLLIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,8 @@ Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (oldset)
|
if (oldset)
|
||||||
{
|
{
|
||||||
if (!MemoryManager::copy_to_user_typed(oldset, ¤t->signal_mask)) return err(EFAULT);
|
sigset_t mask = current->signal_mask.value();
|
||||||
|
if (!MemoryManager::copy_to_user_typed(oldset, &mask)) return err(EFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set)
|
if (set)
|
||||||
@ -89,8 +90,8 @@ Result<u64> sys_sigprocmask(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
switch (how)
|
switch (how)
|
||||||
{
|
{
|
||||||
case SIG_BLOCK: current->signal_mask |= kset; break;
|
case SIG_BLOCK: current->signal_mask = current->signal_mask.value() | kset; break;
|
||||||
case SIG_UNBLOCK: current->signal_mask &= ~kset; break;
|
case SIG_UNBLOCK: current->signal_mask = current->signal_mask.value() & ~kset; break;
|
||||||
case SIG_SETMASK: current->signal_mask = kset; break;
|
case SIG_SETMASK: current->signal_mask = kset; break;
|
||||||
default: return err(EINVAL);
|
default: return err(EINVAL);
|
||||||
}
|
}
|
||||||
|
@ -148,10 +148,10 @@ void Thread::process_pending_signals(Registers* current_regs)
|
|||||||
for (int i = 0; i < NSIG; i++)
|
for (int i = 0; i < NSIG; i++)
|
||||||
{
|
{
|
||||||
int signo = i + 1;
|
int signo = i + 1;
|
||||||
if (signo != SIGKILL && signo != SIGSTOP && signal_mask & (1 << i)) continue;
|
if (signo != SIGKILL && signo != SIGSTOP && signal_mask.get(i)) continue;
|
||||||
if (pending_signals & (1 << i))
|
if (pending_signals.get(i))
|
||||||
{
|
{
|
||||||
pending_signals &= ~(1 << i);
|
pending_signals.set(i, false);
|
||||||
kinfoln("signal: executing signal %d for thread %d", signo, id);
|
kinfoln("signal: executing signal %d for thread %d", signo, id);
|
||||||
auto handler = signal_handlers[i];
|
auto handler = signal_handlers[i];
|
||||||
if (signo != SIGKILL && signo != SIGSTOP && handler.sa_handler == SIG_IGN)
|
if (signo != SIGKILL && signo != SIGSTOP && handler.sa_handler == SIG_IGN)
|
||||||
@ -190,11 +190,11 @@ bool Thread::will_ignore_pending_signal()
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < NSIG; i++)
|
for (int i = 0; i < NSIG; i++)
|
||||||
{
|
{
|
||||||
if (pending_signals & (1 << i))
|
if (pending_signals.get(i))
|
||||||
{
|
{
|
||||||
int signo = i + 1;
|
int signo = i + 1;
|
||||||
if (signo == SIGKILL || signo == SIGSTOP) return false;
|
if (signo == SIGKILL || signo == SIGSTOP) return false;
|
||||||
if (signal_mask & (1 << i)) continue;
|
if (signal_mask.get(i)) continue;
|
||||||
auto handler = signal_handlers[i];
|
auto handler = signal_handlers[i];
|
||||||
if (handler.sa_handler == SIG_IGN) return true;
|
if (handler.sa_handler == SIG_IGN) return true;
|
||||||
if (handler.sa_handler == SIG_DFL && default_actions[i] == DefaultSignalAction::Ignore) return true;
|
if (handler.sa_handler == SIG_DFL && default_actions[i] == DefaultSignalAction::Ignore) return true;
|
||||||
@ -206,8 +206,10 @@ bool Thread::will_ignore_pending_signal()
|
|||||||
|
|
||||||
void Thread::send_signal(int signo)
|
void Thread::send_signal(int signo)
|
||||||
{
|
{
|
||||||
|
if (is_kernel) return;
|
||||||
|
|
||||||
check(signo > 0 && signo <= NSIG);
|
check(signo > 0 && signo <= NSIG);
|
||||||
pending_signals |= 1 << (signo - 1);
|
pending_signals.set(signo - 1, true);
|
||||||
|
|
||||||
if (state == ThreadState::Waiting || state == ThreadState::Sleeping)
|
if (state == ThreadState::Waiting || state == ThreadState::Sleeping)
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "memory/AddressSpace.h"
|
#include "memory/AddressSpace.h"
|
||||||
#include <bits/signal.h>
|
#include <bits/signal.h>
|
||||||
|
#include <luna/Bitset.h>
|
||||||
#include <luna/LinkedList.h>
|
#include <luna/LinkedList.h>
|
||||||
#include <luna/OwnedPtr.h>
|
#include <luna/OwnedPtr.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
@ -105,8 +106,8 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
SharedPtr<VFS::Inode>* parent_inode = nullptr);
|
||||||
|
|
||||||
struct sigaction signal_handlers[NSIG];
|
struct sigaction signal_handlers[NSIG];
|
||||||
sigset_t signal_mask { 0 };
|
Bitset<sigset_t> signal_mask { 0 };
|
||||||
sigset_t pending_signals { 0 };
|
Bitset<sigset_t> pending_signals { 0 };
|
||||||
bool interrupted { false };
|
bool interrupted { false };
|
||||||
|
|
||||||
bool unrestricted_task { false };
|
bool unrestricted_task { false };
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
#ifndef _SYS_SYSCALL_H
|
#ifndef _SYS_SYSCALL_H
|
||||||
#define _SYS_SYSCALL_H
|
#define _SYS_SYSCALL_H
|
||||||
|
|
||||||
/* This header just forwards to luna/Syscall.h, which is used in the kernel as well. */
|
/* This header just forwards to the kernel API header Syscall.h. */
|
||||||
#include <luna/Syscall.h>
|
#include <moon/Syscall.h>
|
||||||
#undef enumerate_syscalls
|
#undef enumerate_syscalls
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -117,9 +117,7 @@ extern "C"
|
|||||||
|
|
||||||
raise(SIGABRT);
|
raise(SIGABRT);
|
||||||
|
|
||||||
// There is no way we could end up here, unless there is some sort of race condition or the kernel decided to
|
__builtin_trap();
|
||||||
// change the default action for SIGABRT because it's a Tuesday.
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__noreturn void _Exit(int status)
|
__noreturn void _Exit(int status)
|
||||||
|
@ -1,37 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @file Alignment.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Functions to align addresses and sizes to specified boundaries.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/TypeTraits.h>
|
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
template <usize alignment, typename T> constexpr inline T is_aligned(T value)
|
/**
|
||||||
|
* @brief Check whether a value is aligned to a specific alignment.
|
||||||
|
*
|
||||||
|
* @tparam alignment The alignment to use. It must be provided as a template parameter to help the compiler with
|
||||||
|
* optimizations. For best results, use a power of two.
|
||||||
|
* @tparam T The type of the value.
|
||||||
|
* @param value The value to check.
|
||||||
|
* @return constexpr bool Whether the value is aligned.
|
||||||
|
*/
|
||||||
|
template <usize alignment, typename T> constexpr inline bool is_aligned(T value)
|
||||||
{
|
{
|
||||||
return (value % alignment == 0);
|
return (value % alignment == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(is_aligned<512>(1024u));
|
/**
|
||||||
static_assert(!is_aligned<32>(235u));
|
* @brief Find the closest aligned value that is smaller than the value provided.
|
||||||
static_assert(is_aligned<4096>(40960u));
|
*
|
||||||
|
* @tparam alignment The alignment to use. It must be provided as a template parameter to help the compiler with
|
||||||
|
* optimizations. For best results, use a power of two.
|
||||||
|
* @tparam T The type of the value.
|
||||||
|
* @param value The value to use.
|
||||||
|
* @return constexpr T The aligned value.
|
||||||
|
*/
|
||||||
template <usize alignment, typename T> constexpr inline T align_down(T value)
|
template <usize alignment, typename T> constexpr inline T align_down(T value)
|
||||||
{
|
{
|
||||||
return value - value % alignment;
|
return value - value % alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(align_down<512>(598ul) == 512ul);
|
/**
|
||||||
static_assert(align_down<64>(194ul) == 192ul);
|
* @brief Find the closest aligned value that is bigger than the value provided.
|
||||||
static_assert(align_down<32>(64ul) == 64ul);
|
*
|
||||||
|
* @tparam alignment The alignment to use. It must be provided as a template parameter to help the compiler with
|
||||||
|
* optimizations. For best results, use a power of two.
|
||||||
|
* @tparam T The type of the value.
|
||||||
|
* @param value The value to use.
|
||||||
|
* @return constexpr T The aligned value.
|
||||||
|
*/
|
||||||
template <usize alignment, typename T> constexpr inline T align_up(T value)
|
template <usize alignment, typename T> constexpr inline T align_up(T value)
|
||||||
{
|
{
|
||||||
if (is_aligned<alignment>(value)) return value;
|
if (is_aligned<alignment>(value)) return value;
|
||||||
return align_down<alignment>(value) + alignment;
|
return align_down<alignment>(value) + alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(align_up<512>(598ul) == 1024ul);
|
/**
|
||||||
static_assert(align_up<64>(194ul) == 256ul);
|
* @brief Offset a pointer by exactly <offset> bytes, no matter the type.
|
||||||
static_assert(align_up<32>(64ul) == 64ul);
|
*
|
||||||
|
* Generally only useful for pointers to void (or char), because breaking other pointers' alignments is not a good
|
||||||
// Offset a pointer by exactly <offset> bytes, no matter the type. Useful to avoid the quirks that come from C pointer
|
* idea...
|
||||||
// arithmetic.
|
*
|
||||||
|
* @tparam T The underlying type of the pointer.
|
||||||
|
* @tparam Offset The type of the offset.
|
||||||
|
* @param ptr The pointer to offset.
|
||||||
|
* @param offset The offset to use (depending on the type, this could be negative).
|
||||||
|
* @return constexpr T* The new pointer.
|
||||||
|
*/
|
||||||
template <typename T, typename Offset> constexpr inline T* offset_ptr(T* ptr, Offset offset)
|
template <typename T, typename Offset> constexpr inline T* offset_ptr(T* ptr, Offset offset)
|
||||||
{
|
{
|
||||||
return (T*)((u8*)ptr + offset);
|
return (T*)((u8*)ptr + offset);
|
||||||
|
@ -1,16 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @file Alloc.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Fallible version of new.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Heap.h>
|
#include <luna/Heap.h>
|
||||||
#include <luna/PlacementNew.h>
|
#include <luna/PlacementNew.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocate a value on the heap and initialize it.
|
||||||
|
*
|
||||||
|
* When this value is no longer used, you must call delete to destroy it.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the value.
|
||||||
|
* @tparam Args The types of arguments to pass to the value's constructor.
|
||||||
|
* @param args The arguments to pass to the value's constructor.
|
||||||
|
* @return Result<T*> An error, or a pointer to the new value.
|
||||||
|
*/
|
||||||
template <typename T, class... Args> [[nodiscard]] Result<T*> make(Args... args)
|
template <typename T, class... Args> [[nodiscard]] Result<T*> make(Args... args)
|
||||||
{
|
{
|
||||||
T* const result = (T*)TRY(malloc_impl(sizeof(T)));
|
T* const result = (T*)TRY(malloc_impl(sizeof(T), false, false));
|
||||||
new (result) T(args...);
|
new (result) T(args...);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void destroy(T* item)
|
|
||||||
{
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* @file Atomic.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Atomic value operations.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An enum representing the different C++11 memory orders.
|
||||||
|
*/
|
||||||
enum class MemoryOrder
|
enum class MemoryOrder
|
||||||
{
|
{
|
||||||
Relaxed = __ATOMIC_RELAXED,
|
Relaxed = __ATOMIC_RELAXED,
|
||||||
@ -10,48 +22,114 @@ enum class MemoryOrder
|
|||||||
SeqCst = __ATOMIC_SEQ_CST,
|
SeqCst = __ATOMIC_SEQ_CST,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An atomic wrapper around a simple integer value.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the value.
|
||||||
|
*/
|
||||||
template <typename T> class Atomic
|
template <typename T> class Atomic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Atomic object.
|
||||||
|
*/
|
||||||
Atomic() : m_value()
|
Atomic() : m_value()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Atomic object.
|
||||||
|
*
|
||||||
|
* @param value The value to use.
|
||||||
|
*/
|
||||||
Atomic(T value) : m_value(value)
|
Atomic(T value) : m_value(value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store a new value inside this object.
|
||||||
|
*
|
||||||
|
* @param other The value to store.
|
||||||
|
* @return T The updated value.
|
||||||
|
*/
|
||||||
T operator=(T other)
|
T operator=(T other)
|
||||||
{
|
{
|
||||||
store(other);
|
store(other);
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform an atomic load of the stored value.
|
||||||
|
*
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The loaded value.
|
||||||
|
*/
|
||||||
T load(MemoryOrder order = MemoryOrder::SeqCst) const
|
T load(MemoryOrder order = MemoryOrder::SeqCst) const
|
||||||
{
|
{
|
||||||
return __atomic_load_n(&m_value, (int)order);
|
return __atomic_load_n(&m_value, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the stored value atomically.
|
||||||
|
*
|
||||||
|
* @return T The stored value.
|
||||||
|
*/
|
||||||
operator T() const
|
operator T() const
|
||||||
{
|
{
|
||||||
return load();
|
return load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store a new value atomically.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
*/
|
||||||
void store(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
void store(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_store_n(&m_value, value, (int)order);
|
return __atomic_store_n(&m_value, value, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store a new value atomically, and return the old value.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The old value.
|
||||||
|
*/
|
||||||
T exchange(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
T exchange(T value, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_exchange_n(&m_value, value, (int)order);
|
return __atomic_exchange_n(&m_value, value, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_strong returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param success The memory order to use in case of success.
|
||||||
|
* @param failure The memory order to use in case of failure.
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_strong(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
bool compare_exchange_strong(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
||||||
{
|
{
|
||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)success, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)success, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_strong returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_strong(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
bool compare_exchange_strong(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
||||||
@ -60,11 +138,40 @@ template <typename T> class Atomic
|
|||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)order, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, false, (int)order, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* This variant may be more performant than compare_exchange_strong, but is allowed to sometimes spuriously fail
|
||||||
|
* (fail even if the two values did match).
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_weak returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param success The memory order to use in case of success.
|
||||||
|
* @param failure The memory order to use in case of failure.
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match (or there was a spurious failure) and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_weak(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
bool compare_exchange_weak(T& expected, T desired, MemoryOrder success, MemoryOrder failure)
|
||||||
{
|
{
|
||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)success, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)success, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare the current value against an expected value and exchange it with a desired value only if they
|
||||||
|
* match, all in a single atomic step.
|
||||||
|
*
|
||||||
|
* This variant may be more performant than compare_exchange_strong, but is allowed to sometimes spuriously fail
|
||||||
|
* (fail even if the two values did match).
|
||||||
|
*
|
||||||
|
* @param expected The expected value. After compare_exchange_weak returns, it will be set to the value that used
|
||||||
|
* to be held.
|
||||||
|
* @param desired The value to store if the two values match.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return true The values match and the current value was updated to match desired.
|
||||||
|
* @return false The values did not match (or there was a spurious failure) and the current value stays the same.
|
||||||
|
*/
|
||||||
bool compare_exchange_weak(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
bool compare_exchange_weak(T& expected, T desired, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
MemoryOrder failure = (order == MemoryOrder::AcqRel) ? MemoryOrder::Acquire
|
||||||
@ -73,41 +180,87 @@ template <typename T> class Atomic
|
|||||||
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)order, (int)failure);
|
return __atomic_compare_exchange_n(&m_value, &expected, desired, true, (int)order, (int)failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically add a value to the stored value and return the previous value.
|
||||||
|
*
|
||||||
|
* @param other The value to add.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The original stored value.
|
||||||
|
*/
|
||||||
T fetch_add(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
T fetch_add(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_fetch_add(&m_value, other, (int)order);
|
return __atomic_fetch_add(&m_value, other, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Atomically subtract a value from the stored value and return the previous value.
|
||||||
|
*
|
||||||
|
* @param other The value to add.
|
||||||
|
* @param order The memory order to use (keep it to the default if you don't know what you're doing).
|
||||||
|
* @return T The original stored value.
|
||||||
|
*/
|
||||||
T fetch_sub(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
T fetch_sub(T other, MemoryOrder order = MemoryOrder::SeqCst)
|
||||||
{
|
{
|
||||||
return __atomic_fetch_sub(&m_value, other, (int)order);
|
return __atomic_fetch_sub(&m_value, other, (int)order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Increment the stored value and return it.
|
||||||
|
*
|
||||||
|
* @return T The new value.
|
||||||
|
*/
|
||||||
T operator++()
|
T operator++()
|
||||||
{
|
{
|
||||||
return fetch_add(1) + 1;
|
return fetch_add(1) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetch the stored value, increment it and return the original value.
|
||||||
|
*
|
||||||
|
* @return T The original value.
|
||||||
|
*/
|
||||||
T operator++(int)
|
T operator++(int)
|
||||||
{
|
{
|
||||||
return fetch_add(1);
|
return fetch_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decrement the stored value and return it.
|
||||||
|
*
|
||||||
|
* @return T The new value.
|
||||||
|
*/
|
||||||
T operator--()
|
T operator--()
|
||||||
{
|
{
|
||||||
return fetch_sub(1) - 1;
|
return fetch_sub(1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetch the stored value, decrement it and return the original value.
|
||||||
|
*
|
||||||
|
* @return T The original value.
|
||||||
|
*/
|
||||||
T operator--(int)
|
T operator--(int)
|
||||||
{
|
{
|
||||||
return fetch_sub(1);
|
return fetch_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a value to the stored value and return the result.
|
||||||
|
*
|
||||||
|
* @param other The value to add.
|
||||||
|
* @return T The resulting value.
|
||||||
|
*/
|
||||||
T operator+=(const T& other)
|
T operator+=(const T& other)
|
||||||
{
|
{
|
||||||
return fetch_add(other) + other;
|
return fetch_add(other) + other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subtract a value from the stored value and return the result.
|
||||||
|
*
|
||||||
|
* @param other The value to subtract.
|
||||||
|
* @return T The resulting value.
|
||||||
|
*/
|
||||||
T operator-=(const T& other)
|
T operator-=(const T& other)
|
||||||
{
|
{
|
||||||
return fetch_sub(other) - other;
|
return fetch_sub(other) - other;
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file Attributes.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Macro wrappers around GCC attributes.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define _weak __attribute__((weak))
|
#define _weak __attribute__((weak))
|
||||||
|
@ -1,8 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @file Badge.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A simple way to control who can call functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A struct that can only be constructed by one type, used to control access to functions/methods.
|
||||||
|
*
|
||||||
|
* Example: There is a private method FooClass::foo() that you want to be callable by BarClass without making it a
|
||||||
|
* friend.
|
||||||
|
*
|
||||||
|
* So, make FooClass::foo() public and make it take a Badge<BarClass> which will only be constructible by BarClass, thus
|
||||||
|
* limiting the method even though it is public.
|
||||||
|
*
|
||||||
|
* @tparam T The type that can construct this badge.
|
||||||
|
*/
|
||||||
template <class T> struct Badge
|
template <class T> struct Badge
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Badge.
|
||||||
|
*
|
||||||
|
* This can only be done by the type T.
|
||||||
|
*/
|
||||||
constexpr Badge() = default;
|
constexpr Badge() = default;
|
||||||
|
|
||||||
Badge(const Badge<T>&) = delete;
|
Badge(const Badge<T>&) = delete;
|
||||||
|
@ -1,13 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @file Base64.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Base64 encoding and decoding.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
|
|
||||||
namespace Base64
|
namespace Base64
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @brief Encode a string into Base64.
|
||||||
|
*
|
||||||
|
* @param data The string to encode.
|
||||||
|
* @return Result<String> An error, or the resulting Base64 string.
|
||||||
|
*/
|
||||||
Result<String> encode(StringView data);
|
Result<String> encode(StringView data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encode a slice of data into Base64.
|
||||||
|
*
|
||||||
|
* @param data The data to encode.
|
||||||
|
* @return Result<String> An error, or the resulting Base64 string.
|
||||||
|
*/
|
||||||
Result<String> encode(Slice<const u8> data);
|
Result<String> encode(Slice<const u8> data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encode a buffer into Base64.
|
||||||
|
*
|
||||||
|
* @param data The buffer to encode.
|
||||||
|
* @return Result<String> An error, or the resulting Base64 string.
|
||||||
|
*/
|
||||||
Result<String> encode(const Buffer& data);
|
Result<String> encode(const Buffer& data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode a Base64 string.
|
||||||
|
*
|
||||||
|
* @param data The string to decode.
|
||||||
|
* @param allow_garbage_chars Whether to skip non-Base64 characters instead of returning an error.
|
||||||
|
* @return Result<Buffer> An error, or the raw decoded data.
|
||||||
|
*/
|
||||||
Result<Buffer> decode(StringView data, bool allow_garbage_chars = false);
|
Result<Buffer> decode(StringView data, bool allow_garbage_chars = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode a Base64 string, returning a string instead of raw data.
|
||||||
|
*
|
||||||
|
* Note that decoded Base64 may be anything; invalid UTF-8, null characters in the middle of data, etc...
|
||||||
|
* For this reason it is not recommended to use this function unless you're sure that what is decoded will be a
|
||||||
|
* valid string (so, don't use this with untrusted user input).
|
||||||
|
*
|
||||||
|
* @param data The string to decode.
|
||||||
|
* @param allow_garbage_chars Whether to skip non-Base64 characters instead of returning an error.
|
||||||
|
* @return Result<String> An error, or the decoded string.
|
||||||
|
*/
|
||||||
Result<String> decode_string(StringView data, bool allow_garbage_chars = false);
|
Result<String> decode_string(StringView data, bool allow_garbage_chars = false);
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,195 @@
|
|||||||
|
/**
|
||||||
|
* @file Bitmap.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief An interface to an array of bits.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class providing an interface to an array of bits.
|
||||||
|
*/
|
||||||
class Bitmap
|
class Bitmap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new empty Bitmap object.
|
||||||
|
*
|
||||||
|
* This object is invalid until initialize() is called on it.
|
||||||
|
*/
|
||||||
Bitmap();
|
Bitmap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Bitmap object.
|
||||||
|
*
|
||||||
|
* @param location The memory to use. This memory is not managed by Bitmap; if it is dynamically allocated, you must
|
||||||
|
* free it after it is no longer used by the bitmap.
|
||||||
|
* @param size_in_bytes The size (in bytes, the number of bits available will be 8 times more) of the memory used.
|
||||||
|
*/
|
||||||
Bitmap(void* location, usize size_in_bytes);
|
Bitmap(void* location, usize size_in_bytes);
|
||||||
|
|
||||||
// Naive initialization functions.
|
/**
|
||||||
|
* @brief Initialize a Bitmap object.
|
||||||
|
*
|
||||||
|
* If the object was previously initialized, you should call move() instead.
|
||||||
|
*
|
||||||
|
* @param location The memory to use. This memory is not managed by Bitmap; if it is dynamically allocated, you must
|
||||||
|
* free it after it is no longer used by the bitmap.
|
||||||
|
* @param size_in_bytes The size (in bytes, the number of bits available will be 8 times more) of the memory used.
|
||||||
|
*/
|
||||||
void initialize(void* location, usize size_in_bytes);
|
void initialize(void* location, usize size_in_bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize a previously initialized Bitmap object and return the memory location it was previously using.
|
||||||
|
*
|
||||||
|
* If the object was not previously initialized, you should call initialize() instead.
|
||||||
|
*
|
||||||
|
* @param new_location The memory to use. This memory is not managed by Bitmap; if it is dynamically allocated, you
|
||||||
|
* must free it after it is no longer used by the bitmap.
|
||||||
|
* @param new_location_size_in_bytes The size (in bytes, the number of bits available will be 8 times more) of the
|
||||||
|
* memory used.
|
||||||
|
* @return void* The old memory location previously used by the bitmap.
|
||||||
|
*/
|
||||||
void* move(void* new_location, usize new_location_size_in_bytes);
|
void* move(void* new_location, usize new_location_size_in_bytes);
|
||||||
|
|
||||||
// Dynamic memory initialization functions.
|
/**
|
||||||
Result<void> allocate(usize size_in_bytes);
|
* @brief Change the value of the bit at a specific index.
|
||||||
Result<void*> resize(usize new_size_in_bytes);
|
*
|
||||||
Result<void> deallocate();
|
* @param index The index of the bit to change.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
void set(usize index, bool value);
|
void set(usize index, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read the value of the bit at a specific index.
|
||||||
|
*
|
||||||
|
* @param index The index of the bit to read.
|
||||||
|
* @return bool The value of the specified bit.
|
||||||
|
*/
|
||||||
bool get(usize index) const;
|
bool get(usize index) const;
|
||||||
|
|
||||||
// size() returns size in bits! If you want the size in bytes, call size_in_bytes().
|
/**
|
||||||
|
* @brief Return the size in bits of the bitmap.
|
||||||
|
*
|
||||||
|
* @return usize The size in bits.
|
||||||
|
*/
|
||||||
usize size() const
|
usize size() const
|
||||||
{
|
{
|
||||||
return m_size_in_bytes * 8;
|
return m_size_in_bytes * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the size in bytes of the bitmap.
|
||||||
|
*
|
||||||
|
* @return usize The size in bytes.
|
||||||
|
*/
|
||||||
usize size_in_bytes() const
|
usize size_in_bytes() const
|
||||||
{
|
{
|
||||||
return m_size_in_bytes;
|
return m_size_in_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the memory location used by the bitmap.
|
||||||
|
*
|
||||||
|
* @return void* The memory location used. If it is NULL, the bitmap was not initialized.
|
||||||
|
*/
|
||||||
void* location() const
|
void* location() const
|
||||||
{
|
{
|
||||||
return (void*)m_location;
|
return (void*)m_location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the bitmap has been initialized.
|
||||||
|
*
|
||||||
|
* @return true The bitmap was initialized by a constructor or initialize().
|
||||||
|
* @return false The bitmap was not initialized and you should not call other methods on it until initialize() is
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
bool initialized() const
|
bool initialized() const
|
||||||
{
|
{
|
||||||
return m_location;
|
return m_location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the first bit with a specific value.
|
||||||
|
*
|
||||||
|
* @param value The value to look for.
|
||||||
|
* @param begin If different from 0, the bit index to start looking at.
|
||||||
|
* @return Option<usize> If a matching bit was found, the index of said bit, otherwise an empty Option.
|
||||||
|
*/
|
||||||
Option<usize> find(bool value, usize begin = 0) const;
|
Option<usize> find(bool value, usize begin = 0) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the first bit with a specific value and toggle it.
|
||||||
|
*
|
||||||
|
* @param value The value to look for.
|
||||||
|
* @param begin If different from 0, the bit index to start looking at.
|
||||||
|
* @return Option<usize> If a matching bit was found, the index of said bit, otherwise an empty Option.
|
||||||
|
*/
|
||||||
Option<usize> find_and_toggle(bool value, usize begin = 0);
|
Option<usize> find_and_toggle(bool value, usize begin = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the first region of bits all with a specific value.
|
||||||
|
*
|
||||||
|
* @param value The value to look for.
|
||||||
|
* @param count The number of consecutive bits that should all match the value.
|
||||||
|
* @param begin If different from 0, the bit index to start looking at.
|
||||||
|
* @return Option<usize> If a matching region was found, the index of the first bit in said region, otherwise an
|
||||||
|
* empty Option.
|
||||||
|
*/
|
||||||
Option<usize> find_region(bool value, usize count, usize begin = 0) const;
|
Option<usize> find_region(bool value, usize count, usize begin = 0) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the first region of bits all with a specific value, and toggle them all.
|
||||||
|
*
|
||||||
|
* @param value The value to look for.
|
||||||
|
* @param count The number of consecutive bits that should all match the value.
|
||||||
|
* @param begin If different from 0, the bit index to start looking at.
|
||||||
|
* @return Option<usize> If a matching region was found, the index of the first bit in said region, otherwise an
|
||||||
|
* empty Option.
|
||||||
|
*/
|
||||||
Option<usize> find_and_toggle_region(bool value, usize count, usize begin = 0);
|
Option<usize> find_and_toggle_region(bool value, usize count, usize begin = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether a region of bits all match a value.
|
||||||
|
*
|
||||||
|
* @param start The bit index of the first bit in the region.
|
||||||
|
* @param bits The number of bits in the region.
|
||||||
|
* @param value The value to check against.
|
||||||
|
* @return bool Whether the region matches.
|
||||||
|
*/
|
||||||
bool match_region(usize start, usize bits, bool value);
|
bool match_region(usize start, usize bits, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether a region of bits all match a value, returning an error if the region is outside of the
|
||||||
|
* Bitmap's bounds (instead of crashing).
|
||||||
|
*
|
||||||
|
* @param start The bit index of the first bit in the region.
|
||||||
|
* @param bits The number of bits in the region.
|
||||||
|
* @param value The value to check against.
|
||||||
|
* @return Result<bool> An error if the region is out of bounds, or whether the region matches.
|
||||||
|
*/
|
||||||
Result<bool> try_match_region(usize start, usize bits, bool value);
|
Result<bool> try_match_region(usize start, usize bits, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the entire bitmap to a value.
|
||||||
|
*
|
||||||
|
* @param value The value to use.
|
||||||
|
*/
|
||||||
void clear(bool value);
|
void clear(bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set a region of bits to a value.
|
||||||
|
*
|
||||||
|
* @param start The bit index of the first bit in the region.
|
||||||
|
* @param bits The number of bits in the region.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
void clear_region(usize start, usize bits, bool value);
|
void clear_region(usize start, usize bits, bool value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
119
libluna/include/luna/Bitset.h
Normal file
119
libluna/include/luna/Bitset.h
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* @file Bitset.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A wrapper class to modify/inspect specific bits of an integer type.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Check.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A wrapper class to modify/inspect specific bits of an integer type, to avoid manual error-prone bit shifting.
|
||||||
|
*
|
||||||
|
* @tparam T The integer type to use.
|
||||||
|
*/
|
||||||
|
template <typename T> class Bitset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new empty Bitset object.
|
||||||
|
*/
|
||||||
|
Bitset() : m_value(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Bitset object with a value.
|
||||||
|
*
|
||||||
|
* @param value The value to use.
|
||||||
|
*/
|
||||||
|
Bitset(T value) : m_value(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a copy of another Bitset object.
|
||||||
|
*
|
||||||
|
* @param other The original object.
|
||||||
|
*/
|
||||||
|
Bitset(const Bitset<T>& other) : m_value(other.m_value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the internal value of this bitset.
|
||||||
|
*
|
||||||
|
* @param value The new value.
|
||||||
|
* @return Bitset<T>& A reference to this itset.
|
||||||
|
*/
|
||||||
|
Bitset<T>& operator=(T value)
|
||||||
|
{
|
||||||
|
m_value = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set this bitset to the same value as another bitset.
|
||||||
|
*
|
||||||
|
* @param other The pther bitset.
|
||||||
|
* @return Bitset<T>& A reference to this bitset.
|
||||||
|
*/
|
||||||
|
Bitset<T>& operator=(const Bitset<T>& other)
|
||||||
|
{
|
||||||
|
if (&other == this) return *this;
|
||||||
|
m_value = other.m_value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the value contained in this bitset.
|
||||||
|
*
|
||||||
|
* @return T The contained value.
|
||||||
|
*/
|
||||||
|
T value() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modify one of the individual bits in this bitset.
|
||||||
|
*
|
||||||
|
* @param index The index of the bit (this will set/unset 1 << index). It must be less than the bit width of the
|
||||||
|
* underlying type.
|
||||||
|
* @param b The value to set the bit to.
|
||||||
|
*/
|
||||||
|
void set(int index, bool b)
|
||||||
|
{
|
||||||
|
check((usize)index < (sizeof(T) * 8));
|
||||||
|
if (b) m_value |= (((T)1) << index);
|
||||||
|
else
|
||||||
|
m_value &= ~(((T)1) << index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the value of one of the individual bits in this bitset.
|
||||||
|
*
|
||||||
|
* @param index The index of the bit (this will read 1 << index). It must be less than the bit width of the
|
||||||
|
* underlying type.
|
||||||
|
* @return bool The value of the requested bit.
|
||||||
|
*/
|
||||||
|
bool get(int index)
|
||||||
|
{
|
||||||
|
check((usize)index < (sizeof(T) * 8));
|
||||||
|
return m_value & (((T)1) << index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear all the bits in this bitset.
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_value;
|
||||||
|
};
|
@ -1,7 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @file Buffer.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A managed wrapper around a resizable buffer of arbitrary memory.
|
||||||
|
*
|
||||||
|
* @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>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A managed wrapper around a resizable buffer of arbitrary memory.
|
||||||
|
*/
|
||||||
class Buffer
|
class Buffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -11,45 +23,138 @@ class Buffer
|
|||||||
Buffer(Buffer&& other);
|
Buffer(Buffer&& other);
|
||||||
Buffer(const Buffer& other) = delete; // For now.
|
Buffer(const Buffer& other) = delete; // For now.
|
||||||
|
|
||||||
|
Buffer& operator=(Buffer&&);
|
||||||
|
Buffer& operator=(const Buffer&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a Buffer object, allocating a specific amount of memory for it.
|
||||||
|
*
|
||||||
|
* @param size The number of bytes to allocate.
|
||||||
|
* @return Result<Buffer> An error, or the newly created buffer.
|
||||||
|
*/
|
||||||
static Result<Buffer> create_sized(usize size);
|
static Result<Buffer> create_sized(usize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resize the buffer.
|
||||||
|
*
|
||||||
|
* The existing data is kept, unless the new size is smaller than the old size, in which case only the first
|
||||||
|
* new_size bytes are kept.
|
||||||
|
*
|
||||||
|
* @param new_size The new size of the buffer, in bytes.
|
||||||
|
* @return Result<void> Whether the operation succeeded.
|
||||||
|
*/
|
||||||
Result<void> try_resize(usize new_size);
|
Result<void> try_resize(usize new_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Expand the buffer and return a pointer to the beginning of this new expanded area.
|
||||||
|
*
|
||||||
|
* @param size The amount of bytes to expand the buffer by.
|
||||||
|
* @return Result<u8*> An error, or a pointer to the new area of the buffer.
|
||||||
|
*/
|
||||||
Result<u8*> slice_at_end(usize size);
|
Result<u8*> slice_at_end(usize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to an area of the buffer, expanding it if necessary.
|
||||||
|
*
|
||||||
|
* @param offset The offset inside the buffer to start at.
|
||||||
|
* @param size The amount of bytes to reserve.
|
||||||
|
* @return Result<u8*> An error, or a pointer to the requested area.
|
||||||
|
*/
|
||||||
Result<u8*> slice(usize offset, usize size);
|
Result<u8*> slice(usize offset, usize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add data to the end of the buffer.
|
||||||
|
*
|
||||||
|
* @param data A pointer to the data to add.
|
||||||
|
* @param size The amount of bytes to add.
|
||||||
|
* @return Result<void> Whether the operation succeeded.
|
||||||
|
*/
|
||||||
Result<void> append_data(const u8* data, usize size);
|
Result<void> append_data(const u8* data, usize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove data from the beginning of the buffer and return it.
|
||||||
|
*
|
||||||
|
* @param data A pointer to store the removed data in.
|
||||||
|
* @param size The amount of bytes to remove.
|
||||||
|
* @return usize The amount of bytes actually removed (may be less if the buffer was smaller than the requested
|
||||||
|
* size).
|
||||||
|
*/
|
||||||
usize dequeue_data(u8* data, usize size);
|
usize dequeue_data(u8* data, usize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the data contained in the buffer.
|
||||||
|
*
|
||||||
|
* This pointer may be invalid after the buffer is resized.
|
||||||
|
*
|
||||||
|
* @return u8* The contained data.
|
||||||
|
*/
|
||||||
u8* data()
|
u8* data()
|
||||||
{
|
{
|
||||||
return m_data;
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the data contained in the buffer.
|
||||||
|
*
|
||||||
|
* This pointer may be invalid after the buffer is resized.
|
||||||
|
*
|
||||||
|
* @return const u8* The contained data.
|
||||||
|
*/
|
||||||
const u8* data() const
|
const u8* data() const
|
||||||
{
|
{
|
||||||
return m_data;
|
return m_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the data contained in the buffer, moving the data out of the buffer.
|
||||||
|
*
|
||||||
|
* This call will empty the buffer, making it the caller's responsibility to manage the data, including freeing it
|
||||||
|
* when no longer used.
|
||||||
|
*
|
||||||
|
* @return u8* The released data.
|
||||||
|
*/
|
||||||
u8* release_data();
|
u8* release_data();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the end of the data contained in the buffer.
|
||||||
|
*
|
||||||
|
* This pointer points past the data; as such, dereferencing it directly is undefined behavior.
|
||||||
|
*
|
||||||
|
* @return u8* The end of the data.
|
||||||
|
*/
|
||||||
u8* end()
|
u8* end()
|
||||||
{
|
{
|
||||||
return m_data + m_size;
|
return m_data + m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the end of the data contained in the buffer.
|
||||||
|
*
|
||||||
|
* This pointer points past the data; as such, dereferencing it directly is undefined behavior.
|
||||||
|
*
|
||||||
|
* @return const u8* The end of the data.
|
||||||
|
*/
|
||||||
const u8* end() const
|
const u8* end() const
|
||||||
{
|
{
|
||||||
return m_data + m_size;
|
return m_data + m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the size of the buffer in bytes.
|
||||||
|
*
|
||||||
|
* @return usize The buffer's size.
|
||||||
|
*/
|
||||||
usize size() const
|
usize size() const
|
||||||
{
|
{
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check whether the buffer is empty.
|
||||||
|
*
|
||||||
|
* @return true The buffer is empty.
|
||||||
|
* @return false The buffer is not empty.
|
||||||
|
*/
|
||||||
bool is_empty() const
|
bool is_empty() const
|
||||||
{
|
{
|
||||||
return m_size == 0;
|
return m_size == 0;
|
||||||
|
@ -1,7 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @file CPath.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Basic operations on paths using C-style strings.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
char* basename(char*);
|
/**
|
||||||
char* dirname(char*);
|
* @brief Get the base name of a path.
|
||||||
|
*
|
||||||
|
* @param path The path to use. This will be modified.
|
||||||
|
* @return char* A pointer to the base name (usually points inside path).
|
||||||
|
*/
|
||||||
|
char* basename(char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the parent directory of a path.
|
||||||
|
*
|
||||||
|
* @param path The path to use. This will be modified.
|
||||||
|
* @return char* A pointer to the directory (usually points inside path).
|
||||||
|
*/
|
||||||
|
char* dirname(char* path);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @file CRC32.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief CRC32 checksum calculation.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A class to calculate a CRC32 checksum.
|
||||||
|
*/
|
||||||
class CRC32
|
class CRC32
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Add data to the checksum.
|
||||||
|
*
|
||||||
|
* @param data The data to add.
|
||||||
|
* @param size The amount of bytes to add.
|
||||||
|
*/
|
||||||
void append(const u8* data, usize size);
|
void append(const u8* data, usize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate the final checksum.
|
||||||
|
*
|
||||||
|
* @return u32 The calculated checksum.
|
||||||
|
*/
|
||||||
u32 digest();
|
u32 digest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file CString.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Implementations of C-string and memory manipulation functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @file CType.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Implementations of C-like character classification functions, prefixed with an underscore to avoid conflicting
|
||||||
|
* with libc.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
inline constexpr int _isascii(int c)
|
inline constexpr int _isascii(int c)
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @file Check.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Always-enabled assertions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/SourceLocation.h>
|
#include <luna/SourceLocation.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called when a libluna assertion fails. Some targets already have implementations of this function, but
|
||||||
|
* otherwise it must be implemented by the user.
|
||||||
|
*
|
||||||
|
* In the kernel, the implementation is in src/Log.cpp.
|
||||||
|
* For POSIX systems, libluna provides an implementation in src/ImplPOSIX.cpp.
|
||||||
|
*
|
||||||
|
* @param location The source location at which the assertion failure occurred.
|
||||||
|
* @param expr The expression or custom error message to display.
|
||||||
|
* @return bool Unused, since the function never returns.
|
||||||
|
*/
|
||||||
[[noreturn]] extern bool __check_failed(SourceLocation location, const char* expr);
|
[[noreturn]] extern bool __check_failed(SourceLocation location, const char* expr);
|
||||||
|
|
||||||
#ifndef STRINGIZE_VALUE_OF
|
#ifndef STRINGIZE_VALUE_OF
|
||||||
@ -8,32 +28,63 @@
|
|||||||
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
|
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Like check(), but with a custom error message.
|
/**
|
||||||
|
* @brief Verify an expression is true, or crash at runtime with a custom error message.
|
||||||
|
*/
|
||||||
#define expect(expr, message) \
|
#define expect(expr, message) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), message); } \
|
if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), message); } \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify an expression is true, or crash at runtime with a custom error message, indicating a different source
|
||||||
|
* location (useful for reporting caller errors, see Result::release_value() for an example).
|
||||||
|
*/
|
||||||
#define expect_at(expr, location, message) \
|
#define expect_at(expr, location, message) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) [[unlikely]] { __check_failed(location, message); } \
|
if (!(expr)) [[unlikely]] { __check_failed(location, message); } \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// Fail with an error message and location.
|
/**
|
||||||
|
* @brief Crash at runtime with a custom error message.
|
||||||
|
*/
|
||||||
#define fail(message) __check_failed(SourceLocation::current(), message)
|
#define fail(message) __check_failed(SourceLocation::current(), message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Crash at runtime with a custom error message, indicating a different source location (useful for
|
||||||
|
* reporting caller errors, see Result::release_value() for an example).
|
||||||
|
*/
|
||||||
#define fail_at(location, message) __check_failed(location, message)
|
#define fail_at(location, message) __check_failed(location, message)
|
||||||
|
|
||||||
// Like assert(), but always enabled.
|
/**
|
||||||
|
* @brief Verify an expression is true, or crash at runtime.
|
||||||
|
*/
|
||||||
#define check(expr) \
|
#define check(expr) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), #expr); } \
|
if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), #expr); } \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify an expression is true, or crash at runtime, indicating a different source location (useful for
|
||||||
|
* reporting caller errors, see Result::release_value() for an example).
|
||||||
|
*/
|
||||||
#define check_at(expr, location) \
|
#define check_at(expr, location) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) [[unlikely]] { __check_failed(location, #expr); } \
|
if (!(expr)) [[unlikely]] { __check_failed(location, #expr); } \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark a code section as unreachable, which will crash if reached.
|
||||||
|
*
|
||||||
|
* Often it may be more performant to use the compiler's __builtin_unreachable() for this purpose (when the code is
|
||||||
|
* clearly unreachable but the compiler cannot figure it out on its own, see the implementation of _exit() in libc), if
|
||||||
|
* there is just no possible way for execution flow to reach this code segment, but the compiler builtin results in
|
||||||
|
* undefined behavior if reached. Therefore, if a bug may cause this unreachable code to be reached, you may prefer to
|
||||||
|
* use this macro as it has well-defined behavior (crashes at runtime).
|
||||||
|
*/
|
||||||
#define unreachable() __check_failed(SourceLocation::current(), "Reached unreachable code")
|
#define unreachable() __check_failed(SourceLocation::current(), "Reached unreachable code")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark a code section as unimplemented, which will crash if reached.
|
||||||
|
*/
|
||||||
#define todo() __check_failed(SourceLocation::current(), "Reached a TODO!")
|
#define todo() __check_failed(SourceLocation::current(), "Reached a TODO!")
|
||||||
|
@ -1,9 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @file CircularQueue.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Lock-free FIFO data structures.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Atomic.h>
|
#include <luna/Atomic.h>
|
||||||
#include <luna/Heap.h>
|
#include <luna/Heap.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An atomic lock-free circular FIFO queue.
|
||||||
|
*
|
||||||
|
* @tparam T The type of data to store in this queue.
|
||||||
|
* @tparam Size The amount of elements to make space for.
|
||||||
|
*/
|
||||||
template <typename T, usize Size> class CircularQueue
|
template <typename T, usize Size> class CircularQueue
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
@ -16,11 +31,24 @@ template <typename T, usize Size> class CircularQueue
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_empty()
|
/**
|
||||||
|
* @brief Return whether the queue is empty.
|
||||||
|
*
|
||||||
|
* @return true The queue is empty.
|
||||||
|
* @return false The queue is not empty.
|
||||||
|
*/
|
||||||
|
bool is_empty() const
|
||||||
{
|
{
|
||||||
return m_tail.load() == m_head.load();
|
return m_tail.load() == m_head.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push a value onto the queue.
|
||||||
|
*
|
||||||
|
* @param value The value to push.
|
||||||
|
* @return true The operation succeeded.
|
||||||
|
* @return false The queue was full or someone else was trying to push a value at the same time.
|
||||||
|
*/
|
||||||
bool try_push(const T& value)
|
bool try_push(const T& value)
|
||||||
{
|
{
|
||||||
usize current_tail = m_tail.load(MemoryOrder::Relaxed);
|
usize current_tail = m_tail.load(MemoryOrder::Relaxed);
|
||||||
@ -39,6 +67,13 @@ template <typename T, usize Size> class CircularQueue
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pop a value from the queue.
|
||||||
|
*
|
||||||
|
* @param value The variable to store the value into.
|
||||||
|
* @return true The operation succeeded.
|
||||||
|
* @return false The queue was empty or someone else was trying to pop a value at the same time.
|
||||||
|
*/
|
||||||
bool try_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
usize current_head = m_head.load(MemoryOrder::Relaxed);
|
usize current_head = m_head.load(MemoryOrder::Relaxed);
|
||||||
@ -63,6 +98,13 @@ template <typename T, usize Size> class CircularQueue
|
|||||||
Atomic<usize> m_tail = 0;
|
Atomic<usize> m_tail = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An atomic lock-free circular FIFO queue.
|
||||||
|
*
|
||||||
|
* In this variant the size is set at runtime.
|
||||||
|
*
|
||||||
|
* @tparam T The type of data to store in this queue.
|
||||||
|
*/
|
||||||
template <typename T> class DynamicCircularQueue
|
template <typename T> class DynamicCircularQueue
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -76,18 +118,41 @@ template <typename T> class DynamicCircularQueue
|
|||||||
if (m_data) free_impl(m_data);
|
if (m_data) free_impl(m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_empty()
|
/**
|
||||||
|
* @brief Return whether the queue is empty.
|
||||||
|
*
|
||||||
|
* @return true The queue is empty.
|
||||||
|
* @return false The queue is not empty.
|
||||||
|
*/
|
||||||
|
bool is_empty() const
|
||||||
{
|
{
|
||||||
return m_tail.load() == m_head.load();
|
return m_tail.load() == m_head.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the size of the queue and allocate memory for it.
|
||||||
|
*
|
||||||
|
* This should not be used to grow the queue, as all existing data is lost. In most cases, this function will only
|
||||||
|
* be called once to set the initial size of the queue and that's it.
|
||||||
|
*
|
||||||
|
* @param size The amount of elements to make space for.
|
||||||
|
* @return Result<void> Whether the operation succeeded.
|
||||||
|
*/
|
||||||
Result<void> set_size(usize size)
|
Result<void> set_size(usize size)
|
||||||
{
|
{
|
||||||
|
if (m_data) free_impl(m_data);
|
||||||
m_data = (T*)TRY(calloc_impl(size + 1, sizeof(T), false));
|
m_data = (T*)TRY(calloc_impl(size + 1, sizeof(T), false));
|
||||||
m_capacity = size + 1;
|
m_capacity = size + 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push a value onto the queue.
|
||||||
|
*
|
||||||
|
* @param value The value to push.
|
||||||
|
* @return true The operation succeeded.
|
||||||
|
* @return false The queue was full or someone else was trying to push a value at the same time.
|
||||||
|
*/
|
||||||
bool try_push(const T& value)
|
bool try_push(const T& value)
|
||||||
{
|
{
|
||||||
check(m_capacity);
|
check(m_capacity);
|
||||||
@ -107,6 +172,13 @@ template <typename T> class DynamicCircularQueue
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pop a value from the queue.
|
||||||
|
*
|
||||||
|
* @param value The variable to store the value into.
|
||||||
|
* @return true The operation succeeded.
|
||||||
|
* @return false The queue was empty or someone else was trying to pop a value at the same time.
|
||||||
|
*/
|
||||||
bool try_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
check(m_capacity);
|
check(m_capacity);
|
||||||
|
@ -1,7 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* @file DebugLog.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Debug logging for platform-agnostic functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Attributes.h>
|
#include <luna/Attributes.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The actual debug log implementation. This must be provided by the platform or user.
|
||||||
|
*
|
||||||
|
* In the kernel, the implementation is located in src/Log.cpp.
|
||||||
|
* For POSIX systems, libluna provides an implementation in src/ImplPOSIX.cpp.
|
||||||
|
*
|
||||||
|
* The printed message must be followed by an implicit newline.
|
||||||
|
*
|
||||||
|
* @param format The format string (in the style of printf).
|
||||||
|
* @param ap The variadic argument list.
|
||||||
|
*/
|
||||||
extern void debug_log_impl(const char* format, va_list ap);
|
extern void debug_log_impl(const char* format, va_list ap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Log a formatted message.
|
||||||
|
*
|
||||||
|
* @param format The format string (in the style of printf).
|
||||||
|
* @param ... The format arguments.
|
||||||
|
*/
|
||||||
void dbgln(const char* format, ...) _format(1, 2);
|
void dbgln(const char* format, ...) _format(1, 2);
|
||||||
|
@ -1,16 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @file Format.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief C-style string formatting.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Attributes.h>
|
#include <luna/Attributes.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
typedef Result<void> (*callback_t)(char, void*);
|
/**
|
||||||
|
* @brief The callback type for cstyle_format.
|
||||||
|
*
|
||||||
|
* The first parameter is the resulting character, and the second one is the arbitrary argument passed to cstyle_format.
|
||||||
|
* Functions of this callback type can return errors, which will be propagated by cstyle_format.
|
||||||
|
*/
|
||||||
|
typedef Result<void> (*FormatCallback)(char, void*);
|
||||||
|
|
||||||
// Format output according to a format string and a list of variadic arguments. callback is called with arg for every
|
/**
|
||||||
// character in the resulting string.
|
* @brief Create a formatted string.
|
||||||
Result<usize> cstyle_format(const char* format, callback_t callback, void* arg, va_list ap);
|
*
|
||||||
|
* Since this function uses a callback, it is very flexible; it can format output to a buffer, file, or whatever else is
|
||||||
|
* needed by the caller.
|
||||||
|
*
|
||||||
|
* This function lacks floating-point support, and wide character support, but otherwise, it has almost all C printf
|
||||||
|
* features: width, precision, alignment, size modifiers, and many more...
|
||||||
|
*
|
||||||
|
* This function is used to implemented all printf-related functions in Luna's libc and is the most tested component in
|
||||||
|
* libluna (see TestFormat.cpp, which tests it through String::format).
|
||||||
|
*
|
||||||
|
* @param format The C-style format string.
|
||||||
|
* @param callback The function to be called for every character in the resulting string.
|
||||||
|
* @param arg An arbitrary argument to pass to the callback function.
|
||||||
|
* @param ap The variadic argument list.
|
||||||
|
* @return Result<usize> An error, or the number of characters in the resulting string.
|
||||||
|
*/
|
||||||
|
Result<usize> cstyle_format(const char* format, FormatCallback callback, void* arg, va_list ap);
|
||||||
|
|
||||||
// Convenience function which outputs into a fixed-size buffer (not unlike vsnprintf)
|
/**
|
||||||
|
* @brief Format a string into a fixed-size buffer.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to store the resulting string into.
|
||||||
|
* @param max The maximum number of bytes to store in the buffer.
|
||||||
|
* @param format The format string.
|
||||||
|
* @param ap The variadic argument list.
|
||||||
|
* @return usize The number of characters in the formatted string. If it is more than or equal to max, the string was
|
||||||
|
* truncated.
|
||||||
|
*/
|
||||||
usize vstring_format(char* buf, usize max, const char* format, va_list ap);
|
usize vstring_format(char* buf, usize max, const char* format, va_list ap);
|
||||||
|
|
||||||
// Convenience function which outputs into a fixed-size buffer (not unlike snprintf)
|
/**
|
||||||
|
* @brief Format a string into a fixed-size buffer.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to store the resulting string into.
|
||||||
|
* @param max The maximum number of bytes to store in the buffer.
|
||||||
|
* @param format The format string.
|
||||||
|
* @param ... The format arguments.
|
||||||
|
* @return usize The number of characters in the formatted string. If it is more than or equal to max, the string was
|
||||||
|
* truncated.
|
||||||
|
*/
|
||||||
usize string_format(char* buf, usize max, const char* format, ...) _format(3, 4);
|
usize string_format(char* buf, usize max, const char* format, ...) _format(3, 4);
|
||||||
|
@ -1,16 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @file Hash.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Common hash functions for use in hash tables.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/CString.h>
|
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate the hash of an area of memory.
|
||||||
|
*
|
||||||
|
* @param mem A pointer to the memory to hash.
|
||||||
|
* @param size The amount of bytes to use.
|
||||||
|
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
|
||||||
|
* @return u64 The calculated hash.
|
||||||
|
*/
|
||||||
u64 hash_memory(const void* mem, usize size, u64 salt);
|
u64 hash_memory(const void* mem, usize size, u64 salt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate the hash of a value.
|
||||||
|
*
|
||||||
|
* The default implementation simply hashes the raw memory representation of the value. This may not be suitable for
|
||||||
|
* some types, so those can define a template specialization of this function to do their own hashing.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the value to hash.
|
||||||
|
* @param value The value to hash.
|
||||||
|
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
|
||||||
|
* @return u64 The calculated hash.
|
||||||
|
*/
|
||||||
template <typename T> u64 hash(const T& value, u64 salt)
|
template <typename T> u64 hash(const T& value, u64 salt)
|
||||||
{
|
{
|
||||||
return hash_memory(&value, sizeof(value), salt);
|
return hash_memory(&value, sizeof(value), salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template specialization of hash() for C-strings.
|
||||||
|
*
|
||||||
|
* This function hashes the actual string instead of the pointer value.
|
||||||
|
*
|
||||||
|
* @param value The C-string to hash.
|
||||||
|
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
|
||||||
|
* @return u64 The calculated hash.
|
||||||
|
*/
|
||||||
template <> u64 hash(const char* const& value, u64 salt);
|
template <> u64 hash(const char* const& value, u64 salt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swap two values of the same type.
|
||||||
|
*
|
||||||
|
* FIXME: This function should be moved to a more appropriate header.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the values to swap.
|
||||||
|
* @param a The first value to swap.
|
||||||
|
* @param b The second value to swap.
|
||||||
|
*/
|
||||||
template <typename T> static void swap(T* a, T* b)
|
template <typename T> static void swap(T* a, T* b)
|
||||||
{
|
{
|
||||||
char* x = (char*)a;
|
char* x = (char*)a;
|
||||||
|
@ -1,35 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* @file HashMap.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Map between keys and values with best-case constant time lookup.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/HashTable.h>
|
#include <luna/HashTable.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal representation of a key-value pair in a HashMap.
|
||||||
|
*
|
||||||
|
* @tparam K The key type.
|
||||||
|
* @tparam V The value type.
|
||||||
|
*/
|
||||||
template <typename K, typename V> struct HashPair
|
template <typename K, typename V> struct HashPair
|
||||||
{
|
{
|
||||||
K key;
|
K key;
|
||||||
Option<V> value;
|
Option<V> value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compare two HashPair objects's keys.
|
||||||
|
*
|
||||||
|
* @param other The HashPair to compare against.
|
||||||
|
* @return true The keys match.
|
||||||
|
* @return false The keys do not match.
|
||||||
|
*/
|
||||||
bool operator==(const HashPair<K, V>& other) const
|
bool operator==(const HashPair<K, V>& other) const
|
||||||
{
|
{
|
||||||
return key == other.key;
|
return key == other.key;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template specialization of hash() for HashPair objects.
|
||||||
|
*
|
||||||
|
* This function hashes only the key, ignoring the value.
|
||||||
|
*
|
||||||
|
* @tparam K The key type.
|
||||||
|
* @tparam V The value type.
|
||||||
|
* @param value The HashPair to hash.
|
||||||
|
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
|
||||||
|
* @return u64 The calculated hash.
|
||||||
|
*/
|
||||||
template <typename K, typename V> u64 hash(const HashPair<K, V>& value, u64 salt)
|
template <typename K, typename V> u64 hash(const HashPair<K, V>& value, u64 salt)
|
||||||
{
|
{
|
||||||
return hash(value.key, salt);
|
return hash(value.key, salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A map between keys and values with best-case constant time lookup.
|
||||||
|
*
|
||||||
|
* @tparam K The key type.
|
||||||
|
* @tparam V The value type.
|
||||||
|
*/
|
||||||
template <typename K, typename V> struct HashMap
|
template <typename K, typename V> struct HashMap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Try to insert a key-value pair into the HashMap.
|
||||||
|
*
|
||||||
|
* @param key The key to use.
|
||||||
|
* @param value The value to use.
|
||||||
|
* @return Result<bool> An error, true if the insertion succeeded, or false if the key already existed.
|
||||||
|
*/
|
||||||
Result<bool> try_set(const K& key, V&& value)
|
Result<bool> try_set(const K& key, V&& value)
|
||||||
{
|
{
|
||||||
return m_table.try_set(HashPair<K, V> { key, move(value) });
|
return m_table.try_set(HashPair<K, V> { key, move(value) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to insert a key-value pair into the HashMap.
|
||||||
|
*
|
||||||
|
* @param key The key to use.
|
||||||
|
* @param value The value to use.
|
||||||
|
* @return Result<bool> An error, true if the insertion succeeded, or false if the key already existed.
|
||||||
|
*/
|
||||||
Result<bool> try_set(const K& key, const V& value)
|
Result<bool> try_set(const K& key, const V& value)
|
||||||
{
|
{
|
||||||
return m_table.try_set(HashPair<K, V> { key, value });
|
return m_table.try_set(HashPair<K, V> { key, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the associated value for a key.
|
||||||
|
*
|
||||||
|
* @param key The key to use.
|
||||||
|
* @return Option<V> The associated value, or an empty option if the key did not exist.
|
||||||
|
*/
|
||||||
Option<V> try_get(const K& key)
|
Option<V> try_get(const K& key)
|
||||||
{
|
{
|
||||||
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
|
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
|
||||||
@ -37,6 +96,14 @@ template <typename K, typename V> struct HashMap
|
|||||||
return p->value;
|
return p->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a pointer to the associated value for a key inside the HashMap.
|
||||||
|
*
|
||||||
|
* This pointer should always be checked before usage.
|
||||||
|
*
|
||||||
|
* @param key The key to use.
|
||||||
|
* @return V* A pointer to the associated value, or nullptr if the key did not exist.
|
||||||
|
*/
|
||||||
V* try_get_ref(const K& key)
|
V* try_get_ref(const K& key)
|
||||||
{
|
{
|
||||||
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
|
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
|
||||||
@ -44,21 +111,44 @@ template <typename K, typename V> struct HashMap
|
|||||||
return p->value.value_ptr();
|
return p->value.value_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a key-value pair from this HashMap.
|
||||||
|
*
|
||||||
|
* @param key The key to use.
|
||||||
|
* @return true The pair was successfully removed.
|
||||||
|
* @return false The key did not exist.
|
||||||
|
*/
|
||||||
bool try_remove(const K& key)
|
bool try_remove(const K& key)
|
||||||
{
|
{
|
||||||
return m_table.try_remove(HashPair<K, V> { key, {} });
|
return m_table.try_remove(HashPair<K, V> { key, {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the number of key-value pairs that can currently fit in the HashMap.
|
||||||
|
*
|
||||||
|
* The number of actual entries will always be smaller than this (unless it is 0), since the HashMap grows before
|
||||||
|
* the number of entries reaches the capacity.
|
||||||
|
*
|
||||||
|
* @return usize The current capacity.
|
||||||
|
*/
|
||||||
usize capacity() const
|
usize capacity() const
|
||||||
{
|
{
|
||||||
return m_table.capacity();
|
return m_table.capacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the number of key-value pairs currently contained in this HashMap.
|
||||||
|
*
|
||||||
|
* @return usize The number of pairs contained.
|
||||||
|
*/
|
||||||
usize size() const
|
usize size() const
|
||||||
{
|
{
|
||||||
return m_table.size();
|
return m_table.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the HashMap.
|
||||||
|
*/
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
m_table.clear();
|
m_table.clear();
|
||||||
|
@ -1,23 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* @file HashTable.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Table of values with best-case constant time lookup.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Hash.h>
|
#include <luna/Hash.h>
|
||||||
#include <luna/Heap.h>
|
#include <luna/Heap.h>
|
||||||
#include <luna/Option.h>
|
#include <luna/Option.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A table of values with best-case constant time lookup.
|
||||||
|
*
|
||||||
|
* @tparam T The type of values to store.
|
||||||
|
*/
|
||||||
template <typename T> class HashTable
|
template <typename T> class HashTable
|
||||||
{
|
{
|
||||||
static constexpr usize GROW_RATE = 2;
|
static constexpr usize GROW_RATE = 2;
|
||||||
static constexpr usize GROW_FACTOR = 16;
|
static constexpr usize GROW_FACTOR = 2;
|
||||||
|
static constexpr usize INITIAL_CAPACITY = 32;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Try to insert a value into the table.
|
||||||
|
*
|
||||||
|
* @param value The value to insert.
|
||||||
|
* @return Result<bool> An error, true if the insertion succeeded, or false if the value already existed.
|
||||||
|
*/
|
||||||
Result<bool> try_set(const T& value)
|
Result<bool> try_set(const T& value)
|
||||||
{
|
{
|
||||||
T copy { value };
|
T copy { value };
|
||||||
return try_set(move(copy));
|
return try_set(move(copy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Try to insert a value into the table.
|
||||||
|
*
|
||||||
|
* @param value The value to insert.
|
||||||
|
* @return Result<bool> An error, true if the insertion succeeded, or false if the value already existed.
|
||||||
|
*/
|
||||||
Result<bool> try_set(T&& value)
|
Result<bool> try_set(T&& value)
|
||||||
{
|
{
|
||||||
if (should_grow()) TRY(rehash(m_capacity + GROW_FACTOR));
|
if (should_grow()) TRY(rehash(m_capacity ? m_capacity * GROW_FACTOR : INITIAL_CAPACITY));
|
||||||
|
|
||||||
u64 index = hash(value, m_salt) % m_capacity;
|
u64 index = hash(value, m_salt) % m_capacity;
|
||||||
|
|
||||||
@ -45,6 +72,12 @@ template <typename T> class HashTable
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find a value inside the table, returning a pointer to it if found.
|
||||||
|
*
|
||||||
|
* @param value The value to compare against.
|
||||||
|
* @return T* A pointer to the value inside the table if found, or nullptr if it was not found.
|
||||||
|
*/
|
||||||
T* try_find(const T& value)
|
T* try_find(const T& value)
|
||||||
{
|
{
|
||||||
if (!m_size) return nullptr;
|
if (!m_size) return nullptr;
|
||||||
@ -69,6 +102,13 @@ template <typename T> class HashTable
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a value from this table.
|
||||||
|
*
|
||||||
|
* @param value The value to remove.
|
||||||
|
* @return true The value was successfully removed.
|
||||||
|
* @return false The value did not exist.
|
||||||
|
*/
|
||||||
bool try_remove(const T& value)
|
bool try_remove(const T& value)
|
||||||
{
|
{
|
||||||
if (!m_size) return false;
|
if (!m_size) return false;
|
||||||
@ -99,6 +139,9 @@ template <typename T> class HashTable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the table.
|
||||||
|
*/
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
if (m_capacity)
|
if (m_capacity)
|
||||||
@ -110,11 +153,24 @@ template <typename T> class HashTable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the number of values that can currently fit in the table.
|
||||||
|
*
|
||||||
|
* The number of actual entries will always be smaller than this (unless it is 0), since the table grows before
|
||||||
|
* the number of entries reaches the capacity.
|
||||||
|
*
|
||||||
|
* @return usize The current capacity.
|
||||||
|
*/
|
||||||
usize capacity() const
|
usize capacity() const
|
||||||
{
|
{
|
||||||
return m_capacity;
|
return m_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the number of values currently contained in the table.
|
||||||
|
*
|
||||||
|
* @return usize The number of values contained.
|
||||||
|
*/
|
||||||
usize size() const
|
usize size() const
|
||||||
{
|
{
|
||||||
return m_size;
|
return m_size;
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @file Heap.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief libluna implementations of malloc-family functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Freestanding definitions of std::nothrow and std::align_val_t.
|
||||||
|
*
|
||||||
|
*/
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
struct nothrow_t
|
struct nothrow_t
|
||||||
@ -15,16 +28,100 @@ namespace std
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Freestanding implementation of new (std::nothrow).
|
||||||
|
*
|
||||||
|
* @param size The amount of memory to allocate.
|
||||||
|
* @return void* A pointer to allocated memory, or nullptr on failure.
|
||||||
|
*/
|
||||||
void* operator new(usize size, const std::nothrow_t&) noexcept;
|
void* operator new(usize size, const std::nothrow_t&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Freestanding implementation of new (std::nothrow) [].
|
||||||
|
*
|
||||||
|
* @param size The amount of memory to allocate.
|
||||||
|
* @return void* A pointer to allocated memory, or nullptr on failure.
|
||||||
|
*/
|
||||||
void* operator new[](usize size, const std::nothrow_t&) noexcept;
|
void* operator new[](usize size, const std::nothrow_t&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Freestanding implementation of aligned delete.
|
||||||
|
*
|
||||||
|
* @param ptr The memory to delete.
|
||||||
|
* @param size The amount of memory to deallocate.
|
||||||
|
* @param alignment The alignment of the memory to delete.
|
||||||
|
*/
|
||||||
void operator delete(void* ptr, usize size, std::align_val_t alignment) noexcept;
|
void operator delete(void* ptr, usize size, std::align_val_t alignment) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called to allocate pages of memory. Some targets already have implementations of this function, but
|
||||||
|
* otherwise it must be implemented by the user.
|
||||||
|
*
|
||||||
|
* In the kernel, the implementation is in src/memory/Heap.cpp.
|
||||||
|
* For POSIX systems, libluna provides an implementation in src/ImplPOSIX.cpp.
|
||||||
|
*
|
||||||
|
* @param count The number of memory pages to allocate.
|
||||||
|
* @return Result<void*> An error, or a pointer to the beginning of the first allocated page.
|
||||||
|
*/
|
||||||
extern Result<void*> allocate_pages_impl(usize count);
|
extern Result<void*> allocate_pages_impl(usize count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called to free pages of memory. Some targets already have implementations of this function, but
|
||||||
|
* otherwise it must be implemented by the user.
|
||||||
|
*
|
||||||
|
* In the kernel, the implementation is in src/memory/Heap.cpp.
|
||||||
|
* For POSIX systems, libluna provides an implementation in src/ImplPOSIX.cpp.
|
||||||
|
*
|
||||||
|
* @param address The address of the first allocated page.
|
||||||
|
* @param count The number of memory pages to free.
|
||||||
|
* @return Result<void> Whether the operation succeeded.
|
||||||
|
*/
|
||||||
extern Result<void> release_pages_impl(void* address, usize count);
|
extern Result<void> release_pages_impl(void* address, usize count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief libluna's malloc implementation.
|
||||||
|
*
|
||||||
|
* @param size The amount of bytes to allocate.
|
||||||
|
* @param may_realloc Specifies whether the program may call realloc() on the returned memory in the future. Enables
|
||||||
|
* some optimizations if false.
|
||||||
|
* @param should_scrub Specifies whether the memory should be scrubbed with a uniform pattern, helpful for debugging
|
||||||
|
* purposes.
|
||||||
|
* @return Result<void*> An error, or the allocated memory.
|
||||||
|
*/
|
||||||
Result<void*> malloc_impl(usize size, bool may_realloc = true, bool should_scrub = true);
|
Result<void*> malloc_impl(usize size, bool may_realloc = true, bool should_scrub = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief libluna's calloc implementation.
|
||||||
|
*
|
||||||
|
* @param nmemb The number of elements to allocate.
|
||||||
|
* @param size The size in bytes of each element.
|
||||||
|
* @param may_realloc Specifies whether the program may call realloc() on the returned memory in the future. Enables
|
||||||
|
* some optimizations if false.
|
||||||
|
* @return Result<void*> An error, or the allocated memory.
|
||||||
|
*/
|
||||||
Result<void*> calloc_impl(usize nmemb, usize size, bool may_realloc = true);
|
Result<void*> calloc_impl(usize nmemb, usize size, bool may_realloc = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief libluna's realloc implementation.
|
||||||
|
*
|
||||||
|
* @param ptr The memory to resize.
|
||||||
|
* @param size The new size in bytes.
|
||||||
|
* @param may_realloc_again Specifies whether the program may call realloc() again on the returned memory in the future.
|
||||||
|
* Enables some optimizations if false.
|
||||||
|
* @return Result<void*> An error, or the resized memory (may not be the same pointer, the caller should use this
|
||||||
|
* instead of ptr from now on).
|
||||||
|
*/
|
||||||
Result<void*> realloc_impl(void* ptr, usize size, bool may_realloc_again = true);
|
Result<void*> realloc_impl(void* ptr, usize size, bool may_realloc_again = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief libluna's free implementation.
|
||||||
|
*
|
||||||
|
* @param ptr The memory to free.
|
||||||
|
* @return Result<void> Whether the operation succeeded.
|
||||||
|
*/
|
||||||
Result<void> free_impl(void* ptr);
|
Result<void> free_impl(void* ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dump heap usage stats to debug output.
|
||||||
|
*/
|
||||||
void dump_heap_usage();
|
void dump_heap_usage();
|
||||||
|
@ -1,5 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @file Ignore.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Do nothing.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Do nothing.
|
||||||
|
*
|
||||||
|
* Calling
|
||||||
|
* ignore(a, b, c);
|
||||||
|
* is a more compact equivalent of doing:
|
||||||
|
* (void)a;
|
||||||
|
* (void)b;
|
||||||
|
* (void)c;
|
||||||
|
*
|
||||||
|
* This function is used to discard unused variables avoiding compiler warnings, if you know they'll be used in the
|
||||||
|
* future.
|
||||||
|
*
|
||||||
|
* @tparam Args The list of ignored variable types.
|
||||||
|
*/
|
||||||
template <class... Args> constexpr void ignore(Args...)
|
template <class... Args> constexpr void ignore(Args...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file Base64.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Base64 encoding and decoding.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/Base64.h>
|
#include <luna/Base64.h>
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
#include <luna/DebugLog.h>
|
#include <luna/DebugLog.h>
|
||||||
@ -152,6 +161,6 @@ namespace Base64
|
|||||||
u8 nul_byte = '\0';
|
u8 nul_byte = '\0';
|
||||||
TRY(buf.append_data(&nul_byte, 1));
|
TRY(buf.append_data(&nul_byte, 1));
|
||||||
|
|
||||||
return String { (char*)buf.release_data() };
|
return String { (char*)buf.release_data(), buf.size() - 1 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file Bitmap.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief An interface to an array of bits.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/Bitmap.h>
|
#include <luna/Bitmap.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
#include <luna/Check.h>
|
#include <luna/Check.h>
|
||||||
@ -17,24 +26,6 @@ void Bitmap::initialize(void* location, usize size_in_bytes)
|
|||||||
m_size_in_bytes = size_in_bytes;
|
m_size_in_bytes = size_in_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> Bitmap::allocate(usize size_in_bytes)
|
|
||||||
{
|
|
||||||
initialize(TRY(malloc_impl(size_in_bytes)), size_in_bytes);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void*> Bitmap::resize(usize new_size_in_bytes)
|
|
||||||
{
|
|
||||||
m_location = (u8*)TRY(realloc_impl(m_location, new_size_in_bytes));
|
|
||||||
m_size_in_bytes = new_size_in_bytes;
|
|
||||||
return (void*)m_location;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> Bitmap::deallocate()
|
|
||||||
{
|
|
||||||
return free_impl(m_location);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* Bitmap::move(void* new_location, usize new_location_size_in_bytes)
|
void* Bitmap::move(void* new_location, usize new_location_size_in_bytes)
|
||||||
{
|
{
|
||||||
if (new_location_size_in_bytes > m_size_in_bytes) memcpy(new_location, m_location, m_size_in_bytes);
|
if (new_location_size_in_bytes > m_size_in_bytes) memcpy(new_location, m_location, m_size_in_bytes);
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file Buffer.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A managed wrapper around a resizable buffer of arbitrary memory.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
#include <luna/Heap.h>
|
#include <luna/Heap.h>
|
||||||
@ -15,6 +24,16 @@ Buffer::Buffer(Buffer&& other) : m_data(other.data()), m_size(other.size())
|
|||||||
other.m_data = nullptr;
|
other.m_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Buffer& Buffer::operator=(Buffer&& other)
|
||||||
|
{
|
||||||
|
if (&other == this) return *this;
|
||||||
|
if (m_data) free_impl(m_data);
|
||||||
|
m_data = other.m_data;
|
||||||
|
m_size = other.m_size;
|
||||||
|
other.m_data = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Buffer::~Buffer()
|
Buffer::~Buffer()
|
||||||
{
|
{
|
||||||
if (m_data) free_impl(m_data);
|
if (m_data) free_impl(m_data);
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file CPath.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Basic operations on paths using C-style strings.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/CPath.h>
|
#include <luna/CPath.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @file CRC32.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief CRC32 checksum calculation.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/CRC32.h>
|
#include <luna/CRC32.h>
|
||||||
#include <luna/DebugLog.h>
|
|
||||||
|
|
||||||
static const u32 crc_table[] = {
|
static const u32 crc_table[] = {
|
||||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832,
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832,
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
#include <luna/Alloc.h>
|
/**
|
||||||
|
* @file CString.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Implementations of C-string and memory manipulation functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
|
#include <luna/Heap.h>
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file CppABI.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Implementation of some C++ ABI internal functions, mainly __cxa_atexit.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
typedef void* (*cxa_atexit_func_t)(void*);
|
typedef void* (*cxa_atexit_func_t)(void*);
|
||||||
|
|
||||||
struct cxa_atexit_entry
|
struct cxa_atexit_entry
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file DebugLog.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Debug logging for platform-agnostic functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/DebugLog.h>
|
#include <luna/DebugLog.h>
|
||||||
|
|
||||||
void dbgln(const char* format, ...)
|
void dbgln(const char* format, ...)
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file Format.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief C-style string formatting.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022-2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
#include <luna/Format.h>
|
#include <luna/Format.h>
|
||||||
#include <luna/NumberParsing.h>
|
#include <luna/NumberParsing.h>
|
||||||
@ -22,7 +31,7 @@ typedef int flags_t;
|
|||||||
struct format_state
|
struct format_state
|
||||||
{
|
{
|
||||||
usize count;
|
usize count;
|
||||||
callback_t callback;
|
FormatCallback callback;
|
||||||
void* arg;
|
void* arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -385,7 +394,7 @@ static Result<void> va_output_integer(char specifier, conv_state& vstate, format
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<usize> cstyle_format(const char* format, callback_t callback, void* arg, va_list ap)
|
Result<usize> cstyle_format(const char* format, FormatCallback callback, void* arg, va_list ap)
|
||||||
{
|
{
|
||||||
format_state state;
|
format_state state;
|
||||||
state.callback = callback;
|
state.callback = callback;
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @file Hash.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Common hash functions for use in hash tables.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <luna/CString.h>
|
||||||
#include <luna/Hash.h>
|
#include <luna/Hash.h>
|
||||||
|
|
||||||
u64 hash_memory(const void* mem, usize size, u64 salt)
|
u64 hash_memory(const void* mem, usize size, u64 salt)
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @file Heap.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief libluna implementations of malloc-family functions.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/Alloc.h>
|
#include <luna/Alloc.h>
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
@ -425,12 +434,12 @@ void dump_heap_usage()
|
|||||||
#ifdef USE_FREESTANDING
|
#ifdef USE_FREESTANDING
|
||||||
void* operator new(usize size, const std::nothrow_t&) noexcept
|
void* operator new(usize size, const std::nothrow_t&) noexcept
|
||||||
{
|
{
|
||||||
return malloc_impl(size).value_or(nullptr);
|
return malloc_impl(size, false, false).value_or(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* operator new[](usize size, const std::nothrow_t&) noexcept
|
void* operator new[](usize size, const std::nothrow_t&) noexcept
|
||||||
{
|
{
|
||||||
return malloc_impl(size).value_or(nullptr);
|
return malloc_impl(size, false, false).value_or(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator delete(void* p) noexcept
|
void operator delete(void* p) noexcept
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
/* POSIX userspace implementation of libluna hooks. */
|
/**
|
||||||
|
* @file ImplPOSIX.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief POSIX userspace implementation of libluna hooks.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <luna/Attributes.h>
|
#include <luna/Attributes.h>
|
||||||
#include <luna/Result.h>
|
#include <luna/Result.h>
|
||||||
|
@ -14,6 +14,9 @@ set(SOURCES
|
|||||||
src/Mode.cpp
|
src/Mode.cpp
|
||||||
src/Prompt.cpp
|
src/Prompt.cpp
|
||||||
src/Security.cpp
|
src/Security.cpp
|
||||||
|
src/LocalServer.cpp
|
||||||
|
src/LocalClient.cpp
|
||||||
|
src/IPC.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(os ${SOURCES})
|
add_library(os ${SOURCES})
|
||||||
|
157
libos/include/os/IPC.h
Normal file
157
libos/include/os/IPC.h
Normal 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 || rc.error() == EINTR)) continue;
|
||||||
|
|
||||||
|
if (response_id == 0) // Error result
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int code;
|
||||||
|
rc = client.recv_typed(code);
|
||||||
|
if (rc.has_error() && (rc.error() == EAGAIN || rc.error() == EINTR)) 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 || rc.error() == EINTR)) 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);
|
||||||
|
}
|
||||||
|
}
|
99
libos/include/os/LocalClient.h
Normal file
99
libos/include/os/LocalClient.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* @file LocalClient.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UNIX local domain client class.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/OwnedPtr.h>
|
||||||
|
#include <luna/StringView.h>
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A client used to connect to a local server socket.
|
||||||
|
*/
|
||||||
|
class LocalClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create a new client object and connect it to a local server.
|
||||||
|
*
|
||||||
|
* @param path The path of the server socket to connect to.
|
||||||
|
* @param blocking Whether the client should block if no data is available and recv() is called.
|
||||||
|
* @return Result<OwnedPtr<LocalClient>> An error, or a new client object.
|
||||||
|
*/
|
||||||
|
static Result<OwnedPtr<LocalClient>> connect(StringView path, bool blocking);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the underlying socket file descriptor used by this object.
|
||||||
|
*
|
||||||
|
* @return int The file descriptor.
|
||||||
|
*/
|
||||||
|
int fd() const
|
||||||
|
{
|
||||||
|
return m_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read arbitrary data from the server. The call will block if there is no data and this object has not
|
||||||
|
* been created as non-blocking.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to read data into.
|
||||||
|
* @param length The maximum amount of bytes to read.
|
||||||
|
* @return Result<usize> An error, or the number of bytes read.
|
||||||
|
*/
|
||||||
|
Result<usize> recv(u8* buf, usize length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read an object from the server. The call will block if there is no data and this object has not been
|
||||||
|
* created as non-blocking.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the object.
|
||||||
|
* @param out A reference to the object to read data into.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
template <typename T> Result<void> recv_typed(T& out)
|
||||||
|
{
|
||||||
|
TRY(recv((u8*)&out, sizeof(T)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send arbitrary data to the server.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to send data from.
|
||||||
|
* @param length The amount of bytes to send.
|
||||||
|
* @return Result<usize> An error, or the number of bytes actually sent.
|
||||||
|
*/
|
||||||
|
Result<usize> send(const u8* buf, usize length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send an object to the server.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the object.
|
||||||
|
* @param out A reference to the object to send data from.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
template <typename T> Result<void> send_typed(const T& out)
|
||||||
|
{
|
||||||
|
TRY(send((const u8*)&out, sizeof(T)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disconnect from the attached server.
|
||||||
|
*
|
||||||
|
* This will make any further reads on this connection return ECONNRESET, and will make this object invalid.
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
~LocalClient();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fd;
|
||||||
|
};
|
||||||
|
}
|
142
libos/include/os/LocalServer.h
Normal file
142
libos/include/os/LocalServer.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* @file LocalServer.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UNIX local domain server class.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/OwnedPtr.h>
|
||||||
|
#include <luna/Result.h>
|
||||||
|
#include <luna/StringView.h>
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A local domain server, used to communicate between processes on the same machine.
|
||||||
|
*/
|
||||||
|
class LocalServer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create a new server object and bind it to a local address.
|
||||||
|
*
|
||||||
|
* @param path The path to use for the server socket.
|
||||||
|
* @param blocking Whether the server should block if no connections are available when calling accept().
|
||||||
|
* @return Result<OwnedPtr<LocalServer>> An error, or a new server object.
|
||||||
|
*/
|
||||||
|
static Result<OwnedPtr<LocalServer>> create(StringView path, bool blocking);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Activate the server and start listening for connections.
|
||||||
|
*
|
||||||
|
* @param backlog The number of unaccepted connections to keep.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
Result<void> listen(int backlog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the underlying socket file descriptor used by this object.
|
||||||
|
*
|
||||||
|
* @return int The file descriptor.
|
||||||
|
*/
|
||||||
|
int fd() const
|
||||||
|
{
|
||||||
|
return m_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An interface to communicate with clients connected to a local server.
|
||||||
|
*/
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Read arbitrary data from the client. The call will block if there is no data and the parent server
|
||||||
|
* object has not been created as non-blocking.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to read data into.
|
||||||
|
* @param length The maximum amount of bytes to read.
|
||||||
|
* @return Result<usize> An error, or the number of bytes read.
|
||||||
|
*/
|
||||||
|
Result<usize> recv(u8* buf, usize length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read an object from the client. The call will block if there is no data and the parent server
|
||||||
|
* object has not been created as non-blocking.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the object.
|
||||||
|
* @param out A reference to the object to read data into.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
template <typename T> Result<void> recv_typed(T& out)
|
||||||
|
{
|
||||||
|
TRY(recv((u8*)&out, sizeof(T)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send arbitrary data to the client.
|
||||||
|
*
|
||||||
|
* @param buf The buffer to send data from.
|
||||||
|
* @param length The amount of bytes to send.
|
||||||
|
* @return Result<usize> An error, or the number of bytes actually sent.
|
||||||
|
*/
|
||||||
|
Result<usize> send(const u8* buf, usize length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send an object to the client.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the object.
|
||||||
|
* @param out A reference to the object to send data from.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
template <typename T> Result<void> send_typed(const T& out)
|
||||||
|
{
|
||||||
|
TRY(send((const u8*)&out, sizeof(T)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disconnect from the attached client.
|
||||||
|
*
|
||||||
|
* This will make any further reads on the client return ECONNRESET, and will make this object invalid.
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the underlying socket file descriptor used by this object.
|
||||||
|
*
|
||||||
|
* @return int The file descriptor.
|
||||||
|
*/
|
||||||
|
int fd() const
|
||||||
|
{
|
||||||
|
return m_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client(Client&& other);
|
||||||
|
Client(int fd);
|
||||||
|
~Client();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Accept a new incoming connection and return a handle to it. If there are no incoming connections,
|
||||||
|
* accept() either blocks until there is one (if the object was created with blocking=true), or returns EAGAIN
|
||||||
|
* (if the object was created with blocking=false).
|
||||||
|
*
|
||||||
|
* @return Result<Client> An error, or a handle to the new connection.
|
||||||
|
*/
|
||||||
|
Result<Client> accept();
|
||||||
|
|
||||||
|
~LocalServer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fd;
|
||||||
|
bool m_blocking;
|
||||||
|
};
|
||||||
|
}
|
@ -41,7 +41,7 @@ namespace os
|
|||||||
* _exit(2). If NULL, the promises are not changed.
|
* _exit(2). If NULL, the promises are not changed.
|
||||||
* @param execpromises The promises to apply on the next call to execve(2), separated by spaces. If empty, the
|
* @param execpromises The promises to apply on the next call to execve(2), separated by spaces. If empty, the
|
||||||
* process may only call _exit(2). If NULL, the execpromises are not changed.
|
* process may only call _exit(2). If NULL, the execpromises are not changed.
|
||||||
* @return Result<void> Whether the operation succeded.
|
* @return Result<void> Whether the operation succeeded.
|
||||||
*/
|
*/
|
||||||
Result<void> pledge(const char* promises, const char* execpromises);
|
Result<void> pledge(const char* promises, const char* execpromises);
|
||||||
}
|
}
|
||||||
|
43
libos/src/IPC.cpp
Normal file
43
libos/src/IPC.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
if (rc.error() == EINTR)
|
||||||
|
return {}; // Let the caller check for anything having happened because a signal handler ran.
|
||||||
|
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.
|
||||||
|
if (rc.error() == EINTR)
|
||||||
|
return {}; // Let the caller check for anything having happened because a signal handler ran.
|
||||||
|
return rc.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler(client, id);
|
||||||
|
}
|
||||||
|
}
|
68
libos/src/LocalClient.cpp
Normal file
68
libos/src/LocalClient.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @file LocalClient.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UNIX local domain client class.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <os/LocalClient.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
Result<OwnedPtr<LocalClient>> LocalClient::connect(StringView path, bool blocking)
|
||||||
|
{
|
||||||
|
auto client = TRY(make_owned<LocalClient>());
|
||||||
|
|
||||||
|
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0) return err(errno);
|
||||||
|
|
||||||
|
struct sockaddr_un un;
|
||||||
|
un.sun_family = AF_UNIX;
|
||||||
|
strncpy(un.sun_path, path.chars(), sizeof(un.sun_path));
|
||||||
|
|
||||||
|
if (::connect(sockfd, (struct sockaddr*)&un, sizeof(un)) < 0)
|
||||||
|
{
|
||||||
|
close(sockfd);
|
||||||
|
return err(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blocking) { fcntl(sockfd, F_SETFL, O_NONBLOCK); }
|
||||||
|
|
||||||
|
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
|
client->m_fd = sockfd;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalClient::~LocalClient()
|
||||||
|
{
|
||||||
|
close(m_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> LocalClient::recv(u8* buf, usize length)
|
||||||
|
{
|
||||||
|
ssize_t nread = read(m_fd, buf, length);
|
||||||
|
if (nread < 0) return err(errno);
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> LocalClient::send(const u8* buf, usize length)
|
||||||
|
{
|
||||||
|
ssize_t nwrite = write(m_fd, buf, length);
|
||||||
|
if (nwrite < 0) return err(errno);
|
||||||
|
return nwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalClient::disconnect()
|
||||||
|
{
|
||||||
|
close(m_fd);
|
||||||
|
m_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
101
libos/src/LocalServer.cpp
Normal file
101
libos/src/LocalServer.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* @file LocalServer.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UNIX local domain server class.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <os/FileSystem.h>
|
||||||
|
#include <os/LocalServer.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
Result<OwnedPtr<LocalServer>> LocalServer::create(StringView path, bool blocking)
|
||||||
|
{
|
||||||
|
auto server = TRY(make_owned<LocalServer>());
|
||||||
|
|
||||||
|
(void)os::FileSystem::remove(path); // We explicitly ignore any error here, either it doesn't exist (which is
|
||||||
|
// fine), or it cannot be removed, which will make bind() fail later.
|
||||||
|
|
||||||
|
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0) return err(errno);
|
||||||
|
|
||||||
|
struct sockaddr_un un;
|
||||||
|
un.sun_family = AF_UNIX;
|
||||||
|
strncpy(un.sun_path, path.chars(), sizeof(un.sun_path));
|
||||||
|
|
||||||
|
if (bind(sockfd, (struct sockaddr*)&un, sizeof(un)) < 0)
|
||||||
|
{
|
||||||
|
close(sockfd);
|
||||||
|
return err(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blocking) { fcntl(sockfd, F_SETFL, O_NONBLOCK); }
|
||||||
|
server->m_blocking = blocking;
|
||||||
|
|
||||||
|
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
|
server->m_fd = sockfd;
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> LocalServer::listen(int backlog)
|
||||||
|
{
|
||||||
|
if (::listen(m_fd, backlog) < 0) return err(errno);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<LocalServer::Client> LocalServer::accept()
|
||||||
|
{
|
||||||
|
int fd = ::accept(m_fd, nullptr, nullptr);
|
||||||
|
if (fd < 0) return err(errno);
|
||||||
|
if (!m_blocking) fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||||
|
return Client { fd };
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalServer::~LocalServer()
|
||||||
|
{
|
||||||
|
close(m_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalServer::Client::Client(Client&& other) : m_fd(other.m_fd)
|
||||||
|
{
|
||||||
|
other.m_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalServer::Client::Client(int fd) : m_fd(fd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalServer::Client::~Client()
|
||||||
|
{
|
||||||
|
if (m_fd >= 0) close(m_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> LocalServer::Client::recv(u8* buf, usize length)
|
||||||
|
{
|
||||||
|
ssize_t nread = read(m_fd, buf, length);
|
||||||
|
if (nread < 0) return err(errno);
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<usize> LocalServer::Client::send(const u8* buf, usize length)
|
||||||
|
{
|
||||||
|
ssize_t nwrite = write(m_fd, buf, length);
|
||||||
|
if (nwrite < 0) return err(errno);
|
||||||
|
return nwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalServer::Client::disconnect()
|
||||||
|
{
|
||||||
|
close(m_fd);
|
||||||
|
m_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
30
libui/CMakeLists.txt
Normal file
30
libui/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# The UI and graphics library for Luna.
|
||||||
|
|
||||||
|
file(GLOB HEADERS include/ui/*.h)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
${HEADERS}
|
||||||
|
include/ui/ipc/Server.h
|
||||||
|
include/ui/ipc/Client.h
|
||||||
|
src/Canvas.cpp
|
||||||
|
src/Rect.cpp
|
||||||
|
src/Font.cpp
|
||||||
|
src/Image.cpp
|
||||||
|
src/App.cpp
|
||||||
|
src/Window.cpp
|
||||||
|
src/Layout.cpp
|
||||||
|
src/Alignment.cpp
|
||||||
|
src/Container.cpp
|
||||||
|
src/Button.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ui ${SOURCES})
|
||||||
|
target_compile_options(ui PRIVATE ${COMMON_FLAGS} -fno-threadsafe-statics)
|
||||||
|
target_include_directories(ui PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/)
|
||||||
|
target_include_directories(ui PUBLIC ${LUNA_BASE}/usr/include)
|
||||||
|
target_link_libraries(ui PUBLIC os)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ui
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy ${CMAKE_CURRENT_BINARY_DIR}/libui.a ${LUNA_BASE}/usr/lib/libui.a
|
||||||
|
)
|
30
libui/include/ui/Alignment.h
Normal file
30
libui/include/ui/Alignment.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* @file Alignment.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UI component alignment.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <ui/Rect.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
enum class VerticalAlignment
|
||||||
|
{
|
||||||
|
Top,
|
||||||
|
Center,
|
||||||
|
Bottom
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class HorizontalAlignment
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right
|
||||||
|
};
|
||||||
|
|
||||||
|
Rect align(Rect container, Rect contained, VerticalAlignment valign, HorizontalAlignment halign);
|
||||||
|
}
|
65
libui/include/ui/App.h
Normal file
65
libui/include/ui/App.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* @file App.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UI application event loop.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/HashMap.h>
|
||||||
|
#include <os/LocalClient.h>
|
||||||
|
#include <ui/Window.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class App
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
App();
|
||||||
|
~App();
|
||||||
|
|
||||||
|
Result<void> init(int, char**);
|
||||||
|
Result<int> run();
|
||||||
|
|
||||||
|
Rect screen_rect();
|
||||||
|
|
||||||
|
os::LocalClient& client()
|
||||||
|
{
|
||||||
|
return *m_client;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_should_close(bool b)
|
||||||
|
{
|
||||||
|
m_should_close = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_main_window(Window* window)
|
||||||
|
{
|
||||||
|
check(!m_main_window);
|
||||||
|
m_main_window = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* main_window()
|
||||||
|
{
|
||||||
|
return m_main_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> register_window(OwnedPtr<Window>&& window, Badge<Window>);
|
||||||
|
void unregister_window(Window* window, Badge<Window>);
|
||||||
|
|
||||||
|
Result<void> handle_ipc_event(u8 id);
|
||||||
|
|
||||||
|
static App& the();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static App* s_app;
|
||||||
|
OwnedPtr<os::LocalClient> m_client;
|
||||||
|
Window* m_main_window { nullptr };
|
||||||
|
HashMap<int, OwnedPtr<Window>> m_windows;
|
||||||
|
bool m_should_close { false };
|
||||||
|
|
||||||
|
Window* find_window(int id);
|
||||||
|
};
|
||||||
|
}
|
35
libui/include/ui/Button.h
Normal file
35
libui/include/ui/Button.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* @file Button.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A clickable component that triggers an action when pressed.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <ui/Widget.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class Button : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Button(Rect rect);
|
||||||
|
|
||||||
|
void set_widget(Widget& widget);
|
||||||
|
void set_action(void (*action)(void));
|
||||||
|
|
||||||
|
Result<EventResult> handle_mouse_move(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_leave(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||||
|
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||||
|
Result<void> draw(Canvas& canvas) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_hovered { false };
|
||||||
|
bool m_clicked { false };
|
||||||
|
Widget* m_child;
|
||||||
|
void (*m_action)(void);
|
||||||
|
};
|
||||||
|
}
|
73
libui/include/ui/Canvas.h
Normal file
73
libui/include/ui/Canvas.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* @file Canvas.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Drawable surfaces.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Result.h>
|
||||||
|
#include <luna/Types.h>
|
||||||
|
#include <ui/Color.h>
|
||||||
|
#include <ui/Point.h>
|
||||||
|
#include <ui/Rect.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A drawable surface.
|
||||||
|
*/
|
||||||
|
struct Canvas
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int stride;
|
||||||
|
u8* ptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new Canvas object.
|
||||||
|
*
|
||||||
|
* @param ptr The memory to use for the canvas. It must be of at least width * height * 4 bytes of length.
|
||||||
|
* @param width The width of the canvas.
|
||||||
|
* @param height The height of the canvas.
|
||||||
|
* @return Canvas The new Canvas object.
|
||||||
|
*/
|
||||||
|
static Canvas create(u8* ptr, int width, int height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a new Canvas that represents a subsection of the current one.
|
||||||
|
*
|
||||||
|
* @param rect The dimensions of the new canvas. If these exceed the bounds of the current canvas, they will be
|
||||||
|
* clamped.
|
||||||
|
* @return Canvas The new Canvas object.
|
||||||
|
*/
|
||||||
|
Canvas subcanvas(Rect rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the dimensions of the current canvas.
|
||||||
|
*
|
||||||
|
* @return Rect This canvas's dimensions, as a Rect object.
|
||||||
|
*/
|
||||||
|
Rect rect()
|
||||||
|
{
|
||||||
|
return Rect { .pos = { 0, 0 }, .width = width, .height = height };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill the entire canvas with one color.
|
||||||
|
*
|
||||||
|
* @param color The color to use.
|
||||||
|
*/
|
||||||
|
void fill(Color color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill the canvas with pixels.
|
||||||
|
*
|
||||||
|
* @param pixels The array of pixels (must be at least width*height).
|
||||||
|
* @param stride The number of pixels to skip to go to the next line.
|
||||||
|
*/
|
||||||
|
void fill(u32* pixels, int stride);
|
||||||
|
};
|
||||||
|
};
|
113
libui/include/ui/Color.h
Normal file
113
libui/include/ui/Color.h
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* @file Color.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief RGBA colors.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A 32-bit ARGB color.
|
||||||
|
*/
|
||||||
|
struct Color
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
u32 raw;
|
||||||
|
u8 colors[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the blue value of this color.
|
||||||
|
*
|
||||||
|
* @return constexpr u8 The blue value.
|
||||||
|
*/
|
||||||
|
constexpr u8 red() const
|
||||||
|
{
|
||||||
|
return colors[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the green value of this color.
|
||||||
|
*
|
||||||
|
* @return constexpr u8 The green value.
|
||||||
|
*/
|
||||||
|
constexpr u8 green() const
|
||||||
|
{
|
||||||
|
return colors[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the blue value of this color.
|
||||||
|
*
|
||||||
|
* @return constexpr u8 The blue value.
|
||||||
|
*/
|
||||||
|
constexpr u8 blue() const
|
||||||
|
{
|
||||||
|
return colors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the alpha value of this color.
|
||||||
|
*
|
||||||
|
* @return constexpr u8 The alpha value.
|
||||||
|
*/
|
||||||
|
constexpr u8 alpha() const
|
||||||
|
{
|
||||||
|
return colors[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new color from a 32-bit ARGB integer.
|
||||||
|
*
|
||||||
|
* @param raw The integer representing the color.
|
||||||
|
* @return constexpr Color The new color.
|
||||||
|
*/
|
||||||
|
static constexpr Color from_u32(u32 raw)
|
||||||
|
{
|
||||||
|
return Color { .raw = raw };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new color from its separate RGBA values (from 0 to 255).
|
||||||
|
*
|
||||||
|
* @param red The red value.
|
||||||
|
* @param green The green value.
|
||||||
|
* @param blue The blue value.
|
||||||
|
* @param alpha The alpha value.
|
||||||
|
* @return constexpr Color The new color.
|
||||||
|
*/
|
||||||
|
static constexpr Color from_rgba(u8 red, u8 green, u8 blue, u8 alpha)
|
||||||
|
{
|
||||||
|
return Color { .colors = { blue, green, red, alpha } };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new color from its separate RGB values (from 0 to 255).
|
||||||
|
*
|
||||||
|
* @param red The red value.
|
||||||
|
* @param green The green value.
|
||||||
|
* @param blue The blue value.
|
||||||
|
* @return constexpr Color The new color.
|
||||||
|
*/
|
||||||
|
static constexpr Color from_rgb(u8 red, u8 green, u8 blue)
|
||||||
|
{
|
||||||
|
return from_rgba(red, green, blue, 0xff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr Color WHITE = Color::from_rgb(0xff, 0xff, 0xff);
|
||||||
|
static constexpr Color BLACK = Color::from_rgb(0x00, 0x00, 0x00);
|
||||||
|
static constexpr Color GRAY = Color::from_rgb(0x80, 0x80, 0x80);
|
||||||
|
|
||||||
|
static constexpr Color BLUE = Color::from_rgb(0x00, 0x00, 0xff);
|
||||||
|
static constexpr Color GREEN = Color::from_rgb(0x00, 0xff, 0x00);
|
||||||
|
static constexpr Color RED = Color::from_rgb(0xff, 0x00, 0x00);
|
||||||
|
|
||||||
|
static constexpr Color CYAN = Color::from_rgb(0x00, 0xff, 0xff);
|
||||||
|
};
|
34
libui/include/ui/Container.h
Normal file
34
libui/include/ui/Container.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @file Container.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A container widget to pad and align objects inside it.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <ui/Alignment.h>
|
||||||
|
#include <ui/Widget.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class Container : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Container(Rect rect, VerticalAlignment valign, HorizontalAlignment halign);
|
||||||
|
|
||||||
|
void set_widget(Widget& widget);
|
||||||
|
|
||||||
|
Result<EventResult> handle_mouse_move(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_leave(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||||
|
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||||
|
Result<void> draw(Canvas& canvas) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Widget* m_widget;
|
||||||
|
VerticalAlignment m_valign;
|
||||||
|
HorizontalAlignment m_halign;
|
||||||
|
};
|
||||||
|
}
|
120
libui/include/ui/Font.h
Normal file
120
libui/include/ui/Font.h
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/**
|
||||||
|
* @file Font.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief PSF font loading and rendering.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Buffer.h>
|
||||||
|
#include <luna/SharedPtr.h>
|
||||||
|
#include <os/Path.h>
|
||||||
|
#include <ui/Canvas.h>
|
||||||
|
|
||||||
|
#define PSF_FONT_MAGIC 0x864ab572
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A class holding PSF font data, used for direct rendering of glyphs into a canvas.
|
||||||
|
*/
|
||||||
|
class Font : public Shareable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief An enum used to select a font weight when loading a font.
|
||||||
|
*/
|
||||||
|
enum FontWeight
|
||||||
|
{
|
||||||
|
Regular,
|
||||||
|
Bold,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a Font object from a font file.
|
||||||
|
*
|
||||||
|
* @param path The full path to the font file.
|
||||||
|
* @return Result<SharedPtr<Font>> An error, or the loaded Font object.
|
||||||
|
*/
|
||||||
|
static Result<SharedPtr<Font>> load(const os::Path& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a system font by name.
|
||||||
|
*
|
||||||
|
* @param name The name of the font to load (the default system font is "Tamsyn").
|
||||||
|
* @param weight The weight of the font (regular or bold).
|
||||||
|
* @return Result<SharedPtr<Font>> An error, or the loaded Font object.
|
||||||
|
*/
|
||||||
|
static Result<SharedPtr<Font>> load_builtin(StringView name, FontWeight weight);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the system's default font.
|
||||||
|
*
|
||||||
|
* @return SharedPtr<Font> The default font.
|
||||||
|
*/
|
||||||
|
static SharedPtr<Font> default_font();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a pointer to the system's default bold font.
|
||||||
|
*
|
||||||
|
* @return SharedPtr<Font> The default bold font.
|
||||||
|
*/
|
||||||
|
static SharedPtr<Font> default_bold_font();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Render a single Unicode code point onto a canvas, using this font's glyphs.
|
||||||
|
*
|
||||||
|
* @param codepoint The code point to render.
|
||||||
|
* @param color The color to draw the code point in.
|
||||||
|
* @param canvas The canvas to use.
|
||||||
|
*/
|
||||||
|
void render(wchar_t codepoint, ui::Color color, ui::Canvas& canvas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Render a Unicode text string onto a canvas, using this font's glyphs.
|
||||||
|
*
|
||||||
|
* @param text The string to render (must be null-terminated).
|
||||||
|
* @param color The color to draw the code point in.
|
||||||
|
* @param canvas The canvas to use.
|
||||||
|
*/
|
||||||
|
void render(const wchar_t* text, ui::Color color, ui::Canvas& canvas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the width of this font's glyphs.
|
||||||
|
*
|
||||||
|
* @return int The width.
|
||||||
|
*/
|
||||||
|
int width() const
|
||||||
|
{
|
||||||
|
return m_psf_header.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the height of this font's glyphs.
|
||||||
|
*
|
||||||
|
* @return int The height.
|
||||||
|
*/
|
||||||
|
int height() const
|
||||||
|
{
|
||||||
|
return m_psf_header.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct PSFHeader
|
||||||
|
{
|
||||||
|
u32 magic;
|
||||||
|
u32 version; // zero
|
||||||
|
u32 headersize;
|
||||||
|
u32 flags; // 0 if there's no unicode table
|
||||||
|
u32 numglyph;
|
||||||
|
u32 bytesperglyph;
|
||||||
|
int height;
|
||||||
|
int width;
|
||||||
|
};
|
||||||
|
|
||||||
|
PSFHeader m_psf_header;
|
||||||
|
Buffer m_font_data;
|
||||||
|
};
|
||||||
|
};
|
96
libui/include/ui/Image.h
Normal file
96
libui/include/ui/Image.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* @file Image.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief TGA image loading and rendering.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Buffer.h>
|
||||||
|
#include <luna/SharedPtr.h>
|
||||||
|
#include <os/Path.h>
|
||||||
|
#include <ui/Widget.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief An image in the TGA file format.
|
||||||
|
*/
|
||||||
|
class Image : public Shareable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Load a new TGA image from a file.
|
||||||
|
*
|
||||||
|
* @param path The path to open.
|
||||||
|
* @return Result<SharedPtr<Image>> An error, or a new Image object.
|
||||||
|
*/
|
||||||
|
static Result<SharedPtr<Image>> load(const os::Path& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the array of pixels contained in the image.
|
||||||
|
*
|
||||||
|
* @return u32* The array of pixels.
|
||||||
|
*/
|
||||||
|
u32* pixels()
|
||||||
|
{
|
||||||
|
return (u32*)m_image_data.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the width of the image.
|
||||||
|
*
|
||||||
|
* @return u16 The width.
|
||||||
|
*/
|
||||||
|
u16 width()
|
||||||
|
{
|
||||||
|
return m_tga_header.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the height of the image.
|
||||||
|
*
|
||||||
|
* @return u16 The height.
|
||||||
|
*/
|
||||||
|
u16 height()
|
||||||
|
{
|
||||||
|
return m_tga_header.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct [[gnu::packed]] TGAHeader
|
||||||
|
{
|
||||||
|
u8 idlen;
|
||||||
|
u8 colormap;
|
||||||
|
u8 encoding;
|
||||||
|
u16 cmaporig, cmaplen;
|
||||||
|
u8 cmapent;
|
||||||
|
u16 x;
|
||||||
|
u16 y;
|
||||||
|
u16 w;
|
||||||
|
u16 h;
|
||||||
|
u8 bpp;
|
||||||
|
u8 pixeltype;
|
||||||
|
};
|
||||||
|
|
||||||
|
TGAHeader m_tga_header;
|
||||||
|
Buffer m_image_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ImageWidget final : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Result<OwnedPtr<ImageWidget>> load(const os::Path& path);
|
||||||
|
|
||||||
|
Result<EventResult> handle_mouse_move(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_leave(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||||
|
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||||
|
Result<void> draw(Canvas& canvas) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SharedPtr<Image> m_image;
|
||||||
|
};
|
||||||
|
}
|
69
libui/include/ui/Layout.h
Normal file
69
libui/include/ui/Layout.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* @file Layout.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Layout widgets to organize content.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Vector.h>
|
||||||
|
#include <ui/Widget.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
enum class AdjustHeight
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AdjustWidth
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
|
class HorizontalLayout final : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HorizontalLayout(AdjustHeight adjust_height = AdjustHeight::Yes, AdjustWidth adjust_width = AdjustWidth::Yes);
|
||||||
|
|
||||||
|
Result<EventResult> handle_mouse_move(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_leave(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||||
|
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||||
|
|
||||||
|
Result<void> draw(Canvas& canvas) override;
|
||||||
|
|
||||||
|
Result<void> add_widget(Widget& widget);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<Widget*> m_widgets;
|
||||||
|
AdjustHeight m_adjust_height;
|
||||||
|
AdjustWidth m_adjust_width;
|
||||||
|
int m_used_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VerticalLayout final : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VerticalLayout(AdjustHeight adjust_height = AdjustHeight::Yes, AdjustWidth adjust_width = AdjustWidth::Yes);
|
||||||
|
|
||||||
|
Result<EventResult> handle_mouse_move(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_leave(Point position) override;
|
||||||
|
Result<EventResult> handle_mouse_down(Point position, int buttons) override;
|
||||||
|
Result<EventResult> handle_mouse_up(Point position, int buttons) override;
|
||||||
|
|
||||||
|
Result<void> draw(Canvas& canvas) override;
|
||||||
|
|
||||||
|
Result<void> add_widget(Widget& widget);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<Widget*> m_widgets;
|
||||||
|
AdjustHeight m_adjust_height;
|
||||||
|
AdjustWidth m_adjust_width;
|
||||||
|
int m_used_height;
|
||||||
|
};
|
||||||
|
}
|
21
libui/include/ui/Mouse.h
Normal file
21
libui/include/ui/Mouse.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @file Mouse.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Mouse buttons.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <moon/Mouse.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
enum MouseButtons
|
||||||
|
{
|
||||||
|
LEFT = moon::Left,
|
||||||
|
MIDDLE = moon::Middle,
|
||||||
|
RIGHT = moon::Right,
|
||||||
|
};
|
||||||
|
}
|
22
libui/include/ui/Point.h
Normal file
22
libui/include/ui/Point.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* @file Point.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief 2D space points.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A point in 2D space.
|
||||||
|
*/
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
int x { 0 };
|
||||||
|
int y { 0 };
|
||||||
|
};
|
||||||
|
}
|
81
libui/include/ui/Rect.h
Normal file
81
libui/include/ui/Rect.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* @file Rect.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A simple 2D rectangle representation.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <ui/Point.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief A simple rectangle.
|
||||||
|
*/
|
||||||
|
struct Rect
|
||||||
|
{
|
||||||
|
Point pos;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a point is contained in this rectangle.
|
||||||
|
*
|
||||||
|
* @param point The point to check.
|
||||||
|
* @return true The point is contained inside the rectangle.
|
||||||
|
* @return false The point is not contained inside the rectangle.
|
||||||
|
*/
|
||||||
|
bool contains(Point point);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if another rectangle is contained in this one.
|
||||||
|
*
|
||||||
|
* @param point The rectangle to check.
|
||||||
|
* @return true The other rectangle is contained inside this one.
|
||||||
|
* @return false The other rectangle is not contained inside this one.
|
||||||
|
*/
|
||||||
|
bool contains(Rect rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Normalize a point to fit inside this rectangle.
|
||||||
|
*
|
||||||
|
* @param point The original point.
|
||||||
|
* @return Point The normalized point.
|
||||||
|
*/
|
||||||
|
Point normalize(Point point);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transform an absolute position to a position relative to this rectangle.
|
||||||
|
*
|
||||||
|
* @param pos The original absolute position.
|
||||||
|
* @return Point The position relative to this rectangle.
|
||||||
|
*/
|
||||||
|
Point relative(Point pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transform a position relative to this rectangle to an absolute position.
|
||||||
|
*
|
||||||
|
* @param pos The original relative position.
|
||||||
|
* @return Point The absolute position.
|
||||||
|
*/
|
||||||
|
Point absolute(Point pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transform another rectangle relative to this one to an absolute rectangle.
|
||||||
|
*
|
||||||
|
* @param rect The original relative rectangle.
|
||||||
|
* @return Point The absolute rectangle.
|
||||||
|
*/
|
||||||
|
Rect absolute(Rect rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a copy of this rectangle with no negative values (normalized to 0).
|
||||||
|
*
|
||||||
|
* @return Rect The new rectangle.
|
||||||
|
*/
|
||||||
|
Rect normalized();
|
||||||
|
};
|
||||||
|
}
|
68
libui/include/ui/Widget.h
Normal file
68
libui/include/ui/Widget.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @file Widget.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Abstract widget class.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Result.h>
|
||||||
|
#include <ui/Canvas.h>
|
||||||
|
#include <ui/Point.h>
|
||||||
|
#include <ui/Rect.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
enum class EventResult
|
||||||
|
{
|
||||||
|
DidHandle,
|
||||||
|
DidNotHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Result<EventResult> handle_mouse_move(Point position);
|
||||||
|
virtual Result<EventResult> handle_mouse_down(Point position, int buttons);
|
||||||
|
virtual Result<EventResult> handle_mouse_up(Point position, int buttons);
|
||||||
|
virtual Result<EventResult> handle_mouse_leave(Point position);
|
||||||
|
|
||||||
|
virtual Result<void> draw(Canvas& canvas);
|
||||||
|
|
||||||
|
void set_window(Window* window, Rect rect, Badge<Window>)
|
||||||
|
{
|
||||||
|
m_window = window;
|
||||||
|
m_rect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_parent(Widget* parent)
|
||||||
|
{
|
||||||
|
m_parent = parent;
|
||||||
|
m_window = parent->m_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* parent()
|
||||||
|
{
|
||||||
|
return m_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* window()
|
||||||
|
{
|
||||||
|
return m_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect& rect()
|
||||||
|
{
|
||||||
|
return m_rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Widget* m_parent { nullptr };
|
||||||
|
Window* m_window;
|
||||||
|
Rect m_rect { 0, 0, 50, 50 };
|
||||||
|
};
|
||||||
|
}
|
66
libui/include/ui/Window.h
Normal file
66
libui/include/ui/Window.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* @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/Mouse.h>
|
||||||
|
#include <ui/Rect.h>
|
||||||
|
#include <ui/Widget.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
class Window
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Result<Window*> create(Rect rect, bool decorated = true);
|
||||||
|
|
||||||
|
void set_title(StringView title);
|
||||||
|
|
||||||
|
void set_background(Color color)
|
||||||
|
{
|
||||||
|
m_background = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_main_widget(Widget& widget)
|
||||||
|
{
|
||||||
|
check(!m_main_widget);
|
||||||
|
widget.set_window(this, m_canvas.rect(), {});
|
||||||
|
m_main_widget = &widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas& canvas()
|
||||||
|
{
|
||||||
|
return m_canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
Result<void> draw();
|
||||||
|
Result<void> handle_mouse_move(ui::Point position);
|
||||||
|
Result<void> handle_mouse_buttons(ui::Point position, int buttons);
|
||||||
|
|
||||||
|
int id() const
|
||||||
|
{
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Window();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_id;
|
||||||
|
Canvas m_canvas;
|
||||||
|
Widget* m_main_widget { nullptr };
|
||||||
|
Color m_background { ui::BLACK };
|
||||||
|
Option<int> m_old_mouse_buttons;
|
||||||
|
};
|
||||||
|
}
|
55
libui/include/ui/ipc/Client.h
Normal file
55
libui/include/ui/ipc/Client.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @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>
|
||||||
|
#include <ui/Point.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
enum ClientMessages : u8
|
||||||
|
{
|
||||||
|
IPC_ENUM_CLIENT(ui),
|
||||||
|
CREATE_WINDOW_RESPONSE_ID,
|
||||||
|
WINDOW_CLOSE_REQUEST_ID,
|
||||||
|
MOUSE_EVENT_REQUEST_ID,
|
||||||
|
GET_SCREEN_RECT_RESPONSE_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CreateWindowResponse
|
||||||
|
{
|
||||||
|
static constexpr u8 ID = CREATE_WINDOW_RESPONSE_ID;
|
||||||
|
|
||||||
|
int window;
|
||||||
|
IPC_STRING(shm_path);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WindowCloseRequest
|
||||||
|
{
|
||||||
|
static constexpr u8 ID = WINDOW_CLOSE_REQUEST_ID;
|
||||||
|
|
||||||
|
int window;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MouseEventRequest
|
||||||
|
{
|
||||||
|
static constexpr u8 ID = MOUSE_EVENT_REQUEST_ID;
|
||||||
|
|
||||||
|
int window;
|
||||||
|
Point position;
|
||||||
|
int buttons;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GetScreenRectResponse
|
||||||
|
{
|
||||||
|
static constexpr u8 ID = GET_SCREEN_RECT_RESPONSE_ID;
|
||||||
|
|
||||||
|
Rect rect;
|
||||||
|
};
|
||||||
|
}
|
66
libui/include/ui/ipc/Server.h
Normal file
66
libui/include/ui/ipc/Server.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* @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,
|
||||||
|
SET_WINDOW_TITLE_ID,
|
||||||
|
INVALIDATE_ID,
|
||||||
|
CLOSE_WINDOW_ID,
|
||||||
|
GET_SCREEN_RECT_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CreateWindowRequest
|
||||||
|
{
|
||||||
|
using ResponseType = CreateWindowResponse;
|
||||||
|
static constexpr u8 ID = CREATE_WINDOW_ID;
|
||||||
|
|
||||||
|
ui::Rect rect;
|
||||||
|
bool decorated;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CloseWindowRequest
|
||||||
|
{
|
||||||
|
static constexpr u8 ID = CLOSE_WINDOW_ID;
|
||||||
|
|
||||||
|
int window;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GetScreenRectRequest
|
||||||
|
{
|
||||||
|
using ResponseType = GetScreenRectResponse;
|
||||||
|
static constexpr u8 ID = GET_SCREEN_RECT_ID;
|
||||||
|
|
||||||
|
int _shadow; // Unused.
|
||||||
|
};
|
||||||
|
}
|
40
libui/src/Alignment.cpp
Normal file
40
libui/src/Alignment.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* @file Alignment.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief UI component alignment.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ui/Alignment.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Rect align(Rect container, Rect contained, VerticalAlignment valign, HorizontalAlignment halign)
|
||||||
|
{
|
||||||
|
Rect result;
|
||||||
|
result.width = contained.width;
|
||||||
|
result.height = contained.height;
|
||||||
|
result.pos.y = container.pos.y;
|
||||||
|
result.pos.x = container.pos.x;
|
||||||
|
|
||||||
|
switch (valign)
|
||||||
|
{
|
||||||
|
case VerticalAlignment::Top: break;
|
||||||
|
case VerticalAlignment::Center: result.pos.y += (container.height - contained.height) / 2; break;
|
||||||
|
case VerticalAlignment::Bottom: result.pos.y += container.height - contained.height; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (halign)
|
||||||
|
{
|
||||||
|
case HorizontalAlignment::Left: break;
|
||||||
|
case HorizontalAlignment::Center: result.pos.x += (container.width - contained.width) / 2; break;
|
||||||
|
case HorizontalAlignment::Right: result.pos.x += container.width - contained.width; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
128
libui/src/App.cpp
Normal file
128
libui/src/App.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* @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/File.h>
|
||||||
|
#include <os/IPC.h>
|
||||||
|
#include <ui/App.h>
|
||||||
|
#include <ui/ipc/Client.h>
|
||||||
|
#include <ui/ipc/Server.h>
|
||||||
|
|
||||||
|
Result<void> handle_ipc_client_event(os::LocalClient&, u8 id)
|
||||||
|
{
|
||||||
|
return ui::App::the().handle_ipc_event(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
check(m_main_window);
|
||||||
|
while (!m_should_close) { TRY(os::IPC::check_for_messages(*m_client)); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
App& App::the()
|
||||||
|
{
|
||||||
|
check(s_app);
|
||||||
|
return *s_app;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect App::screen_rect()
|
||||||
|
{
|
||||||
|
ui::GetScreenRectRequest request {};
|
||||||
|
auto response = os::IPC::send_sync<ui::GetScreenRectResponse>(*m_client, request).release_value();
|
||||||
|
return response.rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> App::register_window(OwnedPtr<Window>&& window, Badge<Window>)
|
||||||
|
{
|
||||||
|
int id = window->id();
|
||||||
|
check(TRY(m_windows.try_set(id, move(window))));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::unregister_window(Window* window, Badge<Window>)
|
||||||
|
{
|
||||||
|
int id = window->id();
|
||||||
|
check(m_windows.try_remove(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* App::find_window(int id)
|
||||||
|
{
|
||||||
|
auto* window = m_windows.try_get_ref(id);
|
||||||
|
check(window);
|
||||||
|
return window->ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define READ_MESSAGE(request) \
|
||||||
|
do { \
|
||||||
|
auto rc = m_client->recv_typed(request); \
|
||||||
|
if (rc.has_error()) \
|
||||||
|
{ \
|
||||||
|
if (rc.error() == EAGAIN) { continue; } \
|
||||||
|
if (rc.error() == EINTR) { continue; } \
|
||||||
|
else \
|
||||||
|
return rc.release_error(); \
|
||||||
|
} \
|
||||||
|
break; \
|
||||||
|
} while (true)
|
||||||
|
|
||||||
|
Result<void> App::handle_ipc_event(u8 id)
|
||||||
|
{
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case WINDOW_CLOSE_REQUEST_ID: {
|
||||||
|
WindowCloseRequest request;
|
||||||
|
READ_MESSAGE(request);
|
||||||
|
os::eprintln("ui: Window close request from server! Shall comply.");
|
||||||
|
auto* window = find_window(request.window);
|
||||||
|
window->close();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case MOUSE_EVENT_REQUEST_ID: {
|
||||||
|
MouseEventRequest request;
|
||||||
|
READ_MESSAGE(request);
|
||||||
|
auto* window = find_window(request.window);
|
||||||
|
window->handle_mouse_move(request.position);
|
||||||
|
window->handle_mouse_buttons(request.position, request.buttons);
|
||||||
|
window->draw();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
default: fail("Unexpected IPC request from server!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
libui/src/Button.cpp
Normal file
68
libui/src/Button.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @file Button.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A clickable component that triggers an action when pressed.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ui/Button.h>
|
||||||
|
#include <ui/Mouse.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Button::Button(Rect rect)
|
||||||
|
{
|
||||||
|
m_rect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::set_widget(Widget& widget)
|
||||||
|
{
|
||||||
|
widget.rect() = m_rect;
|
||||||
|
m_child = &widget;
|
||||||
|
widget.set_parent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::set_action(void (*action)(void))
|
||||||
|
{
|
||||||
|
m_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Button::handle_mouse_move(Point position)
|
||||||
|
{
|
||||||
|
m_hovered = true;
|
||||||
|
return m_child->handle_mouse_move(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Button::handle_mouse_leave(Point position)
|
||||||
|
{
|
||||||
|
m_hovered = m_clicked = false;
|
||||||
|
return m_child->handle_mouse_leave(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Button::handle_mouse_down(Point position, int buttons)
|
||||||
|
{
|
||||||
|
auto result = TRY(m_child->handle_mouse_down(position, buttons));
|
||||||
|
if (result == EventResult::DidNotHandle)
|
||||||
|
{
|
||||||
|
if (!m_clicked && (buttons == ui::MouseButtons::LEFT))
|
||||||
|
{
|
||||||
|
m_clicked = true;
|
||||||
|
m_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EventResult::DidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Button::handle_mouse_up(Point position, int buttons)
|
||||||
|
{
|
||||||
|
if (buttons & ui::MouseButtons::LEFT) m_clicked = false;
|
||||||
|
return m_child->handle_mouse_up(position, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> Button::draw(Canvas& canvas)
|
||||||
|
{
|
||||||
|
return m_child->draw(canvas);
|
||||||
|
}
|
||||||
|
}
|
62
libui/src/Canvas.cpp
Normal file
62
libui/src/Canvas.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* @file Canvas.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Drawable surfaces.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ui/Canvas.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Canvas Canvas::create(u8* ptr, int width, int height)
|
||||||
|
{
|
||||||
|
return Canvas { .width = width, .height = height, .stride = width, .ptr = ptr };
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas Canvas::subcanvas(Rect rect)
|
||||||
|
{
|
||||||
|
if (rect.pos.x < 0) rect.pos.x = 0;
|
||||||
|
if (rect.pos.y < 0) rect.pos.y = 0;
|
||||||
|
if (rect.pos.x + rect.width > width) rect.width = width - rect.pos.x;
|
||||||
|
if (rect.pos.y + rect.height > height) rect.height = height - rect.pos.y;
|
||||||
|
|
||||||
|
u8* p = ptr + rect.pos.x * sizeof(Color) + (rect.pos.y * sizeof(Color) * stride);
|
||||||
|
|
||||||
|
return Canvas { .width = rect.width, .height = rect.height, .stride = stride, .ptr = p };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::fill(Color color)
|
||||||
|
{
|
||||||
|
u8* p = ptr;
|
||||||
|
for (int i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
u32* colorp = (u32*)p;
|
||||||
|
for (int j = 0; j < width; j++)
|
||||||
|
{
|
||||||
|
*colorp = color.raw;
|
||||||
|
colorp++;
|
||||||
|
}
|
||||||
|
p += stride * sizeof(Color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::fill(u32* pixels, int _stride)
|
||||||
|
{
|
||||||
|
u8* p = ptr;
|
||||||
|
for (int i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
u32* colorp = (u32*)p;
|
||||||
|
for (int j = 0; j < width; j++)
|
||||||
|
{
|
||||||
|
u32 pix = pixels[j];
|
||||||
|
if (Color::from_u32(pix).alpha() == 0xff) *colorp = pix;
|
||||||
|
colorp++;
|
||||||
|
}
|
||||||
|
pixels += _stride;
|
||||||
|
p += stride * sizeof(Color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
libui/src/Container.cpp
Normal file
55
libui/src/Container.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @file Container.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief A container widget to pad and align objects inside it.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ui/Container.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Container::Container(Rect rect, VerticalAlignment valign, HorizontalAlignment halign)
|
||||||
|
: m_valign(valign), m_halign(halign)
|
||||||
|
{
|
||||||
|
m_rect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Container::set_widget(Widget& widget)
|
||||||
|
{
|
||||||
|
m_widget = &widget;
|
||||||
|
widget.rect() = ui::align(m_rect, widget.rect(), m_valign, m_halign);
|
||||||
|
widget.set_parent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Container::handle_mouse_move(Point position)
|
||||||
|
{
|
||||||
|
return m_widget->handle_mouse_move(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Container::handle_mouse_leave(Point position)
|
||||||
|
{
|
||||||
|
return m_widget->handle_mouse_leave(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Container::handle_mouse_down(Point position, int buttons)
|
||||||
|
{
|
||||||
|
return m_widget->handle_mouse_down(position, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> Container::handle_mouse_up(Point position, int buttons)
|
||||||
|
{
|
||||||
|
return m_widget->handle_mouse_up(position, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> Container::draw(Canvas& canvas)
|
||||||
|
{
|
||||||
|
auto rect = ui::Rect { m_widget->rect().pos.x - m_rect.pos.x, m_widget->rect().pos.y - m_rect.pos.y,
|
||||||
|
m_widget->rect().width, m_widget->rect().height };
|
||||||
|
auto subcanvas = canvas.subcanvas(rect);
|
||||||
|
return m_widget->draw(subcanvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
libui/src/Font.cpp
Normal file
121
libui/src/Font.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* @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 <os/File.h>
|
||||||
|
#include <ui/Font.h>
|
||||||
|
|
||||||
|
constexpr static int BYTES_PER_PIXEL = (int)sizeof(ui::Color);
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Result<SharedPtr<Font>> Font::load(const os::Path& path)
|
||||||
|
{
|
||||||
|
auto font = TRY(make_shared<Font>());
|
||||||
|
|
||||||
|
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||||
|
|
||||||
|
TRY(file->read_typed(font->m_psf_header));
|
||||||
|
|
||||||
|
if (font->m_psf_header.magic != PSF_FONT_MAGIC)
|
||||||
|
{
|
||||||
|
os::eprintln("ui::Font::load(%s) failed: font magic does not match PSF2 magic", path.name().chars());
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->m_psf_header.version != 0)
|
||||||
|
{
|
||||||
|
os::eprintln("ui::Font::load(%s) failed: font version is unsupported", path.name().chars());
|
||||||
|
return err(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->m_psf_header.flags)
|
||||||
|
{
|
||||||
|
os::eprintln("ui::Font::load(%s) warning: font has a unicode table, which we're ignoring",
|
||||||
|
path.name().chars());
|
||||||
|
// todo(); // Font has a unicode table, oh no!
|
||||||
|
}
|
||||||
|
|
||||||
|
font->m_font_data = TRY(file->read_all()); // Read the rest of the file into the font data buffer.
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<SharedPtr<Font>> Font::load_builtin(StringView name, FontWeight weight)
|
||||||
|
{
|
||||||
|
auto path = TRY(String::format("/usr/share/fonts/%s-%s.psf"_sv, name.chars(),
|
||||||
|
weight == FontWeight::Bold ? "Bold" : "Regular"));
|
||||||
|
|
||||||
|
return load(path.view());
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Font> Font::default_font()
|
||||||
|
{
|
||||||
|
static SharedPtr<ui::Font> s_default_font = {};
|
||||||
|
if (!s_default_font) s_default_font = load("/usr/share/fonts/Tamsyn-Regular.psf").release_value();
|
||||||
|
return s_default_font;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Font> Font::default_bold_font()
|
||||||
|
{
|
||||||
|
static SharedPtr<ui::Font> s_default_bold_font = {};
|
||||||
|
if (!s_default_bold_font) s_default_bold_font = load("/usr/share/fonts/Tamsyn-Bold.psf").release_value();
|
||||||
|
return s_default_bold_font;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::render(wchar_t codepoint, ui::Color color, ui::Canvas& canvas)
|
||||||
|
{
|
||||||
|
const wchar_t str[] = { codepoint, 0 };
|
||||||
|
render(str, color, canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::render(const wchar_t* text, ui::Color color, ui::Canvas& canvas)
|
||||||
|
{
|
||||||
|
usize len = wcslen(text);
|
||||||
|
|
||||||
|
int height = m_psf_header.height;
|
||||||
|
int width = m_psf_header.width;
|
||||||
|
int last_char_width = width;
|
||||||
|
|
||||||
|
if (canvas.width < (m_psf_header.width * static_cast<int>(len)))
|
||||||
|
{
|
||||||
|
len = (canvas.width / width) + 1;
|
||||||
|
last_char_width = canvas.width % width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas.height < height) height = canvas.height;
|
||||||
|
|
||||||
|
const int bytes_per_line = (m_psf_header.width + 7) / 8;
|
||||||
|
|
||||||
|
for (usize i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (i + 1 == len) width = last_char_width;
|
||||||
|
wchar_t codepoint = text[i];
|
||||||
|
|
||||||
|
u8* glyph =
|
||||||
|
m_font_data.data() + (codepoint > 0 && codepoint < (wchar_t)m_psf_header.numglyph ? codepoint : 0) *
|
||||||
|
m_psf_header.bytesperglyph;
|
||||||
|
|
||||||
|
u32 offset = (u32)i * m_psf_header.width * BYTES_PER_PIXEL;
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
u32 line = offset;
|
||||||
|
int mask = 1 << (m_psf_header.width - 1);
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
{
|
||||||
|
if (*((u32*)glyph) & mask) *(u32*)(canvas.ptr + line) = color.raw;
|
||||||
|
mask >>= 1;
|
||||||
|
line += BYTES_PER_PIXEL;
|
||||||
|
}
|
||||||
|
glyph += bytes_per_line;
|
||||||
|
offset += canvas.stride * BYTES_PER_PIXEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
libui/src/Image.cpp
Normal file
68
libui/src/Image.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* @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 <ui/Alignment.h>
|
||||||
|
#include <ui/Image.h>
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
Result<SharedPtr<Image>> Image::load(const os::Path& path)
|
||||||
|
{
|
||||||
|
auto image = TRY(make_shared<Image>());
|
||||||
|
auto file = TRY(os::File::open(path, os::File::ReadOnly));
|
||||||
|
|
||||||
|
TRY(file->read_typed(image->m_tga_header));
|
||||||
|
|
||||||
|
if (image->m_tga_header.encoding != 2) todo();
|
||||||
|
if (image->m_tga_header.bpp != 32) todo();
|
||||||
|
|
||||||
|
Buffer image_id;
|
||||||
|
TRY(file->read(image_id, image->m_tga_header.idlen));
|
||||||
|
|
||||||
|
TRY(file->read(image->m_image_data,
|
||||||
|
image->m_tga_header.w * image->m_tga_header.h * (image->m_tga_header.bpp / 8)));
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<OwnedPtr<ImageWidget>> ImageWidget::load(const os::Path& path)
|
||||||
|
{
|
||||||
|
auto widget = TRY(make_owned<ImageWidget>());
|
||||||
|
widget->m_image = TRY(Image::load(path));
|
||||||
|
widget->m_rect = { 0, 0, widget->m_image->width(), widget->m_image->height() };
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> ImageWidget::handle_mouse_move(Point)
|
||||||
|
{
|
||||||
|
return EventResult::DidNotHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> ImageWidget::handle_mouse_leave(Point)
|
||||||
|
{
|
||||||
|
return EventResult::DidNotHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> ImageWidget::handle_mouse_up(Point, int)
|
||||||
|
{
|
||||||
|
return EventResult::DidNotHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<EventResult> ImageWidget::handle_mouse_down(Point, int)
|
||||||
|
{
|
||||||
|
return EventResult::DidNotHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> ImageWidget::draw(Canvas& canvas)
|
||||||
|
{
|
||||||
|
canvas.subcanvas({ 0, 0, m_image->width(), m_image->height() }).fill(m_image->pixels(), m_image->width());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user