diff options
44 files changed, 1018 insertions, 100 deletions
diff --git a/applications/goldman/makefile b/applications/goldman/makefile index 56f633d..c6de0e7 100644 --- a/applications/goldman/makefile +++ b/applications/goldman/makefile @@ -1,5 +1,5 @@ SOURCES = \ - main.cpp + main.cpp renderer.cpp input.cpp socket.cpp build/%.cpp.o: source/%.cpp @mkdir -p $(@D) diff --git a/applications/goldman/source/input.cpp b/applications/goldman/source/input.cpp new file mode 100644 index 0000000..0cd6922 --- /dev/null +++ b/applications/goldman/source/input.cpp @@ -0,0 +1,21 @@ +#include "input.hpp" +#include "main.hpp" + +[[noreturn]] void input_thread_main() { + + euler::syscall::set_thread_name("input thread"); + + while (true) { + + auto result = euler::syscall::get_input_packet(); + if (std::holds_alternative<euler::syscall::mouse_packet>(result)) { + auto packet = std::get<euler::syscall::mouse_packet>(result); + r->lock(); + r->bump_cursor(packet.x_changed, packet.y_changed); + r->unlock(); + r->dispatch_render(); + } + + } + +} diff --git a/applications/goldman/source/input.hpp b/applications/goldman/source/input.hpp new file mode 100644 index 0000000..108cdad --- /dev/null +++ b/applications/goldman/source/input.hpp @@ -0,0 +1,3 @@ +#pragma once + +[[noreturn]] void input_thread_main(); diff --git a/applications/goldman/source/main.cpp b/applications/goldman/source/main.cpp index d74eaad..475129f 100644 --- a/applications/goldman/source/main.cpp +++ b/applications/goldman/source/main.cpp @@ -1,79 +1,37 @@ #include <daguerre/framebuffer.hpp> #include <daguerre/ppm.hpp> +#include "renderer.hpp" +#include "socket.hpp" +#include "input.hpp" +#include "main.hpp" -daguerre::hilbert_color trans_color; +//TODO: handle errors -void convert_pointer( - daguerre::hilbert_color &dest, const daguerre::hilbert_color &src) { - if (src != trans_color) - dest = src; -} +renderer *r; int main(int, char **) { - trans_color = euler::syscall::encode_color(0xff, 0x00, 0xff); - - daguerre::image<daguerre::hilbert_color> framebuffer = - daguerre::get_hilbert_framebuffer(); - - int fw = framebuffer.width; - int fh = framebuffer.height; - - daguerre::image<daguerre::hilbert_color> double_buffer(fw, fh); + euler::syscall::listener_handle listener; + euler::syscall::create_socket_listener("hilbert.compositor", listener); - std::optional<daguerre::image<daguerre::hilbert_color>> - background_original = daguerre::try_load_ppm("/assets/background.ppm"); - - daguerre::image<daguerre::hilbert_color> background; - - if (background_original.has_value()) - background = background_original->stretch(fw, fh); - else { - background = daguerre::image<daguerre::hilbert_color>(fw, fh); - background.fill(euler::syscall::encode_color(0, 0, 0)); - } + r = new renderer( + daguerre::get_hilbert_framebuffer(), + *daguerre::try_load_ppm("/assets/background.ppm"), + euler::syscall::encode_color(0x00, 0x00, 0x00), + *daguerre::try_load_ppm("/assets/pointer.ppm"), + euler::syscall::encode_color(0xff, 0x00, 0xff)); - std::optional<daguerre::image<daguerre::hilbert_color>> - pointer_original = daguerre::try_load_ppm("/assets/pointer.ppm"); + euler::syscall::start_thread([]() { r->render_thread_main(); }); + euler::syscall::start_thread(input_thread_main); - if (!pointer_original.has_value()) - //TODO - while (1) - ; + r->dispatch_render(); - daguerre::image<daguerre::hilbert_color> - pointer = std::move(*pointer_original); - - int mouse_x = fw / 2; - int mouse_y = fh / 2; + euler::syscall::set_thread_name("socket listener thread"); while (true) { - - double_buffer.copy_from(background, 0, 0); - double_buffer.convert_from(pointer, mouse_x, mouse_y, 0, 0, - std::min(pointer. width, double_buffer. width - mouse_x), - std::min(pointer.height, double_buffer.height - mouse_y), - &convert_pointer); - - framebuffer.copy_from(double_buffer, 0, 0); - - auto result = euler::syscall::get_input_packet(); - if (std::holds_alternative<euler::syscall::mouse_packet>(result)) { - const auto &packet = std::get<euler::syscall::mouse_packet>(result); - mouse_x += packet.x_changed; - mouse_y += packet.y_changed; - if (mouse_x < 0) - mouse_x = 0; - else if (mouse_x >= fw) - mouse_x = fw - 1; - if (mouse_y < 0) - mouse_y = 0; - else if (mouse_y >= fh) - mouse_y = fh - 1; - } - + euler::syscall::stream_handle socket; + euler::syscall::accept_socket_connection(listener, socket); + euler::syscall::start_thread(socket_thread_main, socket); } - return 0; - } diff --git a/applications/goldman/source/main.hpp b/applications/goldman/source/main.hpp new file mode 100644 index 0000000..57de676 --- /dev/null +++ b/applications/goldman/source/main.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "renderer.hpp" + +extern renderer *r; diff --git a/applications/goldman/source/renderer.cpp b/applications/goldman/source/renderer.cpp new file mode 100644 index 0000000..629c375 --- /dev/null +++ b/applications/goldman/source/renderer.cpp @@ -0,0 +1,102 @@ +#include "renderer.hpp" + +renderer::renderer( + daguerre::image<daguerre::hilbert_color> &&fb, + daguerre::image<daguerre::hilbert_color> &&bg, + daguerre::hilbert_color bgc, + daguerre::image<daguerre::hilbert_color> &&c, + daguerre::hilbert_color cbg) + +: framebuffer(std::move(fb)), + double_buffer(framebuffer.width, framebuffer.height), + background(std::move(bg)), cursor(std::move(c)), cursor_background(cbg), + cursor_x(framebuffer.width / 2), cursor_y(framebuffer.height / 2) { + + euler::syscall::create_private_socket(dispatcher_handle_1, dispatcher_handle_2); + + if (background.width != framebuffer.width || + background.height != framebuffer.height) { + + daguerre::image<daguerre::hilbert_color> + new_background(framebuffer.width, framebuffer.height); + + new_background.fill(bgc); + + int from_x = 0; + int from_y = 0; + int to_x = framebuffer.width / 2 - background.width / 2; + int to_y = framebuffer.height / 2 - background.height / 2; + int width = background.width; + int height = background.height; + + daguerre::make_safe(to_x, from_x, width, 0, framebuffer.width); + daguerre::make_safe(to_y, from_y, height, 0, framebuffer.height); + + new_background.copy_from(background, to_x, to_y, from_x, from_y, width, height); + background = std::move(new_background); + + } + +} + +template <class color_t> +void overlay_trans(color_t &to, const color_t &from, const color_t ¶m) { + if (from != param) + to = from; +} + +void renderer::do_render() { + + double_buffer.copy_from(background, 0, 0); + + for (auto it = windows.begin(); it != windows.end(); ++it) + + double_buffer.copy_from( + (*it)->contents, (*it)->x, (*it)->y, 0, 0, + std::min((*it)->contents.width, double_buffer.width - (*it)->x), + std::min((*it)->contents.height, double_buffer.height - (*it)->y)); + + double_buffer.convert_from( + cursor_background, cursor, cursor_x, cursor_y, 0, 0, + std::min(cursor.width, framebuffer.width - cursor_x), + std::min(cursor.height, framebuffer.height - cursor_y), + &overlay_trans); + +} + +[[noreturn]] void renderer::render_thread_main() { + euler::syscall::set_thread_name("render thread"); + while (true) { + uint8_t byte; + euler::syscall::read_from_stream(dispatcher_handle_2, 1, &byte); + mut.lock(); + euler::syscall::clear_socket_read_queue(dispatcher_handle_2); + do_render(); + mut.unlock(); + framebuffer.copy_from(double_buffer, 0, 0); + } +} + +void renderer::bump_cursor(int x_offset, int y_offset) { + + cursor_x += x_offset; + if (cursor_x < 0) + cursor_x = 0; + else if (cursor_x >= framebuffer.width) + cursor_x = framebuffer.width - 1; + + cursor_y += y_offset; + if (cursor_y < 0) + cursor_y = 0; + else if (cursor_y >= framebuffer.height) + cursor_y = framebuffer.height - 1; + +} + +void renderer::add_window(const window *w) { + windows.push_back(w); +} + +void renderer::remove_window(const window *w) { + windows.remove(w); +} diff --git a/applications/goldman/source/renderer.hpp b/applications/goldman/source/renderer.hpp new file mode 100644 index 0000000..14a5964 --- /dev/null +++ b/applications/goldman/source/renderer.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <daguerre/image.hpp> +#include "window.hpp" +#include <mutex> +#include <list> + +class renderer { + + daguerre::image<daguerre::hilbert_color> framebuffer; + daguerre::image<daguerre::hilbert_color> double_buffer; + + daguerre::image<daguerre::hilbert_color> background; + daguerre::image<daguerre::hilbert_color> cursor; + daguerre::hilbert_color cursor_background; + + int cursor_x; + int cursor_y; + + //bottom to top + std::list<const window *> windows; + + std::mutex mut; + + euler::syscall::stream_handle + dispatcher_handle_1, dispatcher_handle_2; + + void do_render(); + +public: + renderer( + daguerre::image<daguerre::hilbert_color> &&framebuffer, + daguerre::image<daguerre::hilbert_color> &&background, + daguerre::hilbert_color background_color, + daguerre::image<daguerre::hilbert_color> &&cursor, + daguerre::hilbert_color cursor_background); + + inline ~renderer() { + euler::syscall::close_stream(dispatcher_handle_1); + euler::syscall::close_stream(dispatcher_handle_2); + } + + renderer(const renderer &) = delete; + renderer &operator =(const renderer &) = delete; + + [[noreturn]] void render_thread_main(); + + inline void lock() { mut.lock(); } + inline void unlock() { mut.unlock(); } + + inline void dispatch_render() { + uint8_t byte = 0; + euler::syscall::write_to_stream(dispatcher_handle_1, 1, &byte); + } + + void bump_cursor(int x_offset, int y_offset); + + void add_window(const window *w); + void remove_window(const window *w); + +}; diff --git a/applications/goldman/source/socket.cpp b/applications/goldman/source/socket.cpp new file mode 100644 index 0000000..b175fe9 --- /dev/null +++ b/applications/goldman/source/socket.cpp @@ -0,0 +1,162 @@ +#include <daguerre/image.hpp> +#include "socket.hpp" +#include "window.hpp" +#include "main.hpp" +#include <memory> +#include <vector> + +struct socket_state { + + euler::syscall::stream_handle socket; + std::vector<window *> windows; + daguerre::hilbert_color window_bg = euler::syscall::encode_color(0, 0, 0); + + bool try_open_window() { + + struct [[gnu::packed]] { + uint32_t width; + uint32_t height; + } body; + + if (euler::syscall::read_from_stream(socket, sizeof(body), &body) != + euler::syscall::stream_result::success) + return false; + + window *w = new window(body.width, body.height); + w->contents.fill(window_bg); + + uint16_t wid = 0; + + while (wid < windows.size()) + if (windows[wid] != 0) + ++wid; + else + break; + + if (wid == windows.size()) + windows.push_back(w); + else + windows[wid] = w; + + r->lock(); + r->add_window(w); + r->unlock(); + + struct [[gnu::packed]] { + uint8_t type; + uint16_t the_window; + } response { + .type = 0x00, + .the_window = wid + }; + + return + euler::syscall::write_to_stream(socket, sizeof(response), &response) == + euler::syscall::stream_result::success; + + } + + bool try_update_window_region() { + + struct [[gnu::packed]] { + uint16_t window; + uint32_t start_x; + uint32_t start_y; + uint32_t width; + uint32_t height; + } body_head; + + if (euler::syscall::read_from_stream(socket, sizeof(body_head), &body_head) != + euler::syscall::stream_result::success) + return false; + + std::vector<daguerre::hilbert_color> data(body_head.width * body_head.height); + + if (euler::syscall::read_from_stream(socket, data.size() * 4, data.data()) != + euler::syscall::stream_result::success) + return false; + + daguerre::image<daguerre::hilbert_color> + data_as_image(body_head.width, body_head.height, data.data(), body_head.width, false); + + if (body_head.window >= windows.size() || !windows[body_head.window]) + return false; + + window *w = windows[body_head.window]; + + r->lock(); + + if ((int)body_head.start_x + data_as_image.width > w->contents.width || + (int)body_head.start_y + data_as_image.height > w->contents.height) { + r->unlock(); + return false; + } + + w->contents.copy_from(data_as_image, body_head.start_x, body_head.start_y); + + r->unlock(); + r->dispatch_render(); + return true; + + } + + bool try_close_window() { + + uint16_t wid; + + if (euler::syscall::read_from_stream(socket, 2, &wid) != + euler::syscall::stream_result::success) + return false; + + if (wid >= windows.size() || !windows[wid]) + return false; + + r->lock(); + + r->remove_window(windows[wid]); + windows[wid] = 0; + + r->unlock(); + r->dispatch_render(); + return true; + + } + + bool try_process_request() { + + uint8_t type; + if (euler::syscall::read_from_stream(socket, 1, &type) != + euler::syscall::stream_result::success) + return false; + + switch (type) { + case 0x00: return try_open_window(); + case 0x01: return try_update_window_region(); + case 0x02: return try_close_window(); + default: return false; + } + + } + +}; + +[[noreturn]] void socket_thread_main(euler::syscall::stream_handle socket) { + + euler::syscall::set_thread_name("socket thread"); + + socket_state *state = new socket_state { + .socket = socket, .windows = {} }; + while (state->try_process_request()) ; + + r->lock(); + for (unsigned i = 0; i < state->windows.size(); ++i) { + r->remove_window(state->windows[i]); + delete state->windows[i]; + } + r->unlock(); + + delete state; + euler::syscall::close_stream(socket); + euler::syscall::end_this_thread(0); + +} diff --git a/applications/goldman/source/socket.hpp b/applications/goldman/source/socket.hpp new file mode 100644 index 0000000..a9dbeb2 --- /dev/null +++ b/applications/goldman/source/socket.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include <euler/syscall.hpp> + +[[noreturn]] void socket_thread_main(euler::syscall::stream_handle socket); diff --git a/applications/goldman/source/window.hpp b/applications/goldman/source/window.hpp new file mode 100644 index 0000000..008af2f --- /dev/null +++ b/applications/goldman/source/window.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <daguerre/image.hpp> + +struct window { + + daguerre::image<daguerre::hilbert_color> contents; + + int x; + int y; + + window(int width, int height) : contents(width, height), x(0), y(0) {} + +}; diff --git a/applications/hello/makefile b/applications/hello/makefile new file mode 100644 index 0000000..5e12644 --- /dev/null +++ b/applications/hello/makefile @@ -0,0 +1,12 @@ +SOURCES = \ + main.cpp + +build/%.cpp.o: source/%.cpp + @mkdir -p $(@D) + $(HILBERT_CC) -c $^ -o $@ + +build/hello.elf: $(SOURCES:%=build/%.o) + $(HILBERT_CC) $^ -ldaguerre -o $@ + +clean: + rm -rf build diff --git a/applications/hello/source/main.cpp b/applications/hello/source/main.cpp new file mode 100644 index 0000000..3f132c6 --- /dev/null +++ b/applications/hello/source/main.cpp @@ -0,0 +1,37 @@ +#include <goldman/protocol.hpp> +#include <daguerre/psf.hpp> + +template <class color_t> +void overlay(color_t &to, const bool &from, const color_t ¶m) { + if (from) + to = param; +} + +int main(int, char **) { + + auto bg = euler::syscall::encode_color(0xaa, 0xaa, 0xaa); + auto fg = euler::syscall::encode_color(0x00, 0x00, 0x00); + + daguerre::image<daguerre::hilbert_color> image(300, 200); + image.fill(bg); + + auto font = daguerre::try_load_psf("/assets/terminus-bold-18x10.psf"); + image.render_text(*font, fg, 10, 10, "Hello, world!", &overlay); + + euler::syscall::stream_handle s; + euler::syscall::connect_to_socket("hilbert.compositor", s); + + goldman::protocol::send_open_window(s, 300, 200); + + uint8_t byte; + euler::syscall::read_from_stream(s, 1, &byte); + + auto w = goldman::protocol::get_window_opened_body(s); + + goldman::protocol::send_update_window_region( + s, w, 0, 0, 300, 200, image.buffer, image.buffer_pitch); + + euler::syscall::read_from_stream(s, 1, &byte); + __builtin_unreachable(); + +} diff --git a/documentation/compositor.txt b/documentation/compositor.txt index 4d8e70f..38bd482 100644 --- a/documentation/compositor.txt +++ b/documentation/compositor.txt @@ -1,11 +1,11 @@ compositors listen on the socket id "hilbert.compositor". when a window is opened by an application, that window can only be referred to -on that stream. the opaque value given in the "window opened" message refers to -that window in future messages on that stream. it is guaranteed to be distinct -for different windows on the same stream, and in no way guaranteed to be -distinct for different windows on different streams. the window is bound -just to the stream, not to the application. if the stream where a window +on that socket. the opaque value given in the "window opened" message refers to +that window in future messages on that socket. it is guaranteed to be distinct +for different windows on the same socket, and in no way guaranteed to be +distinct for different windows on different sockets. the window is bound +just to the socket, not to the application. if the socket where a window was created is gifted to a new process, the new process has complete control over the window, and the compositor does not need to be informed. @@ -37,8 +37,13 @@ messages from applications to compositor: dword: height color rectangle: the data + close window: + byte: 0x02 + window: the window + messages from compositor to application: window opened: byte: 0x00 window: the window + these come in the order the open window requests were received diff --git a/documentation/kernel-interfaces/syscalls.txt b/documentation/kernel-interfaces/syscalls.txt index 399eb19..933ad34 100644 --- a/documentation/kernel-interfaces/syscalls.txt +++ b/documentation/kernel-interfaces/syscalls.txt @@ -198,3 +198,8 @@ get environment variable value: rdi in: pointer to variable name rsi in: variable name length rdx in: pointer to buffer for variable value + +set thread name: + rax in: 24 + rdi in: pointer to thread name + rsi in: thread name length diff --git a/euler/include/euler/syscall.hpp b/euler/include/euler/syscall.hpp index 64456ae..4a3daa4 100644 --- a/euler/include/euler/syscall.hpp +++ b/euler/include/euler/syscall.hpp @@ -1,11 +1,11 @@ #pragma once +#include <std/fwd/string.hpp> +#include <std/fwd/vector.hpp> #include <optional> #include <cstdint> #include <utility> #include <variant> -#include <string> -#include <vector> namespace euler::syscall { @@ -125,6 +125,30 @@ namespace euler::syscall { void start_thread(void (*entry_point)(uint64_t), uint64_t arg); //entry_point must not return + template <class obj_t> + void start_thread(void (*entry_point)(obj_t *), obj_t *arg) { + start_thread((void (*)(uint64_t))entry_point, (uint64_t)arg); + } + + //entry_point must not return + template <class obj_t> + void start_thread(void (*entry_point)(const obj_t *), const obj_t *arg) { + start_thread((void (*)(uint64_t))entry_point, (uint64_t)arg); + } + + //entry_point must not return + template <class obj_t> + void start_thread(void (*entry_point)(obj_t &), obj_t &arg) { + start_thread((void (*)(uint64_t))entry_point, (uint64_t)&arg); + } + + //entry_point must not return + template <class obj_t> + void start_thread(void (*entry_point)(const obj_t &), const obj_t &arg) { + start_thread((void (*)(uint64_t))entry_point, (uint64_t)&arg); + } + + //entry_point must not return void start_thread(void (*entry_point)()); //return value is number of bytes cleared @@ -133,4 +157,9 @@ namespace euler::syscall { std::optional<std::string> try_get_environment_variable( const std::string &name); + void set_thread_name(const std::string &name); + } + +#include <string> +#include <vector> diff --git a/euler/include/list b/euler/include/list new file mode 100644 index 0000000..51eb3ba --- /dev/null +++ b/euler/include/list @@ -0,0 +1,3 @@ +#pragma once + +#include <std/list.hpp> diff --git a/euler/include/mutex b/euler/include/mutex new file mode 100644 index 0000000..7a03381 --- /dev/null +++ b/euler/include/mutex @@ -0,0 +1,4 @@ +#pragma once + +#include <std/unique_lock.hpp> +#include <std/mutex.hpp> diff --git a/euler/include/std/allocator.hpp b/euler/include/std/allocator.hpp index 32ba005..e76feb9 100644 --- a/euler/include/std/allocator.hpp +++ b/euler/include/std/allocator.hpp @@ -1,5 +1,7 @@ #pragma once +#include <std/fwd/allocator.hpp> + #include <euler/heap.hpp> namespace std { diff --git a/euler/include/std/fwd/allocator.hpp b/euler/include/std/fwd/allocator.hpp new file mode 100644 index 0000000..18e14a8 --- /dev/null +++ b/euler/include/std/fwd/allocator.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace std { + template <class T> + struct allocator; +} diff --git a/euler/include/std/fwd/string.hpp b/euler/include/std/fwd/string.hpp new file mode 100644 index 0000000..5e46abf --- /dev/null +++ b/euler/include/std/fwd/string.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace std { + class string; +} diff --git a/euler/include/std/fwd/vector.hpp b/euler/include/std/fwd/vector.hpp new file mode 100644 index 0000000..fd8fe15 --- /dev/null +++ b/euler/include/std/fwd/vector.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include <std/fwd/allocator.hpp> + +namespace std { + template <class T, class Allocator = std::allocator<T>> + class vector; +} diff --git a/euler/include/std/list.hpp b/euler/include/std/list.hpp new file mode 100644 index 0000000..77eaaec --- /dev/null +++ b/euler/include/std/list.hpp @@ -0,0 +1,157 @@ +#pragma once + +#include <memory> + +namespace std { + + template <class T, class Allocator = std::allocator<T>> + class list { + + public: + struct node { + T value; + node *prev; + node *next; + }; + + template <class V> + struct generic_iterator { + + node *the_node; + + bool operator ==(const generic_iterator &other) { + return the_node == other.the_node; + } + + bool operator !=(const generic_iterator &other) { + return the_node != other.the_node; + } + + V &operator *() { + return the_node->value; + } + + V *operator ->() { + return &the_node->value; + } + + generic_iterator &operator ++() { + the_node = the_node->next; + return *this; + } + + }; + + using iterator = generic_iterator<T>; + using const_iterator = generic_iterator<const T>; + + private: + node *first_node; + node *last_node; + size_t count; + + public: + void push_back(const T &value) { + node *n = new node { .value = value, + .prev = last_node, .next = 0 }; + if (last_node) last_node->next = n; + else first_node = n; + last_node = n; + ++count; + } + + void push_back(T &&value) { + node *n = new node { + .value = std::move(value), + .prev = last_node, .next = 0 }; + if (last_node) last_node->next = n; + else first_node = n; + last_node = n; + ++count; + } + + iterator erase(iterator pos) { + --count; + auto *n = pos.the_node; + auto *r = n->next; + if (n->prev) n->prev->next = n->next; + else first_node = n->next; + if (n->next) n->next->prev = n->prev; + else last_node = n->prev; + delete n; + return iterator { .the_node = r }; + } + + iterator begin() const noexcept { + return iterator { .the_node = first_node }; + } + + iterator end() const noexcept { + return iterator { .the_node = 0 }; + } + + size_t remove(const T &value) { + size_t removed = 0; + auto it = begin(); + while (it != end()) + if (*it == value) { + it = erase(it); + ++removed; + } + else + ++it; + count -= removed; + return removed; + } + + list() : first_node(0), last_node(0), count(0) {} + + list(const list &other) : first_node(0), last_node(0), count(0) { + for (node *n = other.first_node; n; n = n->next) + push_back(n->value); + } + + list(list &&other) : first_node(other.first_node), + last_node(other.last_node), count(other.count) { + other.first_node = 0; + other.last_node = 0; + other.count = 0; + } + + void clear() { + + if (count == 0) return; + + for (node *n = first_node->next; n; n = n->next) + delete n->prev; + delete last_node; + + first_node = 0; + last_node = 0; + count = 0; + + } + + ~list() { + clear(); + } + + list &operator =(const list &other) { + clear(); + for (node *n = other.first_node; n; n = n->next) + push_back(n->value); + } + + list &operator =(list &&other) { + clear(); + first_node = other.first_node; + last_node = other.last_node; + count = other.count; + other.first_node = 0; + other.last_node = 0; + other.count = 0; + } + + }; + +} diff --git a/euler/include/std/mutex.hpp b/euler/include/std/mutex.hpp new file mode 100644 index 0000000..0a27877 --- /dev/null +++ b/euler/include/std/mutex.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <euler/syscall.hpp> + +namespace std { + + class mutex { + + euler::syscall::stream_handle in_handle; + euler::syscall::stream_handle out_handle; + + public: + inline mutex() noexcept { + euler::syscall::create_private_socket(in_handle, out_handle); + uint8_t byte = 0; + euler::syscall::write_to_stream(in_handle, 1, &byte); + } + + mutex(const mutex &) = delete; + + inline ~mutex() { + euler::syscall::close_stream(in_handle); + euler::syscall::close_stream(out_handle); + } + + mutex &operator =(const mutex &) = delete; + + inline void lock() { + uint8_t byte; + euler::syscall::read_from_stream(out_handle, 1, &byte); + } + + inline void unlock() { + uint8_t byte = 0; + euler::syscall::write_to_stream(in_handle, 1, &byte); + } + + }; + +} diff --git a/euler/include/std/string.hpp b/euler/include/std/string.hpp index 7ccdbc2..505ee69 100644 --- a/euler/include/std/string.hpp +++ b/euler/include/std/string.hpp @@ -1,5 +1,7 @@ #pragma once +#include <std/fwd/string.hpp> + #include <cstddef> #include <vector> @@ -16,9 +18,7 @@ namespace std { : characters(other.characters) {} constexpr string(string &&other) noexcept - : characters(std::move(other.characters)) { - other.characters.push_back('\0'); - } + : characters(std::move(other.characters)) {} constexpr string(const char *s) { size_t count = 0; @@ -38,7 +38,6 @@ namespace std { constexpr string &operator =(string &&str) noexcept { characters = std::move(str.characters); - str.characters.push_back('\0'); return *this; } diff --git a/euler/include/std/unique_lock.hpp b/euler/include/std/unique_lock.hpp new file mode 100644 index 0000000..14b3645 --- /dev/null +++ b/euler/include/std/unique_lock.hpp @@ -0,0 +1,53 @@ +#pragma once + +namespace std { + + template <class Mutex> + class unique_lock { + + Mutex *the_mutex; + bool has_locked; + + public: + inline unique_lock() noexcept : the_mutex(0) {} + + unique_lock(const unique_lock &other) = delete; + + inline unique_lock(unique_lock &&other) noexcept + : the_mutex(other.the_mutex), has_locked(other.has_locked) { + other.the_mutex = 0; + } + + inline explicit unique_lock(Mutex &m) + : the_mutex(&m), has_locked(true) { + the_mutex->lock(); + } + + inline ~unique_lock() { + if (the_mutex && has_locked) + the_mutex->unlock(); + } + + unique_lock &operator =(const unique_lock &other) = delete; + + inline unique_lock &operator =(unique_lock &&other) { + if (the_mutex && has_locked) + the_mutex->unlock(); + the_mutex = other.the_mutex; + has_locked = other.has_locked; + other.the_mutex = 0; + } + + inline void lock() { + the_mutex->lock(); + has_locked = true; + } + + inline void unlock() { + the_mutex->unlock(); + has_locked = false; + } + + }; + +} diff --git a/euler/include/std/vector.hpp b/euler/include/std/vector.hpp index a1ac21d..1c35d9d 100644 --- a/euler/include/std/vector.hpp +++ b/euler/include/std/vector.hpp @@ -1,10 +1,12 @@ #pragma once +#include <std/fwd/vector.hpp> + #include <memory> namespace std { - template <class T, class Allocator = std::allocator<T>> + template <class T, class Allocator> class vector { public: diff --git a/euler/source/entry.cpp b/euler/source/entry.cpp index e79209c..ab721f1 100644 --- a/euler/source/entry.cpp +++ b/euler/source/entry.cpp @@ -6,6 +6,8 @@ int main(int argc, char **argv); extern "C" [[noreturn]] void _start() { + //TODO: call static initializers + auto argc_raw = euler::syscall::try_get_environment_variable("ARGC"); int argc = argc_raw.has_value() ? std::stoi(argc_raw.value()) : 0; diff --git a/euler/source/std/cstdio.cpp b/euler/source/std/cstdio.cpp index 8c12a7c..485efc3 100644 --- a/euler/source/std/cstdio.cpp +++ b/euler/source/std/cstdio.cpp @@ -1,4 +1,5 @@ #include <cstdio> +#include <string> extern "C" FILE *fopen(const char *filename, const char *mode) { diff --git a/euler/source/std/string.cpp b/euler/source/std/string.cpp index 31c47a5..ae397b1 100644 --- a/euler/source/std/string.cpp +++ b/euler/source/std/string.cpp @@ -44,6 +44,7 @@ namespace std { value = value * base + c - 'A' + 10; else break; + ++i; } if (pos != 0) @@ -75,10 +76,11 @@ namespace std { } std::string operator +(std::string &&lhs, std::string &&rhs) { + size_t og_lhs_s = lhs.size(); std::string s = std::move(lhs); - s.resize(lhs.size() + rhs.size()); + s.resize(og_lhs_s + rhs.size()); for (size_t i = 0; i < rhs.size(); ++i) - s[lhs.size() + i] = rhs[i]; + s[og_lhs_s + i] = rhs[i]; rhs.clear(); return s; } diff --git a/euler/source/stream.cpp b/euler/source/stream.cpp index faf2907..da973ba 100644 --- a/euler/source/stream.cpp +++ b/euler/source/stream.cpp @@ -1,4 +1,5 @@ #include <euler/stream.hpp> +#include <algorithm> #include <cstring> namespace euler { diff --git a/euler/source/syscall.cpp b/euler/source/syscall.cpp index b3ed3a8..0d30c4a 100644 --- a/euler/source/syscall.cpp +++ b/euler/source/syscall.cpp @@ -1,4 +1,6 @@ #include <euler/syscall.hpp> +#include <string> +#include <vector> extern "C" void __euler_do_syscall( uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx); @@ -394,4 +396,15 @@ namespace euler::syscall { } + void set_thread_name(const std::string &name) { + + uint64_t rax = 24; + uint64_t rdi = (uint64_t)name.data(); + uint64_t rsi = name.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + } diff --git a/kernel/include/hilbert/kernel/application.hpp b/kernel/include/hilbert/kernel/application.hpp index a4b59ff..e7c7d2e 100644 --- a/kernel/include/hilbert/kernel/application.hpp +++ b/kernel/include/hilbert/kernel/application.hpp @@ -112,6 +112,8 @@ namespace hilbert::kernel::application { utility::list<string_pair> environment_variables; public: + utility::string name; + app_memory *memory; //set in get_framebuffer syscall @@ -121,13 +123,16 @@ namespace hilbert::kernel::application { uint64_t id; //this class takes ownership of memory - process(app_memory *memory); + process(app_memory *memory, const utility::string &name); ~process(); //arguments are utility::move'd void add_environment_variable( utility::string &&name, utility::string &&value); + //null if unset + utility::string *get_environment_variable(const utility::string &name); + void add_thread(thread *t); void notify_thread_ended(thread *t, int exit_code); void on_end_process(int exit_code); @@ -180,6 +185,8 @@ namespace hilbert::kernel::application { utility::maybe<unsigned> new_socket_stream_id; public: + utility::string name; + process *owner; //the cpu state is saved here when the thread is not running. diff --git a/kernel/include/hilbert/kernel/serial.hpp b/kernel/include/hilbert/kernel/serial.hpp index e7a44f2..7751fa0 100644 --- a/kernel/include/hilbert/kernel/serial.hpp +++ b/kernel/include/hilbert/kernel/serial.hpp @@ -1,5 +1,6 @@ #pragma once +#include <hilbert/kernel/utility.hpp> #include <stdint.h> namespace hilbert::kernel { @@ -15,6 +16,11 @@ namespace hilbert::kernel { } } + static inline void serial_putstr(const utility::string &str) { + for (unsigned i = 0; i < str.count; ++i) + serial_putchar(str.buffer[i]); + } + template <int digits, int dot_every = 4> static inline void serial_puthex(uint64_t n) { for (int d = digits - 1; d >= 0; --d) { diff --git a/kernel/include/hilbert/kernel/utility.hpp b/kernel/include/hilbert/kernel/utility.hpp index b7f1a1b..a21d3fe 100644 --- a/kernel/include/hilbert/kernel/utility.hpp +++ b/kernel/include/hilbert/kernel/utility.hpp @@ -107,6 +107,10 @@ namespace hilbert::kernel::utility { n->value = value; n->next = 0; n->prev = last; + if (last) + last->next = n; + else + first = n; last = n; } @@ -115,12 +119,19 @@ namespace hilbert::kernel::utility { n->value = value; n->next = 0; n->prev = last; + if (last) + last->next = n; + else + first = n; last = n; } void clear() { - for (node *n = first; n; n = n->next) - delete n; + if (first) { + for (node *n = first->next; n; n = n->next) + delete n->prev; + delete last; + } first = 0; last = 0; } diff --git a/kernel/source/application.cpp b/kernel/source/application.cpp index 8070019..6df49cd 100644 --- a/kernel/source/application.cpp +++ b/kernel/source/application.cpp @@ -83,7 +83,8 @@ namespace hilbert::kernel::application { resume_thread(t->saved_state); } - process::process(app_memory *memory) : memory(memory) {} + process::process(app_memory *memory, const utility::string &name) + : name(name), memory(memory) {} process::~process() { delete memory; //:p @@ -94,6 +95,14 @@ namespace hilbert::kernel::application { environment_variables.insert_end({.a = name, .b = value}); } + utility::string *process::get_environment_variable( + const utility::string &name) { + for (auto *i = environment_variables.first; i; i = i->next) + if (i->value.a == name) + return &i->value.b; + return 0; + } + void process::add_thread(thread *t) { threads.insert_end(t); } @@ -197,8 +206,8 @@ namespace hilbert::kernel::application { thread::thread(process *owner, uint64_t entry) : stack_top(owner->memory->map_new_stack()), waiting_for_socket_stream(0), - waiting_to_accept_from(0), waiting_to_connect_to(0), - waiting_for_input(false), owner(owner) { + waiting_to_accept_from(0), waiting_to_connect_to(0), waiting_for_input(false), + name(utility::string("main", 4)), owner(owner) { saved_state.rax = 0; saved_state.rbx = 0; diff --git a/kernel/source/entry.cpp b/kernel/source/entry.cpp index 2389bb1..efa70fd 100644 --- a/kernel/source/entry.cpp +++ b/kernel/source/entry.cpp @@ -214,7 +214,8 @@ extern "C" [[noreturn]] void entry() { if (load_init_result != load_app_result::success) panic(0xc39db3); - application::process *init_process = new application::process(init_memory); + application::process *init_process = + new application::process(init_memory, utility::string("init", 4)); init_process->add_environment_variable( utility::string("ARGC", 4), utility::string("1", 1)); init_process->add_environment_variable( diff --git a/kernel/source/interrupts.cpp b/kernel/source/interrupts.cpp index 41d632c..1a1f4d1 100644 --- a/kernel/source/interrupts.cpp +++ b/kernel/source/interrupts.cpp @@ -79,6 +79,11 @@ extern "C" [[noreturn]] void print_exception() { print_reg("r14", exception_info.r14); print_reg("r15", exception_info.r15); + if (application::running_thread != 0) { + serial_putstr("running app = "); + serial_putstr(application::running_thread->owner->name); + } + panic(0xba40bb); } diff --git a/kernel/source/syscall.cpp b/kernel/source/syscall.cpp index f2a6801..c631df1 100644 --- a/kernel/source/syscall.cpp +++ b/kernel/source/syscall.cpp @@ -183,7 +183,7 @@ namespace hilbert::kernel::syscall { .waiting_to_read = utility::queue<application::thread *>(), .is_other_side_open = true, .other_process = p2, .other_end = 0 }; se2_out = new application::socket_stream_end { - .the_socket = s, .read_queue = s->queue_2, .write_queue = s->queue_2, + .the_socket = s, .read_queue = s->queue_2, .write_queue = s->queue_1, .waiting_to_read = utility::queue<application::thread *>(), .is_other_side_open = true, .other_process = p1, .other_end = se1_out }; se1_out->other_end = se2_out; @@ -494,8 +494,12 @@ namespace hilbert::kernel::syscall { rax = (uint64_t)application::stream_result::other_end_closed; return; } - for (uint64_t i = 0; i < count; ++i) + auto &wtr_queue = ss->other_end->waiting_to_read; + for (uint64_t i = 0; i < count; ++i) { ss->write_queue.insert(buffer[i]); + if (wtr_queue.count > 0) + application::paused_threads->insert(wtr_queue.take()); + } rax = (uint64_t)application::stream_result::success; } @@ -617,7 +621,8 @@ namespace hilbert::kernel::syscall { break; } - application::process *p = new application::process(memory); + application::process *p = + new application::process(memory, file.dir_entry.name); for (uint64_t i = 0; i < psi->env_var_count; ++i) p->add_environment_variable( @@ -735,6 +740,66 @@ namespace hilbert::kernel::syscall { } + void get_environment_variable_length_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { + + const char *name = (const char *)rdi; + uint64_t name_len = rsi; + set_zero(rax, rdi, rsi, rdx); + + auto *app = application::running_thread->owner; + + if (!app->memory->valid_to_read(name, name + name_len, false)) + return; + + utility::string name_string(name, name_len); + utility::string *value = app->get_environment_variable(name_string); + rax = value == 0 ? (uint64_t)-1 : value->count; + + } + + void get_environment_variable_value_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { + + const char *name = (const char *)rdi; + uint64_t name_len = rsi; + char *buffer = (char *)rdx; + set_zero(rax, rdi, rsi, rdx); + + auto *app = application::running_thread->owner; + + if (!app->memory->valid_to_read(name, name + name_len, false)) + return; + + utility::string name_string(name, name_len); + utility::string *value = app->get_environment_variable(name_string); + if (value == 0) + return; + + if (!app->memory->valid_to_read(buffer, buffer + value->count, true)) + return; + + for (unsigned i = 0; i < value->count; ++i) + buffer[i] = value->buffer[i]; + + } + + void set_thread_name_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { + + const char *name = (const char *)rdi; + uint64_t name_len = rsi; + set_zero(rax, rdi, rsi, rdx); + + auto *t = application::running_thread; + + if (!t->owner->memory->valid_to_read(name, name + name_len, false)) + return; + + t->name = utility::string(name, name_len); + + } + void (*handlers[])( uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) = { @@ -759,11 +824,14 @@ namespace hilbert::kernel::syscall { &set_stream_length_syscall, &get_other_end_process_handle_syscall, &start_thread_syscall, - &clear_socket_read_queue_syscall + &clear_socket_read_queue_syscall, + &get_environment_variable_length_syscall, + &get_environment_variable_value_syscall, + &set_thread_name_syscall }; - static constexpr int max_syscall_number = 20; + static constexpr int max_syscall_number = 24; } diff --git a/libraries/daguerre/include/daguerre/impl/fixed-font.hpp b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp index dbff798..1a5f3d8 100644 --- a/libraries/daguerre/include/daguerre/impl/fixed-font.hpp +++ b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp @@ -20,7 +20,7 @@ namespace daguerre { template <class color_t> fixed_font<color_t>::fixed_font(fixed_font<color_t> &&other) : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { - for (int i = 0; 0 < 128; ++i) + for (int i = 0; i < 128; ++i) glyphs[i] = std::move(other.glyphs[i]); other.glyph_width = 0; other.glyph_height = 0; @@ -31,7 +31,7 @@ namespace daguerre { fixed_font<color_t> &&other) { glyph_width = other.glyph_width; glyph_height = other.glyph_height; - for (int i = 0; 0 < 128; ++i) + for (int i = 0; i < 128; ++i) glyphs[i] = std::move(other.glyphs[i]); other.glyph_width = 0; other.glyph_height = 0; diff --git a/libraries/daguerre/include/daguerre/impl/image.hpp b/libraries/daguerre/include/daguerre/impl/image.hpp index 1264879..9160951 100644 --- a/libraries/daguerre/include/daguerre/impl/image.hpp +++ b/libraries/daguerre/include/daguerre/impl/image.hpp @@ -185,7 +185,7 @@ namespace daguerre { const param_t ¶m, const image<other_color_t> &other, int to_x, int to_y, param_converter_t<color_t, other_color_t, param_t> *conversion) { convert_from( - param, other, to_x, to_y, 0, 0, other.width, other.y, conversion); + param, other, to_x, to_y, 0, 0, other.width, other.height, conversion); } template <class color_t> diff --git a/libraries/goldman/include/goldman/protocol.hpp b/libraries/goldman/include/goldman/protocol.hpp new file mode 100644 index 0000000..b7f4d51 --- /dev/null +++ b/libraries/goldman/include/goldman/protocol.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include <euler/syscall.hpp> +#include <memory> + +//TODO: handle stream errors, make thread safe + +namespace goldman::protocol { + + typedef euler::syscall::encoded_color color; + typedef uint16_t window; + + static inline void send_open_window( + euler::syscall::stream_handle socket, uint32_t width, uint32_t height) { + + struct [[gnu::packed]] { + uint8_t type; + uint32_t width; + uint32_t height; + } packet { + .type = 0x00, + .width = width, + .height = height + }; + + euler::syscall::write_to_stream(socket, sizeof(packet), &packet); + + } + + void send_update_window_region( + euler::syscall::stream_handle socket, window the_window, + uint32_t start_x, uint32_t start_y, uint32_t width, + uint32_t height, const color *the_data, size_t data_pitch) { + + struct [[gnu::packed]] { + uint8_t type; + window the_window; + uint32_t start_x; + uint32_t start_y; + uint32_t width; + uint32_t height; + } packet_head { + .type = 0x01, + .the_window = the_window, + .start_x = start_x, + .start_y = start_y, + .width = width, + .height = height + }; + + euler::syscall::write_to_stream(socket, sizeof(packet_head), &packet_head); + for (uint32_t y = 0; y < height; ++y) + euler::syscall::write_to_stream( + socket, width * sizeof(color), the_data + data_pitch * y); + + } + + void send_close_window( + euler::syscall::stream_handle socket, window the_window) { + + struct [[gnu::packed]] { + uint8_t type; + window the_window; + } packet { + .type = 0x02, + .the_window = the_window + }; + + euler::syscall::write_to_stream(socket, sizeof(packet), &packet); + + } + + enum class response_id : uint8_t { + window_opened + }; + + window get_window_opened_body(euler::syscall::stream_handle socket) { + + window w; + euler::syscall::read_from_stream(socket, sizeof(w), &w); + return w; + + } + +} @@ -7,7 +7,8 @@ EXTRA_CC_ARGS = -Wall -Wextra -Og -ggdb -fno-exceptions HILBERT_NASM = nasm -f elf64 HILBERT_CC = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-c++ -std=c++20 \ ${EXTRA_CC_ARGS} -static -mno-sse -I include -I $(abspath euler/include) \ - -I $(abspath libraries/daguerre/include) -I ${MINTSUKI_HEADERS_DIR} + -I $(abspath libraries/daguerre/include) -I ${MINTSUKI_HEADERS_DIR} \ + -I $(abspath libraries/goldman/include) HILBERT_AR = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ar HILBERT_LD = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ld -z noexecstack @@ -25,8 +26,8 @@ LIBSTDCPP_DEP = toolchain/.libstdcpp-done EULER_DEP = toolchain/.euler-done DAGUERRE_DEP = toolchain/.daguerre-done -APP_DEPS = ${GCC_DEP} ${LIBGCC_DEP} ${LIBSTDCPP_DEP} ${EULER_DEP} -LIBRARY_DEPS = ${GCC_DEP} ${LIBSTDCPP_DEP} +APP_DEPS = ${EULER_DEP} +LIBRARY_DEPS = ${LIBSTDCPP_DEP} .PHONY: default run clean clean-dependencies @@ -41,6 +42,7 @@ clean: make -C kernel clean make -C applications/init clean make -C applications/goldman clean + make -C applications/hello clean make -C libraries/daguerre clean clean-dependencies: clean @@ -90,7 +92,7 @@ ${LIBGCC_DEP}: ${GCC_DEP} +make -C dependencies/gcc/build install-target-libgcc touch $@ -${LIBSTDCPP_DEP}: ${GCC_DEP} +${LIBSTDCPP_DEP}: ${LIBGCC_DEP} +make -C dependencies/gcc/build all-target-libstdc++-v3 +make -C dependencies/gcc/build install-target-libstdc++-v3 patch toolchain/usr/x86_64-elf/include/c++/14.1.0/memory patches/gcc-memory.patch @@ -117,13 +119,18 @@ applications/init/build/init.elf: ${APP_DEPS} applications/goldman/build/goldman.elf: ${APP_DEPS} ${DAGUERRE_DEP} +make -C applications/goldman build/goldman.elf +applications/hello/build/hello.elf: ${APP_DEPS} ${DAGUERRE_DEP} + +make -C applications/hello build/hello.elf + build/initfs.tgz: applications/init/build/init.elf \ - applications/goldman/build/goldman.elf + applications/goldman/build/goldman.elf \ + applications/hello/build/hello.elf @mkdir -p build rm -rf build/initfs cp -r skeleton build/initfs cp applications/init/build/init.elf build/initfs/bin/init cp applications/goldman/build/goldman.elf build/initfs/bin/goldman + cp applications/hello/build/hello.elf build/initfs/bin/hello cd build/initfs && tar czf ../initfs.tgz . build/disk.iso: kernel/build/kernel.elf build/initfs.tgz ${LIMINE_DEP} @@ -1,6 +1,5 @@ target remote | qemu-system-x86_64 -gdb stdio -cdrom build/disk.iso -boot d symbol-file kernel/build/kernel.elf -add-symbol-file applications/goldman/build/goldman.elf set disassembly-flavor intel set print asm-demangle on layout src @@ -67,7 +67,7 @@ the following directories and files are released under the text in cc0.txt project structure: - applications/goldman: - in the future, this will be the default compositor. + the default compositor, in a very early stage - applications/init: the initial program loaded by the kernel. currently it just @@ -87,6 +87,9 @@ project structure: - libraries/daguerre: an image loading / rendering library. + - libraries/goldman: + a library for interfacing with the goldman compositor + - patches: a couple patches that are applied to dependencies |