diff --git a/src/PantheonShell.vala b/src/PantheonShell.vala index 1e0f173ff..0dabcb683 100644 --- a/src/PantheonShell.vala +++ b/src/PantheonShell.vala @@ -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, anchor); } internal static void focus_panel (Wl.Client client, Wl.Resource resource) { diff --git a/src/ShellClients/HideTracker.vala b/src/ShellClients/HideTracker.vala index 35ed7a0d4..0d0d367fc 100644 --- a/src/ShellClients/HideTracker.vala +++ b/src/ShellClients/HideTracker.vala @@ -16,17 +16,7 @@ public class Gala.HideTracker : Object { public Meta.Display display { get; construct; } public unowned PanelWindow panel { get; construct; } - private Pantheon.Desktop.HideMode _hide_mode = NEVER; - public Pantheon.Desktop.HideMode hide_mode { - get { - return _hide_mode; - } - set { - _hide_mode = value; - - setup_barrier (); - } - } + public Pantheon.Desktop.HideMode hide_mode { get; set; } private Clutter.PanAction pan_action; @@ -97,6 +87,16 @@ public class Gala.HideTracker : Object { pan_action.pan.connect (on_pan); display.get_stage ().add_action_full ("panel-swipe-gesture", CAPTURE, pan_action); + + panel.notify["anchor"].connect (setup_barrier); + + var monitor_manager = display.get_context ().get_backend ().get_monitor_manager (); + monitor_manager.monitors_changed.connect (() => { + setup_barrier (); //Make sure barriers are still on the primary monitor + schedule_update (); + }); + + setup_barrier (); } #if !HAS_MUTTER45 @@ -152,7 +152,13 @@ public class Gala.HideTracker : Object { }); } - private void update_overlap () { + /** + * Immediately updates the overlap. Usually {@link schedule_update} should be used instead which + * internally calls this but throttles it since it's a somewhat expensive operation. + * On very rare use cases however it is required to update immediately. In that case you can call + * this directly. + */ + public void update_overlap () { overlap = false; focus_overlap = false; focus_maximized_overlap = false; @@ -171,7 +177,7 @@ public class Gala.HideTracker : Object { continue; } - if (!panel.get_custom_window_rect ().overlap (window.get_frame_rect ())) { + if (!panel.window.get_frame_rect ().overlap (window.get_frame_rect ())) { continue; } diff --git a/src/ShellClients/PanelClone.vala b/src/ShellClients/PanelClone.vala index 15fc8d23f..fa42574ad 100644 --- a/src/ShellClients/PanelClone.vala +++ b/src/ShellClients/PanelClone.vala @@ -9,7 +9,7 @@ public class Gala.PanelClone : Object { private const int ANIMATION_DURATION = 250; public WindowManager wm { get; construct; } - public unowned PanelWindow panel { get; construct; } + public unowned PanelWindow? panel { get; construct; } public Pantheon.Desktop.HideMode hide_mode { get { @@ -35,6 +35,8 @@ public class Gala.PanelClone : Object { private SafeWindowClone clone; private Meta.WindowActor actor; + private bool window_ready = false; + private HideTracker? hide_tracker; public PanelClone (WindowManager wm, PanelWindow panel) { @@ -67,16 +69,38 @@ public class Gala.PanelClone : Object { // https://github.com/elementary/gala/issues/2080 panel.window.focused.connect (update_visible); + panel.window.unmanaging.connect (() => panel = null); + update_visible (); update_clone_position (); - Idle.add_once (() => { - if (hide_mode == NEVER) { - show (); - } else { - hide_tracker.schedule_update (); - } - }); + if (Meta.Util.is_wayland_compositor ()) { + // We want to wait for the window to be actually shown before revealing and then still a bit + // more since at the beginning it might be doing some relayouting (e.g. the dock only knows + // its size after it got a dbus connection to gala) which disrupts the animation. + panel.window.shown.connect (() => Timeout.add (100, () => { + if (panel == null) { + return Source.REMOVE; // The window was unmanaged while we were waiting + } + + window_ready = true; + // In the best case we are ready before the manager stops waiting so the on_ready usually + // doesn't do anything. However if the client stalls for some reason or it crashed and has now + // been restarted it will show immediately. + on_ready (); + + ShellClientsManager.get_instance ().notify_client_ready (); + + return Source.REMOVE; + })); + } else { + // On X the window was already shown when it requests being a panel so just mark it ready + window_ready = true; + on_ready (); + ShellClientsManager.get_instance ().notify_client_ready (); + } + + ShellClientsManager.get_instance ().notify["is-waiting"].connect (on_ready); } private void update_visible () { @@ -146,7 +170,7 @@ public class Gala.PanelClone : Object { } private void show () { - if (!panel_hidden) { + if (!panel_hidden || !window_ready || ShellClientsManager.get_instance ().is_waiting) { return; } @@ -171,4 +195,12 @@ public class Gala.PanelClone : Object { panel_hidden = false; } } + + private void on_ready () { + if (hide_mode == NEVER) { + show (); + } else { + hide_tracker.update_overlap (); + } + } } diff --git a/src/ShellClients/PanelWindow.vala b/src/ShellClients/PanelWindow.vala index 107218e11..1d53d4e12 100644 --- a/src/ShellClients/PanelWindow.vala +++ b/src/ShellClients/PanelWindow.vala @@ -11,34 +11,22 @@ public class Gala.PanelWindow : Object { public WindowManager wm { get; construct; } public Meta.Window window { get; construct; } - public bool hidden { get; private set; default = false; } + public Pantheon.Desktop.Anchor anchor { get; construct set; } - public Meta.Side anchor; + private WindowPositioner window_positioner; public DelegateActor delegate_actor; private PanelClone clone; - private uint idle_move_id = 0; - private int width = -1; private int height = -1; - public PanelWindow (WindowManager wm, Meta.Window window, Meta.Side anchor) { - Object (wm: wm, window: window); - - // Meta.Side seems to be currently not supported as GLib.Object property ...? - // At least it always crashed for me with some paramspec, g_type_fundamental backtrace - this.anchor = anchor; + public PanelWindow (WindowManager wm, Meta.Window window, Pantheon.Desktop.Anchor anchor) { + Object (wm: wm, window: window, anchor: anchor); } construct { - window.size_changed.connect (position_window); - window.unmanaging.connect (() => { - if (idle_move_id != 0) { - Source.remove (idle_move_id); - } - if (window_struts.remove (window)) { update_struts (); } @@ -49,13 +37,18 @@ public class Gala.PanelWindow : Object { delegate_actor = new DelegateActor ((Meta.WindowActor) window.get_compositor_private ()); clone = new PanelClone (wm, this); - var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager (); - monitor_manager.monitors_changed.connect (() => update_anchor (anchor)); - monitor_manager.monitors_changed_internal.connect (() => update_anchor (anchor)); + var display = wm.get_display (); - var workspace_manager = wm.get_display ().get_workspace_manager (); + var workspace_manager = display.get_workspace_manager (); workspace_manager.workspace_added.connect (update_strut); workspace_manager.workspace_removed.connect (update_strut); + + window.size_changed.connect (update_strut); + window.position_changed.connect (update_strut); + + window_positioner = new WindowPositioner (display, window, WindowPositioner.Position.from_anchor (anchor)); + + notify["anchor"].connect (() => window_positioner.position = WindowPositioner.Position.from_anchor (anchor)); } #if HAS_MUTTER45 @@ -87,73 +80,9 @@ public class Gala.PanelWindow : Object { this.width = width; this.height = height; - position_window (); - set_hide_mode (clone.hide_mode); // Resetup barriers etc. - } - - public void update_anchor (Meta.Side anchor) { - this.anchor = anchor; - - position_window (); - set_hide_mode (clone.hide_mode); // Resetup barriers etc. - } - - private void position_window () { - var display = wm.get_display (); - var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - var window_rect = window.get_frame_rect (); - - switch (anchor) { - case TOP: - position_window_top (monitor_geom, window_rect); - break; - - case BOTTOM: - position_window_bottom (monitor_geom, window_rect); - break; - - default: - warning ("Side not supported yet"); - break; - } - update_strut (); } -#if HAS_MUTTER45 - private void position_window_top (Mtk.Rectangle monitor_geom, Mtk.Rectangle window_rect) { -#else - private void position_window_top (Meta.Rectangle monitor_geom, Meta.Rectangle window_rect) { -#endif - var x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; - - move_window_idle (x, monitor_geom.y); - } - -#if HAS_MUTTER45 - private void position_window_bottom (Mtk.Rectangle monitor_geom, Mtk.Rectangle window_rect) { -#else - private void position_window_bottom (Meta.Rectangle monitor_geom, Meta.Rectangle window_rect) { -#endif - var x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; - var y = monitor_geom.y + monitor_geom.height - window_rect.height; - - move_window_idle (x, y); - } - - private void move_window_idle (int x, int y) { - if (idle_move_id != 0) { - Source.remove (idle_move_id); - } - - idle_move_id = Idle.add (() => { - window.move_frame (false, x, y); - - idle_move_id = 0; - return Source.REMOVE; - }); - } - public void set_hide_mode (Pantheon.Desktop.HideMode hide_mode) { clone.hide_mode = hide_mode; @@ -177,7 +106,7 @@ public class Gala.PanelWindow : Object { Meta.Strut strut = { rect, - anchor + side_from_anchor (anchor) }; window_struts[window] = strut; @@ -203,4 +132,20 @@ public class Gala.PanelWindow : Object { update_struts (); } } + + private Meta.Side side_from_anchor (Pantheon.Desktop.Anchor anchor) { + switch (anchor) { + case BOTTOM: + return BOTTOM; + + case LEFT: + return LEFT; + + case RIGHT: + return RIGHT; + + default: + return TOP; + } + } } diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index 757b1f08f..1af4e481e 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -20,6 +20,8 @@ public class Gala.ShellClientsManager : Object { return instance; } + public bool is_waiting { get; private set; default = true; } // On session start wait a bit for a synchronized and smooth reveal + public WindowManager wm { get; construct; } private NotificationsClient notifications_client; @@ -28,6 +30,8 @@ public class Gala.ShellClientsManager : Object { private GLib.HashTable panel_windows = new GLib.HashTable (null, null); private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); + private uint currently_starting_panels = 0; + private ShellClientsManager (WindowManager wm) { Object (wm: wm); } @@ -143,16 +147,18 @@ public class Gala.ShellClientsManager : Object { xdisplay.change_property (x_window, atom, (X.Atom) 4, 32, 0, (uchar[]) dock_atom, 1); } - public void set_anchor (Meta.Window window, Meta.Side side) { + public void set_anchor (Meta.Window window, Pantheon.Desktop.Anchor anchor) { if (window in panel_windows) { - panel_windows[window].update_anchor (side); + panel_windows[window].anchor = anchor; return; } + currently_starting_panels++; + make_dock (window); // TODO: Return if requested by window that's not a trusted client? - panel_windows[window] = new PanelWindow (wm, window, side); + panel_windows[window] = new PanelWindow (wm, window, anchor); // connect_after so we make sure the PanelWindow can destroy its barriers and struts window.unmanaging.connect_after ((_window) => panel_windows.remove (_window)); @@ -207,6 +213,18 @@ public class Gala.ShellClientsManager : Object { return positioned; } + //Called by the WM once it has initialized + public void notify_stage_ready () { + Timeout.add_seconds_once (5, () => is_waiting = false); // Failsafe if a client stalls to still show the others + } + + // Called by the {@link PanelClone} when the window it manages is ready. + public void notify_client_ready () { + currently_starting_panels--; + + is_waiting &= currently_starting_panels > 0; + } + //X11 only private void parse_mutter_hints (Meta.Window window) requires (!Meta.Util.is_wayland_compositor ()) { if (window.mutter_hints == null) { @@ -226,8 +244,30 @@ public class Gala.ShellClientsManager : Object { switch (key) { case "anchor": - int parsed; // Will be used as Meta.Side which is a 4 value bitfield so check bounds for that - if (int.try_parse (val, out parsed) && 0 <= parsed && parsed <= 15) { + int meta_side_parsed; // Will be used as Meta.Side which is a 4 value bitfield so check bounds for that + if (int.try_parse (val, out meta_side_parsed) && 0 <= meta_side_parsed && meta_side_parsed <= 15) { + //FIXME: Next major release change dock and wingpanel calls to get rid of this + Pantheon.Desktop.Anchor parsed = TOP; + switch ((Meta.Side) meta_side_parsed) { + case BOTTOM: + parsed = BOTTOM; + break; + + case LEFT: + parsed = LEFT; + break; + + case RIGHT: + parsed = RIGHT; + break; + + default: + break; + } + + set_anchor (window, parsed); + // We need to set a second time because the intention is to call this before the window is shown which it is on wayland + // but on X the window was already shown when we get here so we have to call again to instantly apply it. set_anchor (window, parsed); } else { warning ("Failed to parse %s as anchor", val); diff --git a/src/ShellClients/WindowPositioner.vala b/src/ShellClients/WindowPositioner.vala index 62b7928d7..daf838f14 100644 --- a/src/ShellClients/WindowPositioner.vala +++ b/src/ShellClients/WindowPositioner.vala @@ -7,13 +7,28 @@ public class Gala.WindowPositioner : Object { public enum Position { - CENTER + TOP, + BOTTOM, + CENTER; + + public static Position from_anchor (Pantheon.Desktop.Anchor anchor) { + if (anchor > 1) { + warning ("Position %s not supported yet", anchor.to_string ()); + return CENTER; + } + + return (Position) anchor; + } } public Meta.Display display { get; construct; } public Meta.Window window { get; construct; } - public Position position { get; private set; } - public Variant? position_data { get; private set; } + /** + * This may only be set after the window was shown. + * The initial position should only be given in the constructor. + */ + public Position position { get; construct set; } + public Variant? position_data { get; construct set; } public WindowPositioner (Meta.Display display, Meta.Window window, Position position, Variant? position_data = null) { Object (display: display, window: window, position: position, position_data: position_data); @@ -29,29 +44,34 @@ public class Gala.WindowPositioner : Object { unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager (); monitor_manager.monitors_changed.connect (position_window); monitor_manager.monitors_changed_internal.connect (position_window); - } - /** - * This may only be called after the window was shown. - */ - public void update_position (Position new_position, Variant? new_position_data = null) { - position = new_position; - position_data = new_position_data; - - position_window (); + notify["position"].connect (position_window); + notify["position-data"].connect (position_window); } private void position_window () { int x = 0, y = 0; + var window_rect = window.get_frame_rect (); + switch (position) { case CENTER: var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - var window_rect = window.get_frame_rect (); - x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; y = monitor_geom.y + (monitor_geom.height - window_rect.height) / 2; break; + + case TOP: + var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); + x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; + y = monitor_geom.y; + break; + + case BOTTOM: + var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); + x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2; + y = monitor_geom.y + monitor_geom.height - window_rect.height; + break; } window.move_frame (false, x, y); diff --git a/src/WindowManager.vala b/src/WindowManager.vala index 3b2d08ef3..8bec73f63 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -398,13 +398,13 @@ namespace Gala { stage.show (); - Idle.add (() => { + Idle.add_once (() => { // let the session manager move to the next phase #if WITH_SYSTEMD Systemd.Daemon.notify (true, "READY=1"); #endif - display.get_context ().notify_ready (); - return GLib.Source.REMOVE; + get_display ().get_context ().notify_ready (); + ShellClientsManager.get_instance ().notify_stage_ready (); }); }