mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 11:17:44 +00:00
WindowServer: Add an Overlay class for flicker-free overlay rendering
An Overlay is similar to a transparent window, but has less overhead and does not get rendered within the window stack. Basically, the area that an Overlay occupies forces transparency rendering for any window underneath, which allows us to render them flicker-free. This also adds a new API that allows displaying the screen numbers, e.g. while the user configures the screen layout in DisplaySettings Because other things like drag&drop or the window-size label are not yet converted to use this new mechanism, they will be drawn over the screen-number currently.
This commit is contained in:
parent
42cb38b71a
commit
41859ad3fe
14 changed files with 638 additions and 7 deletions
208
Userland/Services/WindowServer/Overlays.cpp
Normal file
208
Userland/Services/WindowServer/Overlays.cpp
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Overlays.h"
|
||||
#include "Compositor.h"
|
||||
#include "WindowManager.h"
|
||||
|
||||
namespace WindowServer {
|
||||
|
||||
Overlay::~Overlay()
|
||||
{
|
||||
Compositor::the().remove_overlay(*this);
|
||||
}
|
||||
|
||||
bool Overlay::invalidate()
|
||||
{
|
||||
if (m_invalidated)
|
||||
return false;
|
||||
m_invalidated = true;
|
||||
// m_current_rect should only get updated by recompute_overlay_rects()
|
||||
if (!m_current_rect.is_empty())
|
||||
Compositor::the().invalidate_screen(m_current_rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Overlay::set_enabled(bool enable)
|
||||
{
|
||||
if (is_enabled() == enable)
|
||||
return;
|
||||
|
||||
if (enable)
|
||||
Compositor::the().add_overlay(*this);
|
||||
else
|
||||
Compositor::the().remove_overlay(*this);
|
||||
}
|
||||
|
||||
void Overlay::set_rect(Gfx::IntRect const& rect)
|
||||
{
|
||||
if (m_rect == rect)
|
||||
return;
|
||||
m_rect = rect;
|
||||
invalidate();
|
||||
if (is_enabled())
|
||||
Compositor::the().overlay_rects_changed();
|
||||
rect_changed();
|
||||
}
|
||||
|
||||
BitmapOverlay::BitmapOverlay()
|
||||
{
|
||||
clear_bitmaps();
|
||||
}
|
||||
|
||||
void BitmapOverlay::rect_changed()
|
||||
{
|
||||
clear_bitmaps();
|
||||
Overlay::rect_changed();
|
||||
}
|
||||
|
||||
void BitmapOverlay::clear_bitmaps()
|
||||
{
|
||||
m_bitmaps = MultiScaleBitmaps::create_empty();
|
||||
}
|
||||
|
||||
void BitmapOverlay::render(Gfx::Painter& painter, Screen const& screen)
|
||||
{
|
||||
auto scale_factor = screen.scale_factor();
|
||||
auto* bitmap = m_bitmaps->find_bitmap(scale_factor);
|
||||
if (!bitmap) {
|
||||
auto new_bitmap = create_bitmap(scale_factor);
|
||||
if (!new_bitmap)
|
||||
return;
|
||||
bitmap = new_bitmap.ptr();
|
||||
m_bitmaps->add_bitmap(scale_factor, new_bitmap.release_nonnull());
|
||||
}
|
||||
|
||||
painter.blit({}, *bitmap, bitmap->rect());
|
||||
}
|
||||
|
||||
RectangularOverlay::RectangularOverlay()
|
||||
{
|
||||
clear_bitmaps();
|
||||
}
|
||||
|
||||
void RectangularOverlay::rect_changed()
|
||||
{
|
||||
clear_bitmaps();
|
||||
}
|
||||
|
||||
void RectangularOverlay::clear_bitmaps()
|
||||
{
|
||||
m_rendered_bitmaps = MultiScaleBitmaps::create_empty();
|
||||
}
|
||||
|
||||
void RectangularOverlay::render(Gfx::Painter& painter, Screen const& screen)
|
||||
{
|
||||
auto scale_factor = screen.scale_factor();
|
||||
auto* bitmap = m_rendered_bitmaps->find_bitmap(scale_factor);
|
||||
if (!bitmap) {
|
||||
auto new_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect().size(), scale_factor);
|
||||
if (!new_bitmap)
|
||||
return;
|
||||
bitmap = new_bitmap.ptr();
|
||||
|
||||
Gfx::Painter bitmap_painter(*new_bitmap);
|
||||
if (auto* shadow_bitmap = WindowManager::the().overlay_rect_shadow()) {
|
||||
WindowFrame::paint_simple_rect_shadow(bitmap_painter, new_bitmap->rect(), shadow_bitmap->bitmap(scale_factor), true, true);
|
||||
} else {
|
||||
bitmap_painter.fill_rect(new_bitmap->rect(), Color(Color::Black).with_alpha(0xcc));
|
||||
}
|
||||
render_overlay_bitmap(bitmap_painter);
|
||||
m_rendered_bitmaps->add_bitmap(scale_factor, new_bitmap.release_nonnull());
|
||||
}
|
||||
|
||||
painter.blit({}, *bitmap, bitmap->rect());
|
||||
}
|
||||
|
||||
Gfx::IntRect RectangularOverlay::calculate_frame_rect(Gfx::IntRect const& rect)
|
||||
{
|
||||
if (auto* shadow_bitmap = WindowManager::the().overlay_rect_shadow()) {
|
||||
Gfx::IntSize size;
|
||||
int base_size = shadow_bitmap->default_bitmap().height() / 2;
|
||||
size = { base_size, base_size };
|
||||
return rect.inflated(2 * base_size, 2 * base_size);
|
||||
}
|
||||
return rect.inflated(2 * default_frame_thickness, 2 * default_frame_thickness);
|
||||
}
|
||||
|
||||
void RectangularOverlay::set_content_rect(Gfx::IntRect const& rect)
|
||||
{
|
||||
set_rect(calculate_frame_rect(rect));
|
||||
}
|
||||
|
||||
Gfx::Font const* ScreenNumberOverlay::s_font { nullptr };
|
||||
|
||||
ScreenNumberOverlay::ScreenNumberOverlay(Screen& screen)
|
||||
: m_screen(screen)
|
||||
{
|
||||
if (!s_font)
|
||||
pick_font();
|
||||
|
||||
Gfx::IntRect rect {
|
||||
default_offset,
|
||||
default_offset,
|
||||
default_size,
|
||||
default_size
|
||||
};
|
||||
rect.translate_by(screen.rect().location());
|
||||
set_rect(rect);
|
||||
}
|
||||
|
||||
void ScreenNumberOverlay::pick_font()
|
||||
{
|
||||
auto screen_number_content_rect_size = calculate_content_rect_for_screen(Screen::main()).size();
|
||||
auto& font_database = Gfx::FontDatabase::the();
|
||||
auto& default_font = WindowManager::the().font();
|
||||
String best_font_name;
|
||||
int best_font_size = -1;
|
||||
font_database.for_each_font([&](Gfx::Font const& font) {
|
||||
// TODO: instead of picking *any* font we should probably compare font.name()
|
||||
// with default_font.name(). But the default font currently does not provide larger sizes
|
||||
auto size = font.glyph_height();
|
||||
if (size * 2 <= screen_number_content_rect_size.height() && size > best_font_size) {
|
||||
best_font_name = font.qualified_name();
|
||||
best_font_size = size;
|
||||
}
|
||||
});
|
||||
|
||||
if (auto best_font = font_database.get_by_name(best_font_name)) {
|
||||
s_font = best_font.ptr();
|
||||
} else {
|
||||
s_font = &default_font;
|
||||
}
|
||||
|
||||
Compositor::the().for_each_overlay([&](auto& overlay) {
|
||||
if (overlay.zorder() == ZOrder::ScreenNumber)
|
||||
overlay.invalidate();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
Gfx::Font const& ScreenNumberOverlay::font()
|
||||
{
|
||||
if (!s_font) {
|
||||
pick_font();
|
||||
VERIFY(s_font);
|
||||
}
|
||||
return *s_font;
|
||||
}
|
||||
|
||||
void ScreenNumberOverlay::render_overlay_bitmap(Gfx::Painter& painter)
|
||||
{
|
||||
painter.draw_text({ {}, rect().size() }, String::formatted("{}", m_screen.index() + 1), font(), Gfx::TextAlignment::Center, Color::White);
|
||||
}
|
||||
|
||||
Gfx::IntRect ScreenNumberOverlay::calculate_content_rect_for_screen(Screen& screen)
|
||||
{
|
||||
Gfx::IntRect content_rect {
|
||||
screen.rect().location().translated(default_offset, default_offset),
|
||||
{ default_size, default_size }
|
||||
};
|
||||
|
||||
return calculate_frame_rect(content_rect);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue