diff --git a/lib/Plugin.vala b/lib/Plugin.vala index 5eeb4a20..78fb33a3 100644 --- a/lib/Plugin.vala +++ b/lib/Plugin.vala @@ -17,11 +17,179 @@ 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 tracked_actors = new Gee.LinkedList (); + + /** + * 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); + + /** + * 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 (); + + /** + * 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 (); + } } } diff --git a/lib/WindowManager.vala b/lib/WindowManager.vala index 8028bfae..40b11eff 100644 --- a/lib/WindowManager.vala +++ b/lib/WindowManager.vala @@ -47,6 +47,7 @@ namespace Gala public abstract void perform_action (ActionType type); public abstract void update_input_area (); public abstract void move_window (Meta.Window? window, Meta.MotionDirection direction); + public abstract void switch_to_next_workspace (Meta.MotionDirection direction); } } diff --git a/plugins/template/Main.vala b/plugins/template/Main.vala index afcbedcb..ef8eb2c1 100644 --- a/plugins/template/Main.vala +++ b/plugins/template/Main.vala @@ -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 // it under the terms of the GNU General Public License as published by @@ -15,6 +15,13 @@ // along with this program. If not, see . // +// 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 with a gala plugin and how to do them. @@ -22,10 +29,8 @@ 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; 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 // 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 // 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; // 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 - // by setting the region property. If you have multiple areas, you can just - // put more rectangle in this array. - // Gala will listen to updates on this property and update all the areas - // any time this changes for any plugin, so don't change this excessively like - // for example if you play an animation, only set the area on the destination - // point of the animation instead of making it follow the animated element. - X.Xrectangle red_box_area = { - (short)red_box.x, (short)red_box.y, - (short)red_box.width, (short)red_box.height - }; - region = { red_box_area }; + // windows, we need to mark the region where the quad is located. + // The plugin class offers an utility function for this purpose, the track_actor + // function. It will update the region with the allocation of the actor + // whenever its allocation changes. Make sure to set freeze_track to + // true while animating the actor to not make gala update the region + // every single frame. + // You can also handle the region manually by setting the custom_region + // property. The tracked actors and custom regions will be merged by + // the plugin. + track_actor (red_box); // 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. @@ -86,7 +89,7 @@ namespace Gala.Plugins.Template // 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 // in the future - public void destroy () + public override void destroy () { // here you would destroy actors you added to the stage or remove // keybindings @@ -97,11 +100,27 @@ namespace Gala.Plugins.Template } // 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 -// returning the type of the right class -public Type register_plugin () +// your plugin is the one you want to start with and delivers some additional +// details about your plugin. It also gives you the option to choose a specific +// 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 ", // 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. + }; } /* diff --git a/plugins/template/Makefile.am b/plugins/template/Makefile.am new file mode 100644 index 00000000..c26b16e5 --- /dev/null +++ b/plugins/template/Makefile.am @@ -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) + diff --git a/plugins/zoom/Main.vala b/plugins/zoom/Main.vala index 34f90ca2..59202f20 100644 --- a/plugins/zoom/Main.vala +++ b/plugins/zoom/Main.vala @@ -17,16 +17,15 @@ 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; const uint MOUSE_POLL_TIME = 50; uint mouse_poll_timer = 0; float current_zoom = 1.0f; - public void initialize (Gala.WindowManager wm) + public override void initialize (Gala.WindowManager wm) { this.wm = wm; var display = wm.get_screen ().get_display (); @@ -36,7 +35,7 @@ namespace Gala.Plugins.Zoom display.add_keybinding ("zoom-out", schema, 0, zoom_out); } - public void destroy () + public override void destroy () { if (wm == null) 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 + }; } diff --git a/src/InternalUtils.vala b/src/InternalUtils.vala index ec60a656..df0eef9c 100644 --- a/src/InternalUtils.vala +++ b/src/InternalUtils.vala @@ -99,6 +99,7 @@ namespace Gala // add plugin's requested areas if (area == InputArea.FULLSCREEN || area == InputArea.HOT_CORNER) { 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; } } diff --git a/src/PluginManager.vala b/src/PluginManager.vala index 95f44830..043b09ea 100644 --- a/src/PluginManager.vala +++ b/src/PluginManager.vala @@ -17,7 +17,7 @@ namespace Gala { - delegate Type RegisterPluginFunction (); + delegate PluginInfo RegisterPluginFunction (); public class PluginManager : Object { @@ -28,11 +28,21 @@ namespace Gala public bool initialized { get; private set; default = false; } + public signal void regions_changed (); + 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 load_later_plugins; + PluginManager () { plugins = new HashTable (str_hash, str_equal); + load_later_plugins = new Gee.LinkedList (); if (!Module.supported ()) { warning ("Modules are not supported on this platform"); @@ -44,10 +54,12 @@ namespace Gala return; 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; 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) { warning (e.message); @@ -66,48 +78,102 @@ namespace Gala bool load_module (string plugin_name) { - var path = Module.build_path (plugin_dir.get_path (), plugin_name); - var module = Module.open (path, ModuleFlags.BIND_LOCAL); - if (module == null) { - warning (Module.error ()); - return false; - } + var path = Module.build_path (plugin_dir.get_path (), plugin_name); + var module = Module.open (path, ModuleFlags.BIND_LOCAL); + if (module == null) { + warning (Module.error ()); + return false; + } - void* function; - module.symbol ("register_plugin", out function); - if (function == null) { - warning ("%s failed to register: register_plugin() function not found", plugin_name); - return false; - } - RegisterPluginFunction register = (RegisterPluginFunction)function; + void* function; + module.symbol ("register_plugin", out function); + if (function == null) { + warning ("%s failed to register: register_plugin() function not found", plugin_name); + return false; + } + RegisterPluginFunction register = (RegisterPluginFunction)function; - var type = register (); - if (type.is_a (typeof (Plugin)) == false) { - warning ("%s does not return a class of type Plugin", plugin_name); - return false; - } + var info = register (); + if (info.plugin_type.is_a (typeof (Plugin)) == false) { + warning ("%s does not return a class of type Plugin", plugin_name); + return false; + } - module.make_resident (); + if (!check_provides (info.name, info.provides)) { + return false; + } - var plugin = (Plugin)Object.@new (type); - plugins.set (plugin_name, plugin); + info.module_name = plugin_name; + module.make_resident (); - if (initialized) { - initialize_plugin (plugin_name, plugin); - get_all_regions (true); - } + if (info.load_can_wait) { + load_later_plugins.add (info); + } 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.notify["region"].connect (() => { + plugin.region_changed.connect (() => { 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) { wm = _wm; @@ -118,6 +184,13 @@ namespace Gala 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) { if (update) { diff --git a/src/Widgets/WorkspaceView.vala b/src/Widgets/WorkspaceView.vala index d3c02ce8..a7e5ddf2 100644 --- a/src/Widgets/WorkspaceView.vala +++ b/src/Widgets/WorkspaceView.vala @@ -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 () { hide (); @@ -261,7 +239,7 @@ namespace Gala if ((event.modifier_state & Clutter.ModifierType.SHIFT_MASK) != 0) wm.move_window (display.get_focus_window (), MotionDirection.LEFT); else - switch_to_next_workspace (MotionDirection.LEFT); + wm.switch_to_next_workspace (MotionDirection.LEFT); last_switch_time = current_time; @@ -270,7 +248,7 @@ namespace Gala if ((event.modifier_state & Clutter.ModifierType.SHIFT_MASK) != 0) wm.move_window (display.get_focus_window (), MotionDirection.RIGHT); else - switch_to_next_workspace (MotionDirection.RIGHT); + wm.switch_to_next_workspace (MotionDirection.RIGHT); last_switch_time = current_time; @@ -454,12 +432,5 @@ namespace Gala wins.x = 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); - } } } diff --git a/src/WindowManager.vala b/src/WindowManager.vala index 6c92b9df..46eb7f5e 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -21,11 +21,11 @@ namespace Gala { public class WindowManagerGala : Meta.Plugin, WindowManager { - PluginInfo info; + Meta.PluginInfo info; - WindowSwitcher winswitcher; - WorkspaceView workspace_view; - WindowOverview window_overview; + WindowSwitcher? winswitcher = null; + WorkspaceView? workspace_view = null; + WindowOverview? window_overview = null; // used to detect which corner was used to trigger an action Clutter.Actor? last_hotcorner; @@ -44,10 +44,10 @@ namespace Gala Gee.HashSet unmaximizing = new Gee.HashSet (); Gee.HashSet mapping = new Gee.HashSet (); Gee.HashSet destroying = new Gee.HashSet (); - + 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"}; Prefs.set_ignore_request_hide_titlebar (true); @@ -77,9 +77,12 @@ namespace Gala screensaver = Bus.get_proxy_sync (BusType.SESSION, "org.gnome.ScreenSaver", "/org/gnome/ScreenSaver"); 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; stage.background_color = Clutter.Color.from_string (color); @@ -114,28 +117,12 @@ namespace Gala window_group.add_child (background_group); 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); stage.remove_child (top_window_group); ui_group.add_child (top_window_group); /*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_workspace_by_index (0).activate (screen.get_display ().get_current_time ()); }); @@ -182,19 +169,10 @@ namespace Gala } 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-down", () => {}); - KeyBinding.set_custom_handler ("switch-to-workspace-left", workspace_view.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-left", 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-down", () => {}); @@ -214,13 +192,54 @@ namespace Gala 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 (); stage.show (); // let the session manager move to the next phase Meta.register_with_session (); + + Idle.add (() => { + plugin_manager.load_waiting_plugins (); + return false; + }); return false; } @@ -264,14 +283,48 @@ namespace Gala 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 () { var schema = BehaviorSettings.get_default ().schema; var screen = get_screen (); - if (screensaver != null && screensaver.get_active ()) { - InternalUtils.set_input_area (screen, InputArea.NONE); - return; + if (screensaver != null) { + try { + if (screensaver.get_active ()) { + InternalUtils.set_input_area (screen, InputArea.NONE); + return; + } + } catch (IOError e) { warning (e.message); } } if (modal_count > 0) @@ -1018,7 +1071,7 @@ namespace Gala return modal_count > 0; } - public override unowned PluginInfo? plugin_info () + public override unowned Meta.PluginInfo? plugin_info () { return info; }