Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement PantheonShellX11 protocol #1997

Closed
wants to merge 13 commits into from
5 changes: 4 additions & 1 deletion data/io.elementary.desktop.wm.shell
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
[io.elementary.wingpanel]
launch-on-x=true
args=io.elementary.wingpanel
supports-id=false

[io.elementary.desktop.agent-polkit]
launch-on-x=true
args=/usr/libexec/policykit-1-pantheon/io.elementary.desktop.agent-polkit
supports-id=false

[io.elementary.dock]
launch-on-x=false
launch-on-x=true
args=io.elementary.dock
supports-id=true
25 changes: 25 additions & 0 deletions src/InternalUtils.vala
Original file line number Diff line number Diff line change
Expand Up @@ -336,5 +336,30 @@ namespace Gala {
return { 0, 0, (int) screen_width, (int) screen_height };
}
}

/**
* Returns Meta.Side that correspondes to Pantheon.Desktop.Anchor.
*/
public static Meta.Side anchor_to_side (Pantheon.Desktop.Anchor anchor) {
Meta.Side side = TOP;
switch (anchor) {
case TOP:
break;

case BOTTOM:
side = BOTTOM;
break;

case LEFT:
side = LEFT;
break;

case RIGHT:
side = RIGHT;
break;
}

return side;
}
}
}
20 changes: 1 addition & 19 deletions src/PantheonShell.vala
Original file line number Diff line number Diff line change
Expand Up @@ -243,25 +243,7 @@ namespace Gala {
return;
}

Meta.Side side = TOP;
switch (anchor) {
case TOP:
break;

case BOTTOM:
side = BOTTOM;
break;

case LEFT:
side = LEFT;
break;

case RIGHT:
side = RIGHT;
break;
}

ShellClientsManager.get_instance ().set_anchor (window, side);
ShellClientsManager.get_instance ().set_anchor (window, InternalUtils.anchor_to_side (anchor));
}

internal static void focus_panel (Wl.Client client, Wl.Resource resource) {
Expand Down
66 changes: 66 additions & 0 deletions src/PantheonShellX11.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
*/

[DBus (name = "io.elementary.gala.PantheonShellX11")]
public class Gala.PantheonShellX11 : GLib.Object {
private static PantheonShellX11 instance;
private static Meta.Display display;

[DBus (visible = false)]
public static void init (Meta.Display display) {
PantheonShellX11.display = display;

Bus.own_name (
BusType.SESSION,
"io.elementary.gala.PantheonShellX11",
BusNameOwnerFlags.NONE,
(connection) => {
if (instance == null) {
instance = new PantheonShellX11 ();
}

try {
connection.register_object ("/io/elementary/gala/PantheonShellX11", instance);
} catch (Error e) {
warning (e.message);
}
},
() => {},
() => warning ("Could not acquire name")
);
}

public void set_anchor (string id, Pantheon.Desktop.Anchor anchor) throws GLib.Error {
foreach (unowned var window in display.list_all_windows ()) {
if (window.title == id) {
ShellClientsManager.get_instance ().set_anchor (window, InternalUtils.anchor_to_side (anchor));
}
}
}

public void set_size (string id, int width, int height) throws GLib.Error {
foreach (unowned var window in display.list_all_windows ()) {
if (window.title == id) {
ShellClientsManager.get_instance ().set_size (window, width, height);
}
}
}

public void set_hide_mode (string id, Pantheon.Desktop.HideMode hide_mode) throws GLib.Error {
foreach (unowned var window in display.list_all_windows ()) {
if (window.title == id) {
ShellClientsManager.get_instance ().set_hide_mode (window, hide_mode);
}
}
}

public void make_centered (string id) throws GLib.Error {
foreach (unowned var window in display.list_all_windows ()) {
if (window.title == id) {
ShellClientsManager.get_instance ().make_centered (window);
}
}
}
}
81 changes: 76 additions & 5 deletions src/ShellClients/ManagedClient.vala
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,28 @@ public class Gala.ManagedClient : Object {

public Meta.Display display { get; construct; }
public string[] args { get; construct; }
public bool supports_id { get; construct; }

public Meta.WaylandClient? wayland_client { get; private set; }

private Meta.WaylandClient? wayland_client;
private Subprocess? subprocess;
// id is used to identify X11 clients
private string? id;

// currently used to avoid overlapping
private static Gee.HashSet<string> ids;

public ManagedClient (Meta.Display display, string[] args, bool supports_id) {
Object (display: display, args: args, supports_id: supports_id);
}

~ManagedClient () {
if (id != null) {
ids.remove (id);
}
}

public ManagedClient (Meta.Display display, string[] args) {
Object (display: display, args: args);
static construct {
ids = new Gee.HashSet<string> ();
}

construct {
Expand All @@ -44,9 +59,53 @@ public class Gala.ManagedClient : Object {
});
} else {
start_x.begin ();

if (supports_id) {
display.window_created.connect ((window) => {
if (window.title == id) {
window_created (window);
}
});
}
}
}

public void make_dock (Meta.Window window) {
if (Meta.Util.is_wayland_compositor ()) {
wayland_client.make_dock (window);
} else {
make_dock_x11 (window);
}
}

private void make_dock_x11 (Meta.Window window) requires (
!Meta.Util.is_wayland_compositor () && supports_id && window.title == id
) {
unowned var x11_display = display.get_x11_display ();

#if HAS_MUTTER46
var x_window = x11_display.lookup_xwindow (window);
#else
var x_window = window.get_xwindow ();
#endif
// gtk3's gdk_x11_window_set_type_hint() is used as a reference
unowned var xdisplay = x11_display.get_xdisplay ();
var atom = xdisplay.intern_atom ("_NET_WM_WINDOW_TYPE", false);
var dock_atom = xdisplay.intern_atom ("_NET_WM_WINDOW_TYPE_DOCK", false);

// (X.Atom) 4 is XA_ATOM
// 32 is format
// 0 means replace
xdisplay.change_property (x_window, atom, (X.Atom) 4, 32, 0, (uchar[]) dock_atom, 1);
}

public bool owns_window (Meta.Window window) {
var is_wayland = Meta.Util.is_wayland_compositor ();

return is_wayland && wayland_client.owns_window (window) ||
!is_wayland && supports_id && window.title == id;
}

private async void start_wayland () {
var subprocess_launcher = new GLib.SubprocessLauncher (STDERR_PIPE | STDOUT_PIPE);
try {
Expand All @@ -72,7 +131,19 @@ public class Gala.ManagedClient : Object {

private async void start_x () {
try {
subprocess = new Subprocess.newv (args, NONE);
var _args = args;

if (supports_id) {
while (id == null || id in ids) {
id = Uuid.string_random ();
}
ids.add (id);

_args += "--id";
_args += id;
}

subprocess = new Subprocess.newv (_args, NONE);
yield subprocess.wait_async ();

//Restart the daemon if it crashes
Expand Down
4 changes: 2 additions & 2 deletions src/ShellClients/NotificationsClient.vala
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ public class Gala.NotificationsClient : Object {
}

construct {
client = new ManagedClient (display, { "io.elementary.notifications" });
client = new ManagedClient (display, { "io.elementary.notifications" }, false);

client.window_created.connect ((window) => {
window.set_data (NOTIFICATION_DATA_KEY, true);
window.make_above ();
#if HAS_MUTTER46
client.wayland_client.make_dock (window);
client.make_dock (window);
#endif
});
}
Expand Down
7 changes: 4 additions & 3 deletions src/ShellClients/ShellClientsManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ public class Gala.ShellClientsManager : Object {

try {
var args = key_file.get_string_list (group, "args");
protocol_clients += new ManagedClient (wm.get_display (), args);
var supports_id = key_file.get_boolean (group, "supports-id");
protocol_clients += new ManagedClient (wm.get_display (), args, supports_id);
} catch (Error e) {
warning ("Failed to load launch args for client %s: %s", group, e.message);
}
Expand All @@ -100,8 +101,8 @@ public class Gala.ShellClientsManager : Object {

private void make_dock (Meta.Window window) {
foreach (var client in protocol_clients) {
if (client.wayland_client.owns_window (window)) {
client.wayland_client.make_dock (window);
if (client.owns_window (window)) {
client.make_dock (window);
break;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/WindowManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ namespace Gala {
DBusAccelerator.init (this);
MediaFeedback.init ();

if (!Meta.Util.is_wayland_compositor ()) {
PantheonShellX11.init (display);
}

WindowListener.init (display);
KeyboardManager.init (display);
window_tracker = new WindowTracker ();
Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ gala_bin_sources = files(
'MediaFeedback.vala',
'NotificationStack.vala',
'PantheonShell.vala',
'PantheonShellX11.vala',
'PluginManager.vala',
'ScreenSaverManager.vala',
'ScreenshotManager.vala',
Expand Down
Loading