Skip to content

Commit

Permalink
[GTK] Fix various crashes with --gtk-version=4
Browse files Browse the repository at this point in the history
R=thestig

Fixed: 1471028
Change-Id: Ic60c0d3b7a09397eb88118bb5931b493b2ed5ec1
Low-Coverage-Reason: There are no GTK tests.
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4779944
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1185444}
  • Loading branch information
tanderson-google authored and Chromium LUCI CQ committed Aug 18, 2023
1 parent 001eb17 commit e195357
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 67 deletions.
1 change: 1 addition & 0 deletions ui/gtk/gdk.sigs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ GdkMonitor* gdk_display_get_monitor(GdkDisplay *display, int monitor_num);
void gdk_monitor_get_geometry(GdkMonitor *monitor, GdkRectangle *geometry);
int gdk_monitor_get_scale_factor(GdkMonitor *monitor);
GdkMonitor* gdk_display_get_primary_monitor(GdkDisplay *display);
GListModel* gdk_display_get_monitors(GdkDisplay *self);
1 change: 1 addition & 0 deletions ui/gtk/gsk.sigs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ GskRenderNode* gsk_gl_shader_node_get_child(GskRenderNode* node, guint idx);
guint gsk_container_node_get_n_children(GskRenderNode* node);
guint gsk_gl_shader_node_get_n_children(GskRenderNode* node);
GdkTexture* gsk_texture_node_get_texture(GskRenderNode* node);
float gsk_opacity_node_get_opacity(const GskRenderNode* node);
57 changes: 47 additions & 10 deletions ui/gtk/gtk_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,19 @@ bool GtkUi::Initialize() {

// Listen for scale factor changes.
GdkDisplay* display = gdk_display_get_default();
g_signal_connect_after(display, "monitor-added",
G_CALLBACK(OnMonitorAddedThunk), this);
const int n_monitors = gdk_display_get_n_monitors(display);
for (int i = 0; i < n_monitors; i++) {
TrackMonitor(gdk_display_get_monitor(display, i));
if (GtkCheckVersion(4)) {
GListModel* monitors = gdk_display_get_monitors(display);
g_signal_connect_after(monitors, "items-changed",
G_CALLBACK(OnMonitorsChangedThunk), this);
const guint n_monitors = g_list_model_get_n_items(monitors);
OnMonitorsChanged(monitors, 0, 0, n_monitors);
} else {
g_signal_connect_after(display, "monitor-added",
G_CALLBACK(OnMonitorAddedThunk), this);
const int n_monitors = gdk_display_get_n_monitors(display);
for (int i = 0; i < n_monitors; i++) {
TrackMonitor(gdk_display_get_monitor(display, i));
}
}

LoadGtkValues();
Expand Down Expand Up @@ -676,6 +684,16 @@ void GtkUi::OnMonitorAdded(GdkDisplay* display, GdkMonitor* monitor) {
UpdateDeviceScaleFactor();
}

void GtkUi::OnMonitorsChanged(GListModel* monitors,
guint position,
guint removed,
guint added) {
for (size_t i = position; i < position + added; i++) {
TrackMonitor(static_cast<GdkMonitor*>(g_list_model_get_item(monitors, i)));
}
UpdateDeviceScaleFactor();
}

void GtkUi::LoadGtkValues() {
// TODO(thomasanderson): GtkThemeService had a comment here about having to
// muck with the raw Prefs object to remove prefs::kCurrentThemeImages or else
Expand Down Expand Up @@ -905,16 +923,35 @@ ui::DisplayConfig GtkUi::GetDisplayConfig() const {
platform_->IncludeFontScaleInDeviceScale() ? FontScale() : 1.0;

GdkDisplay* display = gdk_display_get_default();
GdkMonitor* primary = gdk_display_get_primary_monitor(display);
GdkMonitor* primary = nullptr;
std::vector<GdkMonitor*> monitors;
if (GtkCheckVersion(4)) {
GListModel* list = gdk_display_get_monitors(display);
auto n_monitors = g_list_model_get_n_items(list);
if (!n_monitors) {
return config;
}
primary = static_cast<GdkMonitor*>(g_list_model_get_item(list, 0));
monitors.reserve(n_monitors);
for (unsigned int i = 0; i < n_monitors; ++i) {
monitors.push_back(
static_cast<GdkMonitor*>(g_list_model_get_item(list, i)));
}
} else {
primary = gdk_display_get_primary_monitor(display);
const int n_monitors = gdk_display_get_n_monitors(display);
monitors.reserve(n_monitors);
for (int i = 0; i < n_monitors; i++) {
monitors.push_back(gdk_display_get_monitor(display, i));
}
}
if (!primary) {
return config;
}
config.primary_scale =
std::max(1, gdk_monitor_get_scale_factor(primary)) * font_scale;
const int n_monitors = gdk_display_get_n_monitors(display);
config.display_geometries.reserve(n_monitors);
for (int i = 0; i < n_monitors; i++) {
GdkMonitor* monitor = gdk_display_get_monitor(display, i);
config.display_geometries.reserve(monitors.size());
for (GdkMonitor* monitor : monitors) {
GdkRectangle geometry;
gdk_monitor_get_geometry(monitor, &geometry);
int monitor_scale = std::max(1, gdk_monitor_get_scale_factor(monitor));
Expand Down
8 changes: 8 additions & 0 deletions ui/gtk/gtk_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ class GtkUi : public ui::LinuxUiAndTheme {

CHROMEG_CALLBACK_1(GtkUi, void, OnMonitorAdded, GdkDisplay*, GdkMonitor*);

CHROMEG_CALLBACK_3(GtkUi,
void,
OnMonitorsChanged,
GListModel*,
guint,
guint,
guint);

// Loads all GTK-provided settings.
void LoadGtkValues();

Expand Down
126 changes: 84 additions & 42 deletions ui/gtk/gtk_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gtk/gtk_compat.h"
#include "ui/gtk/gtk_types.h"
#include "ui/gtk/gtk_ui.h"
#include "ui/gtk/gtk_ui_platform.h"
#include "ui/linux/linux_ui.h"
Expand All @@ -37,6 +38,54 @@ namespace {

const char kAuraTransientParent[] = "aura-transient-parent";

GskRenderNode* GetRenderNodeChild(GskRenderNode* node) {
switch (gsk_render_node_get_node_type(node)) {
case GSK_TRANSFORM_NODE:
return gsk_transform_node_get_child(node);
case GSK_OPACITY_NODE:
return gsk_opacity_node_get_child(node);
case GSK_COLOR_MATRIX_NODE:
return gsk_color_matrix_node_get_child(node);
case GSK_REPEAT_NODE:
return gsk_repeat_node_get_child(node);
case GSK_CLIP_NODE:
return gsk_clip_node_get_child(node);
case GSK_ROUNDED_CLIP_NODE:
return gsk_rounded_clip_node_get_child(node);
case GSK_SHADOW_NODE:
return gsk_shadow_node_get_child(node);
case GSK_BLUR_NODE:
return gsk_blur_node_get_child(node);
case GSK_DEBUG_NODE:
return gsk_debug_node_get_child(node);
default:
return nullptr;
}
}

std::vector<GskRenderNode*> GetRenderNodeChildren(GskRenderNode* node) {
std::vector<GskRenderNode*> result;
size_t n_children = 0;
GskRenderNode* (*get_child)(GskRenderNode*, guint) = nullptr;
switch (gsk_render_node_get_node_type(node)) {
case GSK_CONTAINER_NODE:
n_children = gsk_container_node_get_n_children(node);
get_child = gsk_container_node_get_child;
break;
case GSK_GL_SHADER_NODE:
n_children = gsk_gl_shader_node_get_n_children(node);
get_child = gsk_gl_shader_node_get_child;
break;
default:
return result;
}
result.reserve(n_children);
for (size_t i = 0; i < n_children; i++) {
result.push_back(get_child(node, i));
}
return result;
}

GtkCssContext AppendCssNodeToStyleContextImpl(
GtkCssContext context,
const std::string& name,
Expand Down Expand Up @@ -113,6 +162,18 @@ GtkWidget* CreateDummyWindow() {
return window;
}

double GetOpacityFromRenderNode(GskRenderNode* node) {
DCHECK(GtkCheckVersion(4));
if (!node) {
return 1;
}

if (gsk_render_node_get_node_type(node) == GSK_OPACITY_NODE) {
return gsk_opacity_node_get_opacity(node);
}
return GetOpacityFromRenderNode(GetRenderNodeChild(node));
}

} // namespace

const char* GtkCssMenu() {
Expand Down Expand Up @@ -670,58 +731,39 @@ float GetDeviceScaleFactor() {

GdkTexture* GetTextureFromRenderNode(GskRenderNode* node) {
DCHECK(GtkCheckVersion(4));
struct {
GskRenderNodeType node_type;
GskRenderNode* (*get_child)(GskRenderNode*);
} constexpr simple_getters[] = {
{GSK_TRANSFORM_NODE, gsk_transform_node_get_child},
{GSK_OPACITY_NODE, gsk_opacity_node_get_child},
{GSK_COLOR_MATRIX_NODE, gsk_color_matrix_node_get_child},
{GSK_REPEAT_NODE, gsk_repeat_node_get_child},
{GSK_CLIP_NODE, gsk_clip_node_get_child},
{GSK_ROUNDED_CLIP_NODE, gsk_rounded_clip_node_get_child},
{GSK_SHADOW_NODE, gsk_shadow_node_get_child},
{GSK_BLUR_NODE, gsk_blur_node_get_child},
{GSK_DEBUG_NODE, gsk_debug_node_get_child},
};
struct {
GskRenderNodeType node_type;
guint (*get_n_children)(GskRenderNode*);
GskRenderNode* (*get_child)(GskRenderNode*, guint);
} constexpr container_getters[] = {
{GSK_CONTAINER_NODE, gsk_container_node_get_n_children,
gsk_container_node_get_child},
{GSK_GL_SHADER_NODE, gsk_gl_shader_node_get_n_children,
gsk_gl_shader_node_get_child},
};

if (!node) {
return nullptr;
}

auto node_type = gsk_render_node_get_node_type(node);
if (node_type == GSK_TEXTURE_NODE) {
if (gsk_render_node_get_node_type(node) == GSK_TEXTURE_NODE) {
return gsk_texture_node_get_texture(node);
}
for (const auto& getter : simple_getters) {
if (node_type == getter.node_type) {
if (auto* texture = GetTextureFromRenderNode(getter.get_child(node))) {
return texture;
}
}

if (auto* texture = GetTextureFromRenderNode(GetRenderNodeChild(node))) {
return texture;
}
for (const auto& getter : container_getters) {
if (node_type != getter.node_type) {
continue;
}
for (guint i = 0; i < getter.get_n_children(node); ++i) {
if (auto* texture = GetTextureFromRenderNode(getter.get_child(node, i))) {
return texture;
}
for (GskRenderNode* child : GetRenderNodeChildren(node)) {
if (auto* texture = GetTextureFromRenderNode(child)) {
return texture;
}
return nullptr;
}
return nullptr;
}

double GetOpacityFromContext(GtkStyleContext* context) {
double opacity = 1;
if (!GtkCheckVersion(4)) {
GtkStyleContextGet(context, "opacity", &opacity, nullptr);
return opacity;
}

auto* snapshot = gtk_snapshot_new();
gtk_snapshot_render_background(snapshot, context, 0, 0, 1, 1);
if (auto* node = gtk_snapshot_free_to_node(snapshot)) {
opacity = GetOpacityFromRenderNode(node);
gsk_render_node_unref(node);
}
return opacity;
}

} // namespace gtk
2 changes: 2 additions & 0 deletions ui/gtk/gtk_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ float GetDeviceScaleFactor();
// This should only be called on Gtk4.
GdkTexture* GetTextureFromRenderNode(GskRenderNode* node);

double GetOpacityFromContext(GtkStyleContext* context);

} // namespace gtk

#endif // UI_GTK_GTK_UTIL_H_
3 changes: 1 addition & 2 deletions ui/gtk/native_theme_gtk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ SkBitmap GetWidgetBitmap(const gfx::Size& size,
CairoSurface surface(bitmap);
cairo_t* cr = surface.cairo();

double opacity = 1;
GtkStyleContextGet(context, "opacity", &opacity, nullptr);
double opacity = GetOpacityFromContext(context);
if (opacity < 1)
cairo_push_group(cr);

Expand Down
33 changes: 20 additions & 13 deletions ui/gtk/window_frame_provider_gtk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,21 @@ std::string GetThemeName() {
GtkCssContext WindowContext(bool solid_frame, bool focused) {
std::string selector = "window.background.";
selector += solid_frame ? "solid-csd" : "csd";
if (!focused)
if (!focused) {
selector += ":inactive";
}
return AppendCssNodeToStyleContext({}, selector);
}

GtkCssContext DecorationContext(bool solid_frame, bool focused) {
auto context = WindowContext(solid_frame, focused);
// GTK4 renders the decoration directly on the window.
if (!GtkCheckVersion(4))
if (!GtkCheckVersion(4)) {
context = AppendCssNodeToStyleContext(context, "decoration");
if (!focused)
}
if (!focused) {
gtk_style_context_set_state(context, GTK_STATE_FLAG_BACKDROP);
}

// The web contents is rendered after the frame border, so remove bottom
// rounded corners otherwise their borders would get covered up.
Expand All @@ -69,8 +72,9 @@ GtkCssContext HeaderContext(bool solid_frame, bool focused) {
auto context = WindowContext(solid_frame, focused);
context =
AppendCssNodeToStyleContext(context, "headerbar.header-bar.titlebar");
if (!focused)
if (!focused) {
gtk_style_context_set_state(context, GTK_STATE_FLAG_BACKDROP);
}
return context;
}

Expand All @@ -87,10 +91,10 @@ SkBitmap PaintBitmap(const gfx::Size& bitmap_size,

auto bounds = render_bounds;

double opacity = 1;
GtkStyleContextGet(context, "opacity", &opacity, nullptr);
if (opacity < 1)
double opacity = GetOpacityFromContext(context);
if (opacity < 1) {
cairo_push_group(cr);
}

cairo_scale(cr, scale, scale);
gtk_render_background(context, cr, bounds.x(), bounds.y(), bounds.width(),
Expand Down Expand Up @@ -156,10 +160,10 @@ bool HeaderIsTranslucent() {
// The arbitrary square size to render a sample header.
constexpr int kHeaderSize = 32;
auto context = HeaderContext(false, false);
double opacity = 1.0f;
GtkStyleContextGet(context, "opacity", &opacity, nullptr);
if (opacity < 1.0f)
double opacity = GetOpacityFromContext(context);
if (opacity < 1.0) {
return true;
}
ApplyCssToContext(context, R"(window, headerbar {
box-shadow: none;
border: none;
Expand Down Expand Up @@ -202,8 +206,9 @@ WindowFrameProviderGtk::Asset::~Asset() = default;
void WindowFrameProviderGtk::Asset::CloneFrom(
const WindowFrameProviderGtk::Asset& src) {
valid = src.valid;
if (!valid)
if (!valid) {
return;
}

frame_size_px = src.frame_size_px;
frame_thickness_px = src.frame_thickness_px;
Expand Down Expand Up @@ -270,8 +275,9 @@ void WindowFrameProviderGtk::PaintWindowFrame(

auto draw_image = [&](int src_x, int src_y, int src_w, int src_h, int dst_x,
int dst_y, int dst_w, int dst_h) {
if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0)
if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
return;
}
canvas->DrawImageInt(image, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w,
dst_h, false);
};
Expand Down Expand Up @@ -344,8 +350,9 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
}

auto& asset = assets_[ToRoundedScale(scale)];
if (asset.valid)
if (asset.valid) {
return;
}

asset.frame_size_px = std::ceil(kMaxFrameSizeDip * scale);

Expand Down

0 comments on commit e195357

Please sign in to comment.