mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:12:45 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									b4da4e8fbd
								
							
						
					
					
						commit
						b0e3f73375
					
				
					 46 changed files with 283 additions and 292 deletions
				
			
		|  | @ -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; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ public: | ||||||
|     Console(); |     Console(); | ||||||
|     virtual ~Console() override; |     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 read(byte* buffer, size_t size) override; | ||||||
|     virtual ssize_t write(const byte* data, size_t size) override; |     virtual ssize_t write(const byte* data, size_t size) override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,23 +11,60 @@ struct GUI_WindowFlags { enum { | ||||||
| 
 | 
 | ||||||
| typedef unsigned GUI_Color; | 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 { | struct GUI_CreateWindowParameters { | ||||||
|     Rect rect; |     GUI_Rect rect; | ||||||
|     Color background_color; |     Color background_color; | ||||||
|     unsigned flags { 0 }; |     unsigned flags { 0 }; | ||||||
|     char title[128]; |     char title[128]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum class GUI_WidgetType : unsigned { | enum class GUI_MouseButton : unsigned char { | ||||||
|     Label, |     NoButton = 0, | ||||||
|     Button, |     Left = 1, | ||||||
|  |     Right = 2, | ||||||
|  |     Middle = 4, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct GUI_CreateWidgetParameters { | struct GUI_Event { | ||||||
|     GUI_WidgetType type; |     enum Type : unsigned { | ||||||
|     Rect rect; |         Invalid, | ||||||
|     Color background_color; |         Paint, | ||||||
|     bool opaque { true }; |         MouseMove, | ||||||
|     unsigned flags { 0 }; |         MouseDown, | ||||||
|     char text[256]; |         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 }; } | ||||||
|  |  | ||||||
|  | @ -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(); |     return !m_queue.is_empty(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ private: | ||||||
|     // ^CharacterDevice
 |     // ^CharacterDevice
 | ||||||
|     virtual ssize_t read(byte* buffer, size_t) override; |     virtual ssize_t read(byte* buffer, size_t) override; | ||||||
|     virtual ssize_t write(const 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); |     void emit(byte); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -62,7 +62,8 @@ WIDGETS_OBJS = \ | ||||||
|     ../Widgets/ListBox.o \
 |     ../Widgets/ListBox.o \
 | ||||||
|     ../Widgets/CheckBox.o \
 |     ../Widgets/CheckBox.o \
 | ||||||
|     ../Widgets/TextBox.o \
 |     ../Widgets/TextBox.o \
 | ||||||
|     ../Widgets/AbstractScreen.o |     ../Widgets/AbstractScreen.o \
 | ||||||
|  |     ../Widgets/GUIEventDevice.o \
 | ||||||
| 
 | 
 | ||||||
| AK_OBJS = \
 | AK_OBJS = \
 | ||||||
|     ../AK/String.o \
 |     ../AK/String.o \
 | ||||||
|  |  | ||||||
|  | @ -116,7 +116,7 @@ byte PS2MouseDevice::mouse_read() | ||||||
|     return IO::in8(0x60); |     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(); |     return !m_buffer.is_empty(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ public: | ||||||
|     static PS2MouseDevice& the(); |     static PS2MouseDevice& the(); | ||||||
| 
 | 
 | ||||||
|     // ^CharacterDevice
 |     // ^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 read(byte* buffer, size_t) override; | ||||||
|     virtual ssize_t write(const byte* buffer, size_t) override; |     virtual ssize_t write(const byte* buffer, size_t) override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| #include "Scheduler.h" | #include "Scheduler.h" | ||||||
| #include "FIFO.h" | #include "FIFO.h" | ||||||
| #include "KSyms.h" | #include "KSyms.h" | ||||||
|  | #include <Widgets/Window.h> | ||||||
| 
 | 
 | ||||||
| //#define DEBUG_IO
 | //#define DEBUG_IO
 | ||||||
| //#define TASK_DEBUG
 | //#define TASK_DEBUG
 | ||||||
|  | @ -1057,7 +1058,7 @@ ssize_t Process::sys$read(int fd, void* outbuf, size_t nread) | ||||||
|     if (!descriptor) |     if (!descriptor) | ||||||
|         return -EBADF; |         return -EBADF; | ||||||
|     if (descriptor->is_blocking()) { |     if (descriptor->is_blocking()) { | ||||||
|         if (!descriptor->has_data_available_for_reading()) { |         if (!descriptor->has_data_available_for_reading(*this)) { | ||||||
|             m_blocked_fd = fd; |             m_blocked_fd = fd; | ||||||
|             block(BlockedRead); |             block(BlockedRead); | ||||||
|             sched_yield(); |             sched_yield(); | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| #include <AK/AKString.h> | #include <AK/AKString.h> | ||||||
| #include <AK/Vector.h> | #include <AK/Vector.h> | ||||||
| #include <AK/WeakPtr.h> | #include <AK/WeakPtr.h> | ||||||
|  | #include <AK/Lock.h> | ||||||
| 
 | 
 | ||||||
| class FileDescriptor; | class FileDescriptor; | ||||||
| class PageDirectory; | class PageDirectory; | ||||||
|  | @ -191,13 +192,12 @@ public: | ||||||
| 
 | 
 | ||||||
|     int gui$create_window(const GUI_CreateWindowParameters*); |     int gui$create_window(const GUI_CreateWindowParameters*); | ||||||
|     int gui$destroy_window(int window_id); |     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(); |     DisplayInfo get_display_info(); | ||||||
| 
 | 
 | ||||||
|     static void initialize(); |     static void initialize(); | ||||||
|     static void initialize_gui_statics(); |     static void initialize_gui_statics(); | ||||||
|  |     int make_window_id(); | ||||||
| 
 | 
 | ||||||
|     void crash() NORETURN; |     void crash() NORETURN; | ||||||
|     static int reap(Process&) WARN_UNUSED_RESULT; |     static int reap(Process&) WARN_UNUSED_RESULT; | ||||||
|  | @ -248,6 +248,9 @@ public: | ||||||
| 
 | 
 | ||||||
|     bool is_root() const { return m_euid == 0; } |     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: | private: | ||||||
|     friend class MemoryManager; |     friend class MemoryManager; | ||||||
|     friend class Scheduler; |     friend class Scheduler; | ||||||
|  | @ -342,8 +345,11 @@ private: | ||||||
| 
 | 
 | ||||||
|     RetainPtr<Region> m_display_framebuffer_region; |     RetainPtr<Region> m_display_framebuffer_region; | ||||||
| 
 | 
 | ||||||
|     Vector<WeakPtr<Window>> m_windows; |     HashMap<int, OwnPtr<Window>> m_windows; | ||||||
|     Vector<WeakPtr<Widget>> m_widgets; | 
 | ||||||
|  |     Vector<GUI_Event> m_gui_events; | ||||||
|  |     SpinLock m_gui_events_lock; | ||||||
|  |     int m_next_window_id { 1 }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| extern Process* current; | extern Process* current; | ||||||
|  |  | ||||||
|  | @ -22,6 +22,14 @@ void Process::initialize_gui_statics() | ||||||
|     new EventLoop; |     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() | static void wait_for_gui_server() | ||||||
| { | { | ||||||
|     // FIXME: Time out after a while and return an error.
 |     // 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; |         return -EFAULT; | ||||||
| 
 | 
 | ||||||
|     auto params = *user_params; |     auto params = *user_params; | ||||||
|  |     Rect rect = params.rect; | ||||||
| 
 | 
 | ||||||
|     if (params.rect.is_empty()) |     if (rect.is_empty()) | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
| 
 | 
 | ||||||
|     ProcessPagingScope scope(EventLoop::main().server_process()); |     ProcessPagingScope scope(EventLoop::main().server_process()); | ||||||
| 
 | 
 | ||||||
|     auto* window = new Window; |     int window_id = make_window_id(); | ||||||
|  |     if (!window_id) | ||||||
|  |         return -ENOMEM; | ||||||
|  | 
 | ||||||
|  |     auto window = make<Window>(*this, window_id); | ||||||
|     if (!window) |     if (!window) | ||||||
|         return -ENOMEM; |         return -ENOMEM; | ||||||
| 
 | 
 | ||||||
|     int window_id = m_windows.size(); |  | ||||||
|     m_windows.append(window->makeWeakPtr()); |  | ||||||
| 
 |  | ||||||
|     window->setTitle(params.title); |     window->setTitle(params.title); | ||||||
|     window->setRect(params.rect); |     window->setRect(rect); | ||||||
| 
 | 
 | ||||||
|     auto* main_widget = new Widget; |     m_windows.set(window_id, move(window)); | ||||||
|     window->setMainWidget(main_widget); |     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()); | ||||||
|     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()); |  | ||||||
| 
 | 
 | ||||||
|     return window_id; |     return window_id; | ||||||
| } | } | ||||||
|  | @ -70,65 +76,9 @@ int Process::gui$destroy_window(int window_id) | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     if (window_id >= static_cast<int>(m_windows.size())) |     if (window_id >= static_cast<int>(m_windows.size())) | ||||||
|         return -EBADWINDOW; |         return -EBADWINDOW; | ||||||
|     auto* window = m_windows[window_id].ptr(); |     auto it = m_windows.find(window_id); | ||||||
|     if (!window) |     if (it == m_windows.end()) | ||||||
|         return -EBADWINDOW; |         return -EBADWINDOW; | ||||||
|     window->deleteLater(); |     m_windows.remove(window_id); | ||||||
|     return 0; |     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())) |  | ||||||
|         return -EBADWINDOW; |  | ||||||
|     auto* widget = m_widgets[widget_id].ptr(); |  | ||||||
|     if (!widget) |  | ||||||
|         return -EBADWIDGET; |  | ||||||
|     widget->deleteLater(); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ static const dword time_slice = 5; // *10 = 50ms | ||||||
| 
 | 
 | ||||||
| Process* current; | Process* current; | ||||||
| static Process* s_colonel_process; | static Process* s_colonel_process; | ||||||
|  | static bool s_in_yield; | ||||||
| 
 | 
 | ||||||
| struct TaskRedirectionData { | struct TaskRedirectionData { | ||||||
|     word selector; |     word selector; | ||||||
|  | @ -51,7 +52,7 @@ bool Scheduler::pick_next() | ||||||
|         if (process.state() == Process::BlockedRead) { |         if (process.state() == Process::BlockedRead) { | ||||||
|             ASSERT(process.m_blocked_fd != -1); |             ASSERT(process.m_blocked_fd != -1); | ||||||
|             // FIXME: Block until the amount of data wanted is available.
 |             // 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(); |                 process.unblock(); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  | @ -142,6 +143,9 @@ bool Scheduler::pick_next() | ||||||
| 
 | 
 | ||||||
| bool Scheduler::yield() | bool Scheduler::yield() | ||||||
| { | { | ||||||
|  |     ASSERT(!s_in_yield); | ||||||
|  |     s_in_yield = true; | ||||||
|  | 
 | ||||||
|     if (!current) { |     if (!current) { | ||||||
|         kprintf("PANIC: sched_yield() with !current"); |         kprintf("PANIC: sched_yield() with !current"); | ||||||
|         HANG; |         HANG; | ||||||
|  | @ -150,9 +154,12 @@ bool Scheduler::yield() | ||||||
|     //dbgprintf("%s<%u> yield()\n", current->name().characters(), current->pid());
 |     //dbgprintf("%s<%u> yield()\n", current->name().characters(), current->pid());
 | ||||||
| 
 | 
 | ||||||
|     InterruptDisabler disabler; |     InterruptDisabler disabler; | ||||||
|     if (!pick_next()) |     if (!pick_next()) { | ||||||
|  |         s_in_yield = false; | ||||||
|         return 1; |         return 1; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     s_in_yield = false; | ||||||
|     //dbgprintf("yield() jumping to new process: %x (%s)\n", current->farPtr().selector, current->name().characters());
 |     //dbgprintf("yield() jumping to new process: %x (%s)\n", current->farPtr().selector, current->name().characters());
 | ||||||
|     switch_now(); |     switch_now(); | ||||||
|     return 0; |     return 0; | ||||||
|  | @ -271,6 +278,7 @@ void Scheduler::initialize() | ||||||
|     initialize_redirection(); |     initialize_redirection(); | ||||||
|     s_colonel_process = Process::create_kernel_process("colonel", nullptr); |     s_colonel_process = Process::create_kernel_process("colonel", nullptr); | ||||||
|     current = nullptr; |     current = nullptr; | ||||||
|  |     s_in_yield = false; | ||||||
|     load_task_register(s_redirection.selector); |     load_task_register(s_redirection.selector); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -191,10 +191,6 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, | ||||||
|         return current->gui$create_window((const GUI_CreateWindowParameters*)arg1); |         return current->gui$create_window((const GUI_CreateWindowParameters*)arg1); | ||||||
|     case Syscall::SC_gui_destroy_window: |     case Syscall::SC_gui_destroy_window: | ||||||
|         return current->gui$destroy_window((int)arg1); |         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: |     default: | ||||||
|         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); |         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|  | @ -68,8 +68,6 @@ | ||||||
|     __ENUMERATE_SYSCALL(sync) \ |     __ENUMERATE_SYSCALL(sync) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_create_window) \ |     __ENUMERATE_SYSCALL(gui_create_window) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_destroy_window) \ |     __ENUMERATE_SYSCALL(gui_destroy_window) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_create_widget) \ |  | ||||||
|     __ENUMERATE_SYSCALL(gui_destroy_widget) \ |  | ||||||
| 
 | 
 | ||||||
| namespace Syscall { | namespace Syscall { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ ssize_t TTY::write(const byte* buffer, size_t size) | ||||||
|     return 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(); |     return !m_buffer.is_empty(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read(byte*, size_t) override; |     virtual ssize_t read(byte*, size_t) override; | ||||||
|     virtual ssize_t write(const 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 int ioctl(Process&, unsigned request, unsigned arg) override final; | ||||||
| 
 | 
 | ||||||
|     virtual String tty_name() const = 0; |     virtual String tty_name() const = 0; | ||||||
|  |  | ||||||
|  | @ -4,12 +4,6 @@ | ||||||
| #include <Widgets/FrameBuffer.h> | #include <Widgets/FrameBuffer.h> | ||||||
| #include <Widgets/WindowManager.h> | #include <Widgets/WindowManager.h> | ||||||
| #include <Widgets/EventLoop.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> | #include <Widgets/Window.h> | ||||||
| 
 | 
 | ||||||
| void WindowComposer_main() | void WindowComposer_main() | ||||||
|  | @ -20,52 +14,7 @@ void WindowComposer_main() | ||||||
| 
 | 
 | ||||||
|     FrameBuffer framebuffer((dword*)info.framebuffer, info.width, info.height); |     FrameBuffer framebuffer((dword*)info.framebuffer, info.width, info.height); | ||||||
| 
 | 
 | ||||||
|     MsgBox(nullptr, "Serenity Operating System"); |     WindowManager::the(); | ||||||
| 
 |  | ||||||
|     { |  | ||||||
|         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); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     dbgprintf("Entering WindowComposer main loop.\n"); |     dbgprintf("Entering WindowComposer main loop.\n"); | ||||||
|     EventLoop::main().exec(); |     EventLoop::main().exec(); | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| #include "IRQHandler.h" | #include "IRQHandler.h" | ||||||
| #include "PIC.h" | #include "PIC.h" | ||||||
| 
 | 
 | ||||||
| //#define PAGE_FAULT_DEBUG
 | #define PAGE_FAULT_DEBUG | ||||||
| 
 | 
 | ||||||
| struct DescriptorTablePointer { | struct DescriptorTablePointer { | ||||||
|     word size; |     word size; | ||||||
|  | @ -34,7 +34,7 @@ word gdt_alloc_entry() | ||||||
| 
 | 
 | ||||||
| void gdt_free_entry(word entry) | void gdt_free_entry(word entry) | ||||||
| { | { | ||||||
|     s_gdt_freelist->unchecked_append(entry); |     s_gdt_freelist->append(entry); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" void handle_irq(); | extern "C" void handle_irq(); | ||||||
|  | @ -325,7 +325,7 @@ void gdt_init() | ||||||
|     s_gdt_freelist = new Vector<word, KmallocEternalAllocator>(); |     s_gdt_freelist = new Vector<word, KmallocEternalAllocator>(); | ||||||
|     s_gdt_freelist->ensureCapacity(256); |     s_gdt_freelist->ensureCapacity(256); | ||||||
|     for (size_t i = s_gdtLength; i < 256; ++i) |     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_gdtLength = 256; | ||||||
|     s_gdtr.address = s_gdt; |     s_gdtr.address = s_gdt; | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ | ||||||
| #include <VirtualFileSystem/RandomDevice.h> | #include <VirtualFileSystem/RandomDevice.h> | ||||||
| #include <VirtualFileSystem/Ext2FileSystem.h> | #include <VirtualFileSystem/Ext2FileSystem.h> | ||||||
| #include <VirtualFileSystem/VirtualFileSystem.h> | #include <VirtualFileSystem/VirtualFileSystem.h> | ||||||
|  | #include <Widgets/GUIEventDevice.h> | ||||||
| #include "MemoryManager.h" | #include "MemoryManager.h" | ||||||
| #include "ProcFileSystem.h" | #include "ProcFileSystem.h" | ||||||
| #include "RTC.h" | #include "RTC.h" | ||||||
|  | @ -34,6 +35,7 @@ VirtualConsole* tty2; | ||||||
| VirtualConsole* tty3; | VirtualConsole* tty3; | ||||||
| Keyboard* keyboard; | Keyboard* keyboard; | ||||||
| PS2MouseDevice* ps2mouse; | PS2MouseDevice* ps2mouse; | ||||||
|  | GUIEventDevice* gui_event_device; | ||||||
| 
 | 
 | ||||||
| #ifdef STRESS_TEST_SPAWNING | #ifdef STRESS_TEST_SPAWNING | ||||||
| static void spawn_stress() NORETURN; | static void spawn_stress() NORETURN; | ||||||
|  | @ -75,6 +77,7 @@ static void init_stage2() | ||||||
| 
 | 
 | ||||||
|     vfs->register_character_device(*keyboard); |     vfs->register_character_device(*keyboard); | ||||||
|     vfs->register_character_device(*ps2mouse); |     vfs->register_character_device(*ps2mouse); | ||||||
|  |     vfs->register_character_device(*gui_event_device); | ||||||
|     vfs->register_character_device(*tty0); |     vfs->register_character_device(*tty0); | ||||||
|     vfs->register_character_device(*tty1); |     vfs->register_character_device(*tty1); | ||||||
|     vfs->register_character_device(*tty2); |     vfs->register_character_device(*tty2); | ||||||
|  | @ -94,7 +97,7 @@ static void init_stage2() | ||||||
|     environment.append("TERM=ansi"); |     environment.append("TERM=ansi"); | ||||||
| 
 | 
 | ||||||
|     int error; |     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 | #ifdef SPAWN_GUI_TEST_APP | ||||||
|     Process::create_user_process("/bin/guitest", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0); |     Process::create_user_process("/bin/guitest", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0); | ||||||
| #endif | #endif | ||||||
|  | @ -132,6 +135,7 @@ void init() | ||||||
| 
 | 
 | ||||||
|     keyboard = new Keyboard; |     keyboard = new Keyboard; | ||||||
|     ps2mouse = new PS2MouseDevice; |     ps2mouse = new PS2MouseDevice; | ||||||
|  |     gui_event_device = new GUIEventDevice; | ||||||
| 
 | 
 | ||||||
|     VirtualConsole::initialize(); |     VirtualConsole::initialize(); | ||||||
|     tty0 = new VirtualConsole(0, VirtualConsole::AdoptCurrentVGABuffer); |     tty0 = new VirtualConsole(0, VirtualConsole::AdoptCurrentVGABuffer); | ||||||
|  |  | ||||||
|  | @ -8,6 +8,8 @@ mknod mnt/dev/tty0 c 4 0 | ||||||
| mknod mnt/dev/tty1 c 4 1 | mknod mnt/dev/tty1 c 4 1 | ||||||
| mknod mnt/dev/tty2 c 4 2 | mknod mnt/dev/tty2 c 4 2 | ||||||
| mknod mnt/dev/tty3 c 4 3 | 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 -R ../Base/* mnt/ | ||||||
| cp -v ../Userland/sh mnt/bin/sh | cp -v ../Userland/sh mnt/bin/sh | ||||||
| cp -v ../Userland/id mnt/bin/id | cp -v ../Userland/id mnt/bin/id | ||||||
|  |  | ||||||
|  | @ -43,7 +43,6 @@ | ||||||
|     __ERROR(EAFNOSUPPORT,   "Address family not supported") \ |     __ERROR(EAFNOSUPPORT,   "Address family not supported") \ | ||||||
|     __ERROR(EWHYTHO,        "Failed without setting an error code (Bug!)") \ |     __ERROR(EWHYTHO,        "Failed without setting an error code (Bug!)") \ | ||||||
|     __ERROR(EBADWINDOW,     "Bad Window ID") \ |     __ERROR(EBADWINDOW,     "Bad Window ID") \ | ||||||
|     __ERROR(EBADWIDGET,     "Bad Widget ID") \ |  | ||||||
| 
 | 
 | ||||||
| enum __errno_values { | enum __errno_values { | ||||||
| #undef __ERROR | #undef __ERROR | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <AK/printf.cpp> | #include <AK/printf.cpp> | ||||||
|  | #include <Kernel/Syscall.h> | ||||||
| 
 | 
 | ||||||
| extern "C" { | extern "C" { | ||||||
| 
 | 
 | ||||||
|  | @ -225,6 +226,20 @@ void rewind(FILE* stream) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void sys_putch(char*&, char ch) | 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); |     putchar(ch); | ||||||
| } | } | ||||||
|  | @ -254,7 +269,7 @@ int printf(const char* fmt, ...) | ||||||
| { | { | ||||||
|     va_list ap; |     va_list ap; | ||||||
|     va_start(ap, fmt); |     va_start(ap, fmt); | ||||||
|     int ret = printfInternal(sys_putch, nullptr, fmt, ap); |     int ret = printfInternal(stdout_putch, nullptr, fmt, ap); | ||||||
|     va_end(ap); |     va_end(ap); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -54,6 +54,7 @@ int vfprintf(FILE*, const char* fmt, va_list); | ||||||
| int vsprintf(char* buffer, const char* fmt, va_list); | int vsprintf(char* buffer, const char* fmt, va_list); | ||||||
| int fprintf(FILE*, const char* fmt, ...); | int fprintf(FILE*, const char* fmt, ...); | ||||||
| int printf(const char* fmt, ...); | int printf(const char* fmt, ...); | ||||||
|  | int sys_printf(const char* fmt, ...); | ||||||
| int sprintf(char* buffer, const char* fmt, ...); | int sprintf(char* buffer, const char* fmt, ...); | ||||||
| int putchar(int ch); | int putchar(int ch); | ||||||
| int putc(int ch, FILE*); | int putc(int ch, FILE*); | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
| int main(int argc, char** argv) | int main(int argc, char** argv) | ||||||
| { | { | ||||||
|     GUI_CreateWindowParameters wparams; |     GUI_CreateWindowParameters wparams; | ||||||
|     wparams.rect = { 200, 200, 300, 200 }; |     wparams.rect = { { 200, 200 }, { 300, 200 } }; | ||||||
|     wparams.background_color = 0xffc0c0; |     wparams.background_color = 0xffc0c0; | ||||||
|     strcpy(wparams.title, "GUI test app"); |     strcpy(wparams.title, "GUI test app"); | ||||||
|     int window_id = syscall(SC_gui_create_window, &wparams); |     int window_id = syscall(SC_gui_create_window, &wparams); | ||||||
|  | @ -20,28 +20,27 @@ int main(int argc, char** argv) | ||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GUI_CreateWidgetParameters label_params; |     int fd = open("/dev/gui_events", O_RDONLY); | ||||||
|     label_params.type = GUI_WidgetType::Label; |     if (fd < 0) { | ||||||
|     label_params.rect = { 20, 20, 260, 20 }; |         perror("open"); | ||||||
|     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"); |  | ||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (;;) { |     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; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     RetainPtr<FileDescriptor> open(int options); |     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 read(byte* buffer, size_t bufferSize) = 0; | ||||||
|     virtual ssize_t write(const byte* buffer, size_t bufferSize) = 0; |     virtual ssize_t write(const byte* buffer, size_t bufferSize) = 0; | ||||||
|  |  | ||||||
|  | @ -173,14 +173,14 @@ bool FileDescriptor::can_write() | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool FileDescriptor::has_data_available_for_reading() | bool FileDescriptor::has_data_available_for_reading(Process& process) | ||||||
| { | { | ||||||
|     if (is_fifo()) { |     if (is_fifo()) { | ||||||
|         ASSERT(fifo_direction() == FIFO::Reader); |         ASSERT(fifo_direction() == FIFO::Reader); | ||||||
|         return m_fifo->can_read(); |         return m_fifo->can_read(); | ||||||
|     } |     } | ||||||
|     if (m_vnode->isCharacterDevice()) |     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; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| 
 | 
 | ||||||
| #ifdef SERENITY | #ifdef SERENITY | ||||||
| class TTY; | class TTY; | ||||||
|  | class Process; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| class FileDescriptor : public Retainable<FileDescriptor> { | class FileDescriptor : public Retainable<FileDescriptor> { | ||||||
|  | @ -27,7 +28,7 @@ public: | ||||||
|     ssize_t write(const byte* data, size_t); |     ssize_t write(const byte* data, size_t); | ||||||
|     int stat(Unix::stat*); |     int stat(Unix::stat*); | ||||||
| 
 | 
 | ||||||
|     bool has_data_available_for_reading(); |     bool has_data_available_for_reading(Process&); | ||||||
|     bool can_write(); |     bool can_write(); | ||||||
| 
 | 
 | ||||||
|     ssize_t get_dir_entries(byte* buffer, size_t); |     ssize_t get_dir_entries(byte* buffer, size_t); | ||||||
|  |  | ||||||
|  | @ -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; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,6 +10,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read(byte* buffer, size_t bufferSize) override; |     virtual ssize_t read(byte* buffer, size_t bufferSize) override; | ||||||
|     virtual ssize_t write(const 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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,6 +10,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read(byte* buffer, size_t bufferSize) override; |     virtual ssize_t read(byte* buffer, size_t bufferSize) override; | ||||||
|     virtual ssize_t write(const 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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ static void mysrand(unsigned seed) | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| bool RandomDevice::has_data_available_for_reading() const | bool RandomDevice::has_data_available_for_reading(Process&) const | ||||||
| { | { | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,6 +10,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read(byte* buffer, size_t bufferSize) override; |     virtual ssize_t read(byte* buffer, size_t bufferSize) override; | ||||||
|     virtual ssize_t write(const 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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,6 +10,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read(byte* buffer, size_t bufferSize) override; |     virtual ssize_t read(byte* buffer, size_t bufferSize) override; | ||||||
|     virtual ssize_t write(const 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; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,8 +37,6 @@ public: | ||||||
|         DeferredDestroy, |         DeferredDestroy, | ||||||
|         WindowBecameInactive, |         WindowBecameInactive, | ||||||
|         WindowBecameActive, |         WindowBecameActive, | ||||||
|         FocusIn, |  | ||||||
|         FocusOut, |  | ||||||
|         WM_Compose, |         WM_Compose, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -82,7 +82,7 @@ void EventLoop::waitForEvent() | ||||||
|     bool prev_right_button = screen.right_mouse_button_pressed(); |     bool prev_right_button = screen.right_mouse_button_pressed(); | ||||||
|     int dx = 0; |     int dx = 0; | ||||||
|     int dy = 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]; |         signed_byte data[3]; | ||||||
|         ssize_t nread = mouse.read((byte*)data, 3); |         ssize_t nread = mouse.read((byte*)data, 3); | ||||||
|         ASSERT(nread == 3); |         ASSERT(nread == 3); | ||||||
|  | @ -90,7 +90,7 @@ void EventLoop::waitForEvent() | ||||||
|         bool right_button = data[0] & 2; |         bool right_button = data[0] & 2; | ||||||
|         dx += data[1]; |         dx += data[1]; | ||||||
|         dy += -data[2]; |         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_left_button = left_button; | ||||||
|             prev_right_button = right_button; |             prev_right_button = right_button; | ||||||
|             screen.on_receive_mouse_data(dx, dy, left_button, right_button); |             screen.on_receive_mouse_data(dx, dy, left_button, right_button); | ||||||
|  |  | ||||||
							
								
								
									
										38
									
								
								Widgets/GUIEventDevice.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								Widgets/GUIEventDevice.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
									
								
								Widgets/GUIEventDevice.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Widgets/GUIEventDevice.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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,7 +4,9 @@ | ||||||
| #include "Window.h" | #include "Window.h" | ||||||
| #include "Label.h" | #include "Label.h" | ||||||
| #include "Button.h" | #include "Button.h" | ||||||
|  | #include "Process.h" | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
| void MsgBox(Window* owner, String&& text) | void MsgBox(Window* owner, String&& text) | ||||||
| { | { | ||||||
|     Font& font = Font::defaultFont(); |     Font& font = Font::defaultFont(); | ||||||
|  | @ -33,7 +35,7 @@ void MsgBox(Window* owner, String&& text) | ||||||
|         buttonHeight |         buttonHeight | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     auto* window = new Window; |     auto* window = new Window(*current, current->make_window_id()); | ||||||
|     window->setTitle("MsgBox"); |     window->setTitle("MsgBox"); | ||||||
|     window->setRect(windowRect); |     window->setRect(windowRect); | ||||||
|     auto* widget = new Widget; |     auto* widget = new Widget; | ||||||
|  | @ -56,4 +58,4 @@ void MsgBox(Window* owner, String&& text) | ||||||
|         button.window()->close(); |         button.window()->close(); | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
| 
 | #endif | ||||||
|  |  | ||||||
|  | @ -1,11 +1,13 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| class Rect; | class Rect; | ||||||
|  | struct GUI_Point; | ||||||
| 
 | 
 | ||||||
| class Point { | class Point { | ||||||
| public: | public: | ||||||
|     Point() { } |     Point() { } | ||||||
|     Point(int x, int y) : m_x(x) , m_y(y) { } |     Point(int x, int y) : m_x(x) , m_y(y) { } | ||||||
|  |     Point(const GUI_Point&); | ||||||
| 
 | 
 | ||||||
|     int x() const { return m_x; } |     int x() const { return m_x; } | ||||||
|     int y() const { return m_y; } |     int y() const { return m_y; } | ||||||
|  | @ -37,6 +39,8 @@ public: | ||||||
|         return !(*this == other); |         return !(*this == other); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     operator GUI_Point() const; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     int m_x { 0 }; |     int m_x { 0 }; | ||||||
|     int m_y { 0 }; |     int m_y { 0 }; | ||||||
|  |  | ||||||
|  | @ -3,6 +3,8 @@ | ||||||
| #include "Point.h" | #include "Point.h" | ||||||
| #include "Size.h" | #include "Size.h" | ||||||
| 
 | 
 | ||||||
|  | struct GUI_Rect; | ||||||
|  | 
 | ||||||
| class Rect { | class Rect { | ||||||
| public: | public: | ||||||
|     Rect() { } |     Rect() { } | ||||||
|  | @ -16,6 +18,7 @@ public: | ||||||
|         , m_size(size) |         , m_size(size) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |     Rect(const GUI_Rect&); | ||||||
| 
 | 
 | ||||||
|     bool is_empty() const |     bool is_empty() const | ||||||
|     { |     { | ||||||
|  | @ -117,6 +120,8 @@ public: | ||||||
|     Point location() const { return m_location; } |     Point location() const { return m_location; } | ||||||
|     Size size() const { return m_size; } |     Size size() const { return m_size; } | ||||||
| 
 | 
 | ||||||
|  |     operator GUI_Rect() const; | ||||||
|  | 
 | ||||||
|     bool operator==(const Rect& other) const |     bool operator==(const Rect& other) const | ||||||
|     { |     { | ||||||
|         return m_location == other.m_location |         return m_location == other.m_location | ||||||
|  |  | ||||||
|  | @ -1,9 +1,12 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | struct GUI_Size; | ||||||
|  | 
 | ||||||
| class Size { | class Size { | ||||||
| public: | public: | ||||||
|     Size() { } |     Size() { } | ||||||
|     Size(int w, int h) : m_width(w), m_height(h) { } |     Size(int w, int h) : m_width(w), m_height(h) { } | ||||||
|  |     Size(const GUI_Size&); | ||||||
| 
 | 
 | ||||||
|     bool is_empty() const { return !m_width || !m_height; } |     bool is_empty() const { return !m_width || !m_height; } | ||||||
| 
 | 
 | ||||||
|  | @ -19,6 +22,8 @@ public: | ||||||
|                m_height == other.m_height; |                m_height == other.m_height; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     operator GUI_Size() const; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     int m_width { 0 }; |     int m_width { 0 }; | ||||||
|     int m_height { 0 }; |     int m_height { 0 }; | ||||||
|  |  | ||||||
|  | @ -56,10 +56,7 @@ void Widget::event(Event& event) | ||||||
|     case Event::MouseMove: |     case Event::MouseMove: | ||||||
|         return mouseMoveEvent(static_cast<MouseEvent&>(event)); |         return mouseMoveEvent(static_cast<MouseEvent&>(event)); | ||||||
|     case Event::MouseDown: |     case Event::MouseDown: | ||||||
|         if (auto* win = window()) { |         // FIXME: Focus self if needed.
 | ||||||
|             // FIXME: if (acceptsFocus())
 |  | ||||||
|             win->setFocusedWidget(this); |  | ||||||
|         } |  | ||||||
|         return mouseDownEvent(static_cast<MouseEvent&>(event)); |         return mouseDownEvent(static_cast<MouseEvent&>(event)); | ||||||
|     case Event::MouseUp: |     case Event::MouseUp: | ||||||
|         return mouseUpEvent(static_cast<MouseEvent&>(event)); |         return mouseUpEvent(static_cast<MouseEvent&>(event)); | ||||||
|  | @ -141,8 +138,7 @@ void Widget::setWindow(Window* window) | ||||||
| 
 | 
 | ||||||
| bool Widget::isFocused() const | bool Widget::isFocused() const | ||||||
| { | { | ||||||
|     if (auto* win = window()) |     // FIXME: Implement.
 | ||||||
|         return win->isActive() &&  win->focusedWidget() == this; |  | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -150,8 +146,7 @@ void Widget::setFocus(bool focus) | ||||||
| { | { | ||||||
|     if (focus == isFocused()) |     if (focus == isFocused()) | ||||||
|         return; |         return; | ||||||
|     if (auto* win = window()) |     // FIXME: Implement.
 | ||||||
|         win->setFocusedWidget(this); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Widget::setFont(RetainPtr<Font>&& font) | void Widget::setFont(RetainPtr<Font>&& font) | ||||||
|  |  | ||||||
|  | @ -3,31 +3,20 @@ | ||||||
| #include "Event.h" | #include "Event.h" | ||||||
| #include "EventLoop.h" | #include "EventLoop.h" | ||||||
| #include "Widget.h" | #include "Widget.h" | ||||||
|  | #include "Process.h" | ||||||
| 
 | 
 | ||||||
| Window::Window(Object* parent) | Window::Window(Process& process, int window_id) | ||||||
|     : Object(parent) |     : m_process(process) | ||||||
|  |     , m_window_id(window_id) | ||||||
| { | { | ||||||
|     WindowManager::the().addWindow(*this); |     WindowManager::the().addWindow(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Window::~Window() | Window::~Window() | ||||||
| { | { | ||||||
|     delete m_mainWidget; |  | ||||||
|     m_mainWidget = nullptr; |  | ||||||
|     if (parent()) |  | ||||||
|         parent()->removeChild(*this); |  | ||||||
|     WindowManager::the().removeWindow(*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) | void Window::setTitle(String&& title) | ||||||
| { | { | ||||||
|     if (m_title == title) |     if (m_title == title) | ||||||
|  | @ -58,51 +47,50 @@ void Window::update(const Rect& rect) | ||||||
|     EventLoop::main().postEvent(this, make<PaintEvent>(rect)); |     EventLoop::main().postEvent(this, make<PaintEvent>(rect)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // FIXME: Just use the same types.
 | ||||||
|  | static GUI_MouseButton to_api(MouseButton button) | ||||||
|  | { | ||||||
|  |     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; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Window::event(Event& event) | void Window::event(Event& event) | ||||||
| { | { | ||||||
|     if (event.isMouseEvent()) { |     GUI_Event gui_event; | ||||||
|         auto& me = static_cast<MouseEvent&>(event); |     gui_event.window_id = window_id(); | ||||||
|         //printf("Window{%p}: %s %d,%d\n", this, me.name(), me.x(), me.y());
 | 
 | ||||||
|         if (m_mainWidget) { |     switch (event.type()) { | ||||||
|             auto result = m_mainWidget->hitTest(me.x(), me.y()); |     case Event::Paint: | ||||||
|             //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);
 |         gui_event.type = GUI_Event::Type::Paint; | ||||||
|             // FIXME: Re-use the existing event instead of crafting a new one?
 |         gui_event.paint.rect = static_cast<PaintEvent&>(event).rect(); | ||||||
|             auto localEvent = make<MouseEvent>(event.type(), result.localX, result.localY, me.button()); |         break; | ||||||
|             return result.widget->event(*localEvent); |     case Event::MouseMove: | ||||||
|         } |         gui_event.type = GUI_Event::Type::MouseMove; | ||||||
|         return Object::event(event); |         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.isPaintEvent()) { |     if (gui_event.type == GUI_Event::Type::Invalid) | ||||||
|         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; |         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); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     if (event.isKeyEvent()) { |     { | ||||||
|         if (m_focusedWidget) |         LOCKER(m_process.gui_events_lock()); | ||||||
|             return m_focusedWidget->event(event); |         m_process.gui_events().append(move(gui_event)); | ||||||
|         return Object::event(event); |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return Object::event(event); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Window::did_paint() | void Window::did_paint() | ||||||
|  | @ -120,24 +108,6 @@ bool Window::isVisible() const | ||||||
|     return WindowManager::the().isVisible(const_cast<Window&>(*this)); |     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() | void Window::close() | ||||||
| { | { | ||||||
|     WindowManager::the().removeWindow(*this); |     WindowManager::the().removeWindow(*this); | ||||||
|  |  | ||||||
|  | @ -7,13 +7,16 @@ | ||||||
| #include <AK/InlineLinkedList.h> | #include <AK/InlineLinkedList.h> | ||||||
| #include <AK/WeakPtr.h> | #include <AK/WeakPtr.h> | ||||||
| 
 | 
 | ||||||
|  | class Process; | ||||||
| class Widget; | class Widget; | ||||||
| 
 | 
 | ||||||
| class Window final : public Object, public InlineLinkedListNode<Window> { | class Window final : public Object, public InlineLinkedListNode<Window> { | ||||||
| public: | public: | ||||||
|     explicit Window(Object* parent = nullptr); |     Window(Process&, int window_id); | ||||||
|     virtual ~Window() override; |     virtual ~Window() override; | ||||||
| 
 | 
 | ||||||
|  |     int window_id() const { return m_window_id; } | ||||||
|  | 
 | ||||||
|     String title() const { return m_title; } |     String title() const { return m_title; } | ||||||
|     void setTitle(String&&); |     void setTitle(String&&); | ||||||
| 
 | 
 | ||||||
|  | @ -30,11 +33,6 @@ public: | ||||||
|     void setPosition(const Point& position) { setRect({ position.x(), position.y(), width(), height() }); } |     void setPosition(const Point& position) { setRect({ position.x(), position.y(), width(), height() }); } | ||||||
|     void setPositionWithoutRepaint(const Point& position) { setRectWithoutRepaint({ 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; |     virtual void event(Event&) override; | ||||||
| 
 | 
 | ||||||
|     bool isBeingDragged() const { return m_isBeingDragged; } |     bool isBeingDragged() const { return m_isBeingDragged; } | ||||||
|  | @ -45,10 +43,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     bool isActive() const; |     bool isActive() const; | ||||||
| 
 | 
 | ||||||
|     Widget* focusedWidget() { return m_focusedWidget.ptr(); } |  | ||||||
|     const Widget* focusedWidget() const { return m_focusedWidget.ptr(); } |  | ||||||
|     void setFocusedWidget(Widget*); |  | ||||||
| 
 |  | ||||||
|     bool isVisible() const; |     bool isVisible() const; | ||||||
| 
 | 
 | ||||||
|     void close(); |     void close(); | ||||||
|  | @ -65,11 +59,10 @@ public: | ||||||
| private: | private: | ||||||
|     String m_title; |     String m_title; | ||||||
|     Rect m_rect; |     Rect m_rect; | ||||||
|     Widget* m_mainWidget { nullptr }; |  | ||||||
|     bool m_isBeingDragged { false }; |     bool m_isBeingDragged { false }; | ||||||
| 
 | 
 | ||||||
|     WeakPtr<Widget> m_focusedWidget; |  | ||||||
| 
 |  | ||||||
|     RetainPtr<GraphicsBitmap> m_backing; |     RetainPtr<GraphicsBitmap> m_backing; | ||||||
|  |     Process& m_process; | ||||||
|  |     int m_window_id { -1 }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling