Compare commits
10 Commits
9ff9d679e9
...
2b0d198b05
Author | SHA1 | Date |
---|---|---|
|
2b0d198b05 | |
|
7798145c74 | |
|
fcfadd7c74 | |
|
c0181820a9 | |
|
76d4e6bd18 | |
|
ccb81de85d | |
|
82f4975f45 | |
|
d457e6ad6a | |
|
abf7c8e68a | |
|
1a38d0c31e |
|
@ -5,7 +5,7 @@ VERSION='git'
|
|||
DOWNLOAD_URL="https://github.com/jewelcodes/tinygb.git#57fdaff675a6b5b963b2b6624868d9698eabe375"
|
||||
|
||||
configure() {
|
||||
:
|
||||
make -f Makefile.banan_os clean
|
||||
}
|
||||
|
||||
build() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue