1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 12:28:12 +00:00

Let userland retain the window backing store while drawing into it.

To start painting, call:
gui$get_window_backing_store()

Then finish up with:
gui$release_window_backing_store()

Process will retain the underlying GraphicsBitmap behind the scenes.
This fixes racing between the WindowServer and GUI clients.

This patch also adds a WSWindowLocker that is exactly what it sounds like.
This commit is contained in:
Andreas Kling 2019-01-24 23:40:12 +01:00
parent ccf3fc4618
commit 86eae0f8df
22 changed files with 244 additions and 102 deletions

View file

@ -7,5 +7,5 @@

Welcome to Serenity OS.
Copyright (C) Andreas Kling, 2018
Copyright (C) Andreas Kling, 2018-2019
All rights reserved.

View file

@ -34,6 +34,7 @@ struct GUI_WindowParameters {
};
struct GUI_WindowBackingStoreInfo {
void* backing_store_id;
GUI_Size size;
size_t bpp;
size_t pitch;

View file

@ -20,6 +20,7 @@ class Region;
class VMObject;
class Zone;
class WSWindow;
class GraphicsBitmap;
#define COOL_GLOBALS
#ifdef COOL_GLOBALS
@ -197,9 +198,12 @@ public:
int gui$create_window(const GUI_WindowParameters*);
int gui$destroy_window(int window_id);
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$get_window_parameters(int window_id, GUI_WindowParameters*);
int gui$set_window_parameters(int window_id, const GUI_WindowParameters*);
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*);
int gui$set_window_rect(int window_id, const GUI_Rect*);
DisplayInfo get_display_info();
@ -360,6 +364,7 @@ private:
RetainPtr<Region> m_display_framebuffer_region;
HashMap<int, OwnPtr<WSWindow>> m_windows;
Vector<RetainPtr<GraphicsBitmap>> m_retained_backing_stores;
Vector<GUI_Event> m_gui_events;
Lock m_gui_events_lock;

View file

@ -97,13 +97,28 @@ int Process::gui$get_window_backing_store(int window_id, GUI_WindowBackingStoreI
if (it == m_windows.end())
return -EBADWINDOW;
auto& window = *(*it).value;
WSWindowLocker locker(window);
auto* backing_store = window.backing();
m_retained_backing_stores.append(backing_store);
info->backing_store_id = backing_store;
info->bpp = sizeof(RGBA32);
info->pitch = window.backing()->pitch();
info->size = window.backing()->size();
info->pixels = reinterpret_cast<RGBA32*>(window.backing()->client_region()->laddr().as_ptr());
info->pitch = backing_store->pitch();
info->size = backing_store->size();
info->pixels = reinterpret_cast<RGBA32*>(backing_store->client_region()->laddr().as_ptr());
return 0;
}
int Process::gui$release_window_backing_store(void* backing_store_id)
{
for (size_t i = 0; i < m_retained_backing_stores.size(); ++i) {
if (m_retained_backing_stores[i].ptr() == backing_store_id) {
m_retained_backing_stores.remove(i);
return 0;
}
}
return -EBADBACKING;
}
int Process::gui$invalidate_window(int window_id, const GUI_Rect* rect)
{
if (window_id < 0)
@ -121,39 +136,82 @@ int Process::gui$invalidate_window(int window_id, const GUI_Rect* rect)
#endif
auto& window = *(*it).value;
Rect invalidation_rect;
if (rect)
if (rect) {
WSWindowLocker locker(window);
invalidation_rect = *rect;
}
WSEventLoop::the().post_event(&window, make<WSWindowInvalidationEvent>(invalidation_rect));
WSEventLoop::the().server_process().request_wakeup();
return 0;
}
int Process::gui$get_window_parameters(int window_id, GUI_WindowParameters* params)
int Process::gui$get_window_title(int window_id, char* buffer, size_t size)
{
if (window_id < 0)
return -EINVAL;
if (!validate_write_typed(params))
if (!validate_write(buffer, size))
return -EFAULT;
auto it = m_windows.find(window_id);
if (it == m_windows.end())
return -EBADWINDOW;
auto& window = *(*it).value;
params->rect = window.rect();
strcpy(params->title, window.title().characters());
String title;
{
WSWindowLocker locker(window);
title = window.title();
}
if (title.length() > size)
return -ERANGE;
memcpy(buffer, title.characters(), title.length());
return title.length();
}
int Process::gui$set_window_title(int window_id, const char* title, size_t size)
{
if (window_id < 0)
return -EINVAL;
if (!validate_read(title, size))
return -EFAULT;
auto it = m_windows.find(window_id);
if (it == m_windows.end())
return -EBADWINDOW;
auto& window = *(*it).value;
String new_title(title, size);
WSEventLoop::the().post_event(&window, make<WSSetWindowTitle>(move(new_title)));
WSEventLoop::the().server_process().request_wakeup();
return 0;
}
int Process::gui$set_window_parameters(int window_id, const GUI_WindowParameters* params)
int Process::gui$get_window_rect(int window_id, GUI_Rect* rect)
{
if (window_id < 0)
return -EINVAL;
if (!validate_read_typed(params))
if (!validate_write_typed(rect))
return -EFAULT;
auto it = m_windows.find(window_id);
if (it == m_windows.end())
return -EBADWINDOW;
auto& window = *(*it).value;
window.set_rect(params->rect);
window.set_title(params->title);
{
WSWindowLocker locker(window);
*rect = window.rect();
}
return 0;
}
int Process::gui$set_window_rect(int window_id, const GUI_Rect* rect)
{
if (window_id < 0)
return -EINVAL;
if (!validate_read_typed(rect))
return -EFAULT;
auto it = m_windows.find(window_id);
if (it == m_windows.end())
return -EBADWINDOW;
auto& window = *(*it).value;
Rect new_rect = *rect;
WSEventLoop::the().post_event(&window, make<WSSetWindowRect>(new_rect));
WSEventLoop::the().server_process().request_wakeup();
return 0;
}

View file

@ -201,12 +201,18 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
return current->gui$destroy_window((int)arg1);
case Syscall::SC_gui_get_window_backing_store:
return current->gui$get_window_backing_store((int)arg1, (GUI_WindowBackingStoreInfo*)arg2);
case Syscall::SC_gui_release_window_backing_store:
return current->gui$release_window_backing_store((void*)arg1);
case Syscall::SC_gui_invalidate_window:
return current->gui$invalidate_window((int)arg1, (const GUI_Rect*)arg2);
case Syscall::SC_gui_set_window_parameters:
return current->gui$set_window_parameters((int)arg1, (const GUI_WindowParameters*)arg2);
case Syscall::SC_gui_get_window_parameters:
return current->gui$get_window_parameters((int)arg1, (GUI_WindowParameters*)arg2);
case Syscall::SC_gui_set_window_title:
return current->gui$set_window_title((int)arg1, (const char*)arg2, (size_t)arg3);
case Syscall::SC_gui_get_window_title:
return current->gui$get_window_title((int)arg1, (char*)arg2, (size_t)arg3);
case Syscall::SC_gui_set_window_rect:
return current->gui$set_window_rect((int)arg1, (const GUI_Rect*)arg2);
case Syscall::SC_gui_get_window_rect:
return current->gui$get_window_rect((int)arg1, (GUI_Rect*)arg2);
default:
kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
break;

View file

@ -67,15 +67,18 @@
__ENUMERATE_SYSCALL(utime) \
__ENUMERATE_SYSCALL(sync) \
__ENUMERATE_SYSCALL(ptsname_r) \
__ENUMERATE_SYSCALL(select) \
__ENUMERATE_SYSCALL(unlink) \
__ENUMERATE_SYSCALL(poll) \
__ENUMERATE_SYSCALL(gui_create_window) \
__ENUMERATE_SYSCALL(gui_destroy_window) \
__ENUMERATE_SYSCALL(gui_get_window_backing_store) \
__ENUMERATE_SYSCALL(gui_release_window_backing_store) \
__ENUMERATE_SYSCALL(gui_invalidate_window) \
__ENUMERATE_SYSCALL(select) \
__ENUMERATE_SYSCALL(gui_get_window_parameters) \
__ENUMERATE_SYSCALL(gui_set_window_parameters) \
__ENUMERATE_SYSCALL(unlink) \
__ENUMERATE_SYSCALL(poll) \
__ENUMERATE_SYSCALL(gui_get_window_title) \
__ENUMERATE_SYSCALL(gui_set_window_title) \
__ENUMERATE_SYSCALL(gui_get_window_rect) \
__ENUMERATE_SYSCALL(gui_set_window_rect) \
struct fd_set;

View file

@ -42,7 +42,9 @@
__ERROR(ENOTIMPL, "Not implemented") \
__ERROR(EAFNOSUPPORT, "Address family not supported") \
__ERROR(EWHYTHO, "Failed without setting an error code (Bug!)") \
__ERROR(EBADWINDOW, "Bad Window ID") \
__ERROR(EBADWINDOW, "Bad window ID") \
__ERROR(EBADBACKING, "Bad backing store ID") \
enum __errno_values {
#undef __ERROR

View file

@ -21,14 +21,32 @@ int gui_get_window_backing_store(int window_id, GUI_WindowBackingStoreInfo* info
__RETURN_WITH_ERRNO(rc, rc, -1);
}
int gui_get_window_parameters(int window_id, GUI_WindowParameters* params)
int gui_release_window_backing_store(void* backing_store_id)
{
int rc = syscall(SC_gui_get_window_parameters, window_id, params);
int rc = syscall(SC_gui_release_window_backing_store, backing_store_id);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
int gui_set_window_parameters(int window_id, const GUI_WindowParameters* params)
int gui_get_window_title(int window_id, char* buffer, size_t size)
{
int rc = syscall(SC_gui_set_window_parameters, window_id, params);
int rc = syscall(SC_gui_get_window_title, window_id, buffer, size);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
int gui_set_window_title(int window_id, const char* title, size_t length)
{
int rc = syscall(SC_gui_set_window_title, window_id, title, length);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
int gui_get_window_rect(int window_id, GUI_Rect* rect)
{
int rc = syscall(SC_gui_get_window_rect, window_id, rect);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
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);
}

View file

@ -8,8 +8,11 @@ __BEGIN_DECLS
int gui_create_window(const GUI_WindowParameters*);
int gui_invalidate_window(int window_id, const GUI_Rect*);
int gui_get_window_backing_store(int window_id, GUI_WindowBackingStoreInfo*);
int gui_get_window_parameters(int window_id, GUI_WindowParameters*);
int gui_set_window_parameters(int window_id, const GUI_WindowParameters*);
int gui_release_window_backing_store(void* backing_store_id);
int gui_get_window_title(int window_id, char*, size_t);
int gui_set_window_title(int window_id, const char*, size_t);
int gui_get_window_rect(int window_id, GUI_Rect*);
int gui_set_window_rect(int window_id, const GUI_Rect*);
__END_DECLS

View file

@ -18,12 +18,13 @@ GWidget::~GWidget()
{
}
void GWidget::set_relative_rect(const Rect& rect, bool should_update)
void GWidget::set_relative_rect(const Rect& rect)
{
if (rect == m_relative_rect)
return;
// FIXME: Make some kind of event loop driven ResizeEvent?
m_relative_rect = rect;
if (should_update)
update();
update();
}
void GWidget::repaint(const Rect& rect)
@ -147,10 +148,3 @@ void GWidget::set_font(RetainPtr<Font>&& font)
else
m_font = move(font);
}
GraphicsBitmap* GWidget::backing()
{
if (auto* w = window())
return w->backing();
return nullptr;
}

View file

@ -51,7 +51,7 @@ public:
virtual const char* class_name() const override { return "GWidget"; }
void set_relative_rect(const Rect&, bool should_update = true);
void set_relative_rect(const Rect&);
Color background_color() const { return m_background_color; }
Color foreground_color() const { return m_foreground_color; }
@ -84,8 +84,6 @@ public:
const Font& font() const { return *m_font; }
void set_font(RetainPtr<Font>&&);
GraphicsBitmap* backing();
private:
GWindow* m_window { nullptr };

View file

@ -39,14 +39,6 @@ GWindow::GWindow(GObject* parent)
exit(1);
}
GUI_WindowBackingStoreInfo backing;
int rc = gui_get_window_backing_store(m_window_id, &backing);
if (rc < 0) {
perror("gui_get_window_backing_store");
exit(1);
}
m_backing = GraphicsBitmap::create_wrapper(backing.size, backing.pixels);
windows().set(m_window_id, this);
}
@ -58,34 +50,16 @@ void GWindow::set_title(String&& title)
{
dbgprintf("GWindow::set_title \"%s\"\n", title.characters());
GUI_WindowParameters params;
int rc = gui_get_window_parameters(m_window_id, &params);
int rc = gui_set_window_title(m_window_id, title.characters(), title.length());
ASSERT(rc == 0);
strcpy(params.title, title.characters());;
rc = gui_set_window_parameters(m_window_id, &params);
ASSERT(rc == 0);
m_title = move(title);
}
void GWindow::set_rect(const Rect& rect)
void GWindow::set_rect(const Rect& a_rect)
{
// FIXME: This is a hack to fudge the race with WSWindowManager trying to display @ old rect.
sleep(10);
dbgprintf("GWindow::set_rect %d,%d %dx%d\n", m_rect.x(), m_rect.y(), m_rect.width(), m_rect.height());
GUI_WindowParameters params;
int rc = gui_get_window_parameters(m_window_id, &params);
dbgprintf("GWindow::set_rect! %d,%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height());
GUI_Rect rect = a_rect;
int rc = gui_set_window_rect(m_window_id, &rect);
ASSERT(rc == 0);
params.rect = rect;
rc = gui_set_window_parameters(m_window_id, &params);
ASSERT(rc == 0);
m_rect = rect;
GUI_WindowBackingStoreInfo backing;
rc = gui_get_window_backing_store(m_window_id, &backing);
if (rc < 0) {
perror("gui_get_window_backing_store");
exit(1);
}
m_backing = GraphicsBitmap::create_wrapper(backing.size, backing.pixels);
}
void GWindow::event(GEvent& event)

View file

@ -16,20 +16,17 @@ public:
int window_id() const { return m_window_id; }
String title() const { return m_title; }
void set_title(String&&);
int x() const { return m_rect.x(); }
int y() const { return m_rect.y(); }
int width() const { return m_rect.width(); }
int height() const { return m_rect.height(); }
int x() const { return rect().x(); }
int y() const { return rect().y(); }
int width() const { return rect().width(); }
int height() const { return rect().height(); }
const Rect& rect() const { return m_rect; }
Rect rect() const;
void set_rect(const Rect&);
void set_rect_without_repaint(const Rect& rect) { m_rect = rect; }
Point position() const { return m_rect.location(); }
void set_position_without_repaint(const Point& position) { set_rect_without_repaint({ position.x(), position.y(), width(), height() }); }
Point position() const { return rect().location(); }
virtual void event(GEvent&) override;
@ -41,16 +38,11 @@ public:
const GWidget* main_widget() const { return m_main_widget; }
void set_main_widget(GWidget*);
GraphicsBitmap* backing() { return m_backing.ptr(); }
void show();
void update();
private:
String m_title;
Rect m_rect;
RetainPtr<GraphicsBitmap> m_backing;
int m_window_id { -1 };
GWidget* m_main_widget { nullptr };

View file

@ -1,6 +1,7 @@
#pragma once
#include "Color.h"
#include "Rect.h"
#include "Size.h"
#include <AK/Retainable.h>
#include <AK/RetainPtr.h>
@ -20,6 +21,7 @@ public:
RGBA32* scanline(int y);
const RGBA32* scanline(int y) const;
Rect rect() const { return { {}, m_size }; }
Size size() const { return m_size; }
int width() const { return m_size.width(); }
int height() const { return m_size.height(); }

View file

@ -7,6 +7,8 @@
#ifdef LIBGUI
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <LibC/gui.h>
#include <LibC/stdio.h>
#endif
#define DEBUG_WIDGET_UNDERDRAW
@ -22,12 +24,20 @@ Painter::Painter(GraphicsBitmap& bitmap)
Painter::Painter(GWidget& widget)
: m_font(&widget.font())
{
m_target = widget.backing();
GUI_WindowBackingStoreInfo backing;
int rc = gui_get_window_backing_store(widget.window()->window_id(), &backing);
if (rc < 0) {
perror("gui_get_window_backing_store");
exit(1);
}
m_backing_store_id = backing.backing_store_id;
m_target = GraphicsBitmap::create_wrapper(backing.size, backing.pixels);
ASSERT(m_target);
m_window = widget.window();
m_translation.move_by(widget.relative_position());
// NOTE: m_clip_rect is in Window coordinates since we are painting into its backing store.
m_clip_rect = widget.relative_rect();
m_clip_rect.intersect(m_target->rect());
#ifdef DEBUG_WIDGET_UNDERDRAW
// If the widget is not opaque, let's not mess it up with debugging color.
@ -39,6 +49,11 @@ Painter::Painter(GWidget& widget)
Painter::~Painter()
{
#ifdef LIBGUI
m_target = nullptr;
int rc = gui_release_window_backing_store(m_backing_store_id);
ASSERT(rc == 0);
#endif
}
void Painter::fill_rect(const Rect& a_rect, Color color)
@ -47,6 +62,9 @@ void Painter::fill_rect(const Rect& a_rect, Color color)
rect.move_by(m_translation);
rect.intersect(m_clip_rect);
if (rect.is_empty())
return;
RGBA32* dst = m_target->scanline(rect.top()) + rect.left();
const unsigned dst_skip = m_target->width();
@ -266,3 +284,13 @@ void Painter::blit(const Point& position, const GraphicsBitmap& source, const Re
src += src_skip;
}
}
void Painter::set_clip_rect(const Rect& rect)
{
m_clip_rect = Rect::intersection(rect, m_target->rect());
}
void Painter::clear_clip_rect()
{
m_clip_rect = m_target->rect();
}

View file

@ -40,8 +40,8 @@ public:
void set_draw_op(DrawOp op) { m_draw_op = op; }
DrawOp draw_op() const { return m_draw_op; }
void set_clip_rect(const Rect& rect) { m_clip_rect = rect; }
void clear_clip_rect() { m_clip_rect = { 0, 0, 1024, 768 }; }
void set_clip_rect(const Rect& rect);
void clear_clip_rect();
Rect clip_rect() const { return m_clip_rect; }
private:
@ -53,6 +53,7 @@ private:
RetainPtr<GraphicsBitmap> m_target;
#ifdef LIBGUI
GWindow* m_window { nullptr };
void* m_backing_store_id { nullptr };
#endif
DrawOp m_draw_op { DrawOp::Copy };
};

View file

@ -26,6 +26,7 @@ void Terminal::create_window()
exit(1);
}
// NOTE: We never release the backing store.
GUI_WindowBackingStoreInfo info;
int rc = gui_get_window_backing_store(m_window_id, &info);
if (rc < 0) {

View file

@ -30,6 +30,7 @@ int main(int argc, char** argv)
return 1;
}
// NOTE: We never release the backing store. This is just a simple app. :^)
GUI_WindowBackingStoreInfo backing;
int rc = gui_get_window_backing_store(window_id, &backing);
if (rc < 0) {

View file

@ -39,6 +39,8 @@ public:
WM_Invalidate,
WindowActivated,
WindowDeactivated,
WM_SetWindowTitle,
WM_SetWindowRect,
};
WSEvent() { }
@ -70,6 +72,34 @@ private:
Rect m_rect;
};
class WSSetWindowTitle final : public WSEvent {
public:
explicit WSSetWindowTitle(String&& title)
: WSEvent(WSEvent::WM_SetWindowTitle)
, m_title(move(title))
{
}
String title() const { return m_title; }
private:
String m_title;
};
class WSSetWindowRect final : public WSEvent {
public:
explicit WSSetWindowRect(const Rect& rect)
: WSEvent(WSEvent::WM_SetWindowRect)
, m_rect(rect)
{
}
Rect rect() const { return m_rect; }
private:
Rect m_rect;
};
class WSPaintEvent final : public WSEvent {
public:
explicit WSPaintEvent(const Rect& rect = Rect())

View file

@ -19,21 +19,28 @@ WSWindow::~WSWindow()
void WSWindow::set_title(String&& title)
{
if (m_title == title)
return;
{
WSWindowLocker locker(*this);
if (m_title == title)
return;
m_title = move(title);
}
m_title = move(title);
WSWindowManager::the().notify_title_changed(*this);
}
void WSWindow::set_rect(const Rect& rect)
{
if (m_rect == rect)
return;
auto old_rect = m_rect;
m_rect = rect;
m_backing = GraphicsBitmap::create(m_process, m_rect.size());
WSWindowManager::the().notify_rect_changed(*this, old_rect, m_rect);
Rect old_rect;
{
WSWindowLocker locker(*this);
if (m_rect == rect)
return;
old_rect = m_rect;
m_rect = rect;
m_backing = GraphicsBitmap::create(m_process, m_rect.size());
}
WSWindowManager::the().notify_rect_changed(*this, old_rect, rect);
}
// FIXME: Just use the same types.
@ -86,6 +93,12 @@ void WSWindow::event(WSEvent& event)
case WSEvent::WM_Invalidate:
WSWindowManager::the().invalidate(*this, static_cast<WSWindowInvalidationEvent&>(event).rect());
return;
case WSEvent::WM_SetWindowRect:
set_rect(static_cast<WSSetWindowRect&>(event).rect());
return;
case WSEvent::WM_SetWindowTitle:
set_title(static_cast<WSSetWindowTitle&>(event).title());
return;
case WSEvent::WindowActivated:
gui_event.type = GUI_Event::Type::WindowActivated;
break;

View file

@ -4,11 +4,13 @@
#include <SharedGraphics/GraphicsBitmap.h>
#include <AK/AKString.h>
#include <AK/InlineLinkedList.h>
#include <AK/Lock.h>
#include "WSEventReceiver.h"
class Process;
class WSWindow final : public WSEventReceiver, public InlineLinkedListNode<WSWindow> {
friend class WSWindowLocker;
public:
WSWindow(Process&, int window_id);
virtual ~WSWindow() override;
@ -46,6 +48,7 @@ public:
WSWindow* m_prev { nullptr };
private:
Lock m_lock;
String m_title;
Rect m_rect;
bool m_is_being_dragged { false };
@ -56,3 +59,11 @@ private:
pid_t m_pid { -1 };
};
class WSWindowLocker {
public:
WSWindowLocker(WSWindow& window) : m_locker(window.m_lock) { }
~WSWindowLocker() { }
private:
Locker m_locker;
};

View file

@ -220,13 +220,13 @@ void WSWindowManager::remove_window(WSWindow& window)
void WSWindowManager::notify_title_changed(WSWindow& window)
{
printf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters());
invalidate(outer_window_rect(window.rect()));
}
void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
{
printf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height());
ASSERT_INTERRUPTS_ENABLED();
LOCKER(m_lock);
invalidate(outer_window_rect(old_rect));
invalidate(outer_window_rect(new_rect));
}
@ -335,6 +335,7 @@ void WSWindowManager::compose()
m_back_painter->fill_rect(dirty_rect, Color(0, 72, 96));
}
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
WSWindowLocker locker(*window);
if (!window->backing())
continue;
if (!any_dirty_rect_intersects_window(*window))