mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 06:12:43 +00:00 
			
		
		
		
	Refactor GUI rendering model to be two-phased.
Instead of clients painting whenever they feel like it, we now ask that they paint in response to a paint message. After finishing painting, clients notify the WindowServer about the rect(s) they painted into and then flush eventually happens, etc. This stuff leaves us with a lot of badly named things. Need to fix that.
This commit is contained in:
		
							parent
							
								
									3a401d5249
								
							
						
					
					
						commit
						7cf3c7461c
					
				
					 16 changed files with 117 additions and 40 deletions
				
			
		|  | @ -201,6 +201,7 @@ public: | |||
|     int gui$get_window_backing_store(int window_id, GUI_WindowBackingStoreInfo*); | ||||
|     int gui$release_window_backing_store(void* backing_store_id); | ||||
|     int gui$invalidate_window(int window_id, const GUI_Rect*); | ||||
|     int gui$notify_paint_finished(int window_id, const GUI_Rect*); | ||||
|     int gui$get_window_title(int window_id, char* buffer, size_t size); | ||||
|     int gui$set_window_title(int window_id, const char* title, size_t size); | ||||
|     int gui$get_window_rect(int window_id, GUI_Rect*); | ||||
|  |  | |||
|  | @ -126,28 +126,50 @@ int Process::gui$release_window_backing_store(void* backing_store_id) | |||
|     return -EBADBACKING; | ||||
| } | ||||
| 
 | ||||
| int Process::gui$invalidate_window(int window_id, const GUI_Rect* rect) | ||||
| int Process::gui$invalidate_window(int window_id, const GUI_Rect* a_rect) | ||||
| { | ||||
|     if (window_id < 0) | ||||
|         return -EINVAL; | ||||
|     if (rect && !validate_read_typed(rect)) | ||||
|     if (a_rect && !validate_read_typed(a_rect)) | ||||
|         return -EFAULT; | ||||
|     auto it = m_windows.find(window_id); | ||||
|     if (it == m_windows.end()) | ||||
|         return -EBADWINDOW; | ||||
| #ifdef LOG_GUI_SYSCALLS | ||||
|     if (!rect) | ||||
|     if (!a_rect) | ||||
|         dbgprintf("%s<%u> gui$invalidate_window (window_id=%d, rect=(entire))\n", name().characters(), pid(), window_id); | ||||
|     else | ||||
|         dbgprintf("%s<%u> gui$invalidate_window (window_id=%d, rect={%d,%d %dx%d})\n", name().characters(), pid(), window_id, rect->location.x, rect->location.y, rect->size.width, rect->size.height); | ||||
|         dbgprintf("%s<%u> gui$invalidate_window (window_id=%d, rect={%d,%d %dx%d})\n", name().characters(), pid(), window_id, a_rect->location.x, a_rect->location.y, a_rect->size.width, a_rect->size.height); | ||||
| #endif | ||||
|     auto& window = *(*it).value; | ||||
|     Rect invalidation_rect; | ||||
|     if (rect) { | ||||
|         WSWindowLocker locker(window); | ||||
|         invalidation_rect = *rect; | ||||
|     } | ||||
|     WSEventLoop::the().post_event(&window, make<WSWindowInvalidationEvent>(invalidation_rect)); | ||||
|     Rect rect; | ||||
|     if (a_rect) | ||||
|         rect = *a_rect; | ||||
|     WSEventLoop::the().post_event(&window, make<WSPaintEvent>(rect)); | ||||
|     WSEventLoop::the().server_process().request_wakeup(); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int Process::gui$notify_paint_finished(int window_id, const GUI_Rect* a_rect) | ||||
| { | ||||
|     if (window_id < 0) | ||||
|         return -EINVAL; | ||||
|     if (a_rect && !validate_read_typed(a_rect)) | ||||
|         return -EFAULT; | ||||
|     auto it = m_windows.find(window_id); | ||||
|     if (it == m_windows.end()) | ||||
|         return -EBADWINDOW; | ||||
| #ifdef LOG_GUI_SYSCALLS | ||||
|     if (!a_rect) | ||||
|         dbgprintf("%s<%u> gui$notify_paint_finished (window_id=%d, rect=(entire))\n", name().characters(), pid(), window_id); | ||||
|     else | ||||
|         dbgprintf("%s<%u> gui$notify_paint_finished (window_id=%d, rect={%d,%d %dx%d})\n", name().characters(), pid(), window_id, a_rect->location.x, a_rect->location.y, a_rect->size.width, a_rect->size.height); | ||||
| #endif | ||||
|     auto& window = *(*it).value; | ||||
|     Rect rect; | ||||
|     if (a_rect) | ||||
|         rect = *a_rect; | ||||
|     WSEventLoop::the().post_event(&window, make<WSWindowInvalidationEvent>(rect)); | ||||
|     WSEventLoop::the().server_process().request_wakeup(); | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -215,6 +215,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, | |||
|         return current->gui$get_window_rect((int)arg1, (GUI_Rect*)arg2); | ||||
|     case Syscall::SC_read_tsc: | ||||
|         return current->sys$read_tsc((dword*)arg1, (dword*)arg2); | ||||
|     case Syscall::SC_gui_notify_paint_finished: | ||||
|         return current->gui$notify_paint_finished((int)arg1, (const GUI_Rect*)arg2); | ||||
|     default: | ||||
|         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); | ||||
|         break; | ||||
|  |  | |||
|  | @ -80,6 +80,7 @@ | |||
|     __ENUMERATE_SYSCALL(gui_set_window_title) \ | ||||
|     __ENUMERATE_SYSCALL(gui_get_window_rect) \ | ||||
|     __ENUMERATE_SYSCALL(gui_set_window_rect) \ | ||||
|     __ENUMERATE_SYSCALL(gui_notify_paint_finished) \ | ||||
| 
 | ||||
| 
 | ||||
| #ifdef SERENITY | ||||
|  |  | |||
|  | @ -50,3 +50,9 @@ int gui_set_window_rect(int window_id, const GUI_Rect* rect) | |||
|     int rc = syscall(SC_gui_set_window_rect, window_id, rect); | ||||
|     __RETURN_WITH_ERRNO(rc, rc, -1); | ||||
| } | ||||
| 
 | ||||
| int gui_notify_paint_finished(int window_id, const GUI_Rect* rect) | ||||
| { | ||||
|     int rc = syscall(SC_gui_notify_paint_finished, window_id, rect); | ||||
|     __RETURN_WITH_ERRNO(rc, rc, -1); | ||||
| } | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ __BEGIN_DECLS | |||
| 
 | ||||
| int gui_create_window(const GUI_WindowParameters*); | ||||
| int gui_invalidate_window(int window_id, const GUI_Rect*); | ||||
| int gui_notify_paint_finished(int window_id, const GUI_Rect*); | ||||
| int gui_get_window_backing_store(int window_id, GUI_WindowBackingStoreInfo*); | ||||
| int gui_release_window_backing_store(void* backing_store_id); | ||||
| int gui_get_window_title(int window_id, char*, size_t); | ||||
|  |  | |||
|  | @ -134,7 +134,7 @@ void GEventLoop::wait_for_event() | |||
|         } | ||||
|         switch (event.type) { | ||||
|         case GUI_Event::Type::Paint: | ||||
|             dbgprintf("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; | ||||
|             dbgprintf("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); | ||||
|             handle_paint_event(event, *window); | ||||
|             break; | ||||
|         case GUI_Event::Type::MouseDown: | ||||
|  |  | |||
|  | @ -106,7 +106,7 @@ void GWidget::update() | |||
|     if (m_has_pending_paint_event) | ||||
|         return; | ||||
|     m_has_pending_paint_event = true; | ||||
|     GEventLoop::main().post_event(w, make<GPaintEvent>(relative_rect())); | ||||
|     w->update(relative_rect()); | ||||
| } | ||||
| 
 | ||||
| GWidget::HitTestResult GWidget::hit_test(int x, int y) | ||||
|  |  | |||
|  | @ -49,11 +49,18 @@ GWindow::~GWindow() | |||
| void GWindow::set_title(String&& title) | ||||
| { | ||||
|     dbgprintf("GWindow::set_title \"%s\"\n", title.characters()); | ||||
|     GUI_WindowParameters params; | ||||
|     int rc = gui_set_window_title(m_window_id, title.characters(), title.length()); | ||||
|     ASSERT(rc == 0); | ||||
| } | ||||
| 
 | ||||
| String GWindow::title() const | ||||
| { | ||||
|     char buffer[256]; | ||||
|     int rc = gui_get_window_title(m_window_id, buffer, sizeof(buffer)); | ||||
|     ASSERT(rc >= 0); | ||||
|     return String(buffer, rc); | ||||
| } | ||||
| 
 | ||||
| void GWindow::set_rect(const Rect& a_rect) | ||||
| { | ||||
|     dbgprintf("GWindow::set_rect! %d,%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height()); | ||||
|  | @ -85,7 +92,7 @@ void GWindow::event(GEvent& event) | |||
|             rect = m_main_widget->rect(); | ||||
|         m_main_widget->event(*make<GPaintEvent>(rect)); | ||||
|         GUI_Rect gui_rect = rect; | ||||
|         int rc = gui_invalidate_window(m_window_id, &gui_rect); | ||||
|         int rc = gui_notify_paint_finished(m_window_id, &gui_rect); | ||||
|         ASSERT(rc == 0); | ||||
|     } | ||||
| 
 | ||||
|  | @ -105,9 +112,11 @@ void GWindow::show() | |||
| { | ||||
| } | ||||
| 
 | ||||
| void GWindow::update() | ||||
| void GWindow::update(const Rect& a_rect) | ||||
| { | ||||
|     GEventLoop::main().post_event(this, make<GPaintEvent>()); | ||||
|     GUI_Rect rect = a_rect; | ||||
|     int rc = gui_invalidate_window(m_window_id, a_rect.is_null() ? nullptr : &rect); | ||||
|     ASSERT(rc == 0); | ||||
| } | ||||
| 
 | ||||
| void GWindow::set_main_widget(GWidget* widget) | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ public: | |||
| 
 | ||||
|     int window_id() const { return m_window_id; } | ||||
| 
 | ||||
|     String title() const; | ||||
|     void set_title(String&&); | ||||
| 
 | ||||
|     int x() const { return rect().x(); } | ||||
|  | @ -40,7 +41,7 @@ public: | |||
| 
 | ||||
|     void show(); | ||||
| 
 | ||||
|     void update(); | ||||
|     void update(const Rect& = Rect()); | ||||
| 
 | ||||
| private: | ||||
|     RetainPtr<GraphicsBitmap> m_backing; | ||||
|  |  | |||
|  | @ -18,18 +18,16 @@ GraphicsBitmap::GraphicsBitmap(Process& process, const Size& size) | |||
|     , m_pitch(size.width() * sizeof(RGBA32)) | ||||
|     , m_client_process(&process) | ||||
| { | ||||
|     InterruptDisabler disabler; | ||||
|     size_t size_in_bytes = size.width() * size.height() * sizeof(RGBA32); | ||||
|     auto vmo = VMObject::create_anonymous(size_in_bytes); | ||||
|     m_client_region = process.allocate_region_with_vmo(LinearAddress(), size_in_bytes, vmo.copyRef(), 0, "GraphicsBitmap (client)", true, true); | ||||
|     m_client_region->set_shared(true); | ||||
|     m_client_region->commit(); | ||||
|     auto& server = WSEventLoop::the().server_process(); | ||||
|     m_server_region = server.allocate_region_with_vmo(LinearAddress(), size_in_bytes, move(vmo), 0, "GraphicsBitmap (server)", true, false); | ||||
|     m_server_region->set_shared(true); | ||||
| 
 | ||||
|     { | ||||
|         auto& server = WSEventLoop::the().server_process(); | ||||
|         InterruptDisabler disabler; | ||||
|         m_server_region = server.allocate_region_with_vmo(LinearAddress(), size_in_bytes, move(vmo), 0, "GraphicsBitmap (server)", true, false); | ||||
|         m_server_region->set_shared(true); | ||||
|     } | ||||
|     m_data = (RGBA32*)m_server_region->laddr().as_ptr(); | ||||
| } | ||||
| #endif | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ Terminal::Line::Line(word columns) | |||
| { | ||||
|     characters = new byte[length]; | ||||
|     attributes = new Attribute[length]; | ||||
|     needs_invalidation = false; | ||||
|     did_paint = false; | ||||
|     memset(characters, ' ', length); | ||||
| } | ||||
| 
 | ||||
|  | @ -603,7 +603,7 @@ void Terminal::paint() | |||
|     Painter painter(*m_backing); | ||||
| 
 | ||||
|     for (size_t i = 0; i < rows(); ++i) | ||||
|         line(i).needs_invalidation = false; | ||||
|         line(i).did_paint = false; | ||||
| 
 | ||||
|     if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) { | ||||
|         int first_scanline = m_inset; | ||||
|  | @ -630,7 +630,7 @@ void Terminal::paint() | |||
|             painter.fill_rect(row_rect(row), line.attributes[0].background_color); | ||||
|         for (word column = 0; column < m_columns; ++column) { | ||||
|             auto& attribute = line.attributes[column]; | ||||
|             line.needs_invalidation = true; | ||||
|             line.did_paint = true; | ||||
|             char ch = line.characters[column]; | ||||
|             auto character_rect = glyph_rect(row, column); | ||||
|             if (!has_only_one_background_color) { | ||||
|  | @ -649,7 +649,7 @@ void Terminal::paint() | |||
|     else | ||||
|         painter.draw_rect(cursor_rect, Color::MidGray); | ||||
| 
 | ||||
|     line(m_cursor_row).needs_invalidation = true; | ||||
|     line(m_cursor_row).did_paint = true; | ||||
| 
 | ||||
|     if (m_belling) { | ||||
|         m_need_full_invalidation = true; | ||||
|  | @ -657,23 +657,38 @@ void Terminal::paint() | |||
|     } | ||||
| 
 | ||||
|     if (m_need_full_invalidation) { | ||||
|         invalidate_window(); | ||||
|         did_paint(); | ||||
|         m_need_full_invalidation = false; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Rect invalidation_rect; | ||||
|     Rect painted_rect; | ||||
|     for (int i = 0; i < m_rows; ++i) { | ||||
|         if (line(i).needs_invalidation) | ||||
|             invalidation_rect = invalidation_rect.united(row_rect(i)); | ||||
|         if (line(i).did_paint) | ||||
|             painted_rect = painted_rect.united(row_rect(i)); | ||||
|     } | ||||
|     invalidate_window(invalidation_rect); | ||||
|     did_paint(painted_rect); | ||||
| } | ||||
| 
 | ||||
| void Terminal::invalidate_window(const Rect& a_rect) | ||||
| void Terminal::did_paint(const Rect& a_rect) | ||||
| { | ||||
|     GUI_Rect rect = a_rect; | ||||
|     int rc = gui_invalidate_window(m_window_id, a_rect.is_null() ? nullptr : &rect); | ||||
|     int rc = gui_notify_paint_finished(m_window_id, a_rect.is_null() ? nullptr : &rect); | ||||
|     if (rc < 0) { | ||||
|         perror("gui_notify_paint_finished"); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Terminal::update() | ||||
| { | ||||
|     Rect rect; | ||||
|     for (int i = 0; i < m_rows; ++i) { | ||||
|         if (line(i).did_paint) | ||||
|             rect = rect.united(row_rect(i)); | ||||
|     } | ||||
|     GUI_Rect gui_rect = rect; | ||||
|     int rc = gui_invalidate_window(m_window_id, rect.is_null() ? nullptr : &gui_rect); | ||||
|     if (rc < 0) { | ||||
|         perror("gui_invalidate_window"); | ||||
|         exit(1); | ||||
|  | @ -695,7 +710,7 @@ void Terminal::set_in_active_window(bool b) | |||
|         return; | ||||
|     m_in_active_window = b; | ||||
|     invalidate_cursor(); | ||||
|     paint(); | ||||
|     update(); | ||||
| } | ||||
| 
 | ||||
| void Terminal::invalidate_cursor() | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ public: | |||
|     void on_char(byte); | ||||
| 
 | ||||
|     void set_in_active_window(bool); | ||||
|     void update(); | ||||
| 
 | ||||
| private: | ||||
|     Font& font() { return *m_font; } | ||||
|  | @ -25,6 +26,7 @@ private: | |||
|     void set_cursor(unsigned row, unsigned column); | ||||
|     void put_character_at(unsigned row, unsigned column, byte ch); | ||||
|     void invalidate_cursor(); | ||||
|     void did_paint(const Rect& = Rect()); | ||||
|     void invalidate_window(const Rect& = Rect()); | ||||
|     void set_window_title(const String&); | ||||
| 
 | ||||
|  | @ -67,7 +69,7 @@ private: | |||
|         bool has_only_one_background_color() const; | ||||
|         byte* characters { nullptr }; | ||||
|         Attribute* attributes { nullptr }; | ||||
|         bool needs_invalidation { false }; | ||||
|         bool did_paint { false }; | ||||
|         bool dirty { false }; | ||||
|         word length { 0 }; | ||||
|     }; | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ int main(int, char**) | |||
| 
 | ||||
|     Terminal terminal; | ||||
|     terminal.create_window(); | ||||
|     terminal.paint(); | ||||
|     terminal.update(); | ||||
| 
 | ||||
|     for (;;) { | ||||
|         fd_set rfds; | ||||
|  | @ -100,7 +100,7 @@ int main(int, char**) | |||
|             assert(nread != 0); | ||||
|             for (ssize_t i = 0; i < nread; ++i) | ||||
|                 terminal.on_char(buffer[i]); | ||||
|             terminal.paint(); | ||||
|             terminal.update(); | ||||
|         } | ||||
| 
 | ||||
|         if (FD_ISSET(event_fd, &rfds)) { | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| #include <Kernel/Syscall.h> | ||||
| #include <SharedGraphics/GraphicsBitmap.h> | ||||
| #include <SharedGraphics/Painter.h> | ||||
| #include "gui.h" | ||||
| #include <LibC/gui.h> | ||||
| 
 | ||||
| static void paint(GraphicsBitmap& bitmap, int width, int height); | ||||
| 
 | ||||
|  | @ -68,8 +68,12 @@ int main(int argc, char** argv) | |||
|         case GUI_Event::Type::WindowDeactivated: dbgprintf("WID=%x WindowDeactivated\n", event.window_id); break; | ||||
|         } | ||||
| 
 | ||||
|         if (event.type == GUI_Event::Type::MouseDown) { | ||||
|         if (event.type == GUI_Event::Type::Paint) { | ||||
|             paint(*bitmap, backing.size.width, backing.size.height); | ||||
|             gui_notify_paint_finished(window_id, nullptr); | ||||
|         } | ||||
| 
 | ||||
|         if (event.type == GUI_Event::Type::MouseDown) { | ||||
|             gui_invalidate_window(window_id, nullptr); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -94,6 +94,21 @@ void WSEventLoop::post_event(WSEventReceiver* receiver, OwnPtr<WSEvent>&& event) | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (event->type() == WSEvent::Paint) { | ||||
|         auto& invalidation_event = static_cast<WSPaintEvent&>(*event); | ||||
|         for (auto& queued_event : m_queued_events) { | ||||
|             if (receiver == queued_event.receiver && queued_event.event->type() == WSEvent::Paint) { | ||||
|                 auto& queued_invalidation_event = static_cast<WSPaintEvent&>(*queued_event.event); | ||||
|                 if (queued_invalidation_event.rect().is_empty() || queued_invalidation_event.rect().contains(invalidation_event.rect())) { | ||||
| #ifdef WSEVENTLOOP_DEBUG | ||||
|                     dbgprintf("Swallow WM_Paint\n"); | ||||
| #endif | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     m_queued_events.append({ receiver, move(event) }); | ||||
| 
 | ||||
|     if (current != m_server_process) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling