diff --git a/userspace/CMakeLists.txt b/userspace/CMakeLists.txt index 69031c7d52..bc952251a9 100644 --- a/userspace/CMakeLists.txt +++ b/userspace/CMakeLists.txt @@ -26,8 +26,9 @@ set(USERSPACE_PROJECTS sync tee test - test-globals test-framebuffer + test-globals + test-mouse test-sort touch u8sum diff --git a/userspace/test-mouse/CMakeLists.txt b/userspace/test-mouse/CMakeLists.txt new file mode 100644 index 0000000000..50f7504747 --- /dev/null +++ b/userspace/test-mouse/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.26) + +project(test-mouse CXX) + +set(SOURCES + main.cpp +) + +add_executable(test-mouse ${SOURCES}) +target_compile_options(test-mouse PUBLIC -O2 -g) +target_link_libraries(test-mouse PUBLIC libc) + +add_custom_target(test-mouse-install + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/test-mouse ${BANAN_BIN}/ + DEPENDS test-mouse +) diff --git a/userspace/test-mouse/main.cpp b/userspace/test-mouse/main.cpp new file mode 100644 index 0000000000..a046fcef50 --- /dev/null +++ b/userspace/test-mouse/main.cpp @@ -0,0 +1,182 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +framebuffer_info_t fb_info; +void* fb_mmap = nullptr; + +int mouse_fd = -1; + +termios original_termios {}; + +void draw_circle(int cx, int cy, int r, uint32_t color) +{ + int min_x = BAN::Math::max(cx - r, 0); + int max_x = BAN::Math::min(cx + r + 1, fb_info.width); + + int min_y = BAN::Math::max(cy - r, 0); + int max_y = BAN::Math::min(cy + r + 1, fb_info.height); + + for (int y = min_y; y < max_y; y++) + { + for (int x = min_x; x < max_x; x++) + { + int dx = x - cx; + int dy = y - cy; + if (dx * dx + dy * dy > r * r) + continue; + static_cast(fb_mmap)[y * fb_info.width + x] = color; + } + } +} + +void cleanup() +{ + if (fb_mmap) + munmap(fb_mmap, fb_info.height * fb_info.width * (BANAN_FB_BPP / 8)); + if (mouse_fd != -1) + close(mouse_fd); + if (original_termios.c_lflag & ECHO) + tcsetattr(STDIN_FILENO, TCSANOW, &original_termios); +} + +int main(int argc, char** argv) +{ + const char* fb_path = "/dev/fb0"; + const char* mouse_path = "/dev/input1"; + + if (argc == 1) + ; + else if (argc == 3) + { + fb_path = argv[1]; + mouse_path = argv[2]; + } + else + { + fprintf(stderr, "usage: %s [FB_PATH MOUSE_PATH]", argv[0]); + return 1; + } + + signal(SIGINT, [](int) { exit(0); }); + if (atexit(cleanup) == -1) + { + perror("atexit"); + return 1; + } + + if (BANAN_FB_BPP != 32) + { + fprintf(stderr, "unsupported bpp\n"); + return 1; + } + + int fb_fd = open(fb_path, O_RDWR); + if (fb_fd == -1) + { + fprintf(stderr, "open: "); + perror(fb_path); + return 1; + } + + if (pread(fb_fd, &fb_info, sizeof(fb_info), -1) == -1) + { + fprintf(stderr, "read: "); + perror(fb_path); + return 1; + } + + size_t fb_bytes = fb_info.width * fb_info.height * (BANAN_FB_BPP / 8); + fb_mmap = mmap(nullptr, fb_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0); + close(fb_fd); + if (fb_mmap == MAP_FAILED) + { + fprintf(stderr, "mmap: "); + perror(fb_path); + return 1; + } + + int mouse_fd = open(mouse_path, O_RDONLY); + if (mouse_fd == -1) + { + fprintf(stderr, "open: "); + perror(mouse_path); + return 1; + } + + if (tcgetattr(STDIN_FILENO, &original_termios) == -1) + { + perror("tcgetattr"); + return 1; + } + + termios termios = original_termios; + termios.c_lflag &= ~ECHO; + if (tcsetattr(STDIN_FILENO, TCSANOW, &termios) == -1) + { + perror("tcsetattr"); + return 1; + } + + uint32_t color = 0xFF0000; + int mouse_x = fb_info.width / 2; + int mouse_y = fb_info.height / 2; + int radius = 10; + + // clear screen and render + memset(fb_mmap, 0x00, fb_bytes); + draw_circle(mouse_x, mouse_y, radius, color); + msync(fb_mmap, fb_bytes, MS_SYNC); + + while (true) + { + using namespace Kernel::Input; + + MouseEvent event; + if (read(mouse_fd, &event, sizeof(event)) == -1) + { + fprintf(stderr, "read: "); + perror(mouse_path); + return 1; + } + + switch (event.type) + { + case MouseEventType::MouseMoveEvent: + draw_circle(mouse_x, mouse_y, radius, 0x000000); + mouse_x = BAN::Math::clamp(mouse_x + event.move_event.rel_x, 0, fb_info.width); + mouse_y = BAN::Math::clamp(mouse_y - event.move_event.rel_y, 0, fb_info.height); + draw_circle(mouse_x, mouse_y, radius, color); + break; + case MouseEventType::MouseScrollEvent: + draw_circle(mouse_x, mouse_y, radius, 0x000000); + radius = BAN::Math::clamp(radius + event.scroll_event.scroll, 1, 50); + draw_circle(mouse_x, mouse_y, radius, color); + break; + case MouseEventType::MouseButtonEvent: + if (!event.button_event.pressed) + break; + switch (event.button_event.button) + { + case MouseButton::Left: color = 0xFF0000; break; + case MouseButton::Right: color = 0x00FF00; break; + case MouseButton::Middle: color = 0x0000FF; break; + case MouseButton::Extra1: color = 0xFFFF00; break; + case MouseButton::Extra2: color = 0x00FFFF; break; + } + draw_circle(mouse_x, mouse_y, radius, color); + break; + default: + break; + } + + msync(fb_mmap, fb_bytes, MS_SYNC); + } +}