mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 12:55:07 +00:00
WindowServer: Fix flickering
Rather than blitting and rendering each window every time, only render what actually changed. And while doing so, only render the portions that are visible on the screen. This avoids flickering because flipping framebuffers isn't always perfectly in sync with the code, so it's possible that the flip happens slightly delayed and we can briefly see the next iteration having partially completed. Also, avoid touching the mouse cursor unless it is in an area that needs updating. This reduces flickering unless it is over an area that is updated often. And because we no longer render the entire screen, we'll save the contents below the cursor so that we can hide it before touching that area. Fixes #2981
This commit is contained in:
parent
f8903acea2
commit
a698a58d3c
11 changed files with 679 additions and 186 deletions
|
@ -65,7 +65,7 @@ void Button::on_mouse_event(const MouseEvent& event)
|
||||||
if (event.type() == Event::MouseDown && event.button() == MouseButton::Left) {
|
if (event.type() == Event::MouseDown && event.button() == MouseButton::Left) {
|
||||||
m_pressed = true;
|
m_pressed = true;
|
||||||
wm.set_cursor_tracking_button(this);
|
wm.set_cursor_tracking_button(this);
|
||||||
wm.invalidate(screen_rect());
|
m_frame.invalidate(m_relative_rect);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ void Button::on_mouse_event(const MouseEvent& event)
|
||||||
// However, we don't know that rect yet. We can make an educated
|
// However, we don't know that rect yet. We can make an educated
|
||||||
// guess which also looks okay even when wrong:
|
// guess which also looks okay even when wrong:
|
||||||
m_hovered = false;
|
m_hovered = false;
|
||||||
wm.invalidate(screen_rect());
|
m_frame.invalidate(m_relative_rect);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ void Button::on_mouse_event(const MouseEvent& event)
|
||||||
m_hovered = rect().contains(event.position());
|
m_hovered = rect().contains(event.position());
|
||||||
wm.set_hovered_button(m_hovered ? this : nullptr);
|
wm.set_hovered_button(m_hovered ? this : nullptr);
|
||||||
if (old_hovered != m_hovered)
|
if (old_hovered != m_hovered)
|
||||||
wm.invalidate(screen_rect());
|
m_frame.invalidate(m_relative_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type() == Event::MouseMove && event.buttons() & (unsigned)MouseButton::Left) {
|
if (event.type() == Event::MouseMove && event.buttons() & (unsigned)MouseButton::Left) {
|
||||||
|
@ -104,7 +104,7 @@ void Button::on_mouse_event(const MouseEvent& event)
|
||||||
bool old_pressed = m_pressed;
|
bool old_pressed = m_pressed;
|
||||||
m_pressed = m_hovered;
|
m_pressed = m_hovered;
|
||||||
if (old_pressed != m_pressed)
|
if (old_pressed != m_pressed)
|
||||||
wm.invalidate(screen_rect());
|
m_frame.invalidate(m_relative_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -584,7 +584,7 @@ OwnPtr<Messages::WindowServer::SetWindowBackingStoreResponse> ClientConnection::
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.flush_immediately())
|
if (message.flush_immediately())
|
||||||
window.invalidate();
|
window.invalidate(false);
|
||||||
|
|
||||||
return make<Messages::WindowServer::SetWindowBackingStoreResponse>();
|
return make<Messages::WindowServer::SetWindowBackingStoreResponse>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,15 @@
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include "WindowManager.h"
|
#include "WindowManager.h"
|
||||||
#include <AK/Memory.h>
|
#include <AK/Memory.h>
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
#include <LibCore/Timer.h>
|
#include <LibCore/Timer.h>
|
||||||
#include <LibGfx/Font.h>
|
#include <LibGfx/Font.h>
|
||||||
#include <LibGfx/Painter.h>
|
#include <LibGfx/Painter.h>
|
||||||
#include <LibThread/BackgroundAction.h>
|
#include <LibThread/BackgroundAction.h>
|
||||||
|
|
||||||
|
//#define COMPOSE_DEBUG
|
||||||
|
//#define OCCLUSIONS_DEBUG
|
||||||
|
|
||||||
namespace WindowServer {
|
namespace WindowServer {
|
||||||
|
|
||||||
Compositor& Compositor::the()
|
Compositor& Compositor::the()
|
||||||
|
@ -96,12 +100,15 @@ void Compositor::init_bitmaps()
|
||||||
else
|
else
|
||||||
m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size);
|
m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size);
|
||||||
|
|
||||||
|
m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size);
|
||||||
|
|
||||||
m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
|
m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
|
||||||
m_back_painter = make<Gfx::Painter>(*m_back_bitmap);
|
m_back_painter = make<Gfx::Painter>(*m_back_bitmap);
|
||||||
|
m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap);
|
||||||
|
|
||||||
m_buffers_are_flipped = false;
|
m_buffers_are_flipped = false;
|
||||||
|
|
||||||
invalidate();
|
invalidate_screen();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::compose()
|
void Compositor::compose()
|
||||||
|
@ -111,26 +118,64 @@ void Compositor::compose()
|
||||||
m_wallpaper_mode = mode_to_enum(wm.config()->read_entry("Background", "Mode", "simple"));
|
m_wallpaper_mode = mode_to_enum(wm.config()->read_entry("Background", "Mode", "simple"));
|
||||||
auto& ws = Screen::the();
|
auto& ws = Screen::the();
|
||||||
|
|
||||||
auto dirty_rects = move(m_dirty_rects);
|
if (!m_invalidated_any) {
|
||||||
|
|
||||||
if (dirty_rects.size() == 0) {
|
|
||||||
// nothing dirtied since the last compose pass.
|
// nothing dirtied since the last compose pass.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dirty_rects.add(Gfx::IntRect::intersection(m_last_geometry_label_rect, Screen::the().rect()));
|
if (m_occlusions_dirty) {
|
||||||
dirty_rects.add(Gfx::IntRect::intersection(m_last_cursor_rect, Screen::the().rect()));
|
m_occlusions_dirty = false;
|
||||||
dirty_rects.add(Gfx::IntRect::intersection(m_last_dnd_rect, Screen::the().rect()));
|
recompute_occlusions();
|
||||||
dirty_rects.add(Gfx::IntRect::intersection(current_cursor_rect(), Screen::the().rect()));
|
|
||||||
|
|
||||||
auto any_dirty_rect_intersects_window = [&dirty_rects](const Window& window) {
|
|
||||||
auto window_frame_rect = window.frame().rect();
|
|
||||||
for (auto& dirty_rect : dirty_rects.rects()) {
|
|
||||||
if (dirty_rect.intersects(window_frame_rect))
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
};
|
auto dirty_screen_rects = move(m_dirty_screen_rects);
|
||||||
|
dirty_screen_rects.add(m_last_geometry_label_rect.intersected(ws.rect()));
|
||||||
|
dirty_screen_rects.add(m_last_dnd_rect.intersected(ws.rect()));
|
||||||
|
if (m_invalidated_cursor) {
|
||||||
|
if (wm.dnd_client())
|
||||||
|
dirty_screen_rects.add(wm.dnd_rect().intersected(ws.rect()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark window regions as dirty that need to be re-rendered
|
||||||
|
wm.for_each_visible_window_from_back_to_front([&](Window& window) {
|
||||||
|
auto frame_rect = window.frame().rect();
|
||||||
|
for (auto& dirty_rect : dirty_screen_rects.rects()) {
|
||||||
|
auto invalidate_rect = dirty_rect.intersected(frame_rect);
|
||||||
|
if (!invalidate_rect.is_empty()) {
|
||||||
|
auto inner_rect_offset = window.rect().location() - frame_rect.location();
|
||||||
|
invalidate_rect.move_by(-(frame_rect.location() + inner_rect_offset));
|
||||||
|
window.invalidate_no_notify(invalidate_rect);
|
||||||
|
m_invalidated_window = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.prepare_dirty_rects();
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Any windows above or below a given window that need to be re-rendered
|
||||||
|
// also require us to re-render that window's intersecting area, regardless
|
||||||
|
// of whether that window has any dirty rectangles
|
||||||
|
wm.for_each_visible_window_from_back_to_front([&](Window& window) {
|
||||||
|
auto& transparency_rects = window.transparency_rects();
|
||||||
|
if (transparency_rects.is_empty())
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
|
||||||
|
auto frame_rect = window.frame().rect();
|
||||||
|
auto& dirty_rects = window.dirty_rects();
|
||||||
|
wm.for_each_visible_window_from_back_to_front([&](Window& w) {
|
||||||
|
if (&w == &window)
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
auto frame_rect2 = w.frame().rect();
|
||||||
|
if (!frame_rect2.intersects(frame_rect))
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
transparency_rects.for_each_intersected(w.dirty_rects(), [&](const Gfx::IntRect& intersected_dirty) {
|
||||||
|
dirty_rects.add(intersected_dirty);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
Color background_color = wm.palette().desktop_background();
|
Color background_color = wm.palette().desktop_background();
|
||||||
String background_color_entry = wm.config()->read_entry("Background", "Color", "");
|
String background_color_entry = wm.config()->read_entry("Background", "Color", "");
|
||||||
|
@ -138,50 +183,127 @@ void Compositor::compose()
|
||||||
background_color = Color::from_string(background_color_entry).value_or(background_color);
|
background_color = Color::from_string(background_color_entry).value_or(background_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paint the wallpaper.
|
#ifdef COMPOSE_DEBUG
|
||||||
for (auto& dirty_rect : dirty_rects.rects()) {
|
dbg() << "COMPOSE: invalidated: window:" << m_invalidated_window << " cursor:" << m_invalidated_cursor << " any: " << m_invalidated_any;
|
||||||
if (any_opaque_window_contains_rect(dirty_rect))
|
for (auto& r : dirty_screen_rects.rects())
|
||||||
continue;
|
dbg() << "dirty screen: " << r;
|
||||||
|
#endif
|
||||||
|
Gfx::DisjointRectSet flush_rects;
|
||||||
|
Gfx::DisjointRectSet flush_transparent_rects;
|
||||||
|
Gfx::DisjointRectSet flush_special_rects;
|
||||||
|
auto cursor_rect = current_cursor_rect();
|
||||||
|
bool need_to_draw_cursor = false;
|
||||||
|
|
||||||
|
auto check_restore_cursor_back = [&](const Gfx::IntRect& rect) {
|
||||||
|
if (!need_to_draw_cursor && rect.intersects(cursor_rect)) {
|
||||||
|
// Restore what's behind the cursor if anything touches the area of the cursor
|
||||||
|
need_to_draw_cursor = true;
|
||||||
|
restore_cursor_back();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto prepare_rect = [&](const Gfx::IntRect& rect) {
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " -> flush opaque: " << rect;
|
||||||
|
#endif
|
||||||
|
ASSERT(!flush_rects.intersects(rect));
|
||||||
|
ASSERT(!flush_transparent_rects.intersects(rect));
|
||||||
|
flush_rects.add(rect);
|
||||||
|
check_restore_cursor_back(rect);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto prepare_transparency_rect = [&](const Gfx::IntRect& rect) {
|
||||||
|
// This function may be called multiple times with the same
|
||||||
|
// rect as we walk the window stack from back to front. However,
|
||||||
|
// there should be no overlaps with flush_rects
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " -> flush transparent: " << rect;
|
||||||
|
#endif
|
||||||
|
ASSERT(!flush_rects.intersects(rect));
|
||||||
|
bool have_rect = false;
|
||||||
|
for (auto& r : flush_transparent_rects.rects()) {
|
||||||
|
if (r == rect) {
|
||||||
|
have_rect = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_rect) {
|
||||||
|
flush_transparent_rects.add(rect);
|
||||||
|
check_restore_cursor_back(rect);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!m_cursor_back_bitmap || m_invalidated_cursor)
|
||||||
|
check_restore_cursor_back(cursor_rect);
|
||||||
|
|
||||||
|
auto back_painter = *m_back_painter;
|
||||||
|
auto temp_painter = *m_temp_painter;
|
||||||
|
|
||||||
|
auto paint_wallpaper = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
||||||
// FIXME: If the wallpaper is opaque, no need to fill with color!
|
// FIXME: If the wallpaper is opaque, no need to fill with color!
|
||||||
m_back_painter->fill_rect(dirty_rect, background_color);
|
painter.fill_rect(rect, background_color);
|
||||||
if (m_wallpaper) {
|
if (m_wallpaper) {
|
||||||
if (m_wallpaper_mode == WallpaperMode::Simple) {
|
if (m_wallpaper_mode == WallpaperMode::Simple) {
|
||||||
m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
|
painter.blit(rect.location(), *m_wallpaper, rect);
|
||||||
} else if (m_wallpaper_mode == WallpaperMode::Center) {
|
} else if (m_wallpaper_mode == WallpaperMode::Center) {
|
||||||
Gfx::IntPoint offset { ws.size().width() / 2 - m_wallpaper->size().width() / 2,
|
Gfx::IntPoint offset { ws.size().width() / 2 - m_wallpaper->size().width() / 2,
|
||||||
ws.size().height() / 2 - m_wallpaper->size().height() / 2 };
|
ws.size().height() / 2 - m_wallpaper->size().height() / 2 };
|
||||||
m_back_painter->blit_offset(dirty_rect.location(), *m_wallpaper,
|
painter.blit_offset(rect.location(), *m_wallpaper,
|
||||||
dirty_rect, offset);
|
rect, offset);
|
||||||
} else if (m_wallpaper_mode == WallpaperMode::Tile) {
|
} else if (m_wallpaper_mode == WallpaperMode::Tile) {
|
||||||
m_back_painter->draw_tiled_bitmap(dirty_rect, *m_wallpaper);
|
painter.draw_tiled_bitmap(rect, *m_wallpaper);
|
||||||
} else if (m_wallpaper_mode == WallpaperMode::Scaled) {
|
} else if (m_wallpaper_mode == WallpaperMode::Scaled) {
|
||||||
float hscale = (float)m_wallpaper->size().width() / (float)ws.size().width();
|
float hscale = (float)m_wallpaper->size().width() / (float)ws.size().width();
|
||||||
float vscale = (float)m_wallpaper->size().height() / (float)ws.size().height();
|
float vscale = (float)m_wallpaper->size().height() / (float)ws.size().height();
|
||||||
|
|
||||||
m_back_painter->blit_scaled(dirty_rect, *m_wallpaper, dirty_rect, hscale, vscale);
|
// TODO: this may look ugly, we should scale to a backing bitmap and then blit
|
||||||
|
painter.blit_scaled(rect, *m_wallpaper, rect, hscale, vscale);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
m_opaque_wallpaper_rects.for_each_intersected(dirty_screen_rects, [&](const Gfx::IntRect& render_rect) {
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " render wallpaper opaque: " << render_rect;
|
||||||
|
#endif
|
||||||
|
prepare_rect(render_rect);
|
||||||
|
paint_wallpaper(back_painter, render_rect);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
auto compose_window = [&](Window& window) -> IterationDecision {
|
auto compose_window = [&](Window& window) -> IterationDecision {
|
||||||
if (!any_dirty_rect_intersects_window(window))
|
auto frame_rect = window.frame().rect();
|
||||||
|
if (!frame_rect.intersects(ws.rect()))
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
Gfx::PainterStateSaver saver(*m_back_painter);
|
auto frame_rects = frame_rect.shatter(window.rect());
|
||||||
m_back_painter->add_clip_rect(window.frame().rect());
|
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " window " << window.title() << " frame rect: " << frame_rect;
|
||||||
|
#endif
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> backing_store = window.backing_store();
|
RefPtr<Gfx::Bitmap> backing_store = window.backing_store();
|
||||||
for (auto& dirty_rect : dirty_rects.rects()) {
|
auto compose_window_rect = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
||||||
if (!window.is_fullscreen() && any_opaque_window_above_this_one_contains_rect(window, dirty_rect))
|
if (!window.is_fullscreen()) {
|
||||||
continue;
|
rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) {
|
||||||
Gfx::PainterStateSaver saver(*m_back_painter);
|
// TODO: Should optimize this to use a backing buffer
|
||||||
m_back_painter->add_clip_rect(dirty_rect);
|
Gfx::PainterStateSaver saver(painter);
|
||||||
if (!backing_store)
|
painter.add_clip_rect(intersected_rect);
|
||||||
m_back_painter->fill_rect(dirty_rect, wm.palette().window());
|
#ifdef COMPOSE_DEBUG
|
||||||
if (!window.is_fullscreen())
|
dbg() << " render frame: " << intersected_rect;
|
||||||
window.frame().paint(*m_back_painter);
|
#endif
|
||||||
if (!backing_store)
|
window.frame().paint(painter);
|
||||||
continue;
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backing_store) {
|
||||||
|
if (window.is_opaque())
|
||||||
|
painter.fill_rect(window.rect().intersected(rect), wm.palette().window());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Decide where we would paint this window's backing store.
|
// Decide where we would paint this window's backing store.
|
||||||
// This is subtly different from widow.rect(), because window
|
// This is subtly different from widow.rect(), because window
|
||||||
|
@ -216,54 +338,178 @@ void Compositor::compose()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx::IntRect dirty_rect_in_backing_coordinates = dirty_rect
|
Gfx::IntRect dirty_rect_in_backing_coordinates = rect.intersected(window.rect())
|
||||||
.intersected(window.rect())
|
|
||||||
.intersected(backing_rect)
|
.intersected(backing_rect)
|
||||||
.translated(-backing_rect.location());
|
.translated(-backing_rect.location());
|
||||||
|
|
||||||
if (dirty_rect_in_backing_coordinates.is_empty())
|
if (dirty_rect_in_backing_coordinates.is_empty())
|
||||||
continue;
|
return;
|
||||||
auto dst = backing_rect.location().translated(dirty_rect_in_backing_coordinates.location());
|
auto dst = backing_rect.location().translated(dirty_rect_in_backing_coordinates.location());
|
||||||
|
|
||||||
if (window.client() && window.client()->is_unresponsive()) {
|
if (window.client() && window.client()->is_unresponsive()) {
|
||||||
m_back_painter->blit_filtered(dst, *backing_store, dirty_rect_in_backing_coordinates, [](Color src) {
|
painter.blit_filtered(dst, *backing_store, dirty_rect_in_backing_coordinates, [](Color src) {
|
||||||
return src.to_grayscale().darkened(0.75f);
|
return src.to_grayscale().darkened(0.75f);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
m_back_painter->blit(dst, *backing_store, dirty_rect_in_backing_coordinates, window.opacity());
|
painter.blit(dst, *backing_store, dirty_rect_in_backing_coordinates, window.opacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto background_rect : window.rect().shatter(backing_rect))
|
for (auto background_rect : window.rect().shatter(backing_rect))
|
||||||
m_back_painter->fill_rect(background_rect, wm.palette().window());
|
painter.fill_rect(background_rect, wm.palette().window());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& dirty_rects = window.dirty_rects();
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
for (auto& dirty_rect : dirty_rects.rects())
|
||||||
|
dbg() << " dirty: " << dirty_rect;
|
||||||
|
for (auto& r : window.opaque_rects().rects())
|
||||||
|
dbg() << " opaque: " << r;
|
||||||
|
for (auto& r : window.transparency_rects().rects())
|
||||||
|
dbg() << " transparent: " << r;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Render opaque portions directly to the back buffer
|
||||||
|
auto& opaque_rects = window.opaque_rects();
|
||||||
|
if (!opaque_rects.is_empty()) {
|
||||||
|
opaque_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " render opaque: " << render_rect;
|
||||||
|
#endif
|
||||||
|
prepare_rect(render_rect);
|
||||||
|
Gfx::PainterStateSaver saver(back_painter);
|
||||||
|
back_painter.add_clip_rect(render_rect);
|
||||||
|
compose_window_rect(back_painter, render_rect);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the wallpaper for any transparency directly covering
|
||||||
|
// the wallpaper
|
||||||
|
auto& transparency_wallpaper_rects = window.transparency_wallpaper_rects();
|
||||||
|
if (!transparency_wallpaper_rects.is_empty()) {
|
||||||
|
transparency_wallpaper_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " render wallpaper: " << render_rect;
|
||||||
|
#endif
|
||||||
|
prepare_transparency_rect(render_rect);
|
||||||
|
paint_wallpaper(temp_painter, render_rect);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
auto& transparency_rects = window.transparency_rects();
|
||||||
|
if (!transparency_rects.is_empty()) {
|
||||||
|
transparency_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
||||||
|
#ifdef COMPOSE_DEBUG
|
||||||
|
dbg() << " render transparent: " << render_rect;
|
||||||
|
#endif
|
||||||
|
prepare_transparency_rect(render_rect);
|
||||||
|
Gfx::PainterStateSaver saver(temp_painter);
|
||||||
|
temp_painter.add_clip_rect(render_rect);
|
||||||
|
compose_window_rect(temp_painter, render_rect);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Paint the window stack.
|
// Paint the window stack.
|
||||||
|
if (m_invalidated_window) {
|
||||||
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
|
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
|
||||||
compose_window(*fullscreen_window);
|
compose_window(*fullscreen_window);
|
||||||
} else {
|
} else {
|
||||||
wm.for_each_visible_window_from_back_to_front([&](Window& window) {
|
wm.for_each_visible_window_from_back_to_front([&](Window& window) {
|
||||||
return compose_window(window);
|
compose_window(window);
|
||||||
|
window.clear_dirty_rects();
|
||||||
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
draw_geometry_label();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run_animations();
|
// Check that there are no overlapping transparent and opaque flush rectangles
|
||||||
|
ASSERT(![&]() {
|
||||||
|
for (auto& rect_transparent : flush_transparent_rects.rects()) {
|
||||||
|
for (auto& rect_opaque : flush_rects.rects()) {
|
||||||
|
if (rect_opaque.intersects(rect_transparent)) {
|
||||||
|
dbg() << "Transparent rect " << rect_transparent << " overlaps opaque rect: " << rect_opaque << ": " << rect_opaque.intersected(rect_transparent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}());
|
||||||
|
|
||||||
draw_cursor();
|
// Copy anything rendered to the temporary buffer to the back buffer
|
||||||
|
for (auto& rect : flush_transparent_rects.rects())
|
||||||
|
back_painter.blit(rect.location(), *m_temp_bitmap, rect);
|
||||||
|
|
||||||
|
Gfx::IntRect geometry_label_rect;
|
||||||
|
if (draw_geometry_label(geometry_label_rect))
|
||||||
|
flush_special_rects.add(geometry_label_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_invalidated_any = false;
|
||||||
|
m_invalidated_window = false;
|
||||||
|
m_invalidated_cursor = false;
|
||||||
|
|
||||||
|
if (wm.dnd_client()) {
|
||||||
|
auto dnd_rect = wm.dnd_rect();
|
||||||
|
|
||||||
|
// TODO: render once into a backing bitmap, then just blit...
|
||||||
|
auto render_dnd = [&]() {
|
||||||
|
back_painter.fill_rect(dnd_rect, wm.palette().selection().with_alpha(200));
|
||||||
|
if (!wm.dnd_text().is_empty()) {
|
||||||
|
auto text_rect = dnd_rect;
|
||||||
|
if (wm.dnd_bitmap())
|
||||||
|
text_rect.move_by(wm.dnd_bitmap()->width(), 0);
|
||||||
|
back_painter.draw_text(text_rect, wm.dnd_text(), Gfx::TextAlignment::CenterLeft, wm.palette().selection_text());
|
||||||
|
}
|
||||||
|
if (wm.dnd_bitmap()) {
|
||||||
|
back_painter.blit(dnd_rect.top_left(), *wm.dnd_bitmap(), wm.dnd_bitmap()->rect());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dirty_screen_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
||||||
|
Gfx::PainterStateSaver saver(back_painter);
|
||||||
|
back_painter.add_clip_rect(render_rect);
|
||||||
|
render_dnd();
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
flush_transparent_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
||||||
|
Gfx::PainterStateSaver saver(back_painter);
|
||||||
|
back_painter.add_clip_rect(render_rect);
|
||||||
|
render_dnd();
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
m_last_dnd_rect = dnd_rect;
|
||||||
|
} else {
|
||||||
|
if (!m_last_dnd_rect.is_empty()) {
|
||||||
|
invalidate_screen(m_last_dnd_rect);
|
||||||
|
m_last_dnd_rect = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_animations(flush_special_rects);
|
||||||
|
|
||||||
|
if (need_to_draw_cursor) {
|
||||||
|
flush_rects.add(cursor_rect);
|
||||||
|
if (cursor_rect != m_last_cursor_rect)
|
||||||
|
flush_rects.add(m_last_cursor_rect);
|
||||||
|
draw_cursor(cursor_rect);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_flash_flush) {
|
if (m_flash_flush) {
|
||||||
for (auto& rect : dirty_rects.rects())
|
for (auto& rect : flush_rects.rects())
|
||||||
m_front_painter->fill_rect(rect, Color::Yellow);
|
m_front_painter->fill_rect(rect, Color::Yellow);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_screen_can_set_buffer)
|
if (m_screen_can_set_buffer)
|
||||||
flip_buffers();
|
flip_buffers();
|
||||||
|
|
||||||
for (auto& r : dirty_rects.rects())
|
for (auto& rect : flush_rects.rects())
|
||||||
flush(r);
|
flush(rect);
|
||||||
|
for (auto& rect : flush_transparent_rects.rects())
|
||||||
|
flush(rect);
|
||||||
|
for (auto& rect : flush_special_rects.rects())
|
||||||
|
flush(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::flush(const Gfx::IntRect& a_rect)
|
void Compositor::flush(const Gfx::IntRect& a_rect)
|
||||||
|
@ -301,20 +547,35 @@ void Compositor::flush(const Gfx::IntRect& a_rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::invalidate()
|
void Compositor::invalidate_screen()
|
||||||
{
|
{
|
||||||
m_dirty_rects.clear_with_capacity();
|
invalidate_screen(Screen::the().rect());
|
||||||
invalidate(Screen::the().rect());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::invalidate(const Gfx::IntRect& a_rect)
|
void Compositor::invalidate_screen(const Gfx::IntRect& screen_rect)
|
||||||
{
|
{
|
||||||
auto rect = Gfx::IntRect::intersection(a_rect, Screen::the().rect());
|
m_dirty_screen_rects.add(screen_rect.intersected(Screen::the().rect()));
|
||||||
if (rect.is_empty())
|
|
||||||
|
if (m_invalidated_any)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_dirty_rects.add(rect);
|
m_invalidated_any = true;
|
||||||
|
m_invalidated_window = true;
|
||||||
|
start_compose_async_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compositor::invalidate_window()
|
||||||
|
{
|
||||||
|
if (m_invalidated_window)
|
||||||
|
return;
|
||||||
|
m_invalidated_window = true;
|
||||||
|
m_invalidated_any = true;
|
||||||
|
|
||||||
|
start_compose_async_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compositor::start_compose_async_timer()
|
||||||
|
{
|
||||||
// We delay composition by a timer interval, but to not affect latency too
|
// We delay composition by a timer interval, but to not affect latency too
|
||||||
// much, if a pending compose is not already scheduled, we also schedule an
|
// much, if a pending compose is not already scheduled, we also schedule an
|
||||||
// immediate compose the next spin of the event loop.
|
// immediate compose the next spin of the event loop.
|
||||||
|
@ -331,7 +592,7 @@ bool Compositor::set_background_color(const String& background_color)
|
||||||
bool ret_val = wm.config()->sync();
|
bool ret_val = wm.config()->sync();
|
||||||
|
|
||||||
if (ret_val)
|
if (ret_val)
|
||||||
Compositor::invalidate();
|
Compositor::invalidate_screen();
|
||||||
|
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
@ -344,7 +605,7 @@ bool Compositor::set_wallpaper_mode(const String& mode)
|
||||||
|
|
||||||
if (ret_val) {
|
if (ret_val) {
|
||||||
m_wallpaper_mode = mode_to_enum(mode);
|
m_wallpaper_mode = mode_to_enum(mode);
|
||||||
Compositor::invalidate();
|
Compositor::invalidate_screen();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret_val;
|
return ret_val;
|
||||||
|
@ -360,7 +621,7 @@ bool Compositor::set_wallpaper(const String& path, Function<void(bool)>&& callba
|
||||||
[this, path, callback = move(callback)](RefPtr<Gfx::Bitmap> bitmap) {
|
[this, path, callback = move(callback)](RefPtr<Gfx::Bitmap> bitmap) {
|
||||||
m_wallpaper_path = path;
|
m_wallpaper_path = path;
|
||||||
m_wallpaper = move(bitmap);
|
m_wallpaper = move(bitmap);
|
||||||
invalidate();
|
invalidate_screen();
|
||||||
callback(true);
|
callback(true);
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
@ -375,7 +636,7 @@ void Compositor::flip_buffers()
|
||||||
m_buffers_are_flipped = !m_buffers_are_flipped;
|
m_buffers_are_flipped = !m_buffers_are_flipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::run_animations()
|
void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
||||||
{
|
{
|
||||||
static const int minimize_animation_steps = 10;
|
static const int minimize_animation_steps = 10;
|
||||||
|
|
||||||
|
@ -403,12 +664,12 @@ void Compositor::run_animations()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_back_painter->draw_rect(rect, Color::White);
|
m_back_painter->draw_rect(rect, Color::White);
|
||||||
|
flush_rects.add(rect);
|
||||||
|
invalidate_screen(rect);
|
||||||
|
|
||||||
window.step_minimize_animation();
|
window.step_minimize_animation();
|
||||||
if (window.minimize_animation_index() >= minimize_animation_steps)
|
if (window.minimize_animation_index() >= minimize_animation_steps)
|
||||||
window.end_minimize_animation();
|
window.end_minimize_animation();
|
||||||
|
|
||||||
invalidate(rect);
|
|
||||||
}
|
}
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
@ -427,6 +688,7 @@ bool Compositor::set_resolution(int desired_width, int desired_height)
|
||||||
}
|
}
|
||||||
bool success = Screen::the().set_resolution(desired_width, desired_height);
|
bool success = Screen::the().set_resolution(desired_width, desired_height);
|
||||||
init_bitmaps();
|
init_bitmaps();
|
||||||
|
invalidate_occlusions();
|
||||||
compose();
|
compose();
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -439,19 +701,21 @@ Gfx::IntRect Compositor::current_cursor_rect() const
|
||||||
|
|
||||||
void Compositor::invalidate_cursor()
|
void Compositor::invalidate_cursor()
|
||||||
{
|
{
|
||||||
auto& wm = WindowManager::the();
|
if (m_invalidated_cursor)
|
||||||
if (wm.dnd_client())
|
return;
|
||||||
invalidate(wm.dnd_rect());
|
m_invalidated_cursor = true;
|
||||||
invalidate(current_cursor_rect());
|
m_invalidated_any = true;
|
||||||
|
|
||||||
|
start_compose_async_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::draw_geometry_label()
|
bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_rect)
|
||||||
{
|
{
|
||||||
auto& wm = WindowManager::the();
|
auto& wm = WindowManager::the();
|
||||||
auto* window_being_moved_or_resized = wm.m_move_window ? wm.m_move_window.ptr() : (wm.m_resize_window ? wm.m_resize_window.ptr() : nullptr);
|
auto* window_being_moved_or_resized = wm.m_move_window ? wm.m_move_window.ptr() : (wm.m_resize_window ? wm.m_resize_window.ptr() : nullptr);
|
||||||
if (!window_being_moved_or_resized) {
|
if (!window_being_moved_or_resized) {
|
||||||
m_last_geometry_label_rect = {};
|
m_last_geometry_label_rect = {};
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
auto geometry_string = window_being_moved_or_resized->rect().to_string();
|
auto geometry_string = window_being_moved_or_resized->rect().to_string();
|
||||||
if (!window_being_moved_or_resized->size_increment().is_null()) {
|
if (!window_being_moved_or_resized->size_increment().is_null()) {
|
||||||
|
@ -459,39 +723,40 @@ void Compositor::draw_geometry_label()
|
||||||
int height_steps = (window_being_moved_or_resized->height() - window_being_moved_or_resized->base_size().height()) / window_being_moved_or_resized->size_increment().height();
|
int height_steps = (window_being_moved_or_resized->height() - window_being_moved_or_resized->base_size().height()) / window_being_moved_or_resized->size_increment().height();
|
||||||
geometry_string = String::format("%s (%dx%d)", geometry_string.characters(), width_steps, height_steps);
|
geometry_string = String::format("%s (%dx%d)", geometry_string.characters(), width_steps, height_steps);
|
||||||
}
|
}
|
||||||
auto geometry_label_rect = Gfx::IntRect { 0, 0, wm.font().width(geometry_string) + 16, wm.font().glyph_height() + 10 };
|
geometry_label_rect = Gfx::IntRect { 0, 0, wm.font().width(geometry_string) + 16, wm.font().glyph_height() + 10 };
|
||||||
geometry_label_rect.center_within(window_being_moved_or_resized->rect());
|
geometry_label_rect.center_within(window_being_moved_or_resized->rect());
|
||||||
m_back_painter->fill_rect(geometry_label_rect, wm.palette().window());
|
auto& back_painter = *m_back_painter;
|
||||||
m_back_painter->draw_rect(geometry_label_rect, wm.palette().threed_shadow2());
|
back_painter.fill_rect(geometry_label_rect, wm.palette().window());
|
||||||
m_back_painter->draw_text(geometry_label_rect, geometry_string, Gfx::TextAlignment::Center, wm.palette().window_text());
|
back_painter.draw_rect(geometry_label_rect, wm.palette().threed_shadow2());
|
||||||
|
back_painter.draw_text(geometry_label_rect, geometry_string, Gfx::TextAlignment::Center, wm.palette().window_text());
|
||||||
m_last_geometry_label_rect = geometry_label_rect;
|
m_last_geometry_label_rect = geometry_label_rect;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compositor::draw_cursor()
|
void Compositor::draw_cursor(const Gfx::IntRect& cursor_rect)
|
||||||
{
|
{
|
||||||
auto& wm = WindowManager::the();
|
auto& wm = WindowManager::the();
|
||||||
Gfx::IntRect cursor_rect = current_cursor_rect();
|
|
||||||
m_back_painter->blit(cursor_rect.location(), wm.active_cursor().bitmap(), wm.active_cursor().rect());
|
|
||||||
|
|
||||||
if (wm.dnd_client()) {
|
if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size()) {
|
||||||
auto dnd_rect = wm.dnd_rect();
|
m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, cursor_rect.size());
|
||||||
m_back_painter->fill_rect(dnd_rect, wm.palette().selection().with_alpha(200));
|
m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap);
|
||||||
if (!wm.dnd_text().is_empty()) {
|
|
||||||
auto text_rect = dnd_rect;
|
|
||||||
if (wm.dnd_bitmap())
|
|
||||||
text_rect.move_by(wm.dnd_bitmap()->width(), 0);
|
|
||||||
m_back_painter->draw_text(text_rect, wm.dnd_text(), Gfx::TextAlignment::CenterLeft, wm.palette().selection_text());
|
|
||||||
}
|
|
||||||
if (wm.dnd_bitmap()) {
|
|
||||||
m_back_painter->blit(dnd_rect.top_left(), *wm.dnd_bitmap(), wm.dnd_bitmap()->rect());
|
|
||||||
}
|
|
||||||
m_last_dnd_rect = dnd_rect;
|
|
||||||
} else {
|
|
||||||
m_last_dnd_rect = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_cursor_back_painter->blit({0, 0}, *m_back_bitmap, wm.active_cursor().rect().translated(cursor_rect.location()).intersected(Screen::the().rect()));
|
||||||
|
auto& back_painter = *m_back_painter;
|
||||||
|
back_painter.blit(cursor_rect.location(), wm.active_cursor().bitmap(), wm.active_cursor().rect());
|
||||||
|
|
||||||
m_last_cursor_rect = cursor_rect;
|
m_last_cursor_rect = cursor_rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Compositor::restore_cursor_back()
|
||||||
|
{
|
||||||
|
if (!m_cursor_back_bitmap)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_back_painter->blit(m_last_cursor_rect.location().constrained(Screen::the().rect()), *m_cursor_back_bitmap, { { 0, 0 }, m_last_cursor_rect.intersected(Screen::the().rect()).size() });
|
||||||
|
}
|
||||||
|
|
||||||
void Compositor::notify_display_links()
|
void Compositor::notify_display_links()
|
||||||
{
|
{
|
||||||
ClientConnection::for_each_client([](auto& client) {
|
ClientConnection::for_each_client([](auto& client) {
|
||||||
|
@ -514,28 +779,6 @@ void Compositor::decrement_display_link_count(Badge<ClientConnection>)
|
||||||
m_display_link_notify_timer->stop();
|
m_display_link_notify_timer->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compositor::any_opaque_window_contains_rect(const Gfx::IntRect& rect)
|
|
||||||
{
|
|
||||||
bool found_containing_window = false;
|
|
||||||
WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& window) {
|
|
||||||
if (window.is_minimized())
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
if (window.opacity() < 1.0f)
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
if (window.has_alpha_channel()) {
|
|
||||||
// FIXME: Just because the window has an alpha channel doesn't mean it's not opaque.
|
|
||||||
// Maybe there's some way we could know this?
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
}
|
|
||||||
if (window.frame().rect().contains(rect)) {
|
|
||||||
found_containing_window = true;
|
|
||||||
return IterationDecision::Break;
|
|
||||||
}
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
|
||||||
return found_containing_window;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_window, const Gfx::IntRect& rect)
|
bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_window, const Gfx::IntRect& rect)
|
||||||
{
|
{
|
||||||
bool found_containing_window = false;
|
bool found_containing_window = false;
|
||||||
|
@ -551,9 +794,7 @@ bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
if (window.is_minimized())
|
if (window.is_minimized())
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
if (window.opacity() < 1.0f)
|
if (!window.is_opaque())
|
||||||
return IterationDecision::Continue;
|
|
||||||
if (window.has_alpha_channel())
|
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
if (window.frame().rect().contains(rect)) {
|
if (window.frame().rect().contains(rect)) {
|
||||||
found_containing_window = true;
|
found_containing_window = true;
|
||||||
|
@ -578,6 +819,169 @@ void Compositor::recompute_occlusions()
|
||||||
}
|
}
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#ifdef OCCLUSIONS_DEBUG
|
||||||
|
dbg() << "OCCLUSIONS:";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto screen_rect = Screen::the().rect();
|
||||||
|
|
||||||
|
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
|
||||||
|
WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) {
|
||||||
|
auto& visible_opaque = w.opaque_rects();
|
||||||
|
auto& transparency_rects = w.transparency_rects();
|
||||||
|
auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects();
|
||||||
|
if (&w == fullscreen_window) {
|
||||||
|
if (w.is_opaque()) {
|
||||||
|
visible_opaque = screen_rect;
|
||||||
|
transparency_rects.clear();
|
||||||
|
transparency_wallpaper_rects.clear();
|
||||||
|
} else {
|
||||||
|
visible_opaque.clear();
|
||||||
|
transparency_rects = screen_rect;
|
||||||
|
transparency_wallpaper_rects = screen_rect;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visible_opaque.clear();
|
||||||
|
transparency_rects.clear();
|
||||||
|
transparency_wallpaper_rects.clear();
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_opaque_wallpaper_rects.clear();
|
||||||
|
} else {
|
||||||
|
Gfx::DisjointRectSet visible_rects(screen_rect);
|
||||||
|
bool have_transparent = false;
|
||||||
|
WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) {
|
||||||
|
auto window_frame_rect = w.frame().rect().intersected(screen_rect);
|
||||||
|
w.transparency_wallpaper_rects().clear();
|
||||||
|
auto& visible_opaque = w.opaque_rects();
|
||||||
|
auto& transparency_rects = w.transparency_rects();
|
||||||
|
if (w.is_minimized() || window_frame_rect.is_empty()) {
|
||||||
|
visible_opaque.clear();
|
||||||
|
transparency_rects.clear();
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::DisjointRectSet opaque_covering;
|
||||||
|
if (w.is_opaque()) {
|
||||||
|
visible_opaque = visible_rects.intersected(window_frame_rect);
|
||||||
|
transparency_rects.clear();
|
||||||
|
} else {
|
||||||
|
visible_opaque.clear();
|
||||||
|
transparency_rects = visible_rects.intersected(window_frame_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found_this_window = false;
|
||||||
|
WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w2) {
|
||||||
|
if (!found_this_window) {
|
||||||
|
if (&w == &w2)
|
||||||
|
found_this_window = true;
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w2.is_minimized())
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
auto window_frame_rect2 = w2.frame().rect().intersected(screen_rect);
|
||||||
|
auto covering_rect = window_frame_rect2.intersected(window_frame_rect);
|
||||||
|
if (covering_rect.is_empty())
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
|
||||||
|
if (w2.is_opaque()) {
|
||||||
|
opaque_covering.add(covering_rect);
|
||||||
|
if (opaque_covering.contains(window_frame_rect)) {
|
||||||
|
// This window is entirely covered by another opaque window
|
||||||
|
visible_opaque.clear();
|
||||||
|
transparency_rects.clear();
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visible_opaque.is_empty()) {
|
||||||
|
auto uncovered_opaque = visible_opaque.shatter(covering_rect);
|
||||||
|
visible_opaque = move(uncovered_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transparency_rects.is_empty()) {
|
||||||
|
auto uncovered_transparency = transparency_rects.shatter(covering_rect);
|
||||||
|
transparency_rects = move(uncovered_transparency);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visible_rects.for_each_intersected(covering_rect, [&](const Gfx::IntRect& intersected) {
|
||||||
|
transparency_rects.add(intersected);
|
||||||
|
if (!visible_opaque.is_empty()) {
|
||||||
|
auto uncovered_opaque = visible_opaque.shatter(intersected);
|
||||||
|
visible_opaque = move(uncovered_opaque);
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!transparency_rects.is_empty())
|
||||||
|
have_transparent = true;
|
||||||
|
|
||||||
|
ASSERT(!visible_opaque.intersects(transparency_rects));
|
||||||
|
|
||||||
|
if (w.is_opaque()) {
|
||||||
|
// Determine visible area for the window below
|
||||||
|
auto visible_rects_below_window = visible_rects.shatter(window_frame_rect);
|
||||||
|
visible_rects = move(visible_rects_below_window);
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (have_transparent) {
|
||||||
|
// Determine what transparent window areas need to render the wallpaper first
|
||||||
|
WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w) {
|
||||||
|
auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects();
|
||||||
|
if (w.is_opaque() || w.is_minimized()) {
|
||||||
|
transparency_wallpaper_rects.clear();
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
Gfx::DisjointRectSet& transparency_rects = w.transparency_rects();
|
||||||
|
if (transparency_rects.is_empty()) {
|
||||||
|
transparency_wallpaper_rects.clear();
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
transparency_wallpaper_rects = visible_rects.intersected(transparency_rects);
|
||||||
|
|
||||||
|
auto remaining_visible = visible_rects.shatter(transparency_wallpaper_rects);
|
||||||
|
visible_rects = move(remaining_visible);
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
m_opaque_wallpaper_rects = move(visible_rects);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef OCCLUSIONS_DEBUG
|
||||||
|
for (auto& r : m_opaque_wallpaper_rects.rects())
|
||||||
|
dbg() << " wallpaper opaque: " << r;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wm.for_each_visible_window_from_back_to_front([&](Window& w) {
|
||||||
|
auto window_frame_rect = w.frame().rect().intersected(screen_rect);
|
||||||
|
if (w.is_minimized() || window_frame_rect.is_empty())
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
|
||||||
|
#ifdef OCCLUSIONS_DEBUG
|
||||||
|
dbg() << " Window " << w.title() << " frame rect: " << window_frame_rect;
|
||||||
|
for (auto& r : w.opaque_rects().rects())
|
||||||
|
dbg() << " opaque: " << r;
|
||||||
|
for (auto& r : w.transparency_wallpaper_rects().rects())
|
||||||
|
dbg() << " transparent wallpaper: " << r;
|
||||||
|
for (auto& r : w.transparency_rects().rects())
|
||||||
|
dbg() << " transparent: " << r;
|
||||||
|
#endif
|
||||||
|
ASSERT(!w.opaque_rects().intersects(m_opaque_wallpaper_rects));
|
||||||
|
ASSERT(!w.transparency_rects().intersects(m_opaque_wallpaper_rects));
|
||||||
|
ASSERT(!w.transparency_wallpaper_rects().intersects(m_opaque_wallpaper_rects));
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,9 @@ public:
|
||||||
static Compositor& the();
|
static Compositor& the();
|
||||||
|
|
||||||
void compose();
|
void compose();
|
||||||
void invalidate();
|
void invalidate_window();
|
||||||
void invalidate(const Gfx::IntRect&);
|
void invalidate_screen();
|
||||||
|
void invalidate_screen(const Gfx::IntRect&);
|
||||||
|
|
||||||
bool set_resolution(int desired_width, int desired_height);
|
bool set_resolution(int desired_width, int desired_height);
|
||||||
|
|
||||||
|
@ -70,34 +71,45 @@ public:
|
||||||
void increment_display_link_count(Badge<ClientConnection>);
|
void increment_display_link_count(Badge<ClientConnection>);
|
||||||
void decrement_display_link_count(Badge<ClientConnection>);
|
void decrement_display_link_count(Badge<ClientConnection>);
|
||||||
|
|
||||||
void recompute_occlusions();
|
void invalidate_occlusions() { m_occlusions_dirty = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Compositor();
|
Compositor();
|
||||||
void init_bitmaps();
|
void init_bitmaps();
|
||||||
void flip_buffers();
|
void flip_buffers();
|
||||||
void flush(const Gfx::IntRect&);
|
void flush(const Gfx::IntRect&);
|
||||||
void draw_cursor();
|
|
||||||
void draw_geometry_label();
|
|
||||||
void draw_menubar();
|
void draw_menubar();
|
||||||
void run_animations();
|
void run_animations(Gfx::DisjointRectSet&);
|
||||||
void notify_display_links();
|
void notify_display_links();
|
||||||
bool any_opaque_window_contains_rect(const Gfx::IntRect&);
|
void start_compose_async_timer();
|
||||||
|
void recompute_occlusions();
|
||||||
bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::IntRect&);
|
bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::IntRect&);
|
||||||
|
void draw_cursor(const Gfx::IntRect&);
|
||||||
|
void restore_cursor_back();
|
||||||
|
bool draw_geometry_label(Gfx::IntRect&);
|
||||||
|
|
||||||
RefPtr<Core::Timer> m_compose_timer;
|
RefPtr<Core::Timer> m_compose_timer;
|
||||||
RefPtr<Core::Timer> m_immediate_compose_timer;
|
RefPtr<Core::Timer> m_immediate_compose_timer;
|
||||||
bool m_flash_flush { false };
|
bool m_flash_flush { false };
|
||||||
bool m_buffers_are_flipped { false };
|
bool m_buffers_are_flipped { false };
|
||||||
bool m_screen_can_set_buffer { false };
|
bool m_screen_can_set_buffer { false };
|
||||||
|
bool m_occlusions_dirty { true };
|
||||||
|
bool m_invalidated_any { true };
|
||||||
|
bool m_invalidated_window { false };
|
||||||
|
bool m_invalidated_cursor { false };
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> m_front_bitmap;
|
RefPtr<Gfx::Bitmap> m_front_bitmap;
|
||||||
RefPtr<Gfx::Bitmap> m_back_bitmap;
|
RefPtr<Gfx::Bitmap> m_back_bitmap;
|
||||||
|
RefPtr<Gfx::Bitmap> m_temp_bitmap;
|
||||||
OwnPtr<Gfx::Painter> m_back_painter;
|
OwnPtr<Gfx::Painter> m_back_painter;
|
||||||
OwnPtr<Gfx::Painter> m_front_painter;
|
OwnPtr<Gfx::Painter> m_front_painter;
|
||||||
|
OwnPtr<Gfx::Painter> m_temp_painter;
|
||||||
|
|
||||||
Gfx::DisjointRectSet m_dirty_rects;
|
Gfx::DisjointRectSet m_dirty_screen_rects;
|
||||||
|
Gfx::DisjointRectSet m_opaque_wallpaper_rects;
|
||||||
|
|
||||||
|
RefPtr<Gfx::Bitmap> m_cursor_back_bitmap;
|
||||||
|
OwnPtr<Gfx::Painter> m_cursor_back_painter;
|
||||||
Gfx::IntRect m_last_cursor_rect;
|
Gfx::IntRect m_last_cursor_rect;
|
||||||
Gfx::IntRect m_last_dnd_rect;
|
Gfx::IntRect m_last_dnd_rect;
|
||||||
Gfx::IntRect m_last_geometry_label_rect;
|
Gfx::IntRect m_last_geometry_label_rect;
|
||||||
|
|
|
@ -129,7 +129,7 @@ Window::~Window()
|
||||||
void Window::destroy()
|
void Window::destroy()
|
||||||
{
|
{
|
||||||
m_destroyed = true;
|
m_destroyed = true;
|
||||||
invalidate();
|
set_visible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::set_title(const String& title)
|
void Window::set_title(const String& title)
|
||||||
|
@ -137,21 +137,23 @@ void Window::set_title(const String& title)
|
||||||
if (m_title == title)
|
if (m_title == title)
|
||||||
return;
|
return;
|
||||||
m_title = title;
|
m_title = title;
|
||||||
|
frame().invalidate_title_bar();
|
||||||
WindowManager::the().notify_title_changed(*this);
|
WindowManager::the().notify_title_changed(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::set_rect(const Gfx::IntRect& rect)
|
void Window::set_rect(const Gfx::IntRect& rect)
|
||||||
{
|
{
|
||||||
ASSERT(!rect.is_empty());
|
ASSERT(!rect.is_empty());
|
||||||
Gfx::IntRect old_rect;
|
|
||||||
if (m_rect == rect)
|
if (m_rect == rect)
|
||||||
return;
|
return;
|
||||||
old_rect = m_rect;
|
auto old_rect = m_rect;
|
||||||
m_rect = rect;
|
m_rect = rect;
|
||||||
if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
|
if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
|
||||||
m_backing_store = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, m_rect.size());
|
m_backing_store = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, m_rect.size());
|
||||||
}
|
}
|
||||||
m_frame.notify_window_rect_changed(old_rect, rect);
|
|
||||||
|
invalidate(true);
|
||||||
|
m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::set_rect_without_repaint(const Gfx::IntRect& rect)
|
void Window::set_rect_without_repaint(const Gfx::IntRect& rect)
|
||||||
|
@ -170,7 +172,8 @@ void Window::set_rect_without_repaint(const Gfx::IntRect& rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_frame.notify_window_rect_changed(old_rect, rect);
|
invalidate(true);
|
||||||
|
m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::handle_mouse_event(const MouseEvent& event)
|
void Window::handle_mouse_event(const MouseEvent& event)
|
||||||
|
@ -222,11 +225,12 @@ void Window::set_minimized(bool minimized)
|
||||||
return;
|
return;
|
||||||
m_minimized = minimized;
|
m_minimized = minimized;
|
||||||
update_menu_item_text(PopupMenuItem::Minimize);
|
update_menu_item_text(PopupMenuItem::Minimize);
|
||||||
|
Compositor::the().invalidate_occlusions();
|
||||||
|
Compositor::the().invalidate_screen(frame().rect());
|
||||||
if (!is_blocked_by_modal_window())
|
if (!is_blocked_by_modal_window())
|
||||||
start_minimize_animation();
|
start_minimize_animation();
|
||||||
if (!minimized)
|
if (!minimized)
|
||||||
request_update({ {}, size() });
|
request_update({ {}, size() });
|
||||||
invalidate();
|
|
||||||
WindowManager::the().notify_minimization_state_changed(*this);
|
WindowManager::the().notify_minimization_state_changed(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +247,11 @@ void Window::set_opacity(float opacity)
|
||||||
{
|
{
|
||||||
if (m_opacity == opacity)
|
if (m_opacity == opacity)
|
||||||
return;
|
return;
|
||||||
|
bool was_opaque = is_opaque();
|
||||||
m_opacity = opacity;
|
m_opacity = opacity;
|
||||||
|
if (was_opaque != is_opaque())
|
||||||
|
Compositor::the().invalidate_occlusions();
|
||||||
|
Compositor::the().invalidate_screen(frame().rect());
|
||||||
WindowManager::the().notify_opacity_changed(*this);
|
WindowManager::the().notify_opacity_changed(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,12 +367,21 @@ void Window::set_visible(bool b)
|
||||||
if (m_visible == b)
|
if (m_visible == b)
|
||||||
return;
|
return;
|
||||||
m_visible = b;
|
m_visible = b;
|
||||||
invalidate();
|
|
||||||
|
Compositor::the().invalidate_occlusions();
|
||||||
|
if (m_visible)
|
||||||
|
invalidate(true);
|
||||||
|
else
|
||||||
|
Compositor::the().invalidate_screen(frame().rect());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::invalidate()
|
void Window::invalidate(bool invalidate_frame)
|
||||||
{
|
{
|
||||||
Compositor::the().invalidate(frame().rect());
|
m_invalidated = true;
|
||||||
|
m_invalidated_all = true;
|
||||||
|
m_invalidated_frame |= invalidate_frame;
|
||||||
|
m_dirty_rects.clear();
|
||||||
|
Compositor::the().invalidate_window();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::invalidate(const Gfx::IntRect& rect)
|
void Window::invalidate(const Gfx::IntRect& rect)
|
||||||
|
@ -374,16 +391,46 @@ void Window::invalidate(const Gfx::IntRect& rect)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rect.is_empty()) {
|
if (invalidate_no_notify(rect))
|
||||||
invalidate();
|
Compositor::the().invalidate_window();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
bool Window::invalidate_no_notify(const Gfx::IntRect& rect)
|
||||||
|
{
|
||||||
|
if (m_invalidated_all || rect.is_empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
auto outer_rect = frame().rect();
|
auto outer_rect = frame().rect();
|
||||||
auto inner_rect = rect;
|
auto inner_rect = rect;
|
||||||
inner_rect.move_by(position());
|
inner_rect.move_by(position());
|
||||||
// FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
|
// FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
|
||||||
inner_rect.intersect(outer_rect);
|
inner_rect.intersect(outer_rect);
|
||||||
Compositor::the().invalidate(inner_rect);
|
if (inner_rect.is_empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_invalidated = true;
|
||||||
|
m_dirty_rects.add(inner_rect.translated(-outer_rect.location()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::prepare_dirty_rects()
|
||||||
|
{
|
||||||
|
if (m_invalidated_all) {
|
||||||
|
if (m_invalidated_frame)
|
||||||
|
m_dirty_rects = frame().rect();
|
||||||
|
else
|
||||||
|
m_dirty_rects = rect();
|
||||||
|
} else {
|
||||||
|
m_dirty_rects.move_by(frame().rect().location());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::clear_dirty_rects()
|
||||||
|
{
|
||||||
|
m_invalidated_all = false;
|
||||||
|
m_invalidated_frame = false;
|
||||||
|
m_invalidated = false;
|
||||||
|
m_dirty_rects.clear_with_capacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Window::is_active() const
|
bool Window::is_active() const
|
||||||
|
@ -414,6 +461,8 @@ void Window::set_default_icon()
|
||||||
|
|
||||||
void Window::request_update(const Gfx::IntRect& rect, bool ignore_occlusion)
|
void Window::request_update(const Gfx::IntRect& rect, bool ignore_occlusion)
|
||||||
{
|
{
|
||||||
|
if (rect.is_empty())
|
||||||
|
return;
|
||||||
if (m_pending_paint_rects.is_empty()) {
|
if (m_pending_paint_rects.is_empty()) {
|
||||||
deferred_invoke([this, ignore_occlusion](auto&) {
|
deferred_invoke([this, ignore_occlusion](auto&) {
|
||||||
client()->post_paint_message(*this, ignore_occlusion);
|
client()->post_paint_message(*this, ignore_occlusion);
|
||||||
|
@ -511,6 +560,7 @@ void Window::set_fullscreen(bool fullscreen)
|
||||||
} else if (!m_saved_nonfullscreen_rect.is_empty()) {
|
} else if (!m_saved_nonfullscreen_rect.is_empty()) {
|
||||||
new_window_rect = m_saved_nonfullscreen_rect;
|
new_window_rect = m_saved_nonfullscreen_rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect, new_window_rect));
|
Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect, new_window_rect));
|
||||||
set_rect(new_window_rect);
|
set_rect(new_window_rect);
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,8 +167,13 @@ public:
|
||||||
|
|
||||||
Gfx::IntSize size() const { return m_rect.size(); }
|
Gfx::IntSize size() const { return m_rect.size(); }
|
||||||
|
|
||||||
void invalidate();
|
void invalidate(bool with_frame = true);
|
||||||
void invalidate(const Gfx::IntRect&);
|
void invalidate(const Gfx::IntRect&);
|
||||||
|
bool invalidate_no_notify(const Gfx::IntRect& rect);
|
||||||
|
|
||||||
|
void prepare_dirty_rects();
|
||||||
|
void clear_dirty_rects();
|
||||||
|
Gfx::DisjointRectSet& dirty_rects() { return m_dirty_rects; }
|
||||||
|
|
||||||
virtual void event(Core::Event&) override;
|
virtual void event(Core::Event&) override;
|
||||||
|
|
||||||
|
@ -262,6 +267,21 @@ public:
|
||||||
bool default_positioned() const { return m_default_positioned; }
|
bool default_positioned() const { return m_default_positioned; }
|
||||||
void set_default_positioned(bool p) { m_default_positioned = p; }
|
void set_default_positioned(bool p) { m_default_positioned = p; }
|
||||||
|
|
||||||
|
bool is_invalidated() const { return m_invalidated; }
|
||||||
|
|
||||||
|
bool is_opaque() const
|
||||||
|
{
|
||||||
|
if (opacity() < 1.0f)
|
||||||
|
return false;
|
||||||
|
if (has_alpha_channel())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::DisjointRectSet& opaque_rects() { return m_opaque_rects; }
|
||||||
|
Gfx::DisjointRectSet& transparency_rects() { return m_transparency_rects; }
|
||||||
|
Gfx::DisjointRectSet& transparency_wallpaper_rects() { return m_transparency_wallpaper_rects; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handle_mouse_event(const MouseEvent&);
|
void handle_mouse_event(const MouseEvent&);
|
||||||
void update_menu_item_text(PopupMenuItem item);
|
void update_menu_item_text(PopupMenuItem item);
|
||||||
|
@ -281,6 +301,10 @@ private:
|
||||||
Gfx::IntRect m_rect;
|
Gfx::IntRect m_rect;
|
||||||
Gfx::IntRect m_saved_nonfullscreen_rect;
|
Gfx::IntRect m_saved_nonfullscreen_rect;
|
||||||
Gfx::IntRect m_taskbar_rect;
|
Gfx::IntRect m_taskbar_rect;
|
||||||
|
Gfx::DisjointRectSet m_dirty_rects;
|
||||||
|
Gfx::DisjointRectSet m_opaque_rects;
|
||||||
|
Gfx::DisjointRectSet m_transparency_rects;
|
||||||
|
Gfx::DisjointRectSet m_transparency_wallpaper_rects;
|
||||||
WindowType m_type { WindowType::Normal };
|
WindowType m_type { WindowType::Normal };
|
||||||
bool m_global_cursor_tracking_enabled { false };
|
bool m_global_cursor_tracking_enabled { false };
|
||||||
bool m_automatic_cursor_tracking_enabled { false };
|
bool m_automatic_cursor_tracking_enabled { false };
|
||||||
|
@ -297,6 +321,9 @@ private:
|
||||||
bool m_accessory { false };
|
bool m_accessory { false };
|
||||||
bool m_destroyed { false };
|
bool m_destroyed { false };
|
||||||
bool m_default_positioned { false };
|
bool m_default_positioned { false };
|
||||||
|
bool m_invalidated { true };
|
||||||
|
bool m_invalidated_all { true };
|
||||||
|
bool m_invalidated_frame { true };
|
||||||
WindowTileType m_tiled { WindowTileType::None };
|
WindowTileType m_tiled { WindowTileType::None };
|
||||||
Gfx::IntRect m_untiled_rect;
|
Gfx::IntRect m_untiled_rect;
|
||||||
bool m_occluded { false };
|
bool m_occluded { false };
|
||||||
|
|
|
@ -234,17 +234,31 @@ Gfx::IntRect WindowFrame::rect() const
|
||||||
|
|
||||||
void WindowFrame::invalidate_title_bar()
|
void WindowFrame::invalidate_title_bar()
|
||||||
{
|
{
|
||||||
Compositor::the().invalidate(title_bar_rect().translated(rect().location()));
|
invalidate(title_bar_rect());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFrame::invalidate(Gfx::IntRect relative_rect)
|
||||||
|
{
|
||||||
|
auto frame_rect = rect();
|
||||||
|
auto window_rect = m_window.rect();
|
||||||
|
relative_rect.move_by(frame_rect.x() - window_rect.x(), frame_rect.y() - window_rect.y());
|
||||||
|
m_window.invalidate(relative_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect)
|
void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect)
|
||||||
{
|
{
|
||||||
layout_buttons();
|
layout_buttons();
|
||||||
|
|
||||||
auto& wm = WindowManager::the();
|
auto old_frame_rect = frame_rect_for_window(m_window, old_rect);
|
||||||
wm.invalidate(frame_rect_for_window(m_window, old_rect));
|
auto& compositor = Compositor::the();
|
||||||
wm.invalidate(frame_rect_for_window(m_window, new_rect));
|
for (auto& dirty : old_frame_rect.shatter(rect()))
|
||||||
wm.notify_rect_changed(m_window, old_rect, new_rect);
|
compositor.invalidate_screen(dirty);
|
||||||
|
if (!m_window.is_opaque())
|
||||||
|
compositor.invalidate_screen(rect());
|
||||||
|
|
||||||
|
compositor.invalidate_occlusions();
|
||||||
|
|
||||||
|
WindowManager::the().notify_rect_changed(m_window, old_rect, new_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowFrame::layout_buttons()
|
void WindowFrame::layout_buttons()
|
||||||
|
|
|
@ -47,6 +47,7 @@ public:
|
||||||
void on_mouse_event(const MouseEvent&);
|
void on_mouse_event(const MouseEvent&);
|
||||||
void notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect);
|
void notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect);
|
||||||
void invalidate_title_bar();
|
void invalidate_title_bar();
|
||||||
|
void invalidate(Gfx::IntRect relative_rect);
|
||||||
|
|
||||||
Gfx::IntRect title_bar_rect() const;
|
Gfx::IntRect title_bar_rect() const;
|
||||||
Gfx::IntRect title_bar_icon_rect() const;
|
Gfx::IntRect title_bar_icon_rect() const;
|
||||||
|
|
|
@ -74,7 +74,8 @@ WindowManager::WindowManager(const Gfx::PaletteImpl& palette)
|
||||||
|
|
||||||
reload_config(false);
|
reload_config(false);
|
||||||
|
|
||||||
invalidate();
|
Compositor::the().invalidate_screen();
|
||||||
|
Compositor::the().invalidate_occlusions();
|
||||||
Compositor::the().compose();
|
Compositor::the().compose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +189,7 @@ void WindowManager::add_window(Window& window)
|
||||||
if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher)
|
if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher)
|
||||||
m_switcher.refresh();
|
m_switcher.refresh();
|
||||||
|
|
||||||
Compositor::the().recompute_occlusions();
|
Compositor::the().invalidate_occlusions();
|
||||||
|
|
||||||
if (window.listens_to_wm_events()) {
|
if (window.listens_to_wm_events()) {
|
||||||
for_each_window([&](Window& other_window) {
|
for_each_window([&](Window& other_window) {
|
||||||
|
@ -228,6 +229,8 @@ void WindowManager::move_to_front_and_make_active(Window& window)
|
||||||
for_each_window_in_modal_stack(window, [&](auto& w, bool is_stack_top) {
|
for_each_window_in_modal_stack(window, [&](auto& w, bool is_stack_top) {
|
||||||
move_window_to_front(w, is_stack_top, is_stack_top);
|
move_window_to_front(w, is_stack_top, is_stack_top);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Compositor::the().invalidate_occlusions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input)
|
void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input)
|
||||||
|
@ -237,8 +240,6 @@ void WindowManager::do_move_to_front(Window& window, bool make_active, bool make
|
||||||
m_windows_in_order.remove(&window);
|
m_windows_in_order.remove(&window);
|
||||||
m_windows_in_order.append(&window);
|
m_windows_in_order.append(&window);
|
||||||
|
|
||||||
Compositor::the().recompute_occlusions();
|
|
||||||
|
|
||||||
if (make_active)
|
if (make_active)
|
||||||
set_active_window(&window, make_input);
|
set_active_window(&window, make_input);
|
||||||
|
|
||||||
|
@ -258,16 +259,18 @@ void WindowManager::do_move_to_front(Window& window, bool make_active, bool make
|
||||||
|
|
||||||
void WindowManager::remove_window(Window& window)
|
void WindowManager::remove_window(Window& window)
|
||||||
{
|
{
|
||||||
window.invalidate();
|
|
||||||
m_windows_in_order.remove(&window);
|
m_windows_in_order.remove(&window);
|
||||||
auto* active = active_window();
|
auto* active = active_window();
|
||||||
auto* active_input = active_input_window();
|
auto* active_input = active_input_window();
|
||||||
if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input)))
|
if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input)))
|
||||||
pick_new_active_window(&window);
|
pick_new_active_window(&window);
|
||||||
|
|
||||||
|
Compositor::the().invalidate_screen(window.frame().rect());
|
||||||
|
|
||||||
if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher)
|
if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher)
|
||||||
m_switcher.refresh();
|
m_switcher.refresh();
|
||||||
|
|
||||||
Compositor::the().recompute_occlusions();
|
Compositor::the().invalidate_occlusions();
|
||||||
|
|
||||||
for_each_window_listening_to_wm_events([&window](Window& listener) {
|
for_each_window_listening_to_wm_events([&window](Window& listener) {
|
||||||
if (!(listener.wm_event_mask() & WMEventMask::WindowRemovals))
|
if (!(listener.wm_event_mask() & WMEventMask::WindowRemovals))
|
||||||
|
@ -345,7 +348,6 @@ void WindowManager::notify_title_changed(Window& window)
|
||||||
#ifdef WINDOWMANAGER_DEBUG
|
#ifdef WINDOWMANAGER_DEBUG
|
||||||
dbg() << "[WM] Window{" << &window << "} title set to \"" << window.title() << '"';
|
dbg() << "[WM] Window{" << &window << "} title set to \"" << window.title() << '"';
|
||||||
#endif
|
#endif
|
||||||
invalidate(window.frame().rect());
|
|
||||||
if (m_switcher.is_visible())
|
if (m_switcher.is_visible())
|
||||||
m_switcher.refresh();
|
m_switcher.refresh();
|
||||||
|
|
||||||
|
@ -375,8 +377,6 @@ void WindowManager::notify_rect_changed(Window& window, const Gfx::IntRect& old_
|
||||||
if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher)
|
if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher)
|
||||||
m_switcher.refresh();
|
m_switcher.refresh();
|
||||||
|
|
||||||
Compositor::the().recompute_occlusions();
|
|
||||||
|
|
||||||
tell_wm_listeners_window_rect_changed(window);
|
tell_wm_listeners_window_rect_changed(window);
|
||||||
|
|
||||||
if (window.type() == WindowType::MenuApplet)
|
if (window.type() == WindowType::MenuApplet)
|
||||||
|
@ -387,7 +387,7 @@ void WindowManager::notify_rect_changed(Window& window, const Gfx::IntRect& old_
|
||||||
|
|
||||||
void WindowManager::notify_opacity_changed(Window&)
|
void WindowManager::notify_opacity_changed(Window&)
|
||||||
{
|
{
|
||||||
Compositor::the().recompute_occlusions();
|
Compositor::the().invalidate_occlusions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::notify_minimization_state_changed(Window& window)
|
void WindowManager::notify_minimization_state_changed(Window& window)
|
||||||
|
@ -965,7 +965,6 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
|
||||||
if (new_opacity > 1.0f)
|
if (new_opacity > 1.0f)
|
||||||
new_opacity = 1.0f;
|
new_opacity = 1.0f;
|
||||||
window.set_opacity(new_opacity);
|
window.set_opacity(new_opacity);
|
||||||
window.invalidate();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1255,16 +1254,6 @@ void WindowManager::set_hovered_window(Window* window)
|
||||||
Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowEntered));
|
Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowEntered));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::invalidate()
|
|
||||||
{
|
|
||||||
Compositor::the().invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowManager::invalidate(const Gfx::IntRect& rect)
|
|
||||||
{
|
|
||||||
Compositor::the().invalidate(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ClientConnection* WindowManager::active_client() const
|
const ClientConnection* WindowManager::active_client() const
|
||||||
{
|
{
|
||||||
if (m_active_window)
|
if (m_active_window)
|
||||||
|
@ -1410,7 +1399,7 @@ bool WindowManager::update_theme(String theme_path, String theme_name)
|
||||||
auto wm_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini");
|
auto wm_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini");
|
||||||
wm_config->write_entry("Theme", "Name", theme_name);
|
wm_config->write_entry("Theme", "Name", theme_name);
|
||||||
wm_config->sync();
|
wm_config->sync();
|
||||||
invalidate();
|
Compositor::the().invalidate_screen();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,10 +138,6 @@ public:
|
||||||
const Cursor& drag_cursor() const { return *m_drag_cursor; }
|
const Cursor& drag_cursor() const { return *m_drag_cursor; }
|
||||||
const Cursor& wait_cursor() const { return *m_wait_cursor; }
|
const Cursor& wait_cursor() const { return *m_wait_cursor; }
|
||||||
|
|
||||||
void invalidate(const Gfx::IntRect&);
|
|
||||||
void invalidate();
|
|
||||||
void flush(const Gfx::IntRect&);
|
|
||||||
|
|
||||||
const Gfx::Font& font() const;
|
const Gfx::Font& font() const;
|
||||||
const Gfx::Font& window_title_font() const;
|
const Gfx::Font& window_title_font() const;
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ void WindowSwitcher::set_visible(bool visible)
|
||||||
if (m_visible == visible)
|
if (m_visible == visible)
|
||||||
return;
|
return;
|
||||||
m_visible = visible;
|
m_visible = visible;
|
||||||
Compositor::the().recompute_occlusions();
|
Compositor::the().invalidate_occlusions();
|
||||||
if (m_switcher_window)
|
if (m_switcher_window)
|
||||||
m_switcher_window->set_visible(visible);
|
m_switcher_window->set_visible(visible);
|
||||||
if (!m_visible)
|
if (!m_visible)
|
||||||
|
@ -162,7 +162,7 @@ void WindowSwitcher::select_window_at_index(int index)
|
||||||
void WindowSwitcher::redraw()
|
void WindowSwitcher::redraw()
|
||||||
{
|
{
|
||||||
draw();
|
draw();
|
||||||
Compositor::the().invalidate(m_rect);
|
Compositor::the().invalidate_screen(m_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx::IntRect WindowSwitcher::item_rect(int index) const
|
Gfx::IntRect WindowSwitcher::item_rect(int index) const
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue