Skip to content

Commit 5bce18c

Browse files
Merge branch 'main' into lenemter/guess-app
2 parents 2cdc021 + 628eaa9 commit 5bce18c

File tree

274 files changed

+14000
-9807
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

274 files changed

+14000
-9807
lines changed

daemon-gtk3/Window.vala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
*/
77

88
public class Gala.Daemon.Window : Gtk.Window {
9+
public int width { get; construct; }
10+
public int height { get; construct; }
911
public Gtk.Box content { get; construct; }
1012

1113
public Window (int width, int height) {
12-
Object (
13-
default_width: width,
14-
default_height: height
15-
);
14+
Object (width: width, height: height);
1615
}
1716

1817
class construct {
@@ -34,7 +33,9 @@ public class Gala.Daemon.Window : Gtk.Window {
3433
title = "MODAL";
3534
child = content = new Gtk.Box (HORIZONTAL, 0) {
3635
hexpand = true,
37-
vexpand = true
36+
vexpand = true,
37+
width_request = width,
38+
height_request = height
3839
};
3940

4041
set_visual (get_screen ().get_rgba_visual ());

daemon-gtk3/WindowMenu.vala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,12 @@ public class Gala.Daemon.WindowMenu : Gtk.Menu {
174174
SignalHandler.unblock (always_on_top, always_on_top_sid);
175175
SignalHandler.unblock (on_visible_workspace, on_visible_workspace_sid);
176176

177-
move_right.sensitive = !on_visible_workspace.active;
177+
move_right.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_RIGHT in flags;
178178
if (move_right.sensitive) {
179179
move_right_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-right")[0];
180180
}
181181

182-
move_left.sensitive = !on_visible_workspace.active;
182+
move_left.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_LEFT in flags;
183183
if (move_left.sensitive) {
184184
move_left_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-left")[0];
185185
}

daemon/DBus.vala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ public enum Gala.WindowFlags {
3636
ALWAYS_ON_TOP,
3737
ON_ALL_WORKSPACES,
3838
CAN_CLOSE,
39-
IS_TILED
39+
IS_TILED,
40+
ALLOWS_MOVE_LEFT,
41+
ALLOWS_MOVE_RIGHT
4042
}
4143

4244
[DBus (name = "org.pantheon.gala")]
@@ -173,8 +175,8 @@ public class Gala.Daemon.DBus : GLib.Object {
173175
y /= scale_factor;
174176
}
175177

176-
window.default_width = display_width;
177-
window.default_height = display_height;
178+
window.child.width_request = display_width;
179+
window.child.height_request = display_height;
178180
window.present ();
179181

180182
Gdk.Rectangle rect = {

daemon/WindowMenu.vala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,12 @@ public class Gala.Daemon.WindowMenu : Gtk.Popover {
200200
SignalHandler.unblock (always_on_top, always_on_top_sid);
201201
SignalHandler.unblock (on_visible_workspace, on_visible_workspace_sid);
202202

203-
move_right.sensitive = !on_visible_workspace.active;
203+
move_right.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_RIGHT in flags;
204204
if (move_right.sensitive) {
205205
move_right_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-right")[0];
206206
}
207207

208-
move_left.sensitive = !on_visible_workspace.active;
208+
move_left.sensitive = !on_visible_workspace.active && Gala.WindowFlags.ALLOWS_MOVE_LEFT in flags;
209209
if (move_left.sensitive) {
210210
move_left_accellabel.accel_string = keybind_settings.get_strv ("move-to-workspace-left")[0];
211211
}

data/gala.metainfo.xml.in

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,21 @@
3535
</ul>
3636
</description>
3737
<issues>
38+
<issue url="https://github.com/elementary/dock/issues/68">Cannot move window to display below dock.</issue>
39+
<issue url="https://github.com/elementary/dock/issues/425">Improper dock behaviour when it hides on vertically stacked monitors</issue>
40+
<issue url="https://github.com/elementary/gala/issues/76">PIP: Grabbing an area too close to a Gtk3 window only grabs it's shadow</issue>
41+
<issue url="https://github.com/elementary/gala/issues/132">Closing last window in the window overview doesn't close it</issue>
42+
<issue url="https://github.com/elementary/gala/issues/340">"Move to left workspace" is a no-op on left-most workspace</issue>
43+
<issue url="https://github.com/elementary/gala/issues/2060">Gala.ShadowEffect has performance issues</issue>
44+
<issue url="https://github.com/elementary/gala/issues/2424">Mouse pointer often stuck between displays when external display is the primary display and is arranged above built-in display</issue>
45+
<issue url="https://github.com/elementary/gala/issues/2230">Multitasking view background clipped when using multiple monitors above each other</issue>
46+
<issue url="https://github.com/elementary/gala/issues/2263">Prevent windows from breaking workflow</issue>
3847
<issue url="https://github.com/elementary/gala/issues/2400">Switching workspaces makes the windows move down with multiple monitors</issue>
48+
<issue url="https://github.com/elementary/gala/issues/2401">Context menus appear at the top left corner of the secondary monitor</issue>
49+
<issue url="https://github.com/elementary/gala/issues/2409">Windows sometimes get stuck in an un-interactive state after using gestures to switch workspaces</issue>
50+
<issue url="https://github.com/elementary/gala/issues/2410">Window position not restored correctly when going back from fullscreen</issue>
51+
<issue url="https://github.com/elementary/gala/issues/2414">During workspace switch, windows on secondary display aren't shown</issue>
52+
<issue url="https://github.com/elementary/gala/issues/2415">Wrong animation origin for windows on display 2 on workspace overview</issue>
3953
</issues>
4054
</release>
4155

lib/CloseButton.vala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ public class Gala.CloseButton : Clutter.Actor {
3333
var pixbuf = get_close_button_pixbuf (scale);
3434
if (pixbuf != null) {
3535
try {
36-
var image = new Clutter.Image ();
37-
Cogl.PixelFormat pixel_format = (pixbuf.get_has_alpha () ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888);
38-
image.set_data (pixbuf.get_pixels (), pixel_format, pixbuf.width, pixbuf.height, pixbuf.rowstride);
39-
36+
var image = new Gala.Image.from_pixbuf (pixbuf);
4037
pixbuf_actor.set_content (image);
4138
pixbuf_actor.set_size (pixbuf.width, pixbuf.height);
4239
set_size (pixbuf.width, pixbuf.height);

lib/DragDropAction.vala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,11 @@ namespace Gala {
313313
event.get_coords (out x, out y);
314314

315315
if (!dragging && clicked) {
316+
#if HAS_MUTTER47
317+
var drag_threshold = actor.context.get_settings ().dnd_drag_threshold;
318+
#else
316319
var drag_threshold = Clutter.Settings.get_default ().dnd_drag_threshold;
320+
#endif
317321
if (Math.fabsf (last_x - x) > drag_threshold || Math.fabsf (last_y - y) > drag_threshold) {
318322
handle = drag_begin (last_x, last_y);
319323
if (handle == null) {

lib/Image.vala

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2015 Corentin Noël
3+
* Copyright 2025 elementary, Inc. <https://elementary.io>
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
#if !HAS_MUTTER48
8+
public class Gala.Image : Clutter.Image {
9+
public Image.from_pixbuf (Gdk.Pixbuf pixbuf) {
10+
Object ();
11+
12+
Cogl.PixelFormat pixel_format = (pixbuf.get_has_alpha () ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888);
13+
try {
14+
set_data (pixbuf.get_pixels (), pixel_format, pixbuf.width, pixbuf.height, pixbuf.rowstride);
15+
} catch (Error e) {}
16+
}
17+
}
18+
#else
19+
public class Gala.Image : GLib.Object, Clutter.Content {
20+
Gdk.Pixbuf? pixbuf;
21+
Cogl.Texture? texture;
22+
uint width;
23+
uint height;
24+
25+
public Image.from_pixbuf (Gdk.Pixbuf pixbuf) {
26+
this.pixbuf = pixbuf;
27+
width = pixbuf.width;
28+
height = pixbuf.height;
29+
invalidate ();
30+
}
31+
32+
public bool get_preferred_size (out float width, out float height) {
33+
if (texture == null) {
34+
width = 0;
35+
height = 0;
36+
return false;
37+
}
38+
39+
width = texture.get_width ();
40+
height = texture.get_height ();
41+
return true;
42+
}
43+
44+
public void paint_content (Clutter.Actor actor, Clutter.PaintNode node, Clutter.PaintContext paint_context) {
45+
if (pixbuf != null && texture == null) {
46+
var cogl_context = actor.context.get_backend ().get_cogl_context ();
47+
Cogl.PixelFormat pixel_format = (pixbuf.get_has_alpha () ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888);
48+
try {
49+
texture = new Cogl.Texture2D.from_data (cogl_context, pixbuf.width, pixbuf.height, pixel_format, pixbuf.rowstride, pixbuf.get_pixels ());
50+
if (width != texture.get_width () || height != texture.get_height ()) {
51+
width = texture.get_width ();
52+
height = texture.get_height ();
53+
invalidate_size ();
54+
}
55+
} catch (Error e) {
56+
critical (e.message);
57+
}
58+
}
59+
60+
if (texture == null)
61+
return;
62+
63+
var content_node = actor.create_texture_paint_node (texture);
64+
content_node.set_static_name ("Image Content");
65+
node.add_child (content_node);
66+
}
67+
68+
public void invalidate () {
69+
texture = null;
70+
}
71+
72+
public void invalidate_size () {
73+
}
74+
}
75+
#endif

lib/ShadowEffect.vala

Lines changed: 98 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66

77
public class Gala.ShadowEffect : Clutter.Effect {
8+
private const float INITIAL_OPACITY = 0.25f;
9+
810
private class Shadow {
911
public int users;
1012
public Cogl.Texture texture;
@@ -38,13 +40,13 @@ public class Gala.ShadowEffect : Clutter.Effect {
3840
_css_class = value;
3941
switch (value) {
4042
case "workspace-switcher":
41-
shadow_size = 6;
43+
shadow_size = 3;
4244
break;
4345
case "window":
44-
shadow_size = 55;
46+
shadow_size = 26;
4547
break;
4648
default:
47-
shadow_size = 18;
49+
shadow_size = 9;
4850
break;
4951
}
5052
}
@@ -100,43 +102,12 @@ public class Gala.ShadowEffect : Clutter.Effect {
100102
return shadow.texture;
101103
}
102104

103-
// fill a new texture for this size
104-
var buffer = new Drawing.BufferSurface (width, height);
105-
Drawing.Utilities.cairo_rounded_rectangle (
106-
buffer.context,
107-
shadow_size,
108-
shadow_size,
109-
width - shadow_size * 2,
110-
height - shadow_size * 2,
111-
border_radius
112-
);
113-
114-
buffer.context.set_source_rgba (0, 0, 0, 0.7);
115-
buffer.context.fill ();
116-
117-
buffer.exponential_blur (shadow_size / 2);
118-
119-
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
120-
var cr = new Cairo.Context (surface);
121-
cr.set_source_surface (buffer.surface, 0, 0);
122-
cr.paint ();
123-
124-
cr.save ();
125-
cr.set_operator (Cairo.Operator.CLEAR);
126-
Drawing.Utilities.cairo_rounded_rectangle (cr, shadow_size, shadow_size, actor.width, actor.height, border_radius);
127-
cr.fill ();
128-
cr.restore ();
129-
130-
try {
131-
var texture = new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.BGRA_8888_PRE,
132-
surface.get_stride (), surface.get_data ());
105+
var texture = get_shadow_texture (context, width, height, shadow_size, border_radius);
106+
if (texture != null) {
133107
shadow_cache.@set (current_key, new Shadow (texture));
134-
135-
return texture;
136-
} catch (Error e) {
137-
debug (e.message);
138-
return null;
139108
}
109+
110+
return texture;
140111
}
141112

142113
public override void paint (Clutter.PaintNode node, Clutter.PaintContext context, Clutter.EffectPaintFlags flags) {
@@ -149,8 +120,8 @@ public class Gala.ShadowEffect : Clutter.Effect {
149120
pipeline.set_layer_texture (0, shadow);
150121
}
151122

152-
var opacity = actor.get_paint_opacity () * shadow_opacity / 255.0f;
153-
var alpha = Cogl.Color.from_4f (1.0f, 1.0f, 1.0f, opacity / 255.0f);
123+
var opacity = actor.get_paint_opacity () * shadow_opacity * INITIAL_OPACITY / 255.0f / 255.0f;
124+
var alpha = Cogl.Color.from_4f (1.0f, 1.0f, 1.0f, opacity);
154125
alpha.premultiply ();
155126

156127
pipeline.set_color (alpha);
@@ -220,4 +191,91 @@ public class Gala.ShadowEffect : Clutter.Effect {
220191
return Source.REMOVE;
221192
});
222193
}
194+
195+
private Cogl.Texture? get_shadow_texture (Cogl.Context context, int width, int height, int shadow_size, int corner_radius) {
196+
var data = new uint8[width * height];
197+
198+
// use fast Gaussian blur approximation
199+
var precomputed_colors = new uint8[shadow_size + 1];
200+
for (var i = 0; i <= shadow_size; i++) {
201+
var normalized = (double) i / shadow_size;
202+
precomputed_colors[i] = (uint8) (normalized * normalized * (3.0 - 2.0 * normalized) * 255.0);
203+
}
204+
205+
var total_offset = shadow_size + corner_radius;
206+
207+
var target_row = height - total_offset;
208+
for (var row = total_offset; row < target_row; row++) {
209+
var current_row = row * width;
210+
var current_row_end = current_row + width - 1;
211+
for (int i = 0; i <= shadow_size; i++) {
212+
var current_color = precomputed_colors[i];
213+
214+
data[current_row + i] = current_color;
215+
data[current_row_end - i] = current_color;
216+
}
217+
}
218+
219+
var target_col = width - total_offset;
220+
for (var row = 0; row <= shadow_size; row++) {
221+
var current_row = row * width;
222+
var end_row = (height - row) * width - 1;
223+
var current_color = precomputed_colors[row];
224+
225+
for (var col = total_offset; col < target_col; col++) {
226+
data[current_row + col] = current_color;
227+
data[end_row - col] = current_color;
228+
}
229+
}
230+
231+
var target_square = shadow_size + corner_radius;
232+
for (var y = 0; y < target_square; y++) {
233+
var current_row = width * y;
234+
var current_row_end = current_row + width - 1;
235+
var end_row = (height - 1 - y) * width;
236+
var end_row_end = end_row + width - 1;
237+
238+
var dy = target_square - y;
239+
var dy_squared = dy * dy;
240+
241+
for (var x = 0; x < target_square; x++) {
242+
var dx = target_square - x;
243+
var squared_distance = dx * dx + dy_squared;
244+
245+
if (squared_distance > target_square * target_square) {
246+
continue;
247+
}
248+
249+
if (squared_distance >= corner_radius * corner_radius) {
250+
double sin, cos;
251+
Math.sincos (Math.atan2 (dy, dx), out sin, out cos);
252+
253+
var real_dx = dx - corner_radius * cos;
254+
var real_dy = dy - corner_radius * sin;
255+
256+
// use fast Gaussian blur approximation
257+
var normalized = (double) Math.sqrt (real_dx * real_dx + real_dy * real_dy) / shadow_size;
258+
var current_color = (uint8) (1.0 - normalized * normalized * (3.0 - 2.0 * normalized) * 255.0);
259+
260+
// when we're very close to the rounded corner, our real_distance can be wrong (idk why).
261+
// If we're here, we're not inside the corner yet and that means we must draw something
262+
if (current_color == 0) {
263+
current_color = 255;
264+
}
265+
266+
data[current_row + x] = current_color;
267+
data[current_row_end - x] = current_color;
268+
data[end_row + x] = current_color;
269+
data[end_row_end - x] = current_color;
270+
}
271+
}
272+
}
273+
274+
try {
275+
return new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.A_8, width, data);
276+
} catch (Error e) {
277+
warning ("ShadowEffect: Couldn't create texture");
278+
return null;
279+
}
280+
}
223281
}

0 commit comments

Comments
 (0)