Compare commits

...

10 Commits

Author SHA1 Message Date
Bananymous 2b0d198b05 Terminal: Allow resizing of the terminal 2025-05-05 03:15:30 +03:00
Bananymous 7798145c74 WindowServer: Implement window resizing
windows can now set resizable attribute which allows window server to
resize them
2025-05-05 03:14:50 +03:00
Bananymous fcfadd7c74 LibGUI: Add background color for windows
This is used when resizing a window to fill the empty space
2025-05-05 03:12:31 +03:00
Bananymous c0181820a9 ports/tinygb: Set title like sdl frontend does 2025-05-05 01:10:41 +03:00
Bananymous 76d4e6bd18 LibGUI: Add support for changing window title 2025-05-05 01:10:05 +03:00
Bananymous ccb81de85d snake: Use box drawing characters
This makes it look much better.

Also fix bug with apples generating on top of snake's tail
2025-05-05 00:30:58 +03:00
Bananymous 82f4975f45 LibC: Save callee saved registers on setjmp 2025-05-04 13:54:35 +03:00
Bananymous d457e6ad6a LibC: Fix printf e/E modifier for negative exponents 2025-05-04 13:42:17 +03:00
Bananymous abf7c8e68a LibC: Implement `difftime` 2025-05-04 13:42:17 +03:00
Bananymous 1a38d0c31e LibC: Add g/G modifiers to printf 2025-05-04 13:42:17 +03:00
17 changed files with 350 additions and 93 deletions

View File

@ -5,7 +5,7 @@ VERSION='git'
DOWNLOAD_URL="https://github.com/jewelcodes/tinygb.git#57fdaff675a6b5b963b2b6624868d9698eabe375"
configure() {
:
make -f Makefile.banan_os clean
}
build() {

View File

@ -1,16 +1,14 @@
From 3e565f7d35e842e246db3371776adae74d02ae62 Mon Sep 17 00:00:00 2001
From 7f7c6402e384591bca63021aa97d60a8107de88d Mon Sep 17 00:00:00 2001
From: Bananymous <bananymousosq@gmail.com>
Date: Wed, 23 Apr 2025 13:10:38 +0300
Date: Mon, 5 May 2025 00:59:03 +0300
Subject: [PATCH] Add support for banan-os
---
Makefile.banan_os | 28 +++
src/platform/banan-os/main.cpp | 365 +++++++++++++++++++++++++++++++
src/platform/banan-os/main.cpp.o | Bin 0 -> 23536 bytes
3 files changed, 393 insertions(+)
Makefile.banan_os | 28 +++
src/platform/banan-os/main.cpp | 362 +++++++++++++++++++++++++++++++++
2 files changed, 390 insertions(+)
create mode 100644 Makefile.banan_os
create mode 100644 src/platform/banan-os/main.cpp
create mode 100644 src/platform/banan-os/main.cpp.o
diff --git a/Makefile.banan_os b/Makefile.banan_os
new file mode 100644
@ -48,10 +46,10 @@ index 0000000..22e191e
+ $(LD) $(OBJ) -o tinygb $(LDFLAGS)
diff --git a/src/platform/banan-os/main.cpp b/src/platform/banan-os/main.cpp
new file mode 100644
index 0000000..e9c6a02
index 0000000..94f249e
--- /dev/null
+++ b/src/platform/banan-os/main.cpp
@@ -0,0 +1,365 @@
@@ -0,0 +1,362 @@
+
+/* tinygb - a tiny gameboy emulator
+ (c) 2022 by jewel */
@ -246,10 +244,8 @@ index 0000000..e9c6a02
+ ASSERT(s_window->height() == static_cast<uint32_t>(SGB_HEIGHT * scaling));
+}
+
+int main(int argc, char **argv)
+{
+ if(argc != 2)
+ {
+int main(int argc, char **argv) {
+ if(argc != 2) {
+ fprintf(stdout, "usage: %s rom_name\n", argv[0]);
+ return 1;
+ }
@ -261,9 +257,8 @@ index 0000000..e9c6a02
+ load_keys();
+
+ // open the rom
+ FILE* rom_file = fopen(rom_filename, "r");
+ if (rom_file == nullptr)
+ {
+ FILE *rom_file = fopen(rom_filename, "r");
+ if(!rom_file) {
+ write_log("unable to open %s for reading: %s\n", rom_filename, strerror(errno));
+ return 1;
+ }
@ -275,8 +270,7 @@ index 0000000..e9c6a02
+ write_log("loading rom from file %s, %d KiB\n", rom_filename, rom_size / 1024);
+
+ rom = malloc(rom_size);
+ if (rom == nullptr)
+ {
+ if(!rom) {
+ write_log("unable to allocate memory\n");
+ fclose(rom_file);
+ return 1;
@ -340,31 +334,32 @@ index 0000000..e9c6a02
+ time_t rawtime;
+ struct tm *timeinfo;
+ int sec = 500; // any invalid number
+ char new_title[256];
+ int percentage;
+ int throttle_underflow = 0;
+ int throttle_target = throttle_lo + SPEED_ALLOWANCE;
+
+ for (;;)
+ {
+ while(1) {
+ s_window->poll_events();
+
+ for (timing.current_cycles = 0; timing.current_cycles < timing.main_cycles;)
+ {
+ for(timing.current_cycles = 0; timing.current_cycles < timing.main_cycles; ) {
+ cpu_cycle();
+ display_cycle();
+ timer_cycle();
+ }
+
+
+ time(&rawtime);
+ timeinfo = localtime(&rawtime);
+
+ if (sec != timeinfo->tm_sec)
+ {
+ if(sec != timeinfo->tm_sec) {
+ sec = timeinfo->tm_sec;
+ percentage = (drawn_frames * 1000) / 597;
+ sprintf(new_title, "tinygb (%d fps - %d%%)", drawn_frames, percentage);
+ s_window->set_title(new_title);
+
+ // adjust cpu throttle according to acceptable fps (98%-102%)
+ if (throttle_enabled){
+ if(throttle_enabled) {
+ if(percentage < throttle_lo) {
+ // emulation is too slow
+ if(!throttle_time) {
@ -417,6 +412,7 @@ index 0000000..e9c6a02
+ die(0, "");
+ return 0;
+}
\ No newline at end of file
--
2.49.0

View File

@ -1,3 +1,5 @@
// jmp_buf: esp, eip, ebx, ebp, edi, esi
// int setjmp(jmp_buf env)
.global setjmp
setjmp:
@ -9,6 +11,11 @@ setjmp:
movl (%esp), %eax
movl %eax, 4(%edx)
movl %ebx, 8(%edx)
movl %ebp, 12(%edx)
movl %edi, 16(%edx)
movl %esi, 20(%edx)
xorl %eax, %eax
ret
@ -25,8 +32,12 @@ longjmp:
testl %ecx, %ecx
cmovnzl %ecx, %eax
movl 0(%edx), %esp
movl 4(%edx), %ecx
movl 0(%edx), %esp
movl 4(%edx), %ecx
movl 8(%edx), %ebx
movl 12(%edx), %ebp
movl 16(%edx), %edi
movl 20(%edx), %esi
jmp *%ecx
.size longjmp, . - longjmp

View File

@ -1,3 +1,5 @@
// jmp_buf: rsp, rip, rbx, rbp, r12-r15
// int setjmp(jmp_buf env)
.global setjmp
setjmp:
@ -7,6 +9,13 @@ setjmp:
movq (%rsp), %rax
movq %rax, 8(%rdi)
movq %rbx, 16(%rdi)
movq %rbp, 24(%rdi)
movq %r12, 32(%rdi)
movq %r13, 40(%rdi)
movq %r14, 48(%rdi)
movq %r15, 56(%rdi)
xorq %rax, %rax
ret
@ -19,7 +28,13 @@ longjmp:
testq %rsi, %rsi
cmovnzq %rsi, %rax
movq 0(%rdi), %rsp
movq 8(%rdi), %rcx
movq 0(%rdi), %rsp
movq 8(%rdi), %rcx
movq 16(%rdi), %rbx
movq 24(%rdi), %rbp
movq 32(%rdi), %r12
movq 40(%rdi), %r13
movq 48(%rdi), %r14
movq 56(%rdi), %r15
jmp *%rcx
.size longjmp, . - longjmp

View File

@ -7,8 +7,14 @@
__BEGIN_DECLS
typedef long jmp_buf[2];
typedef long sigjmp_buf[2 + 1 + (sizeof(long long) / sizeof(long))];
#if defined(__x86_64__)
#define _JMP_BUF_REGS 8 // rsp, rip, rbx, rbp, r12-r15
#elif defined(__i686__)
#define _JMP_BUF_REGS 6 // esp, eip, ebx, ebp, edi, esi
#endif
typedef long jmp_buf[_JMP_BUF_REGS];
typedef long sigjmp_buf[_JMP_BUF_REGS + 1 + (sizeof(long long) / sizeof(long))];
#define _longjmp longjmp
void longjmp(jmp_buf env, int val);

View File

@ -240,9 +240,9 @@ static void floating_point_to_exponent_string(char* buffer, T value, bool upper,
// Calculate which number to put as exponent
int exponent = 0;
if (value != (T)0.0)
if (value != static_cast<T>(0.0))
{
exponent = (int)BAN::Math::log10<T>(value);
exponent = BAN::Math::floor<T>(BAN::Math::log10<T>(value));
value /= BAN::Math::pow<T>(10.0, exponent);
}
@ -259,6 +259,19 @@ static void floating_point_to_exponent_string(char* buffer, T value, bool upper,
integer_to_string<int>(buffer + offset, exponent, 10, upper, exponent_options);
}
template<BAN::floating_point T>
static void floating_point_to_maybe_exponent_string(char* buffer, T value, bool upper, const format_options_t options)
{
int percision = 6;
if (options.percision != -1)
percision = options.percision;
const int exponent = (value != static_cast<T>(0.0)) ? BAN::Math::floor<T>(BAN::Math::log10(value)) : 0;
if (exponent < -4 || exponent >= percision)
return floating_point_to_exponent_string(buffer, value, upper, options);
return floating_point_to_string(buffer, value, upper, options);
}
extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun)(int, void*), void* data)
{
int written = 0;
@ -520,8 +533,16 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
}
case 'g':
case 'G':
// TODO
{
switch (options.length)
{
case length_t::L: floating_point_to_maybe_exponent_string<long double>(conversion, va_arg(arguments, long double), *format == 'G', options); break;
default: floating_point_to_maybe_exponent_string<double> (conversion, va_arg(arguments, double), *format == 'G', options); break;
}
string = conversion;
format++;
break;
}
case 'a':
case 'A':
// TODO

View File

@ -1,19 +1,19 @@
#include <setjmp.h>
#include <signal.h>
static_assert(sizeof(sigjmp_buf) == sizeof(jmp_buf) + sizeof(long) + sizeof(sigset_t));
static_assert(sizeof(sigjmp_buf) == (_JMP_BUF_REGS + 1) * sizeof(long) + sizeof(sigset_t));
void siglongjmp(sigjmp_buf env, int val)
{
if (env[2])
pthread_sigmask(SIG_SETMASK, reinterpret_cast<sigset_t*>(&env[3]), nullptr);
if (env[_JMP_BUF_REGS])
pthread_sigmask(SIG_SETMASK, reinterpret_cast<sigset_t*>(&env[_JMP_BUF_REGS + 1]), nullptr);
return longjmp(env, val);
}
int sigsetjmp(sigjmp_buf env, int savemask)
{
env[2] = savemask;
env[_JMP_BUF_REGS] = savemask;
if (savemask)
pthread_sigmask(0, nullptr, reinterpret_cast<sigset_t*>(&env[3]));
pthread_sigmask(0, nullptr, reinterpret_cast<sigset_t*>(&env[_JMP_BUF_REGS + 1]));
return setjmp(env);
}

View File

@ -38,6 +38,11 @@ time_t time(time_t* tloc)
return tp.tv_sec;
}
double difftime(time_t time1, time_t time0)
{
return time1 - time0;
}
// sample implementation from https://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime.html
char* asctime_r(const struct tm* __restrict tm, char* __restrict buf)
{

View File

@ -287,6 +287,15 @@ namespace LibGUI
return on_socket_error(__FUNCTION__);
}
void Window::set_title(BAN::StringView title)
{
WindowPacket::WindowSetTitle packet;
MUST(packet.title.append(title));
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);
}
void Window::set_position(int32_t x, int32_t y)
{
WindowPacket::WindowSetPosition packet;
@ -346,7 +355,7 @@ namespace LibGUI
m_framebuffer_smo = nullptr;
BAN::Vector<uint32_t> framebuffer;
TRY(framebuffer.resize(event.width * event.height, 0xFFFFFFFF));
TRY(framebuffer.resize(event.width * event.height, m_bg_color));
void* framebuffer_addr = smo_map(event.smo_key);
if (framebuffer_addr == nullptr)

View File

@ -165,6 +165,7 @@ namespace LibGUI
WindowSetMouseCapture,
WindowSetSize,
WindowSetFullscreen,
WindowSetTitle,
DestroyWindowEvent,
CloseWindowEvent,
@ -185,6 +186,7 @@ namespace LibGUI
bool focusable;
bool rounded_corners;
bool alpha_channel;
bool resizable;
};
DEFINE_PACKET(
@ -230,6 +232,11 @@ namespace LibGUI
bool, fullscreen
);
DEFINE_PACKET(
WindowSetTitle,
BAN::String, title
);
}
namespace EventPacket

View File

@ -22,6 +22,7 @@ namespace LibGUI
.focusable = true,
.rounded_corners = true,
.alpha_channel = false,
.resizable = false,
};
public:
@ -68,6 +69,7 @@ namespace LibGUI
void set_mouse_capture(bool captured);
void set_fullscreen(bool fullscreen);
void set_title(BAN::StringView title);
void set_position(int32_t x, int32_t y);
@ -81,6 +83,9 @@ namespace LibGUI
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
// used on resize to fill empty space
void set_bg_color(uint32_t bg_color) { m_bg_color = bg_color; }
void poll_events();
void set_socket_error_callback(BAN::Function<void()> callback) { m_socket_error_callback = callback; }
void set_close_window_event_callback(BAN::Function<void()> callback) { m_close_window_event_callback = callback; }
@ -117,6 +122,8 @@ namespace LibGUI
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_bg_color { 0xFFFFFFFF };
BAN::Function<void()> m_socket_error_callback;
BAN::Function<void()> m_close_window_event_callback;
BAN::Function<void()> m_resize_window_event_callback;

View File

@ -116,10 +116,12 @@ void Terminal::run()
auto attributes = LibGUI::Window::default_attributes;
attributes.alpha_channel = true;
attributes.resizable = true;
m_window = MUST(LibGUI::Window::create(600, 400, "Terminal"_sv, attributes));
m_window->fill(m_bg_color);
m_window->invalidate();
m_window->set_bg_color(m_bg_color);
m_font = MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv));
@ -142,6 +144,27 @@ void Terminal::run()
show_cursor();
m_window->set_key_event_callback([&](LibGUI::EventPacket::KeyEvent::event_t event) { on_key_event(event); });
m_window->set_resize_window_event_callback([&] {
if (const auto rem = m_window->height() % m_font.height())
{
m_window->fill_rect(0, m_window->height() - rem, m_window->width(), rem, m_bg_color);
m_window->invalidate(0, m_window->height() - rem, m_window->width(), rem);
}
if (const auto rem = m_window->width() % m_font.width())
{
m_window->fill_rect(m_window->width() - rem, 0, rem, m_window->height(), m_bg_color);
m_window->invalidate(m_window->width() - rem, 0, rem, m_window->height());
}
if (m_cursor.x < cols() && m_cursor.y < rows())
return;
m_cursor.x = BAN::Math::min(m_cursor.x, cols() - 1);
m_cursor.y = BAN::Math::min(m_cursor.y, rows() - 1);
for (auto& pixel : m_cursor_buffer)
pixel = m_bg_color;
});
const int max_fd = BAN::Math::max(m_shell_info.pts_master, m_window->server_fd());
while (!s_shell_exited)

View File

@ -52,6 +52,8 @@ public:
LibGUI::Window::Attributes get_attributes() const { return m_attributes; };
void set_attributes(LibGUI::Window::Attributes attributes) { m_attributes = attributes; };
BAN::ErrorOr<void> set_title(BAN::StringView title) { m_title.clear(); TRY(m_title.append(title)); TRY(prepare_title_bar()); return {}; }
const uint32_t* framebuffer() const { return m_fb_addr; }
uint32_t title_bar_pixel(int32_t abs_x, int32_t abs_y, Position cursor) const

View File

@ -123,19 +123,11 @@ void WindowServer::on_window_invalidate(int fd, const LibGUI::WindowPacket::Wind
return;
}
const int32_t br_x = packet.x + packet.width - 1;
const int32_t br_y = packet.y + packet.height - 1;
if (!target_window->client_size().contains({ br_x, br_y }))
{
dwarnln("invalid Invalidate packet parameters");
return;
}
invalidate({
target_window->client_x() + static_cast<int32_t>(packet.x),
target_window->client_y() + static_cast<int32_t>(packet.y),
static_cast<int32_t>(packet.width),
static_cast<int32_t>(packet.height),
BAN::Math::min<int32_t>(packet.width, target_window->client_width()),
BAN::Math::min<int32_t>(packet.height, target_window->client_height())
});
}
@ -307,7 +299,7 @@ void WindowServer::on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::
if (!target_window)
{
dwarnln("client tried to set window size while not owning a window");
dwarnln("client tried to set window fullscreen while not owning a window");
return;
}
@ -316,6 +308,32 @@ void WindowServer::on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::
invalidate(m_framebuffer.area());
}
void WindowServer::on_window_set_title(int fd, const LibGUI::WindowPacket::WindowSetTitle& packet)
{
BAN::RefPtr<Window> target_window;
for (auto& window : m_client_windows)
{
if (window->client_fd() != fd)
continue;
target_window = window;
break;
}
if (!target_window)
{
dwarnln("client tried to set window title while not owning a window");
return;
}
if (auto ret = target_window->set_title(packet.title); ret.is_error())
{
dwarnln("failed to set window title: {}", ret.error());
return;
}
invalidate(target_window->title_bar_area());
}
void WindowServer::on_key_event(LibInput::KeyEvent event)
{
// Mod key is not passed to clients
@ -362,6 +380,11 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
if (!m_focused_window)
return;
m_is_fullscreen_window = !m_is_fullscreen_window;
if (m_is_fullscreen_window)
{
m_is_moving_window = false;
m_is_resizing_window = false;
}
invalidate(m_framebuffer.area());
return;
}
@ -405,6 +428,40 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
}
}
if (m_is_moving_window && event.button == LibInput::MouseButton::Left && !event.pressed)
{
m_is_moving_window = false;
return;
}
if (m_is_resizing_window && event.button == LibInput::MouseButton::Right && !event.pressed)
{
const auto resize_area = this->resize_area(m_cursor);
m_is_resizing_window = false;
invalidate(resize_area.get_bounding_box(m_focused_window->full_area()));
const auto old_area = m_focused_window->full_area();
if (auto ret = m_focused_window->resize(resize_area.width, resize_area.height - m_focused_window->title_bar_height()); ret.is_error())
{
dwarnln("could not resize client window {}", ret.error());
return;
}
LibGUI::EventPacket::ResizeWindowEvent event;
event.width = m_focused_window->client_width();
event.height = m_focused_window->client_height();
event.smo_key = m_focused_window->smo_key();
if (auto ret = event.send_serialized(m_focused_window->client_fd()); ret.is_error())
{
dwarnln("could not respond to window resize request: {}", ret.error());
return;
}
invalidate(m_focused_window->full_area().get_bounding_box(old_area));
return;
}
// Ignore mouse button events which are not on top of a window
if (!target_window)
return;
@ -412,13 +469,29 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
if (target_window->get_attributes().focusable)
set_focused_window(target_window);
// Handle window moving when mod key is held or mouse press on title bar
const bool can_start_move = m_is_mod_key_held || target_window->title_text_area().contains(m_cursor);
if (event.pressed && event.button == LibInput::MouseButton::Left && !m_is_moving_window && can_start_move)
m_is_moving_window = target_window->get_attributes().movable;
else if (m_is_moving_window && !event.pressed)
m_is_moving_window = false;
else if (!event.pressed && event.button == LibInput::MouseButton::Left && target_window->close_button_area().contains(m_cursor))
if (!m_is_fullscreen_window && event.button == LibInput::MouseButton::Left)
{
const bool can_start_move = m_is_mod_key_held || target_window->title_text_area().contains(m_cursor);
if (can_start_move && !m_is_moving_window && event.pressed)
{
m_is_moving_window = target_window->get_attributes().movable;
return;
}
}
if (!m_is_fullscreen_window && event.button == LibInput::MouseButton::Right)
{
const bool can_start_resize = m_is_mod_key_held;
if (can_start_resize && !m_is_resizing_window && event.pressed)
{
m_is_resizing_window = target_window->get_attributes().resizable;
if (m_is_resizing_window)
m_resize_start = m_cursor;
return;
}
}
if (!event.pressed && event.button == LibInput::MouseButton::Left && target_window->close_button_area().contains(m_cursor))
{
// NOTE: we always have target window if code reaches here
LibGUI::EventPacket::CloseWindowEvent packet;
@ -442,9 +515,6 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
return;
}
}
if (m_is_fullscreen_window)
m_is_moving_window = false;
}
void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
@ -511,6 +581,16 @@ void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
return;
}
if (m_is_resizing_window)
{
const auto max_cursor = Position {
.x = BAN::Math::max(old_cursor.x, new_cursor.x),
.y = BAN::Math::max(old_cursor.y, new_cursor.y),
};
invalidate(resize_area(max_cursor).get_bounding_box(m_focused_window->full_area()));
return;
}
if (m_focused_window)
{
LibGUI::EventPacket::MouseMoveEvent packet;
@ -890,10 +970,24 @@ void WindowServer::invalidate(Rectangle area)
}
}
if (m_is_resizing_window)
{
if (const auto overlap = resize_area(m_cursor).get_overlap(area); overlap.has_value())
{
for (int32_t y_off = 0; y_off < overlap->height; y_off++)
{
for (int32_t x_off = 0; x_off < overlap->width; x_off++)
{
auto& pixel = m_framebuffer.mmap[(overlap->y + y_off) * m_framebuffer.width + (overlap->x + x_off)];
pixel = alpha_blend(0x80000000, pixel);
}
}
}
}
if (!m_is_mouse_captured)
{
const auto cursor = cursor_area();
if (auto overlap = cursor.get_overlap(area); overlap.has_value())
if (const auto overlap = cursor_area().get_overlap(area); overlap.has_value())
{
for (int32_t y_off = 0; y_off < overlap->height; y_off++)
{
@ -1045,6 +1139,16 @@ Rectangle WindowServer::cursor_area() const
return { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height };
}
Rectangle WindowServer::resize_area(Position cursor) const
{
ASSERT(m_is_resizing_window);
return {
.x = m_focused_window->full_x(),
.y = m_focused_window->full_y(),
.width = BAN::Math::max<int32_t>(20, m_focused_window->full_width() + cursor.x - m_resize_start.x),
.height = BAN::Math::max<int32_t>(20, m_focused_window->full_height() + cursor.y - m_resize_start.y),
};
}
void WindowServer::add_client_fd(int fd)
{

View File

@ -38,6 +38,7 @@ public:
void on_window_set_mouse_capture(int fd, const LibGUI::WindowPacket::WindowSetMouseCapture&);
void on_window_set_size(int fd, const LibGUI::WindowPacket::WindowSetSize&);
void on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::WindowSetFullscreen&);
void on_window_set_title(int fd, const LibGUI::WindowPacket::WindowSetTitle&);
void on_key_event(LibInput::KeyEvent event);
void on_mouse_button(LibInput::MouseButtonEvent event);
@ -49,6 +50,7 @@ public:
void sync();
Rectangle cursor_area() const;
Rectangle resize_area(Position cursor) const;
void add_client_fd(int fd);
void remove_client_fd(int fd);
@ -86,6 +88,9 @@ private:
BAN::RefPtr<Window> m_focused_window;
Position m_cursor;
bool m_is_resizing_window { false };
Position m_resize_start;
bool m_is_mouse_captured { false };
bool m_deleted_window { false };

View File

@ -379,6 +379,10 @@ int main()
if (auto ret = LibGUI::WindowPacket::WindowSetFullscreen::deserialize(client_data.packet_buffer.span()); !ret.is_error())
window_server.on_window_set_fullscreen(fd, ret.release_value());
break;
case LibGUI::PacketType::WindowSetTitle:
if (auto ret = LibGUI::WindowPacket::WindowSetTitle::deserialize(client_data.packet_buffer.span()); !ret.is_error())
window_server.on_window_set_title(fd, ret.release_value());
break;
default:
dprintln("unhandled packet type: {}", *reinterpret_cast<uint32_t*>(client_data.packet_buffer.data()));
}

View File

@ -1,6 +1,7 @@
#include <BAN/Vector.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -26,7 +27,7 @@ struct Point
bool g_running = true;
Point g_grid_size = { 21, 21 };
Direction g_direction = Direction::Up;
Point g_head = { 10, 10 };
Point g_head = { g_grid_size.x / 2, g_grid_size.y / 2 };
size_t g_tail_target = 3;
int g_score = 0;
BAN::Vector<Point> g_tail;
@ -53,53 +54,78 @@ Direction query_input()
}
}
void set_grid_tile(Point point, const char* str)
const char* get_tail_char(Direction old_dir, Direction new_dir)
{
printf("\e[%d;%dH%s", (point.y + 1) + 1, (point.x + 1) * 2 + 1, str);
const size_t old_idx = static_cast<size_t>(old_dir) - 2;
const size_t new_idx = static_cast<size_t>(new_dir) - 2;
// left, right, up, down
constexpr const char* tail_char_map[4][4] {
{ "", "", "", "" },
{ "", "", "", "" },
{ "", "", "", "" },
{ "", "", "", "" },
};
return tail_char_map[old_idx][new_idx];
}
Point get_random_point()
void set_grid_tile(Point point, const char* str, int off_x = 0)
{
return { .x = rand() % g_grid_size.x, .y = rand() % g_grid_size.y };
printf("\e[%d;%dH%s", (point.y + 1) + 1, (point.x + 1) * 2 + 1 + off_x, str);
}
__attribute__((format(printf, 1, 2)))
void print_score_line(const char* format, ...)
{
printf("\e[%dH\e[m", g_grid_size.y + 3);
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
void update_apple()
{
for (;;)
BAN::Vector<Point> free_tiles;
for (int y = 0; y < g_grid_size.y; y++)
for (int x = 0; x < g_grid_size.x; x++)
if (const Point point { x, y }; g_head != point && !g_tail.contains(point))
MUST(free_tiles.push_back(point));
if (free_tiles.empty())
{
g_apple = get_random_point();
if (g_head == g_apple)
continue;
for (auto point : g_tail)
if (point == g_apple)
continue;
break;
print_score_line("You won!\n");
exit(0);
}
g_apple = free_tiles[rand() % free_tiles.size()];
set_grid_tile(g_apple, "\e[31mO");
}
void setup_grid()
{
// Move cursor to beginning and clear screen
printf("\e[H\e[J");
printf("\e[H\e[2J");
// Render top line
putchar('#');
for (int x = 1; x < g_grid_size.x + 2; x++)
printf(" #");
putchar('\n');
printf("╔═");
for (int x = 0; x < g_grid_size.x; x++)
printf("══");
printf("\n");
// Render side lines
for (int y = 0; y < g_grid_size.y; y++)
printf("#\e[%dC#\n", g_grid_size.x * 2 + 1);
printf("\e[%dC║\n", g_grid_size.x * 2 + 1);
// Render Bottom line
putchar('#');
for (int x = 1; x < g_grid_size.x + 2; x++)
printf(" #");
putchar('\n');
printf("╚═");
for (int x = 0; x < g_grid_size.x; x++)
printf("══");
printf("");
// Render snake head
printf("\e[32m");
set_grid_tile(g_head, "O");
// Generate and render apple
@ -107,7 +133,7 @@ void setup_grid()
update_apple();
// Render score
printf("\e[%dH\e[mScore: %d", g_grid_size.y + 3, g_score);
print_score_line("Score: %d", g_score);
fflush(stdout);
}
@ -141,6 +167,7 @@ void update()
}
}
const auto old_direction = g_direction;
if (new_direction != g_direction && new_direction != Direction::None)
g_direction = new_direction;
@ -181,7 +208,18 @@ void update()
MUST(g_tail.insert(0, old_head));
if (g_tail.size() > g_tail_target)
{
set_grid_tile(g_tail.back(), " ");
const auto comp = g_tail.size() >= 2 ? g_tail[g_tail.size() - 2] : g_head;
const auto back = g_tail.back();
if (comp.y == back.y)
{
if (comp.x == back.x + 1)
set_grid_tile(back, " ", +1);
if (comp.x == back.x - 1)
set_grid_tile(back, " ", -1);
}
set_grid_tile(back, " ");
g_tail.pop_back();
}
@ -190,12 +228,16 @@ void update()
g_tail_target++;
g_score++;
update_apple();
printf("\e[%dH\e[mScore: %d", g_grid_size.y + 3, g_score);
print_score_line("Score: %d", g_score);
}
set_grid_tile(old_head, "\e[32mo");
set_grid_tile(g_head, "\e[32mO");
printf("\e[32m");
if (g_direction == Direction::Left)
set_grid_tile(g_head, "", +1);
if (g_direction == Direction::Right)
set_grid_tile(g_head, "", -1);
set_grid_tile(old_head, get_tail_char(old_direction, g_direction));
set_grid_tile(g_head, "O");
fflush(stdout);
}