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

wayland: Add make_modal protocol method #1974

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions protocol/pantheon-desktop-shell-v1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@
</description>
</request>

<request name="make_modal">
<description summary="requests to make a surface system modal">
This will block all user input outside the surface and most system shortcuts.
</description>

<arg name="dim" type="uint" summary="1 to dim, 0 to not dim"/>
</request>

<request name="focus">
<description summary="request keyboard focus">
Request keyboard focus, taking it away from any other window.
Expand Down
3 changes: 3 additions & 0 deletions protocol/pantheon-desktop-shell.vapi
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ namespace Pantheon.Desktop {
public Destroy destroy;
public SetKeepAbove set_keep_above;
public MakeCentered make_centered;
public MakeModal make_modal;
public Focus focus;
}

Expand All @@ -79,5 +80,7 @@ namespace Pantheon.Desktop {
[CCode (has_target = false, has_typedef = false)]
public delegate void MakeCentered (Wl.Client client, Wl.Resource resource);
[CCode (has_target = false, has_typedef = false)]
public delegate void MakeModal (Wl.Client client, Wl.Resource resource, uint dim);
[CCode (has_target = false, has_typedef = false)]
public delegate void Destroy (Wl.Client client, Wl.Resource resource);
}
10 changes: 10 additions & 0 deletions src/InternalUtils.vala
Original file line number Diff line number Diff line change
Expand Up @@ -336,5 +336,15 @@ namespace Gala {
return { 0, 0, (int) screen_width, (int) screen_height };
}
}

public static void clutter_actor_reparent (Clutter.Actor actor, Clutter.Actor new_parent) {
if (actor == new_parent)
return;

actor.ref ();
actor.get_parent ().remove_child (actor);
new_parent.add_child (actor);
actor.unref ();
}
}
}
18 changes: 18 additions & 0 deletions src/PantheonShell.vala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ namespace Gala {
destroy_extended_behavior_surface,
set_keep_above,
make_centered,
make_modal,
focus_extended_behavior,
};

Expand Down Expand Up @@ -359,6 +360,23 @@ namespace Gala {
ShellClientsManager.get_instance ().make_centered (window);
}

internal static void make_modal (Wl.Client client, Wl.Resource resource, uint dim) {
unowned ExtendedBehaviorSurface? eb_surface = resource.get_user_data<ExtendedBehaviorSurface> ();
if (eb_surface.wayland_surface == null) {
warning ("Window tried to make modal but wayland surface is null.");
return;
}

Meta.Window? window;
eb_surface.wayland_surface.get ("window", out window, null);
if (window == null) {
warning ("Window tried to make modal but wayland surface had no associated window.");
return;
}

ShellClientsManager.get_instance ().make_modal (window, dim == 1);
}

internal static void destroy_panel_surface (Wl.Client client, Wl.Resource resource) {
resource.destroy ();
}
Expand Down
10 changes: 7 additions & 3 deletions src/ShellClients/ShellClientsManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
public class Gala.ShellClientsManager : Object {
private static ShellClientsManager instance;

public static void init (WindowManager wm) {
public static void init (WindowManagerGala wm) {
if (instance != null) {
return;
}
Expand All @@ -20,15 +20,15 @@ public class Gala.ShellClientsManager : Object {
return instance;
}

public WindowManager wm { get; construct; }
public WindowManagerGala wm { get; construct; }

private NotificationsClient notifications_client;
private ManagedClient[] protocol_clients = {};

private GLib.HashTable<Meta.Window, PanelWindow> windows = new GLib.HashTable<Meta.Window, PanelWindow> (null, null);
private GLib.HashTable<Meta.Window, CenteredWindow> centered_windows = new GLib.HashTable<Meta.Window, CenteredWindow> (null, null);

private ShellClientsManager (WindowManager wm) {
private ShellClientsManager (WindowManagerGala wm) {
Object (wm: wm);
}

Expand Down Expand Up @@ -193,6 +193,10 @@ public class Gala.ShellClientsManager : Object {
window.unmanaging.connect_after (() => centered_windows.remove (window));
}

public void make_modal (Meta.Window window, bool dim) {
wm.modal_actor.make_modal (window, dim);
}

public bool is_positioned_window (Meta.Window window) {
bool positioned = (window in centered_windows) || (window in windows);
window.foreach_ancestor ((ancestor) => {
Expand Down
77 changes: 77 additions & 0 deletions src/Widgets/ModalActor.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <[email protected]>
*/

/**
* This class allows to make windows system modal i.e. dim
* the desktop behind them and only allow interaction with them.
* Not to be confused with WindowManager.push_modal which only
* works for our own Clutter.Actors.
*/
public class Gala.ModalActor : Clutter.Actor {
public Meta.Display display { get; construct; }

private int modal_dialogs = 0;

public ModalActor (Meta.Display display) {
Object (display: display);
}

construct {
background_color = { 0, 0, 0, 200 };
x = 0;
y = 0;
visible = false;
reactive = true;

unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (update_size);

update_size ();
}

private void update_size () {
int width, height;
display.get_size (out width, out height);

set_size (width, height);
}

public void make_modal (Meta.Window window, bool dim) {
modal_dialogs++;
window.unmanaged.connect (unmake_modal);

var actor = (Meta.WindowActor) window.get_compositor_private ();
InternalUtils.clutter_actor_reparent (actor, this);

if (dim) {
background_color = { 0, 0, 0, 200 };
} else {
background_color = { 0, 0, 0, 0 };
}

check_visible ();
}

public void unmake_modal (Meta.Window window) {
modal_dialogs--;
window.unmanaged.disconnect (unmake_modal);

check_visible ();
}

private void check_visible () {
visible = modal_dialogs > 0;

if (visible) {
get_parent ().set_child_above_sibling (this, null);
}
}

public bool is_modal () {
return modal_dialogs > 0;
}
}
9 changes: 9 additions & 0 deletions src/WindowManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ namespace Gala {
*/
public Gala.ActivatableComponent workspace_view { get; protected set; }

public ModalActor modal_actor { get; private set; }

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -244,6 +246,9 @@ namespace Gala {
stage.remove_child (feedback_group);
ui_group.add_child (feedback_group);

modal_actor = new ModalActor (display);
ui_group.insert_child_above (modal_actor, null);

FilterManager.init (this);

/*keybindings*/
Expand Down Expand Up @@ -2338,6 +2343,10 @@ namespace Gala {
}

public override bool keybinding_filter (Meta.KeyBinding binding) {
if (modal_actor.is_modal ()) {
return true;
}

if (!is_modal ())
return false;

Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ gala_bin_sources = files(
'Widgets/DwellClickTimer.vala',
'Widgets/IconGroup.vala',
'Widgets/IconGroupContainer.vala',
'Widgets/ModalActor.vala',
'Widgets/MonitorClone.vala',
'Widgets/MultitaskingView.vala',
'Widgets/PixelPicker.vala',
Expand Down