WindowServer: Load multiple scaled versions of Bitmaps and Cursors

This enables rendering of mixed-scale screen layouts with e.g. high
resolution cursors and window button icons on high-dpi screens while
using lower resolution bitmaps on regular screens.
This commit is contained in:
Tom 2021-06-18 19:21:30 -06:00 committed by Andreas Kling
parent aa15bf81e4
commit 61af9d882e
Notes: sideshowbarker 2024-07-18 11:58:52 +09:00
15 changed files with 269 additions and 99 deletions

View File

@ -9,6 +9,7 @@
#include <LibGfx/StylePainter.h>
#include <WindowServer/Button.h>
#include <WindowServer/Event.h>
#include <WindowServer/Screen.h>
#include <WindowServer/WindowManager.h>
namespace WindowServer {
@ -23,7 +24,7 @@ Button::~Button()
{
}
void Button::paint(Gfx::Painter& painter)
void Button::paint(Screen& screen, Gfx::Painter& painter)
{
auto palette = WindowManager::the().palette();
Gfx::PainterStateSaver saver(painter);
@ -31,10 +32,11 @@ void Button::paint(Gfx::Painter& painter)
Gfx::StylePainter::paint_button(painter, rect(), palette, Gfx::ButtonStyle::Normal, m_pressed, m_hovered);
if (m_icon) {
auto icon_location = rect().center().translated(-(m_icon->width() / 2), -(m_icon->height() / 2));
auto& bitmap = m_icon->bitmap(screen.scale_factor());
auto icon_location = rect().center().translated(-(bitmap.width() / 2), -(bitmap.height() / 2));
if (m_pressed)
painter.translate(1, 1);
painter.blit(icon_location, *m_icon, m_icon->rect());
painter.blit(icon_location, bitmap, bitmap.rect());
}
}

View File

@ -11,10 +11,12 @@
#include <LibGfx/Bitmap.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Rect.h>
#include <WindowServer/MultiScaleBitmaps.h>
namespace WindowServer {
class MouseEvent;
class Screen;
class WindowFrame;
class Button : public Weakable<Button> {
@ -28,7 +30,7 @@ public:
Gfx::IntRect rect() const { return { {}, m_relative_rect.size() }; }
Gfx::IntRect screen_rect() const;
void paint(Gfx::Painter&);
void paint(Screen&, Gfx::Painter&);
void on_mouse_event(const MouseEvent&);
@ -38,12 +40,12 @@ public:
bool is_visible() const { return m_visible; }
void set_icon(const Gfx::Bitmap& icon) { m_icon = icon; }
void set_icon(const RefPtr<MultiScaleBitmaps>& icon) { m_icon = icon; }
private:
WindowFrame& m_frame;
Gfx::IntRect m_relative_rect;
RefPtr<Gfx::Bitmap> m_icon;
RefPtr<MultiScaleBitmaps> m_icon;
bool m_pressed { false };
bool m_visible { true };
bool m_hovered { false };

View File

@ -21,6 +21,7 @@ set(SOURCES
Menu.cpp
MenuItem.cpp
MenuManager.cpp
MultiScaleBitmaps.cpp
Screen.cpp
ScreenLayout.cpp
Window.cpp

View File

@ -682,7 +682,7 @@ void ClientConnection::set_window_custom_cursor(i32 window_id, Gfx::ShareableBit
return;
}
window.set_cursor(Cursor::create(*cursor.bitmap()));
window.set_cursor(Cursor::create(*cursor.bitmap(), 1));
Compositor::the().invalidate_cursor();
}

View File

@ -919,7 +919,7 @@ void Compositor::ScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cur
auto& current_cursor = compositor.m_current_cursor ? *compositor.m_current_cursor : wm.active_cursor();
auto screen_rect = screen.rect();
m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(screen_rect).translated(-screen_rect.location()));
m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(compositor.m_current_cursor_frame));
m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(screen.scale_factor()), current_cursor.source_rect(compositor.m_current_cursor_frame));
m_last_cursor_rect = cursor_rect;
VERIFY(compositor.m_current_cursor_screen == &screen);
m_cursor_back_is_valid = true;

View File

@ -6,6 +6,7 @@
#include <AK/LexicalPath.h>
#include <WindowServer/Cursor.h>
#include <WindowServer/Screen.h>
#include <WindowServer/WindowManager.h>
namespace WindowServer {
@ -77,6 +78,7 @@ CursorParams CursorParams::parse_from_filename(const StringView& cursor_path, co
return { default_hotspot };
}
}
return params;
}
@ -93,37 +95,66 @@ CursorParams CursorParams::constrained(const Gfx::Bitmap& bitmap) const
}
}
if (params.m_have_hotspot)
params.m_hotspot = params.m_hotspot.constrained(rect);
params.m_hotspot.constrain(rect);
else
params.m_hotspot = rect.center();
return params;
}
Cursor::Cursor(NonnullRefPtr<Gfx::Bitmap>&& bitmap, const CursorParams& cursor_params)
: m_bitmap(move(bitmap))
, m_params(cursor_params.constrained(*m_bitmap))
, m_rect(m_bitmap->rect())
Cursor::Cursor(NonnullRefPtr<Gfx::Bitmap>&& bitmap, int scale_factor, const CursorParams& cursor_params)
: m_params(cursor_params.constrained(*bitmap))
, m_rect(bitmap->rect())
{
m_bitmaps.set(scale_factor, move(bitmap));
if (m_params.frames() > 1) {
VERIFY(m_rect.width() % m_params.frames() == 0);
m_rect.set_width(m_rect.width() / m_params.frames());
}
}
Cursor::~Cursor()
{
}
NonnullRefPtr<Cursor> Cursor::create(NonnullRefPtr<Gfx::Bitmap>&& bitmap)
NonnullRefPtr<Cursor> Cursor::create(NonnullRefPtr<Gfx::Bitmap>&& bitmap, int scale_factor)
{
auto hotspot = bitmap->rect().center();
return adopt_ref(*new Cursor(move(bitmap), CursorParams(hotspot)));
return adopt_ref(*new Cursor(move(bitmap), scale_factor, CursorParams(hotspot)));
}
NonnullRefPtr<Cursor> Cursor::create(NonnullRefPtr<Gfx::Bitmap>&& bitmap, const StringView& filename)
RefPtr<Cursor> Cursor::create(const StringView& filename, const StringView& default_filename)
{
auto default_hotspot = bitmap->rect().center();
return adopt_ref(*new Cursor(move(bitmap), CursorParams::parse_from_filename(filename, default_hotspot)));
auto cursor = adopt_ref(*new Cursor());
if (cursor->load(filename, default_filename))
return cursor;
return {};
}
bool Cursor::load(const StringView& filename, const StringView& default_filename)
{
bool did_load_any = false;
bool first = true;
auto load_bitmap = [&](const StringView& path, int scale_factor) {
auto bitmap = Gfx::Bitmap::load_from_file(path, scale_factor);
if (bitmap) {
did_load_any = true;
if (first) {
m_params = CursorParams::parse_from_filename(filename, bitmap->rect().center());
m_rect = bitmap->rect();
first = false;
}
m_bitmaps.set(scale_factor, bitmap.release_nonnull());
}
};
Screen::for_each_scale_factor_in_use([&](int scale_factor) {
load_bitmap(filename, scale_factor);
return IterationDecision::Continue;
});
if (!did_load_any) {
Screen::for_each_scale_factor_in_use([&](int scale_factor) {
load_bitmap(default_filename, scale_factor);
return IterationDecision::Continue;
});
}
return did_load_any;
}
RefPtr<Cursor> Cursor::create(Gfx::StandardCursor standard_cursor)

View File

@ -6,12 +6,15 @@
#pragma once
#include <AK/HashMap.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/StandardCursor.h>
namespace WindowServer {
class CursorParams {
friend class Cursor;
public:
static CursorParams parse_from_filename(const StringView&, const Gfx::IntPoint&);
CursorParams(const Gfx::IntPoint& hotspot)
@ -34,13 +37,27 @@ private:
class Cursor : public RefCounted<Cursor> {
public:
static NonnullRefPtr<Cursor> create(NonnullRefPtr<Gfx::Bitmap>&&, const StringView&);
static NonnullRefPtr<Cursor> create(NonnullRefPtr<Gfx::Bitmap>&&);
static RefPtr<Cursor> create(const StringView&, const StringView&);
static NonnullRefPtr<Cursor> create(NonnullRefPtr<Gfx::Bitmap>&&, int);
static RefPtr<Cursor> create(Gfx::StandardCursor);
~Cursor();
~Cursor() = default;
const CursorParams& params() const { return m_params; }
const Gfx::Bitmap& bitmap() const { return *m_bitmap; }
const Gfx::Bitmap& bitmap(int scale_factor) const
{
auto it = m_bitmaps.find(scale_factor);
if (it == m_bitmaps.end()) {
it = m_bitmaps.find(1);
if (it == m_bitmaps.end())
it = m_bitmaps.begin();
}
// We better found something
if (it == m_bitmaps.end()) {
dbgln("Could not find any bitmap in this Cursor");
VERIFY_NOT_REACHED();
}
return it->value;
}
Gfx::IntRect source_rect(unsigned frame) const
{
@ -51,9 +68,12 @@ public:
Gfx::IntSize size() const { return m_rect.size(); }
private:
Cursor(NonnullRefPtr<Gfx::Bitmap>&&, const CursorParams&);
Cursor() { }
Cursor(NonnullRefPtr<Gfx::Bitmap>&&, int, const CursorParams&);
RefPtr<Gfx::Bitmap> m_bitmap;
bool load(const StringView&, const StringView&);
HashMap<int, NonnullRefPtr<Gfx::Bitmap>> m_bitmaps;
CursorParams m_params;
Gfx::IntRect m_rect;
};

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "MultiScaleBitmaps.h"
#include "Screen.h"
namespace WindowServer {
const Gfx::Bitmap& MultiScaleBitmaps::bitmap(int scale_factor) const
{
auto it = m_bitmaps.find(scale_factor);
if (it == m_bitmaps.end()) {
it = m_bitmaps.find(1);
if (it == m_bitmaps.end())
it = m_bitmaps.begin();
}
// We better found *something*
if (it == m_bitmaps.end()) {
dbgln("Could not find any bitmap in this MultiScaleBitmaps");
VERIFY_NOT_REACHED();
}
return it->value;
}
RefPtr<MultiScaleBitmaps> MultiScaleBitmaps::create(StringView const& filename, StringView const& default_filename)
{
auto per_scale_bitmap = adopt_ref(*new MultiScaleBitmaps());
if (per_scale_bitmap->load(filename, default_filename))
return per_scale_bitmap;
return {};
}
bool MultiScaleBitmaps::load(StringView const& filename, StringView const& default_filename)
{
Optional<Gfx::BitmapFormat> bitmap_format;
bool did_load_any = false;
auto add_bitmap = [&](StringView const& path, int scale_factor) {
auto bitmap = Gfx::Bitmap::load_from_file(path, scale_factor);
if (bitmap) {
auto bitmap_format = bitmap->format();
if (m_format == Gfx::BitmapFormat::Invalid || m_format == bitmap_format) {
if (m_format == Gfx::BitmapFormat::Invalid)
m_format = bitmap_format;
did_load_any = true;
m_bitmaps.set(scale_factor, bitmap.release_nonnull());
} else {
dbgln("Bitmap {} (scale {}) has format inconsistent with the other per-scale bitmaps", path, bitmap->scale());
}
}
};
Screen::for_each_scale_factor_in_use([&](int scale_factor) {
add_bitmap(filename, scale_factor);
return IterationDecision::Continue;
});
if (!did_load_any && !default_filename.is_null() && !default_filename.is_empty()) {
Screen::for_each_scale_factor_in_use([&](int scale_factor) {
add_bitmap(default_filename, scale_factor);
return IterationDecision::Continue;
});
}
return did_load_any;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <LibGfx/Bitmap.h>
namespace WindowServer {
class MultiScaleBitmaps : public RefCounted<MultiScaleBitmaps> {
public:
static RefPtr<MultiScaleBitmaps> create(StringView const& filename, StringView const& default_filename = {});
Gfx::Bitmap const& default_bitmap() const { return bitmap(1); }
Gfx::Bitmap const& bitmap(int scale_factor) const;
Gfx::BitmapFormat format() const { return m_format; }
private:
MultiScaleBitmaps() = default;
bool load(StringView const& filename, StringView const& default_filename);
HashMap<int, NonnullRefPtr<Gfx::Bitmap>> m_bitmaps;
Gfx::BitmapFormat m_format { Gfx::BitmapFormat::Invalid };
};
}

View File

@ -23,6 +23,7 @@ NonnullOwnPtrVector<Screen, default_screen_count> Screen::s_screens;
Screen* Screen::s_main_screen { nullptr };
Gfx::IntRect Screen::s_bounding_screens_rect {};
ScreenLayout Screen::s_layout;
Vector<int, default_scale_factors_in_use_count> Screen::s_scale_factors_in_use;
ScreenInput& ScreenInput::the()
{
@ -83,9 +84,25 @@ bool Screen::apply_layout(ScreenLayout&& screen_layout, String& error_msg)
rollback.disarm();
update_bounding_rect();
update_scale_factors_in_use();
return true;
}
void Screen::update_scale_factors_in_use()
{
s_scale_factors_in_use.clear();
for_each([&](auto& screen) {
auto scale_factor = screen.scale_factor();
// The This doesn't have to be extremely efficient as this
// code is only run when we start up or the screen configuration
// changes. But using a vector allows for efficient iteration,
// which is the most common use case.
if (!s_scale_factors_in_use.contains_slow(scale_factor))
s_scale_factors_in_use.append(scale_factor);
return IterationDecision::Continue;
});
}
Screen::Screen(ScreenLayout::Screen& screen_info)
: m_virtual_rect(screen_info.location, { screen_info.resolution.width() / screen_info.scale_factor, screen_info.resolution.height() / screen_info.scale_factor })
, m_info(screen_info)

View File

@ -9,6 +9,7 @@
#include "ScreenLayout.h"
#include <AK/NonnullOwnPtrVector.h>
#include <Kernel/API/KeyCode.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Color.h>
#include <LibGfx/Rect.h>
#include <LibGfx/Size.h>
@ -23,6 +24,8 @@ constexpr unsigned scroll_step_size_min = 1;
// Most people will probably have 4 screens or less
constexpr size_t default_screen_count = 4;
// We currently only support 2 scale factors: 1x and 2x
constexpr size_t default_scale_factors_in_use_count = 2;
class Screen;
@ -125,6 +128,17 @@ public:
return IterationDecision::Continue;
}
template<typename F>
static IterationDecision for_each_scale_factor_in_use(F f)
{
for (auto& scale_factor : s_scale_factors_in_use) {
IterationDecision decision = f(scale_factor);
if (decision != IterationDecision::Continue)
return decision;
}
return IterationDecision::Continue;
}
void make_main_screen() { s_main_screen = this; }
bool is_main_screen() const { return s_main_screen == this; }
@ -158,6 +172,7 @@ private:
s_screens[i].m_index = i;
}
static void update_bounding_rect();
static void update_scale_factors_in_use();
bool is_opened() const { return m_framebuffer_fd >= 0; }
@ -165,6 +180,7 @@ private:
static Screen* s_main_screen;
static Gfx::IntRect s_bounding_screens_rect;
static ScreenLayout s_layout;
static Vector<int, default_scale_factors_in_use_count> s_scale_factors_in_use;
size_t m_index { 0 };
size_t m_size_in_bytes;

View File

@ -13,6 +13,7 @@
#include <WindowServer/Button.h>
#include <WindowServer/Compositor.h>
#include <WindowServer/Event.h>
#include <WindowServer/MultiScaleBitmaps.h>
#include <WindowServer/Screen.h>
#include <WindowServer/Window.h>
#include <WindowServer/WindowFrame.h>
@ -34,20 +35,19 @@ static Gfx::WindowTheme::WindowType to_theme_window_type(WindowType type)
}
}
static RefPtr<Gfx::Bitmap> s_minimize_icon;
static RefPtr<Gfx::Bitmap> s_maximize_icon;
static RefPtr<Gfx::Bitmap> s_restore_icon;
static RefPtr<Gfx::Bitmap> s_close_icon;
static RefPtr<Gfx::Bitmap> s_close_modified_icon;
static RefPtr<MultiScaleBitmaps> s_minimize_icon;
static RefPtr<MultiScaleBitmaps> s_maximize_icon;
static RefPtr<MultiScaleBitmaps> s_restore_icon;
static RefPtr<MultiScaleBitmaps> s_close_icon;
static RefPtr<MultiScaleBitmaps> s_close_modified_icon;
static String s_last_title_button_icons_path;
static int s_last_title_button_icons_scale;
static RefPtr<Gfx::Bitmap> s_active_window_shadow;
static RefPtr<Gfx::Bitmap> s_inactive_window_shadow;
static RefPtr<Gfx::Bitmap> s_menu_shadow;
static RefPtr<Gfx::Bitmap> s_taskbar_shadow;
static RefPtr<Gfx::Bitmap> s_tooltip_shadow;
static RefPtr<MultiScaleBitmaps> s_active_window_shadow;
static RefPtr<MultiScaleBitmaps> s_inactive_window_shadow;
static RefPtr<MultiScaleBitmaps> s_menu_shadow;
static RefPtr<MultiScaleBitmaps> s_taskbar_shadow;
static RefPtr<MultiScaleBitmaps> s_tooltip_shadow;
static String s_last_active_window_shadow_path;
static String s_last_inactive_window_shadow_path;
static String s_last_menu_shadow_path;
@ -117,7 +117,7 @@ void WindowFrame::set_button_icons()
m_close_button->set_icon(m_window.is_modified() ? *s_close_modified_icon : *s_close_icon);
if (m_window.is_minimizable())
m_minimize_button->set_icon(*s_minimize_icon);
m_minimize_button->set_icon(s_minimize_icon);
if (m_window.is_resizable())
m_maximize_button->set_icon(m_window.is_maximized() ? *s_restore_icon : *s_maximize_icon);
}
@ -125,54 +125,47 @@ void WindowFrame::set_button_icons()
void WindowFrame::reload_config()
{
String icons_path = WindowManager::the().palette().title_button_icons_path();
int icons_scale = WindowManager::the().compositor_icon_scale(); // TODO: We'll need to load icons for all scales in use!
StringBuilder full_path;
if (!s_minimize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
if (!s_minimize_icon || s_last_title_button_icons_path != icons_path) {
full_path.append(icons_path);
full_path.append("window-minimize.png");
if (!(s_minimize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale)))
s_minimize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png", icons_scale);
s_minimize_icon = MultiScaleBitmaps::create(full_path.to_string(), "/res/icons/16x16/downward-triangle.png");
full_path.clear();
}
if (!s_maximize_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
if (!s_maximize_icon || s_last_title_button_icons_path != icons_path) {
full_path.append(icons_path);
full_path.append("window-maximize.png");
if (!(s_maximize_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale)))
s_maximize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png", icons_scale);
s_maximize_icon = MultiScaleBitmaps::create(full_path.to_string(), "/res/icons/16x16/upward-triangle.png");
full_path.clear();
}
if (!s_restore_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
if (!s_restore_icon || s_last_title_button_icons_path != icons_path) {
full_path.append(icons_path);
full_path.append("window-restore.png");
if (!(s_restore_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale)))
s_restore_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png", icons_scale);
s_restore_icon = MultiScaleBitmaps::create(full_path.to_string(), "/res/icons/16x16/window-restore.png");
full_path.clear();
}
if (!s_close_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
if (!s_close_icon || s_last_title_button_icons_path != icons_path) {
full_path.append(icons_path);
full_path.append("window-close.png");
if (!(s_close_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale)))
s_close_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png", icons_scale);
s_close_icon = MultiScaleBitmaps::create(full_path.to_string(), "/res/icons/16x16/window-close.png");
full_path.clear();
}
if (!s_close_modified_icon || s_last_title_button_icons_path != icons_path || s_last_title_button_icons_scale != icons_scale) {
if (!s_close_modified_icon || s_last_title_button_icons_path != icons_path) {
full_path.append(icons_path);
full_path.append("window-close-modified.png");
if (!(s_close_modified_icon = Gfx::Bitmap::load_from_file(full_path.to_string(), icons_scale)))
s_close_modified_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close-modified.png", icons_scale);
s_close_modified_icon = MultiScaleBitmaps::create(full_path.to_string(), "/res/icons/16x16/window-close-modified.png");
full_path.clear();
}
s_last_title_button_icons_path = icons_path;
s_last_title_button_icons_scale = icons_scale;
auto load_shadow = [](const String& path, String& last_path, RefPtr<Gfx::Bitmap>& shadow_bitmap) {
auto load_shadow = [](const String& path, String& last_path, RefPtr<MultiScaleBitmaps>& shadow_bitmap) {
if (path.is_empty()) {
last_path = String::empty();
shadow_bitmap = nullptr;
} else if (!shadow_bitmap || shadow_bitmap->scale() != s_last_title_button_icons_scale || last_path != path) {
shadow_bitmap = Gfx::Bitmap::load_from_file(path, s_last_title_button_icons_scale);
} else if (!shadow_bitmap || last_path != path) {
shadow_bitmap = MultiScaleBitmaps::create(path);
if (shadow_bitmap)
last_path = path;
else
@ -186,7 +179,7 @@ void WindowFrame::reload_config()
load_shadow(WindowManager::the().palette().tooltip_shadow_path(), s_last_tooltip_shadow_path, s_tooltip_shadow);
}
Gfx::Bitmap* WindowFrame::shadow_bitmap() const
MultiScaleBitmaps* WindowFrame::shadow_bitmap() const
{
if (m_window.is_frameless())
return nullptr;
@ -317,7 +310,7 @@ void WindowFrame::paint(Screen& screen, Gfx::Painter& painter, const Gfx::IntRec
cached->paint(*this, painter, rect);
}
void WindowFrame::RenderedCache::paint(WindowFrame& frame, Gfx::Painter& painter, const Gfx::IntRect& rect)
void WindowFrame::PerScaleRenderedCache::paint(WindowFrame& frame, Gfx::Painter& painter, const Gfx::IntRect& rect)
{
auto frame_rect = frame.unconstrained_render_rect();
auto window_rect = frame.window().rect();
@ -357,7 +350,7 @@ void WindowFrame::RenderedCache::paint(WindowFrame& frame, Gfx::Painter& painter
}
}
void WindowFrame::render(Screen&, Gfx::Painter& painter)
void WindowFrame::render(Screen& screen, Gfx::Painter& painter)
{
if (m_window.is_frameless())
return;
@ -371,9 +364,8 @@ void WindowFrame::render(Screen&, Gfx::Painter& painter)
else
return;
for (auto& button : m_buttons) {
button.paint(painter);
}
for (auto& button : m_buttons)
button.paint(screen, painter);
}
void WindowFrame::theme_changed()
@ -386,13 +378,13 @@ void WindowFrame::theme_changed()
m_has_alpha_channel = Gfx::WindowTheme::current().frame_uses_alpha(window_state_for_theme(), WindowManager::the().palette());
}
auto WindowFrame::render_to_cache(Screen& screen) -> RenderedCache*
auto WindowFrame::render_to_cache(Screen& screen) -> PerScaleRenderedCache*
{
auto scale = screen.scale_factor();
RenderedCache* rendered_cache;
PerScaleRenderedCache* rendered_cache;
auto cached_it = m_rendered_cache.find(scale);
if (cached_it == m_rendered_cache.end()) {
auto new_rendered_cache = make<RenderedCache>();
auto new_rendered_cache = make<PerScaleRenderedCache>();
rendered_cache = new_rendered_cache.ptr();
m_rendered_cache.set(scale, move(new_rendered_cache));
} else {
@ -402,7 +394,7 @@ auto WindowFrame::render_to_cache(Screen& screen) -> RenderedCache*
return rendered_cache;
}
void WindowFrame::RenderedCache::render(WindowFrame& frame, Screen& screen)
void WindowFrame::PerScaleRenderedCache::render(WindowFrame& frame, Screen& screen)
{
if (!m_dirty)
return;
@ -417,7 +409,7 @@ void WindowFrame::RenderedCache::render(WindowFrame& frame, Screen& screen)
Gfx::IntPoint shadow_offset;
if (shadow_bitmap) {
auto total_shadow_size = shadow_bitmap->height();
auto total_shadow_size = shadow_bitmap->bitmap(screen.scale_factor()).height();
frame_rect_including_shadow.inflate(total_shadow_size, total_shadow_size);
auto offset = total_shadow_size / 2;
shadow_offset = { offset, offset };
@ -481,7 +473,7 @@ void WindowFrame::RenderedCache::render(WindowFrame& frame, Screen& screen)
painter.clear_rect({ rect.location() - frame_rect_to_update.location(), rect.size() }, { 255, 255, 255, 0 });
if (m_shadow_dirty && shadow_bitmap)
frame.paint_simple_rect_shadow(painter, { { 0, 0 }, frame_rect_including_shadow.size() }, *shadow_bitmap);
frame.paint_simple_rect_shadow(painter, { { 0, 0 }, frame_rect_including_shadow.size() }, shadow_bitmap->bitmap(screen.scale_factor()));
{
Gfx::PainterStateSaver save(painter);
@ -535,7 +527,7 @@ void WindowFrame::set_opacity(float opacity)
Gfx::IntRect WindowFrame::inflated_for_shadow(const Gfx::IntRect& frame_rect) const
{
if (auto* shadow = shadow_bitmap()) {
auto total_shadow_size = shadow->height();
auto total_shadow_size = shadow->default_bitmap().height();
return frame_rect.inflated(total_shadow_size, total_shadow_size);
}
return frame_rect;
@ -678,7 +670,7 @@ Optional<HitTestResult> WindowFrame::hit_test(Gfx::IntPoint const& position)
return cached->hit_test(*this, position, window_relative_position);
}
Optional<HitTestResult> WindowFrame::RenderedCache::hit_test(WindowFrame& frame, Gfx::IntPoint const& position, Gfx::IntPoint const& window_relative_position)
Optional<HitTestResult> WindowFrame::PerScaleRenderedCache::hit_test(WindowFrame& frame, Gfx::IntPoint const& position, Gfx::IntPoint const& window_relative_position)
{
HitTestResult result {
.window = frame.window(),

View File

@ -19,12 +19,13 @@ namespace WindowServer {
class Button;
class Menu;
class MouseEvent;
class Window;
class MultiScaleBitmaps;
class Screen;
class Window;
class WindowFrame {
public:
class RenderedCache {
class PerScaleRenderedCache {
friend class WindowFrame;
public:
@ -60,7 +61,7 @@ public:
void paint(Screen&, Gfx::Painter&, const Gfx::IntRect&);
void render(Screen&, Gfx::Painter&);
RenderedCache* render_to_cache(Screen&);
PerScaleRenderedCache* render_to_cache(Screen&);
void handle_mouse_event(MouseEvent const&);
void handle_titlebar_mouse_event(MouseEvent const&);
@ -123,7 +124,7 @@ private:
void paint_normal_frame(Gfx::Painter&);
void paint_tool_window_frame(Gfx::Painter&);
void paint_menubar(Gfx::Painter&);
Gfx::Bitmap* shadow_bitmap() const;
MultiScaleBitmaps* shadow_bitmap() const;
Gfx::IntRect inflated_for_shadow(const Gfx::IntRect&) const;
void handle_menubar_mouse_event(const MouseEvent&);
@ -140,7 +141,7 @@ private:
Button* m_maximize_button { nullptr };
Button* m_minimize_button { nullptr };
HashMap<int, NonnullOwnPtr<RenderedCache>> m_rendered_cache;
HashMap<int, NonnullOwnPtr<PerScaleRenderedCache>> m_rendered_cache;
RefPtr<Core::Timer> m_flash_timer;
size_t m_flash_counter { 0 };

View File

@ -48,14 +48,10 @@ WindowManager::~WindowManager()
{
}
NonnullRefPtr<Cursor> WindowManager::get_cursor(String const& name)
RefPtr<Cursor> WindowManager::get_cursor(String const& name)
{
static auto const s_default_cursor_path = "/res/cursors/arrow.x2y2.png";
auto path = m_config->read_entry("Cursor", name, s_default_cursor_path);
auto gb = Gfx::Bitmap::load_from_file(path, compositor_icon_scale());
if (gb)
return Cursor::create(*gb, path);
return Cursor::create(*Gfx::Bitmap::load_from_file(s_default_cursor_path), s_default_cursor_path);
return Cursor::create(m_config->read_entry("Cursor", name, s_default_cursor_path), s_default_cursor_path);
}
void WindowManager::reload_config()
@ -1179,7 +1175,7 @@ void WindowManager::process_key_event(KeyEvent& event)
}
if (event.type() == Event::KeyDown && (event.modifiers() == (Mod_Ctrl | Mod_Super | Mod_Shift) && event.key() == Key_I)) {
reload_icon_bitmaps_after_scale_change(!m_allow_hidpi_icons);
reload_icon_bitmaps_after_scale_change();
Compositor::the().invalidate_screen();
return;
}
@ -1595,16 +1591,8 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(Gfx::IntPoint const
return point;
}
int WindowManager::compositor_icon_scale() const
void WindowManager::reload_icon_bitmaps_after_scale_change()
{
if (!m_allow_hidpi_icons)
return 1;
return Screen::main().scale_factor(); // TODO: There is no *one* scale factor...
}
void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icons)
{
m_allow_hidpi_icons = allow_hidpi_icons;
reload_config();
m_window_stack.for_each_window([&](Window& window) {
auto& window_frame = window.frame();

View File

@ -224,8 +224,7 @@ public:
Gfx::IntPoint get_recommended_window_position(Gfx::IntPoint const& desired);
int compositor_icon_scale() const;
void reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icons = true);
void reload_icon_bitmaps_after_scale_change();
void reevaluate_hovered_window(Window* = nullptr);
Window* hovered_window() const { return m_hovered_window.ptr(); }
@ -233,7 +232,7 @@ public:
WindowStack& window_stack() { return m_window_stack; }
private:
NonnullRefPtr<Cursor> get_cursor(String const& name);
RefPtr<Cursor> get_cursor(String const& name);
void process_mouse_event(MouseEvent&);
void process_event_for_doubleclick(Window& window, MouseEvent& event);
@ -257,7 +256,6 @@ private:
void do_move_to_front(Window&, bool, bool);
bool m_allow_hidpi_icons { true };
RefPtr<Cursor> m_hidden_cursor;
RefPtr<Cursor> m_arrow_cursor;
RefPtr<Cursor> m_hand_cursor;