فهرست منبع

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.
Andreas Kling 6 سال پیش
والد
کامیت
b0e3f73375
46فایلهای تغییر یافته به همراه283 افزوده شده و 292 حذف شده
  1. 1 1
      Kernel/Console.cpp
  2. 1 1
      Kernel/Console.h
  3. 48 11
      Kernel/GUITypes.h
  4. 1 1
      Kernel/Keyboard.cpp
  5. 1 1
      Kernel/Keyboard.h
  6. 2 1
      Kernel/Makefile
  7. 1 1
      Kernel/PS2MouseDevice.cpp
  8. 1 1
      Kernel/PS2MouseDevice.h
  9. 2 1
      Kernel/Process.cpp
  10. 10 4
      Kernel/Process.h
  11. 21 71
      Kernel/ProcessGUI.cpp
  12. 10 2
      Kernel/Scheduler.cpp
  13. 0 4
      Kernel/Syscall.cpp
  14. 0 2
      Kernel/Syscall.h
  15. 1 1
      Kernel/TTY.cpp
  16. 1 1
      Kernel/TTY.h
  17. 1 52
      Kernel/WindowComposer.cpp
  18. 3 3
      Kernel/i386.cpp
  19. 5 1
      Kernel/init.cpp
  20. 2 0
      Kernel/sync.sh
  21. 0 1
      LibC/errno_numbers.h
  22. 16 1
      LibC/stdio.cpp
  23. 1 0
      LibC/stdio.h
  24. 18 19
      Userland/guitest.cpp
  25. 1 1
      VirtualFileSystem/CharacterDevice.h
  26. 2 2
      VirtualFileSystem/FileDescriptor.cpp
  27. 2 1
      VirtualFileSystem/FileDescriptor.h
  28. 1 1
      VirtualFileSystem/FullDevice.cpp
  29. 1 1
      VirtualFileSystem/FullDevice.h
  30. 1 1
      VirtualFileSystem/NullDevice.cpp
  31. 1 1
      VirtualFileSystem/NullDevice.h
  32. 1 1
      VirtualFileSystem/RandomDevice.cpp
  33. 1 1
      VirtualFileSystem/RandomDevice.h
  34. 1 1
      VirtualFileSystem/ZeroDevice.cpp
  35. 1 1
      VirtualFileSystem/ZeroDevice.h
  36. 0 2
      Widgets/Event.h
  37. 2 2
      Widgets/EventLoop.cpp
  38. 38 0
      Widgets/GUIEventDevice.cpp
  39. 14 0
      Widgets/GUIEventDevice.h
  40. 4 2
      Widgets/MsgBox.cpp
  41. 4 0
      Widgets/Point.h
  42. 5 0
      Widgets/Rect.h
  43. 5 0
      Widgets/Size.h
  44. 3 8
      Widgets/Widget.cpp
  45. 42 72
      Widgets/Window.cpp
  46. 6 13
      Widgets/Window.h

+ 1 - 1
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;
 }

+ 1 - 1
Kernel/Console.h

@@ -18,7 +18,7 @@ public:
     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;
 

+ 48 - 11
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 }; }

+ 1 - 1
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();
 }

+ 1 - 1
Kernel/Keyboard.h

@@ -39,7 +39,7 @@ private:
     // ^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);
 

+ 2 - 1
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 \

+ 1 - 1
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();
 }

+ 1 - 1
Kernel/PS2MouseDevice.h

@@ -12,7 +12,7 @@ public:
     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;
 

+ 2 - 1
Kernel/Process.cpp

@@ -18,6 +18,7 @@
 #include "Scheduler.h"
 #include "FIFO.h"
 #include "KSyms.h"
+#include <Widgets/Window.h>
 
 //#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();

+ 10 - 4
Kernel/Process.h

@@ -12,6 +12,7 @@
 #include <AK/AKString.h>
 #include <AK/Vector.h>
 #include <AK/WeakPtr.h>
+#include <AK/Lock.h>
 
 class FileDescriptor;
 class PageDirectory;
@@ -191,13 +192,12 @@ public:
 
     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 @@ public:
 
     bool is_root() const { return m_euid == 0; }
 
+    Vector<GUI_Event>& 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 @@ private:
 
     RetainPtr<Region> m_display_framebuffer_region;
 
-    Vector<WeakPtr<Window>> m_windows;
-    Vector<WeakPtr<Widget>> m_widgets;
+    HashMap<int, OwnPtr<Window>> m_windows;
+
+    Vector<GUI_Event> m_gui_events;
+    SpinLock m_gui_events_lock;
+    int m_next_window_id { 1 };
 };
 
 extern Process* current;

+ 21 - 71
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<Window>(*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<int>(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<int>(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<Label*>(widget)->setText(params.text);
-        widget->setFillWithBackgroundColor(params.opaque);
-        break;
-    case GUI_WidgetType::Button:
-        widget = new Button(window.mainWidget());
-        static_cast<Button*>(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<int>(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;
 }
-

+ 10 - 2
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);
 }
 

+ 0 - 4
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;

+ 0 - 2
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 {
 

+ 1 - 1
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();
 }

+ 1 - 1
Kernel/TTY.h

@@ -12,7 +12,7 @@ public:
 
     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;

+ 1 - 52
Kernel/WindowComposer.cpp

@@ -4,12 +4,6 @@
 #include <Widgets/FrameBuffer.h>
 #include <Widgets/WindowManager.h>
 #include <Widgets/EventLoop.h>
-#include <Widgets/MsgBox.h>
-#include <Widgets/TextBox.h>
-#include <Widgets/Label.h>
-#include <Widgets/ListBox.h>
-#include <Widgets/Button.h>
-#include <Widgets/CheckBox.h>
 #include <Widgets/Window.h>
 
 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();

+ 3 - 3
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<word, KmallocEternalAllocator>();
     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;

+ 5 - 1
Kernel/init.cpp

@@ -15,6 +15,7 @@
 #include <VirtualFileSystem/RandomDevice.h>
 #include <VirtualFileSystem/Ext2FileSystem.h>
 #include <VirtualFileSystem/VirtualFileSystem.h>
+#include <Widgets/GUIEventDevice.h>
 #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);

+ 2 - 0
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

+ 0 - 1
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

+ 16 - 1
LibC/stdio.cpp

@@ -9,6 +9,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <AK/printf.cpp>
+#include <Kernel/Syscall.h>
 
 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;
 }

+ 1 - 0
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*);

+ 18 - 19
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;
 }

+ 1 - 1
VirtualFileSystem/CharacterDevice.h

@@ -12,7 +12,7 @@ public:
 
     RetainPtr<FileDescriptor> 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;

+ 2 - 2
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;
 }
 

+ 2 - 1
VirtualFileSystem/FileDescriptor.h

@@ -9,6 +9,7 @@
 
 #ifdef SERENITY
 class TTY;
+class Process;
 #endif
 
 class FileDescriptor : public Retainable<FileDescriptor> {
@@ -27,7 +28,7 @@ public:
     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);

+ 1 - 1
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;
 }

+ 1 - 1
VirtualFileSystem/FullDevice.h

@@ -10,6 +10,6 @@ public:
 
     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;
 };
 

+ 1 - 1
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;
 }

+ 1 - 1
VirtualFileSystem/NullDevice.h

@@ -10,6 +10,6 @@ public:
 
     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;
 };
 

+ 1 - 1
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;
 }

+ 1 - 1
VirtualFileSystem/RandomDevice.h

@@ -10,6 +10,6 @@ public:
 
     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;
 };
 

+ 1 - 1
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;
 }

+ 1 - 1
VirtualFileSystem/ZeroDevice.h

@@ -10,6 +10,6 @@ public:
 
     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;
 };
 

+ 0 - 2
Widgets/Event.h

@@ -37,8 +37,6 @@ public:
         DeferredDestroy,
         WindowBecameInactive,
         WindowBecameActive,
-        FocusIn,
-        FocusOut,
         WM_Compose,
     };
 

+ 2 - 2
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);

+ 38 - 0
Widgets/GUIEventDevice.cpp

@@ -0,0 +1,38 @@
+#include "GUIEventDevice.h"
+#include <Kernel/Process.h>
+#include <AK/Lock.h>
+#include <LibC/errno_numbers.h>
+
+//#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<GUI_Event*>(buffer) = current->gui_events().take_first();
+    return size;
+}
+
+ssize_t GUIEventDevice::write(const byte*, size_t)
+{
+    return -EINVAL;
+}

+ 14 - 0
Widgets/GUIEventDevice.h

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <VirtualFileSystem/CharacterDevice.h>
+
+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;
+};

+ 4 - 2
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

+ 4 - 0
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 @@ public:
         return !(*this == other);
     }
 
+    operator GUI_Point() const;
+
 private:
     int m_x { 0 };
     int m_y { 0 };

+ 5 - 0
Widgets/Rect.h

@@ -3,6 +3,8 @@
 #include "Point.h"
 #include "Size.h"
 
+struct GUI_Rect;
+
 class Rect {
 public:
     Rect() { }
@@ -16,6 +18,7 @@ public:
         , m_size(size)
     {
     }
+    Rect(const GUI_Rect&);
 
     bool is_empty() const
     {
@@ -117,6 +120,8 @@ public:
     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

+ 5 - 0
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 @@ public:
                m_height == other.m_height;
     }
 
+    operator GUI_Size() const;
+
 private:
     int m_width { 0 };
     int m_height { 0 };

+ 3 - 8
Widgets/Widget.cpp

@@ -56,10 +56,7 @@ void Widget::event(Event& event)
     case Event::MouseMove:
         return mouseMoveEvent(static_cast<MouseEvent&>(event));
     case Event::MouseDown:
-        if (auto* win = window()) {
-            // FIXME: if (acceptsFocus())
-            win->setFocusedWidget(this);
-        }
+        // FIXME: Focus self if needed.
         return mouseDownEvent(static_cast<MouseEvent&>(event));
     case Event::MouseUp:
         return mouseUpEvent(static_cast<MouseEvent&>(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>&& font)

+ 42 - 72
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<PaintEvent>(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<MouseEvent&>(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<MouseEvent>(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<PaintEvent&>(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<PaintEvent>(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<PaintEvent&>(event).rect();
+        break;
+    case Event::MouseMove:
+        gui_event.type = GUI_Event::Type::MouseMove;
+        gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
+        break;
+    case Event::MouseDown:
+        gui_event.type = GUI_Event::Type::MouseDown;
+        gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
+        gui_event.mouse.button = to_api(static_cast<MouseEvent&>(event).button());
+        break;
+    case Event::MouseUp:
+        gui_event.type = GUI_Event::Type::MouseUp;
+        gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
+        gui_event.mouse.button = to_api(static_cast<MouseEvent&>(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<Window&>(*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>(Event::FocusIn));
-    }
-    if (previously_focused_widget) {
-        previously_focused_widget->update();
-        EventLoop::main().postEvent(previously_focused_widget, make<Event>(Event::FocusOut));
-    }
-}
-
 void Window::close()
 {
     WindowManager::the().removeWindow(*this);

+ 6 - 13
Widgets/Window.h

@@ -7,13 +7,16 @@
 #include <AK/InlineLinkedList.h>
 #include <AK/WeakPtr.h>
 
+class Process;
 class Widget;
 
 class Window final : public Object, public InlineLinkedListNode<Window> {
 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 @@ public:
     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 @@ public:
 
     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 @@ public:
 private:
     String m_title;
     Rect m_rect;
-    Widget* m_mainWidget { nullptr };
     bool m_isBeingDragged { false };
 
-    WeakPtr<Widget> m_focusedWidget;
-
     RetainPtr<GraphicsBitmap> m_backing;
+    Process& m_process;
+    int m_window_id { -1 };
 };