mirror of
https://github.com/elementary/gala.git
synced 2024-12-24 17:53:19 +03:00
Use separate daemon to display GTK window menus (#179)
* Use separate process to create window menus * Reduce line count with code style changes * Fix some formatting * Code style fixes * Prevent menu closing instantly after open * Resolve warnings in new code * Remove GLib namespace prefix from exceptions * Update main.vala * Remove timeout by positioning the menu outside of the mouse pos * daemon: Use lambda as popup callback * io.elementary.gala -> org.pantheon.gala * org.pantheon.gala.daemon -> gala-daemon * Clean up * Fix daemon object path * Some cleaning
This commit is contained in:
parent
2995cd6495
commit
1970bac81e
146
daemon/Main.vala
Normal file
146
daemon/Main.vala
Normal file
@ -0,0 +1,146 @@
|
||||
//
|
||||
// Copyright (c) 2018 elementary LLC. (https://elementary.io)
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
[DBus (name = "org.gnome.SessionManager")]
|
||||
public interface SessionManager : Object
|
||||
{
|
||||
public abstract async ObjectPath register_client (string app_id, string client_start_id) throws DBusError, IOError;
|
||||
}
|
||||
|
||||
[DBus (name = "org.gnome.SessionManager.ClientPrivate")]
|
||||
public interface SessionClient : Object
|
||||
{
|
||||
public abstract void end_session_response (bool is_ok, string reason) throws DBusError, IOError;
|
||||
|
||||
public signal void stop () ;
|
||||
public signal void query_end_session (uint flags);
|
||||
public signal void end_session (uint flags);
|
||||
public signal void cancel_end_session ();
|
||||
}
|
||||
|
||||
public class Daemon
|
||||
{
|
||||
SessionClient? sclient = null;
|
||||
|
||||
public Daemon ()
|
||||
{
|
||||
register.begin ((o, res)=> {
|
||||
bool success = register.end (res);
|
||||
if (!success) {
|
||||
message ("Failed to register with Session manager");
|
||||
}
|
||||
});
|
||||
|
||||
var menu_daemon = new MenuDaemon ();
|
||||
menu_daemon.setup_dbus ();
|
||||
}
|
||||
|
||||
public void run () {
|
||||
Gtk.main ();
|
||||
}
|
||||
|
||||
public static async SessionClient? register_with_session (string app_id)
|
||||
{
|
||||
ObjectPath? path = null;
|
||||
string? msg = null;
|
||||
string? start_id = null;
|
||||
|
||||
SessionManager? session = null;
|
||||
SessionClient? session_client = null;
|
||||
|
||||
start_id = Environment.get_variable ("DESKTOP_AUTOSTART_ID");
|
||||
if (start_id != null) {
|
||||
Environment.unset_variable ("DESKTOP_AUTOSTART_ID");
|
||||
} else {
|
||||
start_id = "";
|
||||
warning ("DESKTOP_AUTOSTART_ID not set, session registration may be broken (not running via session?)");
|
||||
}
|
||||
|
||||
try {
|
||||
session = yield Bus.get_proxy (BusType.SESSION, "org.gnome.SessionManager", "/org/gnome/SessionManager");
|
||||
} catch (Error e) {
|
||||
warning ("Unable to connect to session manager: %s", e.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
path = yield session.register_client (app_id, start_id);
|
||||
} catch (Error e) {
|
||||
msg = e.message;
|
||||
warning ("Error registering with session manager: %s", e.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
session_client = yield Bus.get_proxy (BusType.SESSION, "org.gnome.SessionManager", path);
|
||||
} catch (Error e) {
|
||||
warning ("Unable to get private sessions client proxy: %s", e.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return session_client;
|
||||
}
|
||||
|
||||
async bool register ()
|
||||
{
|
||||
sclient = yield register_with_session ("org.pantheon.gala.daemon");
|
||||
|
||||
sclient.query_end_session.connect (() => end_session (false));
|
||||
sclient.end_session.connect (() => end_session (false));
|
||||
sclient.stop.connect (() => end_session (true));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void end_session (bool quit)
|
||||
{
|
||||
if (quit) {
|
||||
Gtk.main_quit ();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sclient.end_session_response (true, "");
|
||||
} catch (Error e) {
|
||||
warning ("Unable to respond to session manager: %s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int main (string[] args)
|
||||
{
|
||||
Gtk.init (ref args);
|
||||
|
||||
var ctx = new OptionContext ("Gala Daemon");
|
||||
ctx.set_help_enabled (true);
|
||||
ctx.add_group (Gtk.get_option_group (false));
|
||||
|
||||
try {
|
||||
ctx.parse (ref args);
|
||||
} catch (Error e) {
|
||||
stderr.printf ("Error: %s\n", e.message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var daemon = new Daemon ();
|
||||
daemon.run ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
186
daemon/MenuDaemon.vala
Normal file
186
daemon/MenuDaemon.vala
Normal file
@ -0,0 +1,186 @@
|
||||
//
|
||||
// Copyright (c) 2018 elementary LLC. (https://elementary.io)
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
const string DBUS_NAME = "org.pantheon.gala";
|
||||
const string DBUS_OBJECT_PATH = "/org/pantheon/gala";
|
||||
|
||||
const string DAEMON_DBUS_NAME = "org.pantheon.gala.daemon";
|
||||
const string DAEMON_DBUS_OBJECT_PATH = "/org/pantheon/gala/daemon";
|
||||
|
||||
[DBus (name = "org.pantheon.gala")]
|
||||
public interface WMDBus : GLib.Object
|
||||
{
|
||||
public abstract void perform_action (Gala.ActionType type) throws DBusError, IOError;
|
||||
}
|
||||
|
||||
[DBus (name = "org.pantheon.gala.daemon")]
|
||||
public class MenuDaemon : Object
|
||||
{
|
||||
Gtk.Menu? window_menu = null;
|
||||
Gtk.MenuItem minimize;
|
||||
Gtk.MenuItem maximize;
|
||||
Gtk.MenuItem move;
|
||||
Gtk.MenuItem resize;
|
||||
Gtk.CheckMenuItem always_on_top;
|
||||
Gtk.CheckMenuItem on_visible_workspace;
|
||||
Gtk.MenuItem move_left;
|
||||
Gtk.MenuItem move_right;
|
||||
Gtk.MenuItem close;
|
||||
|
||||
WMDBus? wm_proxy = null;
|
||||
|
||||
[DBus (visible = false)]
|
||||
public void setup_dbus ()
|
||||
{
|
||||
var flags = BusNameOwnerFlags.ALLOW_REPLACEMENT | BusNameOwnerFlags.REPLACE;
|
||||
Bus.own_name (BusType.SESSION, DAEMON_DBUS_NAME, flags, on_bus_acquired, () => {}, null);
|
||||
|
||||
Bus.watch_name (BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE, gala_appeared, lost_gala);
|
||||
}
|
||||
|
||||
void on_gala_get (GLib.Object? o, GLib.AsyncResult? res)
|
||||
{
|
||||
try {
|
||||
wm_proxy = Bus.get_proxy.end (res);
|
||||
} catch (Error e) {
|
||||
warning ("Failed to get Gala proxy: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
void lost_gala ()
|
||||
{
|
||||
wm_proxy = null;
|
||||
}
|
||||
|
||||
void gala_appeared ()
|
||||
{
|
||||
if (wm_proxy == null) {
|
||||
Bus.get_proxy.begin<WMDBus> (BusType.SESSION, DBUS_NAME, DBUS_OBJECT_PATH, 0, null, on_gala_get);
|
||||
}
|
||||
}
|
||||
|
||||
void on_bus_acquired (DBusConnection conn)
|
||||
{
|
||||
try {
|
||||
conn.register_object (DAEMON_DBUS_OBJECT_PATH, this);
|
||||
} catch (Error e) {
|
||||
stderr.printf ("Error registering MenuDaemon: %s\n", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
void perform_action (Gala.ActionType type)
|
||||
{
|
||||
if (wm_proxy != null) {
|
||||
try {
|
||||
wm_proxy.perform_action (type);
|
||||
} catch (Error e) {
|
||||
warning ("Failed to perform Gala action over DBus: %s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_window_menu ()
|
||||
{
|
||||
window_menu = new Gtk.Menu ();
|
||||
|
||||
minimize = new Gtk.MenuItem.with_label (_("Minimize"));
|
||||
minimize.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.MINIMIZE_CURRENT);
|
||||
});
|
||||
window_menu.append (minimize);
|
||||
|
||||
maximize = new Gtk.MenuItem.with_label ("");
|
||||
maximize.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.MAXIMIZE_CURRENT);
|
||||
});
|
||||
window_menu.append (maximize);
|
||||
|
||||
move = new Gtk.MenuItem.with_label (_("Move"));
|
||||
move.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.START_MOVE_CURRENT);
|
||||
});
|
||||
window_menu.append (move);
|
||||
|
||||
resize = new Gtk.MenuItem.with_label (_("Resize"));
|
||||
resize.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.START_RESIZE_CURRENT);
|
||||
});
|
||||
window_menu.append (resize);
|
||||
|
||||
always_on_top = new Gtk.CheckMenuItem.with_label (_("Always on Top"));
|
||||
always_on_top.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.TOGGLE_ALWAYS_ON_TOP_CURRENT);
|
||||
});
|
||||
window_menu.append (always_on_top);
|
||||
|
||||
on_visible_workspace = new Gtk.CheckMenuItem.with_label (_("Always on Visible Workspace"));
|
||||
on_visible_workspace.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.TOGGLE_ALWAYS_ON_VISIBLE_WORKSPACE_CURRENT);
|
||||
});
|
||||
window_menu.append (on_visible_workspace);
|
||||
|
||||
move_left = new Gtk.MenuItem.with_label (_("Move to Workspace Left"));
|
||||
move_left.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.MOVE_CURRENT_WORKSPACE_LEFT);
|
||||
});
|
||||
window_menu.append (move_left);
|
||||
|
||||
move_right = new Gtk.MenuItem.with_label (_("Move to Workspace Right"));
|
||||
move_right.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.MOVE_CURRENT_WORKSPACE_RIGHT);
|
||||
});
|
||||
window_menu.append (move_right);
|
||||
|
||||
close = new Gtk.MenuItem.with_label (_("Close"));
|
||||
close.activate.connect (() => {
|
||||
perform_action (Gala.ActionType.CLOSE_CURRENT);
|
||||
});
|
||||
window_menu.append (close);
|
||||
|
||||
window_menu.show_all ();
|
||||
}
|
||||
|
||||
public void show_window_menu (Gala.WindowFlags flags, int x, int y) throws DBusError, IOError
|
||||
{
|
||||
if (window_menu == null) {
|
||||
init_window_menu ();
|
||||
}
|
||||
|
||||
minimize.visible = Gala.WindowFlags.CAN_MINIMIZE in flags;
|
||||
maximize.visible = Gala.WindowFlags.CAN_MAXIMIZE in flags;
|
||||
maximize.label = Gala.WindowFlags.IS_MAXIMIZED in flags ? _("Unmaximize") : _("Maximize");
|
||||
move.visible = Gala.WindowFlags.ALLOWS_MOVE in flags;
|
||||
resize.visible = Gala.WindowFlags.ALLOWS_RESIZE in flags;
|
||||
always_on_top.active = Gala.WindowFlags.ALWAYS_ON_TOP in flags;
|
||||
on_visible_workspace.active = Gala.WindowFlags.ON_ALL_WORKSPACES in flags;
|
||||
move_right.visible = !on_visible_workspace.active;
|
||||
move_left.visible = !on_visible_workspace.active;
|
||||
close.visible = Gala.WindowFlags.CAN_CLOSE in flags;
|
||||
|
||||
window_menu.popup (null, null, (m, ref px, ref py, out push_in) => {
|
||||
var scale = m.scale_factor;
|
||||
px = x / scale;
|
||||
// Move the menu 1 pixel outside of the pointer or else it closes instantly
|
||||
// on the mouse up event
|
||||
py = (y / scale) + 1;
|
||||
push_in = true;
|
||||
}, 3, Gdk.CURRENT_TIME);
|
||||
}
|
||||
}
|
||||
}
|
11
daemon/meson.build
Normal file
11
daemon/meson.build
Normal file
@ -0,0 +1,11 @@
|
||||
gala_daemon_sources = files(
|
||||
'Main.vala',
|
||||
'MenuDaemon.vala'
|
||||
)
|
||||
|
||||
gala_daemon_bin = executable(
|
||||
'gala-daemon',
|
||||
gala_daemon_sources,
|
||||
dependencies: [gala_dep, gala_base_dep],
|
||||
install: true,
|
||||
)
|
13
data/gala-daemon.desktop
Normal file
13
data/gala-daemon.desktop
Normal file
@ -0,0 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Gala Background Services
|
||||
Comment=Gala Background Services
|
||||
Exec=gala-daemon
|
||||
Terminal=false
|
||||
Categories=GNOME;GTK;System;Core;
|
||||
NoDisplay=true
|
||||
StartupNotify=true
|
||||
X-GNOME-AutoRestart=true
|
||||
X-GNOME-Autostart-Notify=true
|
||||
X-GNOME-Autostart-Phase=Desktop
|
||||
OnlyShowIn=Pantheon
|
@ -24,7 +24,7 @@ i18n.merge_file(
|
||||
install: true,
|
||||
install_dir: join_paths(data_dir, 'applications')
|
||||
)
|
||||
install_data(['gala.desktop', 'gala-wayland.desktop'], install_dir: join_paths(data_dir, 'applications'))
|
||||
install_data(['gala.desktop', 'gala-daemon.desktop', 'gala-wayland.desktop'], install_dir: join_paths(data_dir, 'applications'))
|
||||
|
||||
icons_dir = join_paths(get_option('datadir'), 'icons', 'hicolor')
|
||||
install_data('icons/32x32/multitasking-view.svg', install_dir: join_paths(icons_dir, '32x32', 'apps'))
|
||||
|
@ -27,7 +27,28 @@ namespace Gala
|
||||
CUSTOM_COMMAND,
|
||||
WINDOW_OVERVIEW,
|
||||
WINDOW_OVERVIEW_ALL,
|
||||
SWITCH_TO_WORKSPACE_LAST
|
||||
SWITCH_TO_WORKSPACE_LAST,
|
||||
START_MOVE_CURRENT,
|
||||
START_RESIZE_CURRENT,
|
||||
TOGGLE_ALWAYS_ON_TOP_CURRENT,
|
||||
TOGGLE_ALWAYS_ON_VISIBLE_WORKSPACE_CURRENT,
|
||||
MOVE_CURRENT_WORKSPACE_LEFT,
|
||||
MOVE_CURRENT_WORKSPACE_RIGHT,
|
||||
CLOSE_CURRENT
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum WindowFlags
|
||||
{
|
||||
NONE = 0,
|
||||
CAN_MINIMIZE,
|
||||
CAN_MAXIMIZE,
|
||||
IS_MAXIMIZED,
|
||||
ALLOWS_MOVE,
|
||||
ALLOWS_RESIZE,
|
||||
ALWAYS_ON_TOP,
|
||||
ON_ALL_WORKSPACES,
|
||||
CAN_CLOSE
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,4 +169,3 @@ namespace Gala
|
||||
public abstract void switch_to_next_workspace (Meta.MotionDirection direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,6 +261,7 @@ gala_base_dep = [glib_dep, gee_dep, gtk_dep, plank_dep, bamf_dep, mutter_dep, gr
|
||||
subdir('data')
|
||||
subdir('lib')
|
||||
subdir('src')
|
||||
subdir('daemon')
|
||||
subdir('plugins/maskcorners')
|
||||
subdir('plugins/notify')
|
||||
subdir('plugins/pip')
|
||||
|
@ -1,5 +1,5 @@
|
||||
src/WindowManager.vala
|
||||
src/Widgets/WindowMenu.vala
|
||||
daemon/MenuDaemon.vala
|
||||
data/gala-multitaskingview.desktop.in
|
||||
data/gala-other.desktop.in
|
||||
data/org.pantheon.desktop.gala.gschema.xml.in
|
||||
|
@ -1,157 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2014 Gala Developers
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Authored By: Tom Beckmann
|
||||
//
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
/**
|
||||
* GtkMenu that is spawned on windows e.g. by rightclick on titlebar
|
||||
* Prior to mutter3.14 this was provided by libmutter
|
||||
*/
|
||||
public class WindowMenu : Gtk.Menu
|
||||
{
|
||||
ulong always_on_top_handler_id;
|
||||
ulong on_visible_workspace_handler_id;
|
||||
|
||||
public Meta.Window current_window {
|
||||
get {
|
||||
return _current_window;
|
||||
}
|
||||
set {
|
||||
SignalHandler.block (always_on_top, always_on_top_handler_id);
|
||||
SignalHandler.block (on_visible_workspace, on_visible_workspace_handler_id);
|
||||
|
||||
_current_window = value;
|
||||
update_window ();
|
||||
|
||||
SignalHandler.unblock (always_on_top, always_on_top_handler_id);
|
||||
SignalHandler.unblock (on_visible_workspace, on_visible_workspace_handler_id);
|
||||
}
|
||||
}
|
||||
|
||||
Meta.Window _current_window;
|
||||
|
||||
Gtk.MenuItem minimize;
|
||||
Gtk.MenuItem maximize;
|
||||
Gtk.MenuItem move;
|
||||
Gtk.MenuItem resize;
|
||||
Gtk.CheckMenuItem always_on_top;
|
||||
Gtk.CheckMenuItem on_visible_workspace;
|
||||
Gtk.MenuItem move_left;
|
||||
Gtk.MenuItem move_right;
|
||||
Gtk.MenuItem close;
|
||||
|
||||
public WindowMenu ()
|
||||
{
|
||||
}
|
||||
|
||||
construct
|
||||
{
|
||||
minimize = new Gtk.MenuItem.with_label (_("Minimize"));
|
||||
minimize.activate.connect (() => {
|
||||
current_window.minimize ();
|
||||
});
|
||||
append (minimize);
|
||||
|
||||
maximize = new Gtk.MenuItem.with_label ("");
|
||||
maximize.activate.connect (() => {
|
||||
if (current_window.get_maximized () > 0)
|
||||
current_window.unmaximize (Meta.MaximizeFlags.BOTH);
|
||||
else
|
||||
current_window.maximize (Meta.MaximizeFlags.BOTH);
|
||||
});
|
||||
append (maximize);
|
||||
|
||||
move = new Gtk.MenuItem.with_label (_("Move"));
|
||||
move.activate.connect (() => {
|
||||
current_window.begin_grab_op (Meta.GrabOp.KEYBOARD_MOVING, true,
|
||||
Gtk.get_current_event_time ());
|
||||
});
|
||||
append (move);
|
||||
|
||||
resize = new Gtk.MenuItem.with_label (_("Resize"));
|
||||
resize.activate.connect (() => {
|
||||
current_window.begin_grab_op (Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, true,
|
||||
Gtk.get_current_event_time ());
|
||||
});
|
||||
append (resize);
|
||||
|
||||
always_on_top = new Gtk.CheckMenuItem.with_label (_("Always on Top"));
|
||||
always_on_top_handler_id = always_on_top.activate.connect (() => {
|
||||
if (current_window.is_above ())
|
||||
current_window.unmake_above ();
|
||||
else
|
||||
current_window.make_above ();
|
||||
});
|
||||
append (always_on_top);
|
||||
|
||||
on_visible_workspace = new Gtk.CheckMenuItem.with_label (_("Always on Visible Workspace"));
|
||||
on_visible_workspace_handler_id = on_visible_workspace.activate.connect (() => {
|
||||
if (current_window.on_all_workspaces)
|
||||
current_window.unstick ();
|
||||
else
|
||||
current_window.stick ();
|
||||
});
|
||||
append (on_visible_workspace);
|
||||
|
||||
move_left = new Gtk.MenuItem.with_label (_("Move to Workspace Left"));
|
||||
move_left.activate.connect (() => {
|
||||
var wp = current_window.get_workspace ().get_neighbor (Meta.MotionDirection.LEFT);
|
||||
if (wp != null)
|
||||
current_window.change_workspace (wp);
|
||||
});
|
||||
append (move_left);
|
||||
|
||||
move_right = new Gtk.MenuItem.with_label (_("Move to Workspace Right"));
|
||||
move_right.activate.connect (() => {
|
||||
var wp = current_window.get_workspace ().get_neighbor (Meta.MotionDirection.RIGHT);
|
||||
if (wp != null)
|
||||
current_window.change_workspace (wp);
|
||||
});
|
||||
append (move_right);
|
||||
|
||||
close = new Gtk.MenuItem.with_label (_("Close"));
|
||||
close.activate.connect (() => {
|
||||
current_window.@delete (Gtk.get_current_event_time ());
|
||||
});
|
||||
append (close);
|
||||
}
|
||||
|
||||
void update_window ()
|
||||
{
|
||||
minimize.visible = current_window.can_minimize ();
|
||||
|
||||
maximize.visible = current_window.can_maximize ();
|
||||
maximize.label = current_window.get_maximized () > 0 ? _("Unmaximize") : _("Maximize");
|
||||
|
||||
move.visible = current_window.allows_move ();
|
||||
|
||||
resize.visible = current_window.allows_resize ();
|
||||
|
||||
always_on_top.active = current_window.is_above ();
|
||||
|
||||
on_visible_workspace.active = current_window.on_all_workspaces;
|
||||
|
||||
move_right.visible = !current_window.on_all_workspaces;
|
||||
|
||||
move_left.visible = !current_window.on_all_workspaces;
|
||||
|
||||
close.visible = current_window.can_close ();
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,15 @@ using Meta;
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
const string DAEMON_DBUS_NAME = "org.pantheon.gala.daemon";
|
||||
const string DAEMON_DBUS_OBJECT_PATH = "/org/pantheon/gala/daemon";
|
||||
|
||||
[DBus (name = "org.pantheon.gala.daemon")]
|
||||
public interface Daemon: GLib.Object
|
||||
{
|
||||
public abstract async void show_window_menu (WindowFlags flags, int x, int y) throws Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.login1.Manager")]
|
||||
public interface LoginDRemote : GLib.Object
|
||||
{
|
||||
@ -83,6 +92,7 @@ namespace Gala
|
||||
Window? moving; //place for the window that is being moved over
|
||||
|
||||
LoginDRemote? logind_proxy = null;
|
||||
Daemon? daemon_proxy = null;
|
||||
|
||||
Gee.LinkedList<ModalProxy> modal_stack = new Gee.LinkedList<ModalProxy> ();
|
||||
|
||||
@ -121,6 +131,29 @@ namespace Gala
|
||||
warning ("Failed to get LoginD proxy: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
Bus.watch_name (BusType.SESSION, DAEMON_DBUS_NAME, BusNameWatcherFlags.NONE, daemon_appeared, lost_daemon);
|
||||
}
|
||||
|
||||
void on_menu_get (GLib.Object? o, GLib.AsyncResult? res)
|
||||
{
|
||||
try {
|
||||
daemon_proxy = Bus.get_proxy.end (res);
|
||||
} catch (Error e) {
|
||||
warning ("Failed to get Menu proxy: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
void lost_daemon ()
|
||||
{
|
||||
daemon_proxy = null;
|
||||
}
|
||||
|
||||
void daemon_appeared ()
|
||||
{
|
||||
if (daemon_proxy == null) {
|
||||
Bus.get_proxy.begin<Daemon> (BusType.SESSION, DAEMON_DBUS_NAME, DAEMON_DBUS_OBJECT_PATH, 0, null, on_menu_get);
|
||||
}
|
||||
}
|
||||
|
||||
void prepare_for_sleep (bool suspending)
|
||||
@ -701,6 +734,50 @@ namespace Gala
|
||||
if (current != null && current.window_type == WindowType.NORMAL)
|
||||
current.minimize ();
|
||||
break;
|
||||
case ActionType.START_MOVE_CURRENT:
|
||||
if (current != null && current.allows_move ())
|
||||
current.begin_grab_op (Meta.GrabOp.KEYBOARD_MOVING, true, Gtk.get_current_event_time ());
|
||||
break;
|
||||
case ActionType.START_RESIZE_CURRENT:
|
||||
if (current != null && current.allows_resize ())
|
||||
current.begin_grab_op (Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, true, Gtk.get_current_event_time ());
|
||||
break;
|
||||
case ActionType.TOGGLE_ALWAYS_ON_TOP_CURRENT:
|
||||
if (current == null)
|
||||
break;
|
||||
|
||||
if (current.is_above ())
|
||||
current.unmake_above ();
|
||||
else
|
||||
current.make_above ();
|
||||
break;
|
||||
case ActionType.TOGGLE_ALWAYS_ON_VISIBLE_WORKSPACE_CURRENT:
|
||||
if (current == null)
|
||||
break;
|
||||
|
||||
if (current.on_all_workspaces)
|
||||
current.unstick ();
|
||||
else
|
||||
current.stick ();
|
||||
break;
|
||||
case ActionType.MOVE_CURRENT_WORKSPACE_LEFT:
|
||||
if (current != null) {
|
||||
var wp = current.get_workspace ().get_neighbor (Meta.MotionDirection.LEFT);
|
||||
if (wp != null)
|
||||
current.change_workspace (wp);
|
||||
}
|
||||
break;
|
||||
case ActionType.MOVE_CURRENT_WORKSPACE_RIGHT:
|
||||
if (current != null) {
|
||||
var wp = current.get_workspace ().get_neighbor (Meta.MotionDirection.RIGHT);
|
||||
if (wp != null)
|
||||
current.change_workspace (wp);
|
||||
}
|
||||
break;
|
||||
case ActionType.CLOSE_CURRENT:
|
||||
if (current != null && current.can_close ())
|
||||
current.@delete (Gtk.get_current_event_time ());
|
||||
break;
|
||||
case ActionType.OPEN_LAUNCHER:
|
||||
try {
|
||||
Process.spawn_command_line_async (BehaviorSettings.get_default ().panel_main_menu_action);
|
||||
@ -767,23 +844,44 @@ namespace Gala
|
||||
}
|
||||
}
|
||||
|
||||
WindowMenu? window_menu = null;
|
||||
|
||||
public override void show_window_menu (Meta.Window window, Meta.WindowMenuType menu, int x, int y)
|
||||
{
|
||||
var time = get_screen ().get_display ().get_current_time_roundtrip ();
|
||||
|
||||
switch (menu) {
|
||||
case WindowMenuType.WM:
|
||||
if (window_menu == null)
|
||||
window_menu = new WindowMenu ();
|
||||
if (daemon_proxy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window_menu.current_window = window;
|
||||
window_menu.show_all ();
|
||||
window_menu.popup (null, null, (menu, ref menu_x, ref menu_y, out push_in) => {
|
||||
menu_x = x;
|
||||
menu_y = y;
|
||||
}, Gdk.BUTTON_SECONDARY, time);
|
||||
WindowFlags flags = WindowFlags.NONE;
|
||||
if (window.can_minimize ())
|
||||
flags |= WindowFlags.CAN_MINIMIZE;
|
||||
|
||||
if (window.can_maximize ())
|
||||
flags |= WindowFlags.CAN_MAXIMIZE;
|
||||
|
||||
if (window.get_maximized () > 0)
|
||||
flags |= WindowFlags.IS_MAXIMIZED;
|
||||
|
||||
if (window.allows_move ())
|
||||
flags |= WindowFlags.ALLOWS_MOVE;
|
||||
|
||||
if (window.allows_resize ())
|
||||
flags |= WindowFlags.ALLOWS_RESIZE;
|
||||
|
||||
if (window.is_above ())
|
||||
flags |= WindowFlags.ALWAYS_ON_TOP;
|
||||
|
||||
if (window.on_all_workspaces)
|
||||
flags |= WindowFlags.ON_ALL_WORKSPACES;
|
||||
|
||||
if (window.can_close ())
|
||||
flags |= WindowFlags.CAN_CLOSE;
|
||||
|
||||
try {
|
||||
daemon_proxy.show_window_menu.begin (flags, x, y);
|
||||
} catch (Error e) {
|
||||
message ("Error invoking MenuManager: %s", e.message);
|
||||
}
|
||||
break;
|
||||
case WindowMenuType.APP:
|
||||
// FIXME we don't have any sort of app menus
|
||||
|
@ -33,7 +33,6 @@ gala_bin_sources = files(
|
||||
'Widgets/WindowClone.vala',
|
||||
'Widgets/WindowCloneContainer.vala',
|
||||
'Widgets/WindowIconActor.vala',
|
||||
'Widgets/WindowMenu.vala',
|
||||
'Widgets/WindowOverview.vala',
|
||||
'Widgets/WindowSwitcher.vala',
|
||||
'Widgets/WorkspaceClone.vala',
|
||||
|
Loading…
Reference in New Issue
Block a user