Fix various little problems, add track_actor functionality, add extended

plugin info allowing plugins to replace built-in componenets, allow them
to delay their load.
This commit is contained in:
Tom Beckmann 2014-03-12 20:06:23 +01:00
parent d8ac8c372e
commit e26d4274a0
9 changed files with 481 additions and 132 deletions

View File

@ -17,11 +17,179 @@
namespace Gala namespace Gala
{ {
public interface Plugin : Object public enum PluginFunction
{ {
public abstract X.Xrectangle[] region { get; protected set; } ADDITION,
WINDOW_SWITCHER,
DESKTOP,
WORKSPACE_VIEW,
WINDOW_OVERVIEW
}
public struct PluginInfo
{
string name;
string author;
/**
* Type of your plugin class, has to be derived from the Plugin class.
*/
Type plugin_type;
/**
* This property allows you to override default functionality of gala
* so systems won't be instantiated next to each other. Use
* PluginFunction.ADDITION if no special component is overridden.
*/
PluginFunction provides;
/**
* Indicate that your plugin does not need to be started immediately on
* gala's launch but can wait until there's a bit less stuff going on.
* Especially use this if you're adding a completely new ui component.
*/
bool load_can_wait;
/**
* You don't have to fill this field, it will be filled by gala with
* the filename in which your module was found.
*/
string module_name;
}
/**
* This class has to be implemented by every plugin.
* Additionally, the plugin module is required to have a register_plugin
* function which returns a PluginInfo struct.
* The plugin_type field has to be the type of your plugin class derived
* from this class.
*/
public abstract class Plugin : Object
{
/**
* The region indicates an area where mouse events should be sent to
* the stage, which means your actors, instead of the windows.
*
* It is calculated by the system whenever update_region is called.
* You can influce it with the custom_region and the track_actor function.
*/
public X.Xrectangle[] region { get; set; }
/**
* This list will be merged with the region property. See region for
* more details. Changing this property will cause update_region to be
* called. Default to null.
*/
protected X.Xrectangle[]? custom_region {
get {
return _custom_region;
}
protected set {
_custom_region = value;
update_region ();
}
}
/**
* Set this property to true while animating an actor if you have tracked
* actors to prevent constant recalculations of the regions during an
* animation.
*/
protected bool freeze_track { get; protected set; default = false; }
private X.Xrectangle[]? _custom_region = null;
private Gee.LinkedList<Clutter.Actor> tracked_actors = new Gee.LinkedList<Clutter.Actor> ();
/**
* Emitted when update_region is called. Mainly for internal purposes.
*/
public signal void region_changed ();
/**
* Once this method is called you can start adding actors to the stage
* using via the windowmanager instance that is given to you.
*
* @param wm The window manager.
*/
public abstract void initialize (WindowManager wm); public abstract void initialize (WindowManager wm);
/**
* This method is currently not called in the code, however you should
* still implement it to be compatible whenever we decide to use it.
* It should make sure that everything your plugin added to the stage
* is cleaned up.
*/
public abstract void destroy (); public abstract void destroy ();
/**
* Listen to changes to the allocation of actor and update the region
* accordingly. You may add multiple actors, their shapes will be
* combined when one of them changes.
*
* @param actor The actor to be tracked
*/
public void track_actor (Clutter.Actor actor)
{
tracked_actors.add (actor);
actor.allocation_changed.connect (actor_allocation_changed);
update_region ();
}
/**
* Stop listening to allocation changes and remove the actor's
* allocation from the region array.
*/
public void untrack_actor (Clutter.Actor actor)
{
if (tracked_actors.remove (actor)) {
actor.allocation_changed.disconnect (actor_allocation_changed);
}
}
/**
* You can call this method to force the system to update the region that
* is used by the window manager. It will automatically upon changes to
* the custom_region property and when a tracked actor's allocation changes
* unless freeze_track is set to true. You may need to call this function
* after setting freeze_track back to false after an animation to make the
* wm aware of the new position of the actor in question.
*/
public void update_region ()
{
var has_custom = custom_region != null;
var len = tracked_actors.size + (has_custom ? custom_region.length : 0);
X.Xrectangle[] regions = new X.Xrectangle[len];
var i = 0;
if (has_custom) {
for (var j = 0; j < custom_region.length; j++) {
regions[i++] = custom_region[j];
}
}
foreach (var actor in tracked_actors) {
float x, y, w, h;
actor.get_transformed_position (out x, out y);
actor.get_transformed_size (out w, out h);
if (w == 0 || h == 0)
continue;
regions[i++] = { (short)x, (short)y, (short)w, (short)h };
}
region = regions;
region_changed ();
}
private void actor_allocation_changed (Clutter.ActorBox box, Clutter.AllocationFlags f)
{
if (!freeze_track)
update_region ();
}
} }
} }

View File

@ -47,6 +47,7 @@ namespace Gala
public abstract void perform_action (ActionType type); public abstract void perform_action (ActionType type);
public abstract void update_input_area (); public abstract void update_input_area ();
public abstract void move_window (Meta.Window? window, Meta.MotionDirection direction); public abstract void move_window (Meta.Window? window, Meta.MotionDirection direction);
public abstract void switch_to_next_workspace (Meta.MotionDirection direction);
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Copyright (C) 2012 Tom Beckmann // Copyright (C) 2014 Tom Beckmann
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
@ -15,6 +15,13 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// note on compilation: If you want your own plugin within this source tree
// don't forget to add the new subdirectory to the plugins' Makefile.am
// SUBDIRS list and add your Makefile to the list of Makefiles found at
// about the end of the configure.ac file AC_CONFIG_FILES.
// The API is currently internal until the API is finalized, so you have
// to build it in this source tree.
/* /*
This is a template class showing some of the things that can be done This is a template class showing some of the things that can be done
with a gala plugin and how to do them. with a gala plugin and how to do them.
@ -22,10 +29,8 @@
namespace Gala.Plugins.Template namespace Gala.Plugins.Template
{ {
public class Main : Object, Gala.Plugin public class Main : Gala.Plugin
{ {
public X.Xrectangle[] region { get; protected set; default = {}; }
Gala.WindowManager? wm = null; Gala.WindowManager? wm = null;
const int PADDING = 50; const int PADDING = 50;
@ -33,7 +38,7 @@ namespace Gala.Plugins.Template
// This function is called as soon as Gala has started and gives you // This function is called as soon as Gala has started and gives you
// an instance of the GalaWindowManager class. // an instance of the GalaWindowManager class.
public void initialize (Gala.WindowManager wm) public override void initialize (Gala.WindowManager wm)
{ {
// we will save the instance to our wm property so we can use it later again // we will save the instance to our wm property so we can use it later again
// especially helpful when you have larger plugins with more functions, // especially helpful when you have larger plugins with more functions,
@ -58,18 +63,16 @@ namespace Gala.Plugins.Template
red_box.y = rect.y + rect.height - red_box.height - PADDING; red_box.y = rect.y + rect.height - red_box.height - PADDING;
// to order Gala to deliver mouse events to our box instead of the underlying // to order Gala to deliver mouse events to our box instead of the underlying
// windows, we need to mark the region where the quad is located. We do so // windows, we need to mark the region where the quad is located.
// by setting the region property. If you have multiple areas, you can just // The plugin class offers an utility function for this purpose, the track_actor
// put more rectangle in this array. // function. It will update the region with the allocation of the actor
// Gala will listen to updates on this property and update all the areas // whenever its allocation changes. Make sure to set freeze_track to
// any time this changes for any plugin, so don't change this excessively like // true while animating the actor to not make gala update the region
// for example if you play an animation, only set the area on the destination // every single frame.
// point of the animation instead of making it follow the animated element. // You can also handle the region manually by setting the custom_region
X.Xrectangle red_box_area = { // property. The tracked actors and custom regions will be merged by
(short)red_box.x, (short)red_box.y, // the plugin.
(short)red_box.width, (short)red_box.height track_actor (red_box);
};
region = { red_box_area };
// now we'll add our box into the ui_group. This is where all the shell // now we'll add our box into the ui_group. This is where all the shell
// elements and also the windows and backgrouds are located. // elements and also the windows and backgrouds are located.
@ -86,7 +89,7 @@ namespace Gala.Plugins.Template
// still it might be a good idea to implement it anyway to make sure // still it might be a good idea to implement it anyway to make sure
// your plugin is compatible in case we'd add disabling specific plugins // your plugin is compatible in case we'd add disabling specific plugins
// in the future // in the future
public void destroy () public override void destroy ()
{ {
// here you would destroy actors you added to the stage or remove // here you would destroy actors you added to the stage or remove
// keybindings // keybindings
@ -97,11 +100,27 @@ namespace Gala.Plugins.Template
} }
// this little function just tells Gala which class of those you may have in // this little function just tells Gala which class of those you may have in
// your plugin is the one you want to start with. Make sure it's public and // your plugin is the one you want to start with and delivers some additional
// returning the type of the right class // details about your plugin. It also gives you the option to choose a specific
public Type register_plugin () // function which your plugin fulfils. Gala will then make sure that there is
// no duplicate functionality.
public Gala.PluginInfo register_plugin ()
{ {
return typeof (Gala.Plugins.Template.Main); return {
"template-plugin", // the plugin's name
"Tom Beckmann <tomjonabc@gmail.com>", // you, the author
typeof (Gala.Plugins.Template.Main), // the type of your plugin class
Gala.PluginFunction.ADDITION, // the function which your plugin
// fulfils, ADDITION means nothing
// specific
false // indicates whether your plugin's
// start can be delayed until gala
// has loaded the important stuff or
// if you want your plugin to start
// right away. False means wait.
};
} }
/* /*

View File

@ -0,0 +1,58 @@
include $(top_srcdir)/Makefile.common
VAPIDIR = $(top_srcdir)/vapi
BUILT_SOURCES = libgala_template_la_vala.stamp
libgala_template_la_LTLIBRARIES = libgala-template.la
libgala_template_ladir = $(pkglibdir)/plugins
libgala_template_la_LDFLAGS = \
$(PLUGIN_LDFLAGS) \
$(GALA_CORE_LDFLAGS) \
$(top_builddir)/lib/libgala.la \
$(NULL)
libgala_template_la_CFLAGS = \
$(GALA_CORE_CFLAGS) \
-include config.h \
-w \
-I$(top_builddir)/lib \
$(NULL)
libgala_template_la_VALAFLAGS = \
$(GALA_CORE_VALAFLAGS) \
$(top_builddir)/lib/gala.vapi \
--vapidir $(VAPIDIR) \
$(VAPIDIR)/config.vapi \
$(NULL)
libgala_template_la_LIBADD = \
$(GALA_CORE_LIBS) \
$(NULL)
libgala_template_la_VALASOURCES = \
Main.vala \
$(NULL)
nodist_libgala_template_la_SOURCES = \
$(BUILT_SOURCES) \
$(libgala_template_la_VALASOURCES:.vala=.c) \
$(NULL)
libgala_template_la_vala.stamp: $(libgala_template_la_VALASOURCES)
$(AM_V_VALA)$(VALAC) \
$(libgala_template_la_VALAFLAGS) \
-C \
$(filter %.vala %.c,$^)
$(AM_V_at)touch $@
CLEANFILES = \
$(nodist_libgala_template_la_SOURCES) \
$(NULL)
EXTRA_DIST = \
$(libgala_template_la_VALASOURCES) \
$(NULL)

View File

@ -17,16 +17,15 @@
namespace Gala.Plugins.Zoom namespace Gala.Plugins.Zoom
{ {
public class Main : Object, Gala.Plugin public class Main : Gala.Plugin
{ {
public X.Xrectangle[] region { get; protected set; default = {}; }
Gala.WindowManager? wm = null; Gala.WindowManager? wm = null;
const uint MOUSE_POLL_TIME = 50; const uint MOUSE_POLL_TIME = 50;
uint mouse_poll_timer = 0; uint mouse_poll_timer = 0;
float current_zoom = 1.0f; float current_zoom = 1.0f;
public void initialize (Gala.WindowManager wm) public override void initialize (Gala.WindowManager wm)
{ {
this.wm = wm; this.wm = wm;
var display = wm.get_screen ().get_display (); var display = wm.get_screen ().get_display ();
@ -36,7 +35,7 @@ namespace Gala.Plugins.Zoom
display.add_keybinding ("zoom-out", schema, 0, zoom_out); display.add_keybinding ("zoom-out", schema, 0, zoom_out);
} }
public void destroy () public override void destroy ()
{ {
if (wm == null) if (wm == null)
return; return;
@ -115,8 +114,14 @@ namespace Gala.Plugins.Zoom
} }
} }
public Type register_plugin () public Gala.PluginInfo register_plugin ()
{ {
return typeof (Gala.Plugins.Zoom.Main); return Gala.PluginInfo () {
name = "Zoom",
author = "Gala Developers",
plugin_type = typeof (Gala.Plugins.Zoom.Main),
provides = Gala.PluginFunction.ADDITION,
load_can_wait = false
};
} }

View File

@ -99,6 +99,7 @@ namespace Gala
// add plugin's requested areas // add plugin's requested areas
if (area == InputArea.FULLSCREEN || area == InputArea.HOT_CORNER) { if (area == InputArea.FULLSCREEN || area == InputArea.HOT_CORNER) {
foreach (var rect in PluginManager.get_default ().get_all_regions ()) { foreach (var rect in PluginManager.get_default ().get_all_regions ()) {
print ("%i %i %i %i\n", rect.x, rect.y, rect.width, rect.height);
rects += rect; rects += rect;
} }
} }

View File

@ -17,7 +17,7 @@
namespace Gala namespace Gala
{ {
delegate Type RegisterPluginFunction (); delegate PluginInfo RegisterPluginFunction ();
public class PluginManager : Object public class PluginManager : Object
{ {
@ -28,11 +28,21 @@ namespace Gala
public bool initialized { get; private set; default = false; } public bool initialized { get; private set; default = false; }
public signal void regions_changed ();
X.Xrectangle[] regions; X.Xrectangle[] regions;
public string? window_switcher_provider { get; private set; default = null; }
public string? desktop_provider { get; private set; default = null; }
public string? window_overview_provider { get; private set; default = null; }
public string? workspace_view_provider { get; private set; default = null; }
Gee.LinkedList<PluginInfo?> load_later_plugins;
PluginManager () PluginManager ()
{ {
plugins = new HashTable<string,Plugin> (str_hash, str_equal); plugins = new HashTable<string,Plugin> (str_hash, str_equal);
load_later_plugins = new Gee.LinkedList<PluginInfo?> ();
if (!Module.supported ()) { if (!Module.supported ()) {
warning ("Modules are not supported on this platform"); warning ("Modules are not supported on this platform");
@ -44,10 +54,12 @@ namespace Gala
return; return;
try { try {
var enumerator = plugin_dir.enumerate_children (FileAttribute.STANDARD_NAME, 0); var enumerator = plugin_dir.enumerate_children (FileAttribute.STANDARD_NAME +
"," + FileAttribute.STANDARD_CONTENT_TYPE, 0);
FileInfo info; FileInfo info;
while ((info = enumerator.next_file ()) != null) { while ((info = enumerator.next_file ()) != null) {
load_module (info.get_name ()); if (info.get_content_type () == "application/x-sharedlib")
load_module (info.get_name ());
} }
} catch (Error e) { } catch (Error e) {
warning (e.message); warning (e.message);
@ -66,48 +78,102 @@ namespace Gala
bool load_module (string plugin_name) bool load_module (string plugin_name)
{ {
var path = Module.build_path (plugin_dir.get_path (), plugin_name); var path = Module.build_path (plugin_dir.get_path (), plugin_name);
var module = Module.open (path, ModuleFlags.BIND_LOCAL); var module = Module.open (path, ModuleFlags.BIND_LOCAL);
if (module == null) { if (module == null) {
warning (Module.error ()); warning (Module.error ());
return false; return false;
} }
void* function; void* function;
module.symbol ("register_plugin", out function); module.symbol ("register_plugin", out function);
if (function == null) { if (function == null) {
warning ("%s failed to register: register_plugin() function not found", plugin_name); warning ("%s failed to register: register_plugin() function not found", plugin_name);
return false; return false;
} }
RegisterPluginFunction register = (RegisterPluginFunction)function; RegisterPluginFunction register = (RegisterPluginFunction)function;
var type = register (); var info = register ();
if (type.is_a (typeof (Plugin)) == false) { if (info.plugin_type.is_a (typeof (Plugin)) == false) {
warning ("%s does not return a class of type Plugin", plugin_name); warning ("%s does not return a class of type Plugin", plugin_name);
return false; return false;
} }
module.make_resident (); if (!check_provides (info.name, info.provides)) {
return false;
}
var plugin = (Plugin)Object.@new (type); info.module_name = plugin_name;
plugins.set (plugin_name, plugin); module.make_resident ();
if (initialized) { if (info.load_can_wait) {
initialize_plugin (plugin_name, plugin); load_later_plugins.add (info);
get_all_regions (true); } else {
} load_plugin_class (info);
}
return true; return true;
} }
void initialize_plugin (string name, Plugin plugin) void load_plugin_class (PluginInfo info)
{
var plugin = (Plugin)Object.@new (info.plugin_type);
plugins.set (info.module_name, plugin);
debug ("Loaded plugin %s (%s)", info.name, info.module_name);
if (initialized) {
initialize_plugin (info.module_name, plugin);
get_all_regions (true);
}
}
void initialize_plugin (string plugin_name, Plugin plugin)
{ {
plugin.initialize (wm); plugin.initialize (wm);
plugin.notify["region"].connect (() => { plugin.region_changed.connect (() => {
get_all_regions (true); get_all_regions (true);
regions_changed ();
}); });
} }
bool check_provides (string name, PluginFunction provides)
{
var message = "Plugins %s and %s both provide %s functionality, using first one only";
switch (provides) {
case PluginFunction.WORKSPACE_VIEW:
if (workspace_view_provider != null) {
warning (message, workspace_view_provider, name, "workspace view");
return false;
}
workspace_view_provider = name;
return true;
case PluginFunction.WINDOW_OVERVIEW:
if (window_overview_provider != null) {
warning (message, window_overview_provider, name, "window overview");
return false;
}
window_overview_provider = name;
return true;
case PluginFunction.DESKTOP:
if (desktop_provider != null) {
warning (message, desktop_provider, name, "desktop");
return false;
}
desktop_provider = name;
return true;
case PluginFunction.WINDOW_SWITCHER:
if (window_switcher_provider != null) {
warning (message, window_switcher_provider, name, "window switcher");
return false;
}
window_switcher_provider = name;
return true;
}
return true;
}
public void initialize (WindowManager _wm) public void initialize (WindowManager _wm)
{ {
wm = _wm; wm = _wm;
@ -118,6 +184,13 @@ namespace Gala
initialized = true; initialized = true;
} }
public void load_waiting_plugins ()
{
foreach (var info in load_later_plugins) {
load_plugin_class (info);
}
}
public X.Xrectangle[] get_all_regions (bool update = false) public X.Xrectangle[] get_all_regions (bool update = false)
{ {
if (update) { if (update) {

View File

@ -218,28 +218,6 @@ namespace Gala
} }
} }
void switch_to_next_workspace (MotionDirection direction)
{
var display = screen.get_display ();
var old_index = screen.get_active_workspace_index ();
var neighbor = screen.get_active_workspace ().get_neighbor (direction);
neighbor.activate (display.get_current_time ());
// if we didnt switch, show a nudge-over animation. need to take the indices
// here since the changing only applies after the animation ends
if (old_index == 0 && direction == MotionDirection.LEFT ||
old_index == screen.n_workspaces - 1 && direction == MotionDirection.RIGHT) {
var dest = (direction == MotionDirection.LEFT ? 32.0f : -32.0f);
Compositor.get_window_group_for_screen (screen).animate (Clutter.AnimationMode.LINEAR, 100, x:dest);
Clutter.Threads.Timeout.add (210, () => {
Compositor.get_window_group_for_screen (screen).animate (Clutter.AnimationMode.LINEAR, 150, x:0.0f);
return false;
});
}
}
public override void key_focus_out () public override void key_focus_out ()
{ {
hide (); hide ();
@ -261,7 +239,7 @@ namespace Gala
if ((event.modifier_state & Clutter.ModifierType.SHIFT_MASK) != 0) if ((event.modifier_state & Clutter.ModifierType.SHIFT_MASK) != 0)
wm.move_window (display.get_focus_window (), MotionDirection.LEFT); wm.move_window (display.get_focus_window (), MotionDirection.LEFT);
else else
switch_to_next_workspace (MotionDirection.LEFT); wm.switch_to_next_workspace (MotionDirection.LEFT);
last_switch_time = current_time; last_switch_time = current_time;
@ -270,7 +248,7 @@ namespace Gala
if ((event.modifier_state & Clutter.ModifierType.SHIFT_MASK) != 0) if ((event.modifier_state & Clutter.ModifierType.SHIFT_MASK) != 0)
wm.move_window (display.get_focus_window (), MotionDirection.RIGHT); wm.move_window (display.get_focus_window (), MotionDirection.RIGHT);
else else
switch_to_next_workspace (MotionDirection.RIGHT); wm.switch_to_next_workspace (MotionDirection.RIGHT);
last_switch_time = current_time; last_switch_time = current_time;
@ -454,12 +432,5 @@ namespace Gala
wins.x = 0.0f; wins.x = 0.0f;
wins.animate (Clutter.AnimationMode.EASE_OUT_EXPO, 500, y : 0.0f); wins.animate (Clutter.AnimationMode.EASE_OUT_EXPO, 500, y : 0.0f);
} }
public void handle_switch_to_workspace (Meta.Display display, Meta.Screen screen, Meta.Window? window,
X.Event event, Meta.KeyBinding binding)
{
var direction = (binding.get_name () == "switch-to-workspace-left" ? MotionDirection.LEFT : MotionDirection.RIGHT);
switch_to_next_workspace (direction);
}
} }
} }

View File

@ -21,11 +21,11 @@ namespace Gala
{ {
public class WindowManagerGala : Meta.Plugin, WindowManager public class WindowManagerGala : Meta.Plugin, WindowManager
{ {
PluginInfo info; Meta.PluginInfo info;
WindowSwitcher winswitcher; WindowSwitcher? winswitcher = null;
WorkspaceView workspace_view; WorkspaceView? workspace_view = null;
WindowOverview window_overview; WindowOverview? window_overview = null;
// used to detect which corner was used to trigger an action // used to detect which corner was used to trigger an action
Clutter.Actor? last_hotcorner; Clutter.Actor? last_hotcorner;
@ -47,7 +47,7 @@ namespace Gala
public WindowManagerGala () public WindowManagerGala ()
{ {
info = PluginInfo () {name = "Gala", version = Config.VERSION, author = "Gala Developers", info = Meta.PluginInfo () {name = "Gala", version = Config.VERSION, author = "Gala Developers",
license = "GPLv3", description = "A nice elementary window manager"}; license = "GPLv3", description = "A nice elementary window manager"};
Prefs.set_ignore_request_hide_titlebar (true); Prefs.set_ignore_request_hide_titlebar (true);
@ -77,9 +77,12 @@ namespace Gala
screensaver = Bus.get_proxy_sync (BusType.SESSION, "org.gnome.ScreenSaver", screensaver = Bus.get_proxy_sync (BusType.SESSION, "org.gnome.ScreenSaver",
"/org/gnome/ScreenSaver"); "/org/gnome/ScreenSaver");
screensaver.active_changed.connect (update_input_area); screensaver.active_changed.connect (update_input_area);
} catch (Error e) { warning (e.message); } } catch (Error e) {
screensaver = null;
warning (e.message);
}
var stage = Compositor.get_stage_for_screen (screen) as Clutter.Stage; stage = Compositor.get_stage_for_screen (screen) as Clutter.Stage;
var color = BackgroundSettings.get_default ().primary_color; var color = BackgroundSettings.get_default ().primary_color;
stage.background_color = Clutter.Color.from_string (color); stage.background_color = Clutter.Color.from_string (color);
@ -114,28 +117,12 @@ namespace Gala
window_group.add_child (background_group); window_group.add_child (background_group);
window_group.set_child_below_sibling (background_group, null); window_group.set_child_below_sibling (background_group, null);
workspace_view = new WorkspaceView (this);
workspace_view.visible = false;
winswitcher = new WindowSwitcher (this);
window_overview = new WindowOverview (this);
ui_group.add_child (workspace_view);
ui_group.add_child (winswitcher);
ui_group.add_child (window_overview);
var top_window_group = Compositor.get_top_window_group_for_screen (screen); var top_window_group = Compositor.get_top_window_group_for_screen (screen);
stage.remove_child (top_window_group); stage.remove_child (top_window_group);
ui_group.add_child (top_window_group); ui_group.add_child (top_window_group);
/*keybindings*/ /*keybindings*/
screen.get_display ().add_keybinding ("expose-windows", KeybindingSettings.get_default ().schema, 0, () => {
window_overview.open (true);
});
screen.get_display ().add_keybinding ("expose-all-windows", KeybindingSettings.get_default ().schema, 0, () => {
window_overview.open (true, true);
});
screen.get_display ().add_keybinding ("switch-to-workspace-first", KeybindingSettings.get_default ().schema, 0, () => { screen.get_display ().add_keybinding ("switch-to-workspace-first", KeybindingSettings.get_default ().schema, 0, () => {
screen.get_workspace_by_index (0).activate (screen.get_display ().get_current_time ()); screen.get_workspace_by_index (0).activate (screen.get_display ().get_current_time ());
}); });
@ -182,19 +169,10 @@ namespace Gala
} catch (Error e) { warning (e.message); } } catch (Error e) { warning (e.message); }
}); });
KeyBinding.set_custom_handler ("show-desktop", () => {
workspace_view.show (true);
});
//FIXME we have to investigate this. Apparently alt-tab is now bound to switch-applications
// instead of windows, which we should probably handle too
KeyBinding.set_custom_handler ("switch-applications", winswitcher.handle_switch_windows);
KeyBinding.set_custom_handler ("switch-applications-backward", winswitcher.handle_switch_windows);
KeyBinding.set_custom_handler ("switch-to-workspace-up", () => {}); KeyBinding.set_custom_handler ("switch-to-workspace-up", () => {});
KeyBinding.set_custom_handler ("switch-to-workspace-down", () => {}); KeyBinding.set_custom_handler ("switch-to-workspace-down", () => {});
KeyBinding.set_custom_handler ("switch-to-workspace-left", workspace_view.handle_switch_to_workspace); KeyBinding.set_custom_handler ("switch-to-workspace-left", handle_switch_to_workspace);
KeyBinding.set_custom_handler ("switch-to-workspace-right", workspace_view.handle_switch_to_workspace); KeyBinding.set_custom_handler ("switch-to-workspace-right", handle_switch_to_workspace);
KeyBinding.set_custom_handler ("move-to-workspace-up", () => {}); KeyBinding.set_custom_handler ("move-to-workspace-up", () => {});
KeyBinding.set_custom_handler ("move-to-workspace-down", () => {}); KeyBinding.set_custom_handler ("move-to-workspace-down", () => {});
@ -214,7 +192,43 @@ namespace Gala
BehaviorSettings.get_default ().schema.changed.connect ((key) => update_input_area ()); BehaviorSettings.get_default ().schema.changed.connect ((key) => update_input_area ());
PluginManager.get_default ().initialize (this); // initialize plugins and add default components if no plugin overrides them
var plugin_manager = PluginManager.get_default ();
plugin_manager.initialize (this);
plugin_manager.regions_changed.connect (update_input_area);
if (plugin_manager.workspace_view_provider == null) {
workspace_view = new WorkspaceView (this);
workspace_view.visible = false;
ui_group.add_child (workspace_view);
KeyBinding.set_custom_handler ("show-desktop", () => {
workspace_view.show (true);
});
}
if (plugin_manager.window_switcher_provider == null) {
winswitcher = new WindowSwitcher (this);
ui_group.add_child (winswitcher);
//FIXME we have to investigate this. Apparently alt-tab is now bound to switch-applications
// instead of windows, which we should probably handle too
KeyBinding.set_custom_handler ("switch-applications", winswitcher.handle_switch_windows);
KeyBinding.set_custom_handler ("switch-applications-backward", winswitcher.handle_switch_windows);
}
if (plugin_manager.window_overview_provider == null) {
window_overview = new WindowOverview (this);
ui_group.add_child (window_overview);
screen.get_display ().add_keybinding ("expose-windows", KeybindingSettings.get_default ().schema, 0, () => {
window_overview.open (true);
});
screen.get_display ().add_keybinding ("expose-all-windows", KeybindingSettings.get_default ().schema, 0, () => {
window_overview.open (true, true);
});
}
update_input_area (); update_input_area ();
stage.show (); stage.show ();
@ -222,6 +236,11 @@ namespace Gala
// let the session manager move to the next phase // let the session manager move to the next phase
Meta.register_with_session (); Meta.register_with_session ();
Idle.add (() => {
plugin_manager.load_waiting_plugins ();
return false;
});
return false; return false;
} }
@ -264,14 +283,48 @@ namespace Gala
hot_corner.y = y; hot_corner.y = y;
} }
void handle_switch_to_workspace (Meta.Display display, Meta.Screen screen, Meta.Window? window,
X.Event event, Meta.KeyBinding binding)
{
var direction = (binding.get_name () == "switch-to-workspace-left" ? MotionDirection.LEFT : MotionDirection.RIGHT);
switch_to_next_workspace (direction);
}
public void switch_to_next_workspace (MotionDirection direction)
{
var screen = get_screen ();
var display = screen.get_display ();
var old_index = screen.get_active_workspace_index ();
var neighbor = screen.get_active_workspace ().get_neighbor (direction);
neighbor.activate (display.get_current_time ());
// if we didnt switch, show a nudge-over animation. need to take the indices
// here since the changing only applies after the animation ends
if (old_index == 0 && direction == MotionDirection.LEFT ||
old_index == screen.n_workspaces - 1 && direction == MotionDirection.RIGHT) {
var dest = (direction == MotionDirection.LEFT ? 32.0f : -32.0f);
ui_group.animate (Clutter.AnimationMode.LINEAR, 100, x:dest);
Timeout.add (210, () => {
ui_group.animate (Clutter.AnimationMode.LINEAR, 150, x:0.0f);
return false;
});
}
}
public void update_input_area () public void update_input_area ()
{ {
var schema = BehaviorSettings.get_default ().schema; var schema = BehaviorSettings.get_default ().schema;
var screen = get_screen (); var screen = get_screen ();
if (screensaver != null && screensaver.get_active ()) { if (screensaver != null) {
InternalUtils.set_input_area (screen, InputArea.NONE); try {
return; if (screensaver.get_active ()) {
InternalUtils.set_input_area (screen, InputArea.NONE);
return;
}
} catch (IOError e) { warning (e.message); }
} }
if (modal_count > 0) if (modal_count > 0)
@ -1018,7 +1071,7 @@ namespace Gala
return modal_count > 0; return modal_count > 0;
} }
public override unowned PluginInfo? plugin_info () public override unowned Meta.PluginInfo? plugin_info ()
{ {
return info; return info;
} }