From b0e3f733759a006622b14f75c5379641ef82776f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 14 Jan 2019 14:21:51 +0100 Subject: [PATCH] Start refactoring the windowing system to use an event loop. Userspace programs can now open /dev/gui_events and read a stream of GUI_Event structs one at a time. I was stuck on a stupid problem where we'd reenter Scheduler::yield() due to having one of the has_data_available_for_reading() implementations using locks. --- Kernel/Console.cpp | 2 +- Kernel/Console.h | 2 +- Kernel/GUITypes.h | 59 +++++++++++--- Kernel/Keyboard.cpp | 2 +- Kernel/Keyboard.h | 2 +- Kernel/Makefile | 3 +- Kernel/PS2MouseDevice.cpp | 2 +- Kernel/PS2MouseDevice.h | 2 +- Kernel/Process.cpp | 3 +- Kernel/Process.h | 14 +++- Kernel/ProcessGUI.cpp | 92 +++++---------------- Kernel/Scheduler.cpp | 12 ++- Kernel/Syscall.cpp | 4 - Kernel/Syscall.h | 2 - Kernel/TTY.cpp | 2 +- Kernel/TTY.h | 2 +- Kernel/WindowComposer.cpp | 53 +------------ Kernel/i386.cpp | 6 +- Kernel/init.cpp | 6 +- Kernel/sync.sh | 2 + LibC/errno_numbers.h | 1 - LibC/stdio.cpp | 17 +++- LibC/stdio.h | 1 + Userland/guitest.cpp | 37 +++++---- VirtualFileSystem/CharacterDevice.h | 2 +- VirtualFileSystem/FileDescriptor.cpp | 4 +- VirtualFileSystem/FileDescriptor.h | 3 +- VirtualFileSystem/FullDevice.cpp | 2 +- VirtualFileSystem/FullDevice.h | 2 +- VirtualFileSystem/NullDevice.cpp | 2 +- VirtualFileSystem/NullDevice.h | 2 +- VirtualFileSystem/RandomDevice.cpp | 2 +- VirtualFileSystem/RandomDevice.h | 2 +- VirtualFileSystem/ZeroDevice.cpp | 2 +- VirtualFileSystem/ZeroDevice.h | 2 +- Widgets/Event.h | 2 - Widgets/EventLoop.cpp | 4 +- Widgets/GUIEventDevice.cpp | 38 +++++++++ Widgets/GUIEventDevice.h | 14 ++++ Widgets/MsgBox.cpp | 6 +- Widgets/Point.h | 4 + Widgets/Rect.h | 5 ++ Widgets/Size.h | 5 ++ Widgets/Widget.cpp | 11 +-- Widgets/Window.cpp | 114 ++++++++++----------------- Widgets/Window.h | 19 ++--- 46 files changed, 283 insertions(+), 292 deletions(-) create mode 100644 Widgets/GUIEventDevice.cpp create mode 100644 Widgets/GUIEventDevice.h diff --git a/Kernel/Console.cpp b/Kernel/Console.cpp index 30fc4cd2fb571f..fb8734d8603ffc 100644 --- a/Kernel/Console.cpp +++ b/Kernel/Console.cpp @@ -23,7 +23,7 @@ Console::~Console() { } -bool Console::has_data_available_for_reading() const +bool Console::has_data_available_for_reading(Process&) const { return false; } diff --git a/Kernel/Console.h b/Kernel/Console.h index 74fbaedbec0536..35685928848069 100644 --- a/Kernel/Console.h +++ b/Kernel/Console.h @@ -18,7 +18,7 @@ class Console final : public CharacterDevice { Console(); virtual ~Console() override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; virtual ssize_t read(byte* buffer, size_t size) override; virtual ssize_t write(const byte* data, size_t size) override; diff --git a/Kernel/GUITypes.h b/Kernel/GUITypes.h index 38ecb4d566e5e6..a5ff63a30f8d11 100644 --- a/Kernel/GUITypes.h +++ b/Kernel/GUITypes.h @@ -11,23 +11,60 @@ struct GUI_WindowFlags { enum { typedef unsigned GUI_Color; +struct GUI_Point { + int x; + int y; +}; + +struct GUI_Size { + int width; + int height; +}; + +struct GUI_Rect { + GUI_Point location; + GUI_Size size; +}; + struct GUI_CreateWindowParameters { - Rect rect; + GUI_Rect rect; Color background_color; unsigned flags { 0 }; char title[128]; }; -enum class GUI_WidgetType : unsigned { - Label, - Button, +enum class GUI_MouseButton : unsigned char { + NoButton = 0, + Left = 1, + Right = 2, + Middle = 4, }; -struct GUI_CreateWidgetParameters { - GUI_WidgetType type; - Rect rect; - Color background_color; - bool opaque { true }; - unsigned flags { 0 }; - char text[256]; +struct GUI_Event { + enum Type : unsigned { + Invalid, + Paint, + MouseMove, + MouseDown, + MouseUp, + }; + Type type { Invalid }; + int window_id { -1 }; + + union { + struct { + GUI_Rect rect; + } paint; + struct { + GUI_Point position; + GUI_MouseButton button; + } mouse; + }; }; + +inline Rect::Rect(const GUI_Rect& r) : Rect(r.location, r.size) { } +inline Point::Point(const GUI_Point& p) : Point(p.x, p.y) { } +inline Size::Size(const GUI_Size& s) : Size(s.width, s.height) { } +inline Rect::operator GUI_Rect() const { return { m_location, m_size }; } +inline Point::operator GUI_Point() const { return { m_x, m_y }; } +inline Size::operator GUI_Size() const { return { m_width, m_height }; } diff --git a/Kernel/Keyboard.cpp b/Kernel/Keyboard.cpp index 9632d382c752c0..4ab8447bd72afe 100644 --- a/Kernel/Keyboard.cpp +++ b/Kernel/Keyboard.cpp @@ -114,7 +114,7 @@ Keyboard::~Keyboard() { } -bool Keyboard::has_data_available_for_reading() const +bool Keyboard::has_data_available_for_reading(Process&) const { return !m_queue.is_empty(); } diff --git a/Kernel/Keyboard.h b/Kernel/Keyboard.h index 6b207fcc60b856..9ebb096dbcfc38 100644 --- a/Kernel/Keyboard.h +++ b/Kernel/Keyboard.h @@ -39,7 +39,7 @@ class Keyboard final : public IRQHandler, public CharacterDevice { // ^CharacterDevice virtual ssize_t read(byte* buffer, size_t) override; virtual ssize_t write(const byte* buffer, size_t) override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; void emit(byte); diff --git a/Kernel/Makefile b/Kernel/Makefile index 469f09fe50bdb8..dab935cfe9bc82 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -62,7 +62,8 @@ WIDGETS_OBJS = \ ../Widgets/ListBox.o \ ../Widgets/CheckBox.o \ ../Widgets/TextBox.o \ - ../Widgets/AbstractScreen.o + ../Widgets/AbstractScreen.o \ + ../Widgets/GUIEventDevice.o \ AK_OBJS = \ ../AK/String.o \ diff --git a/Kernel/PS2MouseDevice.cpp b/Kernel/PS2MouseDevice.cpp index dd9a72c2305257..9aea27c96297c5 100644 --- a/Kernel/PS2MouseDevice.cpp +++ b/Kernel/PS2MouseDevice.cpp @@ -116,7 +116,7 @@ byte PS2MouseDevice::mouse_read() return IO::in8(0x60); } -bool PS2MouseDevice::has_data_available_for_reading() const +bool PS2MouseDevice::has_data_available_for_reading(Process&) const { return !m_buffer.is_empty(); } diff --git a/Kernel/PS2MouseDevice.h b/Kernel/PS2MouseDevice.h index 3c8d77f6644162..89e3ce542abf04 100644 --- a/Kernel/PS2MouseDevice.h +++ b/Kernel/PS2MouseDevice.h @@ -12,7 +12,7 @@ class PS2MouseDevice final : public IRQHandler, public CharacterDevice { static PS2MouseDevice& the(); // ^CharacterDevice - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; virtual ssize_t read(byte* buffer, size_t) override; virtual ssize_t write(const byte* buffer, size_t) override; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 8a29eb5cfa2608..7e806b3ee5c73a 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -18,6 +18,7 @@ #include "Scheduler.h" #include "FIFO.h" #include "KSyms.h" +#include //#define DEBUG_IO //#define TASK_DEBUG @@ -1057,7 +1058,7 @@ ssize_t Process::sys$read(int fd, void* outbuf, size_t nread) if (!descriptor) return -EBADF; if (descriptor->is_blocking()) { - if (!descriptor->has_data_available_for_reading()) { + if (!descriptor->has_data_available_for_reading(*this)) { m_blocked_fd = fd; block(BlockedRead); sched_yield(); diff --git a/Kernel/Process.h b/Kernel/Process.h index faa51bea7cc8a2..5739cc00454a19 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -12,6 +12,7 @@ #include #include #include +#include class FileDescriptor; class PageDirectory; @@ -191,13 +192,12 @@ class Process : public InlineLinkedListNode { int gui$create_window(const GUI_CreateWindowParameters*); int gui$destroy_window(int window_id); - int gui$create_widget(int window_id, const GUI_CreateWidgetParameters*); - int gui$destroy_widget(int widget_id); DisplayInfo get_display_info(); static void initialize(); static void initialize_gui_statics(); + int make_window_id(); void crash() NORETURN; static int reap(Process&) WARN_UNUSED_RESULT; @@ -248,6 +248,9 @@ class Process : public InlineLinkedListNode { bool is_root() const { return m_euid == 0; } + Vector& gui_events() { return m_gui_events; } + SpinLock& gui_events_lock() { return m_gui_events_lock; } + private: friend class MemoryManager; friend class Scheduler; @@ -342,8 +345,11 @@ class Process : public InlineLinkedListNode { RetainPtr m_display_framebuffer_region; - Vector> m_windows; - Vector> m_widgets; + HashMap> m_windows; + + Vector m_gui_events; + SpinLock m_gui_events_lock; + int m_next_window_id { 1 }; }; extern Process* current; diff --git a/Kernel/ProcessGUI.cpp b/Kernel/ProcessGUI.cpp index c4ef73dfc5ee69..c0049488bc7dfb 100644 --- a/Kernel/ProcessGUI.cpp +++ b/Kernel/ProcessGUI.cpp @@ -22,6 +22,14 @@ void Process::initialize_gui_statics() new EventLoop; } +int Process::make_window_id() +{ + int new_id = m_next_window_id++; + while (!new_id || m_windows.contains(new_id)) + new_id = m_next_window_id++; + return new_id; +} + static void wait_for_gui_server() { // FIXME: Time out after a while and return an error. @@ -37,28 +45,26 @@ int Process::gui$create_window(const GUI_CreateWindowParameters* user_params) return -EFAULT; auto params = *user_params; + Rect rect = params.rect; - if (params.rect.is_empty()) + if (rect.is_empty()) return -EINVAL; ProcessPagingScope scope(EventLoop::main().server_process()); - auto* window = new Window; - if (!window) + int window_id = make_window_id(); + if (!window_id) return -ENOMEM; - int window_id = m_windows.size(); - m_windows.append(window->makeWeakPtr()); + auto window = make(*this, window_id); + if (!window) + return -ENOMEM; window->setTitle(params.title); - window->setRect(params.rect); + window->setRect(rect); - auto* main_widget = new Widget; - window->setMainWidget(main_widget); - main_widget->setWindowRelativeRect({ 0, 0, params.rect.width(), params.rect.height() }); - main_widget->setBackgroundColor(params.background_color); - main_widget->setFillWithBackgroundColor(true); - dbgprintf("%s<%u> gui$create_window: %d with rect {%d,%d %dx%d}\n", name().characters(), pid(), window_id, params.rect.x(), params.rect.y(), params.rect.width(), params.rect.height()); + m_windows.set(window_id, move(window)); + dbgprintf("%s<%u> gui$create_window: %d with rect {%d,%d %dx%d}\n", name().characters(), pid(), window_id, rect.x(), rect.y(), rect.width(), rect.height()); return window_id; } @@ -70,65 +76,9 @@ int Process::gui$destroy_window(int window_id) return -EINVAL; if (window_id >= static_cast(m_windows.size())) return -EBADWINDOW; - auto* window = m_windows[window_id].ptr(); - if (!window) - return -EBADWINDOW; - window->deleteLater(); - return 0; -} - -int Process::gui$create_widget(int window_id, const GUI_CreateWidgetParameters* user_params) -{ - if (!validate_read_typed(user_params)) - return -EFAULT; - - if (window_id < 0) - return -EINVAL; - if (window_id >= static_cast(m_windows.size())) - return -EINVAL; - if (!m_windows[window_id]) - return -EINVAL; - auto& window = *m_windows[window_id]; - - auto params = *user_params; - - if (params.rect.is_empty()) - return -EINVAL; - - Widget* widget = nullptr; - switch (params.type) { - case GUI_WidgetType::Label: - widget = new Label(window.mainWidget()); - static_cast(widget)->setText(params.text); - widget->setFillWithBackgroundColor(params.opaque); - break; - case GUI_WidgetType::Button: - widget = new Button(window.mainWidget()); - static_cast(widget)->setCaption(params.text); - break; - } - - int widget_id = m_widgets.size(); - m_widgets.append(widget->makeWeakPtr()); - - widget->setWindowRelativeRect(params.rect); - widget->setBackgroundColor(params.background_color); - dbgprintf("%s<%u> gui$create_widget: %d with rect {%d,%d %dx%d}\n", name().characters(), pid(), widget_id, params.rect.x(), params.rect.y(), params.rect.width(), params.rect.height()); - - return window_id; -} - -int Process::gui$destroy_widget(int widget_id) -{ - dbgprintf("%s<%u> gui$destroy_widget (widget_id=%d)\n", name().characters(), pid(), widget_id); - if (widget_id < 0) - return -EINVAL; - if (widget_id >= static_cast(m_widgets.size())) + auto it = m_windows.find(window_id); + if (it == m_windows.end()) return -EBADWINDOW; - auto* widget = m_widgets[widget_id].ptr(); - if (!widget) - return -EBADWIDGET; - widget->deleteLater(); + m_windows.remove(window_id); return 0; } - diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index a730b85432f688..cd797cb72209b1 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -9,6 +9,7 @@ static const dword time_slice = 5; // *10 = 50ms Process* current; static Process* s_colonel_process; +static bool s_in_yield; struct TaskRedirectionData { word selector; @@ -51,7 +52,7 @@ bool Scheduler::pick_next() if (process.state() == Process::BlockedRead) { ASSERT(process.m_blocked_fd != -1); // FIXME: Block until the amount of data wanted is available. - if (process.m_fds[process.m_blocked_fd].descriptor->has_data_available_for_reading()) + if (process.m_fds[process.m_blocked_fd].descriptor->has_data_available_for_reading(process)) process.unblock(); return true; } @@ -142,6 +143,9 @@ bool Scheduler::pick_next() bool Scheduler::yield() { + ASSERT(!s_in_yield); + s_in_yield = true; + if (!current) { kprintf("PANIC: sched_yield() with !current"); HANG; @@ -150,9 +154,12 @@ bool Scheduler::yield() //dbgprintf("%s<%u> yield()\n", current->name().characters(), current->pid()); InterruptDisabler disabler; - if (!pick_next()) + if (!pick_next()) { + s_in_yield = false; return 1; + } + s_in_yield = false; //dbgprintf("yield() jumping to new process: %x (%s)\n", current->farPtr().selector, current->name().characters()); switch_now(); return 0; @@ -271,6 +278,7 @@ void Scheduler::initialize() initialize_redirection(); s_colonel_process = Process::create_kernel_process("colonel", nullptr); current = nullptr; + s_in_yield = false; load_task_register(s_redirection.selector); } diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index 467eb551fea053..a8b53db20dbb9d 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -191,10 +191,6 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->gui$create_window((const GUI_CreateWindowParameters*)arg1); case Syscall::SC_gui_destroy_window: return current->gui$destroy_window((int)arg1); - case Syscall::SC_gui_create_widget: - return current->gui$create_widget((int)arg1, (const GUI_CreateWidgetParameters*)arg2); - case Syscall::SC_gui_destroy_widget: - return current->gui$destroy_widget((int)arg1); default: kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 66a6369d1fa7ef..c6eab7000745d4 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -68,8 +68,6 @@ __ENUMERATE_SYSCALL(sync) \ __ENUMERATE_SYSCALL(gui_create_window) \ __ENUMERATE_SYSCALL(gui_destroy_window) \ - __ENUMERATE_SYSCALL(gui_create_widget) \ - __ENUMERATE_SYSCALL(gui_destroy_widget) \ namespace Syscall { diff --git a/Kernel/TTY.cpp b/Kernel/TTY.cpp index c2a64f9a9ee527..5261e251e1cc08 100644 --- a/Kernel/TTY.cpp +++ b/Kernel/TTY.cpp @@ -42,7 +42,7 @@ ssize_t TTY::write(const byte* buffer, size_t size) return size; } -bool TTY::has_data_available_for_reading() const +bool TTY::has_data_available_for_reading(Process&) const { return !m_buffer.is_empty(); } diff --git a/Kernel/TTY.h b/Kernel/TTY.h index 699dc1ea6d1199..8d92f8939155e4 100644 --- a/Kernel/TTY.h +++ b/Kernel/TTY.h @@ -12,7 +12,7 @@ class TTY : public CharacterDevice { virtual ssize_t read(byte*, size_t) override; virtual ssize_t write(const byte*, size_t) override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; virtual int ioctl(Process&, unsigned request, unsigned arg) override final; virtual String tty_name() const = 0; diff --git a/Kernel/WindowComposer.cpp b/Kernel/WindowComposer.cpp index b616d40398893b..b5fa1ff7d60301 100644 --- a/Kernel/WindowComposer.cpp +++ b/Kernel/WindowComposer.cpp @@ -4,12 +4,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include void WindowComposer_main() @@ -20,52 +14,7 @@ void WindowComposer_main() FrameBuffer framebuffer((dword*)info.framebuffer, info.width, info.height); - MsgBox(nullptr, "Serenity Operating System"); - - { - auto* widgetTestWindow = new Window; - widgetTestWindow->setTitle("Widget test"); - widgetTestWindow->setRect({ 20, 40, 100, 180 }); - - auto* widgetTestWindowWidget = new Widget; - widgetTestWindowWidget->setWindowRelativeRect({ 0, 0, 100, 100 }); - widgetTestWindow->setMainWidget(widgetTestWindowWidget); - - auto* l = new Label(widgetTestWindowWidget); - l->setWindowRelativeRect({ 0, 0, 100, 20 }); - l->setText("Label"); - - auto* b = new Button(widgetTestWindowWidget); - b->setWindowRelativeRect({ 0, 20, 100, 20 }); - b->setCaption("Button"); - - b->onClick = [] (Button& button) { - printf("Button %p clicked!\n", &button); - }; - - auto* c = new CheckBox(widgetTestWindowWidget); - c->setWindowRelativeRect({ 0, 40, 100, 20 }); - c->setCaption("CheckBox"); - - auto *lb = new ListBox(widgetTestWindowWidget); - lb->setWindowRelativeRect({ 0, 60, 100, 100 }); - lb->addItem("This"); - lb->addItem("is"); - lb->addItem("a"); - lb->addItem("ListBox"); - - auto *tb = new TextBox(widgetTestWindowWidget); - tb->setWindowRelativeRect({ 0, 160, 100, 20 }); - tb->setText("Hello!"); - tb->setFocus(true); - - tb->onReturnPressed = [] (TextBox& textBox) { - printf("TextBox %p return pressed: '%s'\n", &textBox, textBox.text().characters()); - MsgBox(nullptr, textBox.text()); - }; - - WindowManager::the().setActiveWindow(widgetTestWindow); - } + WindowManager::the(); dbgprintf("Entering WindowComposer main loop.\n"); EventLoop::main().exec(); diff --git a/Kernel/i386.cpp b/Kernel/i386.cpp index 40bc91d7077c9f..d24dac9c1c3abe 100644 --- a/Kernel/i386.cpp +++ b/Kernel/i386.cpp @@ -7,7 +7,7 @@ #include "IRQHandler.h" #include "PIC.h" -//#define PAGE_FAULT_DEBUG +#define PAGE_FAULT_DEBUG struct DescriptorTablePointer { word size; @@ -34,7 +34,7 @@ word gdt_alloc_entry() void gdt_free_entry(word entry) { - s_gdt_freelist->unchecked_append(entry); + s_gdt_freelist->append(entry); } extern "C" void handle_irq(); @@ -325,7 +325,7 @@ void gdt_init() s_gdt_freelist = new Vector(); s_gdt_freelist->ensureCapacity(256); for (size_t i = s_gdtLength; i < 256; ++i) - s_gdt_freelist->unchecked_append(i * 8); + s_gdt_freelist->append(i * 8); s_gdtLength = 256; s_gdtr.address = s_gdt; diff --git a/Kernel/init.cpp b/Kernel/init.cpp index b16fe7c1cdd7df..50763fd5a79d67 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "MemoryManager.h" #include "ProcFileSystem.h" #include "RTC.h" @@ -34,6 +35,7 @@ VirtualConsole* tty2; VirtualConsole* tty3; Keyboard* keyboard; PS2MouseDevice* ps2mouse; +GUIEventDevice* gui_event_device; #ifdef STRESS_TEST_SPAWNING static void spawn_stress() NORETURN; @@ -75,6 +77,7 @@ static void init_stage2() vfs->register_character_device(*keyboard); vfs->register_character_device(*ps2mouse); + vfs->register_character_device(*gui_event_device); vfs->register_character_device(*tty0); vfs->register_character_device(*tty1); vfs->register_character_device(*tty2); @@ -94,7 +97,7 @@ static void init_stage2() environment.append("TERM=ansi"); int error; - Process::create_user_process("/bin/sh", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0); + //Process::create_user_process("/bin/sh", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0); #ifdef SPAWN_GUI_TEST_APP Process::create_user_process("/bin/guitest", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0); #endif @@ -132,6 +135,7 @@ void init() keyboard = new Keyboard; ps2mouse = new PS2MouseDevice; + gui_event_device = new GUIEventDevice; VirtualConsole::initialize(); tty0 = new VirtualConsole(0, VirtualConsole::AdoptCurrentVGABuffer); diff --git a/Kernel/sync.sh b/Kernel/sync.sh index 4bf0e364191780..5051382e055298 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -8,6 +8,8 @@ mknod mnt/dev/tty0 c 4 0 mknod mnt/dev/tty1 c 4 1 mknod mnt/dev/tty2 c 4 2 mknod mnt/dev/tty3 c 4 3 +mknod mnt/dev/psaux c 10 1 +mknod mnt/dev/gui_events c 66 1 cp -R ../Base/* mnt/ cp -v ../Userland/sh mnt/bin/sh cp -v ../Userland/id mnt/bin/id diff --git a/LibC/errno_numbers.h b/LibC/errno_numbers.h index 6d2aae4b90f703..62fd2c09e2900e 100644 --- a/LibC/errno_numbers.h +++ b/LibC/errno_numbers.h @@ -43,7 +43,6 @@ __ERROR(EAFNOSUPPORT, "Address family not supported") \ __ERROR(EWHYTHO, "Failed without setting an error code (Bug!)") \ __ERROR(EBADWINDOW, "Bad Window ID") \ - __ERROR(EBADWIDGET, "Bad Widget ID") \ enum __errno_values { #undef __ERROR diff --git a/LibC/stdio.cpp b/LibC/stdio.cpp index 23223ca035b37c..5665d2032483d7 100644 --- a/LibC/stdio.cpp +++ b/LibC/stdio.cpp @@ -9,6 +9,7 @@ #include #include #include +#include extern "C" { @@ -225,6 +226,20 @@ void rewind(FILE* stream) } static void sys_putch(char*&, char ch) +{ + syscall(SC_putch, ch); +} + +int sys_printf(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int ret = printfInternal(sys_putch, nullptr, fmt, ap); + va_end(ap); + return ret; +} + +static void stdout_putch(char*&, char ch) { putchar(ch); } @@ -254,7 +269,7 @@ int printf(const char* fmt, ...) { va_list ap; va_start(ap, fmt); - int ret = printfInternal(sys_putch, nullptr, fmt, ap); + int ret = printfInternal(stdout_putch, nullptr, fmt, ap); va_end(ap); return ret; } diff --git a/LibC/stdio.h b/LibC/stdio.h index 664c8d79e53d07..e65ffabd130c03 100644 --- a/LibC/stdio.h +++ b/LibC/stdio.h @@ -54,6 +54,7 @@ int vfprintf(FILE*, const char* fmt, va_list); int vsprintf(char* buffer, const char* fmt, va_list); int fprintf(FILE*, const char* fmt, ...); int printf(const char* fmt, ...); +int sys_printf(const char* fmt, ...); int sprintf(char* buffer, const char* fmt, ...); int putchar(int ch); int putc(int ch, FILE*); diff --git a/Userland/guitest.cpp b/Userland/guitest.cpp index 597458c63508d4..48af3a9d24f11f 100644 --- a/Userland/guitest.cpp +++ b/Userland/guitest.cpp @@ -11,7 +11,7 @@ int main(int argc, char** argv) { GUI_CreateWindowParameters wparams; - wparams.rect = { 200, 200, 300, 200 }; + wparams.rect = { { 200, 200 }, { 300, 200 } }; wparams.background_color = 0xffc0c0; strcpy(wparams.title, "GUI test app"); int window_id = syscall(SC_gui_create_window, &wparams); @@ -20,28 +20,27 @@ int main(int argc, char** argv) return 1; } - GUI_CreateWidgetParameters label_params; - label_params.type = GUI_WidgetType::Label; - label_params.rect = { 20, 20, 260, 20 }; - label_params.opaque = false; - strcpy(label_params.text, "Hello World!"); - int label_id = syscall(SC_gui_create_widget, window_id, &label_params); - if (label_id < 0) { - perror("gui_create_widget"); - return 1; - } - - GUI_CreateWidgetParameters button_params; - button_params.type = GUI_WidgetType::Button; - button_params.rect = { 60, 60, 120, 20 }; - strcpy(button_params.text, "I'm a button!"); - int button_id = syscall(SC_gui_create_widget, window_id, &button_params); - if (button_id < 0) { - perror("gui_create_widget"); + int fd = open("/dev/gui_events", O_RDONLY); + if (fd < 0) { + perror("open"); return 1; } for (;;) { + GUI_Event event; + ssize_t nread = read(fd, &event, sizeof(event)); + if (nread < 0) { + perror("read"); + return 1; + } + assert(nread == sizeof(event)); + switch (event.type) { + case GUI_Event::Type::Paint: sys_printf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height); break; + case GUI_Event::Type::MouseDown: sys_printf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break; + case GUI_Event::Type::MouseUp: sys_printf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break; + case GUI_Event::Type::MouseMove: sys_printf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break; + } + } return 0; } diff --git a/VirtualFileSystem/CharacterDevice.h b/VirtualFileSystem/CharacterDevice.h index 78cb46486a0561..2028ee0f5d8413 100644 --- a/VirtualFileSystem/CharacterDevice.h +++ b/VirtualFileSystem/CharacterDevice.h @@ -12,7 +12,7 @@ class CharacterDevice { RetainPtr open(int options); - virtual bool has_data_available_for_reading() const = 0; + virtual bool has_data_available_for_reading(Process&) const = 0; virtual ssize_t read(byte* buffer, size_t bufferSize) = 0; virtual ssize_t write(const byte* buffer, size_t bufferSize) = 0; diff --git a/VirtualFileSystem/FileDescriptor.cpp b/VirtualFileSystem/FileDescriptor.cpp index 7cc7cdb96181eb..7fb970090755f6 100644 --- a/VirtualFileSystem/FileDescriptor.cpp +++ b/VirtualFileSystem/FileDescriptor.cpp @@ -173,14 +173,14 @@ bool FileDescriptor::can_write() return true; } -bool FileDescriptor::has_data_available_for_reading() +bool FileDescriptor::has_data_available_for_reading(Process& process) { if (is_fifo()) { ASSERT(fifo_direction() == FIFO::Reader); return m_fifo->can_read(); } if (m_vnode->isCharacterDevice()) - return m_vnode->characterDevice()->has_data_available_for_reading(); + return m_vnode->characterDevice()->has_data_available_for_reading(process); return true; } diff --git a/VirtualFileSystem/FileDescriptor.h b/VirtualFileSystem/FileDescriptor.h index 2fc5c41e077610..675f1aeb2b1c1b 100644 --- a/VirtualFileSystem/FileDescriptor.h +++ b/VirtualFileSystem/FileDescriptor.h @@ -9,6 +9,7 @@ #ifdef SERENITY class TTY; +class Process; #endif class FileDescriptor : public Retainable { @@ -27,7 +28,7 @@ class FileDescriptor : public Retainable { ssize_t write(const byte* data, size_t); int stat(Unix::stat*); - bool has_data_available_for_reading(); + bool has_data_available_for_reading(Process&); bool can_write(); ssize_t get_dir_entries(byte* buffer, size_t); diff --git a/VirtualFileSystem/FullDevice.cpp b/VirtualFileSystem/FullDevice.cpp index 58f014895fa390..32f5b0bf109b47 100644 --- a/VirtualFileSystem/FullDevice.cpp +++ b/VirtualFileSystem/FullDevice.cpp @@ -13,7 +13,7 @@ FullDevice::~FullDevice() { } -bool FullDevice::has_data_available_for_reading() const +bool FullDevice::has_data_available_for_reading(Process&) const { return true; } diff --git a/VirtualFileSystem/FullDevice.h b/VirtualFileSystem/FullDevice.h index da2d6d5b19f553..f75c5a6edf7c12 100644 --- a/VirtualFileSystem/FullDevice.h +++ b/VirtualFileSystem/FullDevice.h @@ -10,6 +10,6 @@ class FullDevice final : public CharacterDevice { virtual ssize_t read(byte* buffer, size_t bufferSize) override; virtual ssize_t write(const byte* buffer, size_t bufferSize) override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; }; diff --git a/VirtualFileSystem/NullDevice.cpp b/VirtualFileSystem/NullDevice.cpp index 1ad8c5f7847dee..2fb2e527e15891 100644 --- a/VirtualFileSystem/NullDevice.cpp +++ b/VirtualFileSystem/NullDevice.cpp @@ -12,7 +12,7 @@ NullDevice::~NullDevice() { } -bool NullDevice::has_data_available_for_reading() const +bool NullDevice::has_data_available_for_reading(Process&) const { return true; } diff --git a/VirtualFileSystem/NullDevice.h b/VirtualFileSystem/NullDevice.h index 8cfe658bb73dae..c73f35a9bb2733 100644 --- a/VirtualFileSystem/NullDevice.h +++ b/VirtualFileSystem/NullDevice.h @@ -10,6 +10,6 @@ class NullDevice final : public CharacterDevice { virtual ssize_t read(byte* buffer, size_t bufferSize) override; virtual ssize_t write(const byte* buffer, size_t bufferSize) override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; }; diff --git a/VirtualFileSystem/RandomDevice.cpp b/VirtualFileSystem/RandomDevice.cpp index 0bd2559085ba5e..26d85a869292c3 100644 --- a/VirtualFileSystem/RandomDevice.cpp +++ b/VirtualFileSystem/RandomDevice.cpp @@ -29,7 +29,7 @@ static void mysrand(unsigned seed) } #endif -bool RandomDevice::has_data_available_for_reading() const +bool RandomDevice::has_data_available_for_reading(Process&) const { return true; } diff --git a/VirtualFileSystem/RandomDevice.h b/VirtualFileSystem/RandomDevice.h index 4b5fa98c72a195..4d7436dacb64cf 100644 --- a/VirtualFileSystem/RandomDevice.h +++ b/VirtualFileSystem/RandomDevice.h @@ -10,6 +10,6 @@ class RandomDevice final : public CharacterDevice { virtual ssize_t read(byte* buffer, size_t bufferSize) override; virtual ssize_t write(const byte* buffer, size_t bufferSize) override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; }; diff --git a/VirtualFileSystem/ZeroDevice.cpp b/VirtualFileSystem/ZeroDevice.cpp index 529f87550b08a9..661b776968853d 100644 --- a/VirtualFileSystem/ZeroDevice.cpp +++ b/VirtualFileSystem/ZeroDevice.cpp @@ -12,7 +12,7 @@ ZeroDevice::~ZeroDevice() { } -bool ZeroDevice::has_data_available_for_reading() const +bool ZeroDevice::has_data_available_for_reading(Process&) const { return true; } diff --git a/VirtualFileSystem/ZeroDevice.h b/VirtualFileSystem/ZeroDevice.h index 702857baca0031..9bc64b69084398 100644 --- a/VirtualFileSystem/ZeroDevice.h +++ b/VirtualFileSystem/ZeroDevice.h @@ -10,6 +10,6 @@ class ZeroDevice final : public CharacterDevice { virtual ssize_t read(byte* buffer, size_t bufferSize) override; virtual ssize_t write(const byte* buffer, size_t bufferSize) override; - virtual bool has_data_available_for_reading() const override; + virtual bool has_data_available_for_reading(Process&) const override; }; diff --git a/Widgets/Event.h b/Widgets/Event.h index afc3f2e5ae74d2..1314a4dd61ed02 100644 --- a/Widgets/Event.h +++ b/Widgets/Event.h @@ -37,8 +37,6 @@ class Event { DeferredDestroy, WindowBecameInactive, WindowBecameActive, - FocusIn, - FocusOut, WM_Compose, }; diff --git a/Widgets/EventLoop.cpp b/Widgets/EventLoop.cpp index d4b49661d064e0..57a8118ee506bf 100644 --- a/Widgets/EventLoop.cpp +++ b/Widgets/EventLoop.cpp @@ -82,7 +82,7 @@ void EventLoop::waitForEvent() bool prev_right_button = screen.right_mouse_button_pressed(); int dx = 0; int dy = 0; - while (mouse.has_data_available_for_reading()) { + while (mouse.has_data_available_for_reading(*m_server_process)) { signed_byte data[3]; ssize_t nread = mouse.read((byte*)data, 3); ASSERT(nread == 3); @@ -90,7 +90,7 @@ void EventLoop::waitForEvent() bool right_button = data[0] & 2; dx += data[1]; dy += -data[2]; - if (left_button != prev_left_button || right_button != prev_right_button || !mouse.has_data_available_for_reading()) { + if (left_button != prev_left_button || right_button != prev_right_button || !mouse.has_data_available_for_reading(*m_server_process)) { prev_left_button = left_button; prev_right_button = right_button; screen.on_receive_mouse_data(dx, dy, left_button, right_button); diff --git a/Widgets/GUIEventDevice.cpp b/Widgets/GUIEventDevice.cpp new file mode 100644 index 00000000000000..f9bd86f62318a6 --- /dev/null +++ b/Widgets/GUIEventDevice.cpp @@ -0,0 +1,38 @@ +#include "GUIEventDevice.h" +#include +#include +#include + +//#define GUIEVENTDEVICE_DEBUG + +GUIEventDevice::GUIEventDevice() + : CharacterDevice(66, 1) +{ +} + +GUIEventDevice::~GUIEventDevice() +{ +} + +bool GUIEventDevice::has_data_available_for_reading(Process& process) const +{ + return !process.gui_events().is_empty(); +} + +ssize_t GUIEventDevice::read(byte* buffer, size_t size) +{ +#ifdef GUIEVENTDEVICE_DEBUG + dbgprintf("GUIEventDevice::read(): %s<%u>, size=%u, sizeof(GUI_Event)=%u\n", current->name().characters(), current->pid(), size, sizeof(GUI_Event)); +#endif + if (current->gui_events().is_empty()) + return 0; + LOCKER(current->gui_events_lock()); + ASSERT(size == sizeof(GUI_Event)); + *reinterpret_cast(buffer) = current->gui_events().take_first(); + return size; +} + +ssize_t GUIEventDevice::write(const byte*, size_t) +{ + return -EINVAL; +} diff --git a/Widgets/GUIEventDevice.h b/Widgets/GUIEventDevice.h new file mode 100644 index 00000000000000..061fc96e710334 --- /dev/null +++ b/Widgets/GUIEventDevice.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class GUIEventDevice final : public CharacterDevice { +public: + GUIEventDevice(); + virtual ~GUIEventDevice() override; + +private: + virtual bool has_data_available_for_reading(Process&) const override; + virtual ssize_t read(byte* buffer, size_t bufferSize) override; + virtual ssize_t write(const byte* buffer, size_t bufferSize) override; +}; diff --git a/Widgets/MsgBox.cpp b/Widgets/MsgBox.cpp index 06bfbf0643a571..c6c8ce0c84e463 100644 --- a/Widgets/MsgBox.cpp +++ b/Widgets/MsgBox.cpp @@ -4,7 +4,9 @@ #include "Window.h" #include "Label.h" #include "Button.h" +#include "Process.h" +#if 0 void MsgBox(Window* owner, String&& text) { Font& font = Font::defaultFont(); @@ -33,7 +35,7 @@ void MsgBox(Window* owner, String&& text) buttonHeight ); - auto* window = new Window; + auto* window = new Window(*current, current->make_window_id()); window->setTitle("MsgBox"); window->setRect(windowRect); auto* widget = new Widget; @@ -56,4 +58,4 @@ void MsgBox(Window* owner, String&& text) button.window()->close(); }; } - +#endif diff --git a/Widgets/Point.h b/Widgets/Point.h index 99fd6db1bd6a5c..895bfa44410b33 100644 --- a/Widgets/Point.h +++ b/Widgets/Point.h @@ -1,11 +1,13 @@ #pragma once class Rect; +struct GUI_Point; class Point { public: Point() { } Point(int x, int y) : m_x(x) , m_y(y) { } + Point(const GUI_Point&); int x() const { return m_x; } int y() const { return m_y; } @@ -37,6 +39,8 @@ class Point { return !(*this == other); } + operator GUI_Point() const; + private: int m_x { 0 }; int m_y { 0 }; diff --git a/Widgets/Rect.h b/Widgets/Rect.h index 95f98dd1c70fbe..4131cbba85408c 100644 --- a/Widgets/Rect.h +++ b/Widgets/Rect.h @@ -3,6 +3,8 @@ #include "Point.h" #include "Size.h" +struct GUI_Rect; + class Rect { public: Rect() { } @@ -16,6 +18,7 @@ class Rect { , m_size(size) { } + Rect(const GUI_Rect&); bool is_empty() const { @@ -117,6 +120,8 @@ class Rect { Point location() const { return m_location; } Size size() const { return m_size; } + operator GUI_Rect() const; + bool operator==(const Rect& other) const { return m_location == other.m_location diff --git a/Widgets/Size.h b/Widgets/Size.h index 6fd8a21f6f254f..197a848d345e2d 100644 --- a/Widgets/Size.h +++ b/Widgets/Size.h @@ -1,9 +1,12 @@ #pragma once +struct GUI_Size; + class Size { public: Size() { } Size(int w, int h) : m_width(w), m_height(h) { } + Size(const GUI_Size&); bool is_empty() const { return !m_width || !m_height; } @@ -19,6 +22,8 @@ class Size { m_height == other.m_height; } + operator GUI_Size() const; + private: int m_width { 0 }; int m_height { 0 }; diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp index e26a3c3e7e83d1..d83877e3ffd513 100644 --- a/Widgets/Widget.cpp +++ b/Widgets/Widget.cpp @@ -56,10 +56,7 @@ void Widget::event(Event& event) case Event::MouseMove: return mouseMoveEvent(static_cast(event)); case Event::MouseDown: - if (auto* win = window()) { - // FIXME: if (acceptsFocus()) - win->setFocusedWidget(this); - } + // FIXME: Focus self if needed. return mouseDownEvent(static_cast(event)); case Event::MouseUp: return mouseUpEvent(static_cast(event)); @@ -141,8 +138,7 @@ void Widget::setWindow(Window* window) bool Widget::isFocused() const { - if (auto* win = window()) - return win->isActive() && win->focusedWidget() == this; + // FIXME: Implement. return false; } @@ -150,8 +146,7 @@ void Widget::setFocus(bool focus) { if (focus == isFocused()) return; - if (auto* win = window()) - win->setFocusedWidget(this); + // FIXME: Implement. } void Widget::setFont(RetainPtr&& font) diff --git a/Widgets/Window.cpp b/Widgets/Window.cpp index 5713d3332b6311..bc46a619583f44 100644 --- a/Widgets/Window.cpp +++ b/Widgets/Window.cpp @@ -3,31 +3,20 @@ #include "Event.h" #include "EventLoop.h" #include "Widget.h" +#include "Process.h" -Window::Window(Object* parent) - : Object(parent) +Window::Window(Process& process, int window_id) + : m_process(process) + , m_window_id(window_id) { WindowManager::the().addWindow(*this); } Window::~Window() { - delete m_mainWidget; - m_mainWidget = nullptr; - if (parent()) - parent()->removeChild(*this); WindowManager::the().removeWindow(*this); } -void Window::setMainWidget(Widget* widget) -{ - if (m_mainWidget == widget) - return; - - m_mainWidget = widget; - widget->setWindow(this); -} - void Window::setTitle(String&& title) { if (m_title == title) @@ -58,51 +47,50 @@ void Window::update(const Rect& rect) EventLoop::main().postEvent(this, make(rect)); } -void Window::event(Event& event) +// FIXME: Just use the same types. +static GUI_MouseButton to_api(MouseButton button) { - if (event.isMouseEvent()) { - auto& me = static_cast(event); - //printf("Window{%p}: %s %d,%d\n", this, me.name(), me.x(), me.y()); - if (m_mainWidget) { - auto result = m_mainWidget->hitTest(me.x(), me.y()); - //printf("hit test for %d,%d found: %s{%p} %d,%d\n", me.x(), me.y(), result.widget->class_name(), result.widget, result.localX, result.localY); - // FIXME: Re-use the existing event instead of crafting a new one? - auto localEvent = make(event.type(), result.localX, result.localY, me.button()); - return result.widget->event(*localEvent); - } - return Object::event(event); + switch (button) { + case MouseButton::None: return GUI_MouseButton::NoButton; + case MouseButton::Left: return GUI_MouseButton::Left; + case MouseButton::Right: return GUI_MouseButton::Right; + case MouseButton::Middle: return GUI_MouseButton::Middle; } +} - if (event.isPaintEvent()) { - auto& pe = static_cast(event); - printf("Window[\"%s\"]: paintEvent %d,%d %dx%d\n", title().characters(), - pe.rect().x(), - pe.rect().y(), - pe.rect().width(), - pe.rect().height()); - - if (isBeingDragged()) { - // Ignore paint events during window drag. - return; - } - if (m_mainWidget) { - if (pe.rect().is_empty()) - m_mainWidget->event(*make(m_mainWidget->rect())); - else - m_mainWidget->event(event); - WindowManager::the().did_paint(*this); - return; - } - return Object::event(event); +void Window::event(Event& event) +{ + GUI_Event gui_event; + gui_event.window_id = window_id(); + + switch (event.type()) { + case Event::Paint: + gui_event.type = GUI_Event::Type::Paint; + gui_event.paint.rect = static_cast(event).rect(); + break; + case Event::MouseMove: + gui_event.type = GUI_Event::Type::MouseMove; + gui_event.mouse.position = static_cast(event).position(); + break; + case Event::MouseDown: + gui_event.type = GUI_Event::Type::MouseDown; + gui_event.mouse.position = static_cast(event).position(); + gui_event.mouse.button = to_api(static_cast(event).button()); + break; + case Event::MouseUp: + gui_event.type = GUI_Event::Type::MouseUp; + gui_event.mouse.position = static_cast(event).position(); + gui_event.mouse.button = to_api(static_cast(event).button()); + break; } - if (event.isKeyEvent()) { - if (m_focusedWidget) - return m_focusedWidget->event(event); - return Object::event(event); - } + if (gui_event.type == GUI_Event::Type::Invalid) + return; - return Object::event(event); + { + LOCKER(m_process.gui_events_lock()); + m_process.gui_events().append(move(gui_event)); + } } void Window::did_paint() @@ -120,24 +108,6 @@ bool Window::isVisible() const return WindowManager::the().isVisible(const_cast(*this)); } -void Window::setFocusedWidget(Widget* widget) -{ - if (m_focusedWidget.ptr() == widget) - return; - auto* previously_focused_widget = m_focusedWidget.ptr(); - if (!widget) - m_focusedWidget = nullptr; - else { - m_focusedWidget = widget->makeWeakPtr(); - m_focusedWidget->update(); - EventLoop::main().postEvent(m_focusedWidget.ptr(), make(Event::FocusIn)); - } - if (previously_focused_widget) { - previously_focused_widget->update(); - EventLoop::main().postEvent(previously_focused_widget, make(Event::FocusOut)); - } -} - void Window::close() { WindowManager::the().removeWindow(*this); diff --git a/Widgets/Window.h b/Widgets/Window.h index be53f0fccc3183..2d311c82c760ed 100644 --- a/Widgets/Window.h +++ b/Widgets/Window.h @@ -7,13 +7,16 @@ #include #include +class Process; class Widget; class Window final : public Object, public InlineLinkedListNode { public: - explicit Window(Object* parent = nullptr); + Window(Process&, int window_id); virtual ~Window() override; + int window_id() const { return m_window_id; } + String title() const { return m_title; } void setTitle(String&&); @@ -30,11 +33,6 @@ class Window final : public Object, public InlineLinkedListNode { void setPosition(const Point& position) { setRect({ position.x(), position.y(), width(), height() }); } void setPositionWithoutRepaint(const Point& position) { setRectWithoutRepaint({ position.x(), position.y(), width(), height() }); } - Widget* mainWidget() { return m_mainWidget; } - const Widget* mainWidget() const { return m_mainWidget; } - - void setMainWidget(Widget*); - virtual void event(Event&) override; bool isBeingDragged() const { return m_isBeingDragged; } @@ -45,10 +43,6 @@ class Window final : public Object, public InlineLinkedListNode { bool isActive() const; - Widget* focusedWidget() { return m_focusedWidget.ptr(); } - const Widget* focusedWidget() const { return m_focusedWidget.ptr(); } - void setFocusedWidget(Widget*); - bool isVisible() const; void close(); @@ -65,11 +59,10 @@ class Window final : public Object, public InlineLinkedListNode { private: String m_title; Rect m_rect; - Widget* m_mainWidget { nullptr }; bool m_isBeingDragged { false }; - WeakPtr m_focusedWidget; - RetainPtr m_backing; + Process& m_process; + int m_window_id { -1 }; };