commit c26534ec795f662d4f2e9b7c49e7649d8823b118
Author: Colin Walters <walters@verbum.org>
Date:   Tue Dec 2 18:13:11 2008 -0500

    Add support for a "meta key" which initiates extended WM operations
    
    This patch adds the concept of a special key for WM operations, and
    the default is Super_L, which on extended PC hardware is the
    "Windows key".  What we do is handle the special case of a press
    and release of this key (without any other intervening keys).
    
    Super_L+<key> should still be passed to applications.  In the future
    we may want to also take some of these keybindings (e.g. Super+TAB)
    though.
    
    http://bugzilla.gnome.org/show_bug.cgi?id=563047

diff --git a/src/core/display-private.h b/src/core/display-private.h
index 84fc6b6..296f778 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -37,6 +37,7 @@
 #include "common.h"
 #include "boxes.h"
 #include "display.h"
+#include "prefs.h"
 
 #ifdef HAVE_STARTUP_NOTIFICATION
 #include <libsn/sn.h>
@@ -217,6 +218,8 @@ struct _MetaDisplay
   unsigned int hyper_mask;
   unsigned int super_mask;
   unsigned int meta_mask;
+  MetaKeyCombo overlay_key_combo;
+  gboolean overlay_key_only_pressed;
   
   /* Xinerama cache */
   unsigned int xinerama_cache_invalidated : 1;
@@ -505,4 +508,6 @@ void meta_display_queue_autoraise_callback  (MetaDisplay *display,
                                              MetaWindow  *window);
 void meta_display_remove_autoraise_callback (MetaDisplay *display);
 
+void meta_display_overlay_key_activate (MetaDisplay *display);
+
 #endif
diff --git a/src/core/display.c b/src/core/display.c
index 820e6c6..1340a12 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -129,6 +129,15 @@ typedef struct
 
 G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT);
 
+/* Signals */
+enum
+{
+  OVERLAY_KEY,
+  LAST_SIGNAL
+};
+
+static guint display_signals [LAST_SIGNAL] = { 0 };
+
 /**
  * The display we're managing.  This is a singleton object.  (Historically,
  * this was a list of displays, but there was never any way to add more
@@ -168,7 +177,14 @@ MetaGroup*     get_focussed_group (MetaDisplay *display);
 static void
 meta_display_class_init (MetaDisplayClass *klass)
 {
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  display_signals[OVERLAY_KEY] =
+    g_signal_new ("overlay-key",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);  
 }
 
 /**
@@ -5152,6 +5168,12 @@ meta_display_remove_autoraise_callback (MetaDisplay *display)
     }
 }
 
+void
+meta_display_overlay_key_activate (MetaDisplay *display)
+{
+  g_signal_emit (display, display_signals[OVERLAY_KEY], 0);
+}
+
 #ifdef HAVE_COMPOSITE_EXTENSIONS
 void
 meta_display_get_compositor_version (MetaDisplay *display,
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 20f5a20..6032352 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -279,6 +279,13 @@ reload_keycodes (MetaDisplay *display)
   meta_topic (META_DEBUG_KEYBINDINGS,
               "Reloading keycodes for binding tables\n");
   
+  if (display->overlay_key_combo.keysym 
+      && display->overlay_key_combo.keycode == 0)
+    {
+      display->overlay_key_combo.keycode = XKeysymToKeycode (
+          display->xdisplay, display->overlay_key_combo.keysym);
+    }
+  
   if (display->screen_bindings)
     {
       int i;
@@ -478,6 +485,19 @@ rebuild_screen_binding_table (MetaDisplay *display)
 }
 
 static void
+rebuild_special_screen_bindings (MetaDisplay *display)
+{
+  MetaKeyCombo combo;
+  
+  meta_prefs_get_overlay_binding (&combo);
+
+  if (combo.keysym != None || combo.keycode != 0)
+    {
+      display->overlay_key_combo = combo;
+    }
+}
+
+static void
 rebuild_window_binding_table (MetaDisplay *display)
 {
   const MetaKeyPref *prefs;
@@ -605,6 +625,7 @@ bindings_changed_callback (MetaPreference pref,
     {
     case META_PREF_SCREEN_KEYBINDINGS:
       rebuild_screen_binding_table (display);
+      rebuild_special_screen_bindings (display);      
       reload_keycodes (display);
       reload_modifiers (display);
       regrab_screen_bindings (display);
@@ -655,6 +676,7 @@ meta_display_init_keys (MetaDisplay *display)
 
   rebuild_window_binding_table (display);
   rebuild_screen_binding_table (display);
+  rebuild_special_screen_bindings (display);  
 
   reload_keycodes (display);
   reload_modifiers (display);
@@ -833,11 +855,18 @@ ungrab_all_keys (MetaDisplay *display,
 void
 meta_screen_grab_keys (MetaScreen *screen)
 {
+  MetaDisplay *display = screen->display;  
   if (screen->all_keys_grabbed)
     return;
 
   if (screen->keys_grabbed)
     return;
+  
+  if (display->overlay_key_combo.keycode != 0)
+    meta_grab_key (display, screen->xroot,
+                   display->overlay_key_combo.keysym,
+                   display->overlay_key_combo.keycode,
+                   display->overlay_key_combo.modifiers);
 
   grab_keys (screen->display->screen_bindings,
              screen->display->n_screen_bindings,
@@ -1228,6 +1257,31 @@ primary_modifier_still_pressed (MetaDisplay *display,
     return TRUE;
 }
 
+static gboolean
+process_overlay_key (MetaDisplay *display, 
+                     MetaScreen *Screen, 
+                     XEvent *event,
+                     KeySym keysym)
+{
+  if (event->xkey.keycode != display->overlay_key_combo.keycode)
+    {
+      display->overlay_key_only_pressed = FALSE;
+      return FALSE;
+    }
+
+  if (event->xkey.type == KeyPress)
+    {
+      display->overlay_key_only_pressed = TRUE;
+    }
+  else if (event->xkey.type == KeyRelease && display->overlay_key_only_pressed)
+    {
+      display->overlay_key_only_pressed = FALSE;
+      meta_display_overlay_key_activate (display);
+    }
+  
+  return TRUE;
+}
+
 static const MetaKeyHandler*
 find_handler (const MetaKeyHandler *handlers,
               const char           *name)
@@ -1454,11 +1508,15 @@ meta_display_process_key_event (MetaDisplay *display,
           return;
         }
       }
+  
+  handled = process_overlay_key (display, screen, event, keysym);
+  
   /* Do the normal keybindings */
-  handled = process_event (display->screen_bindings,
-                           display->n_screen_bindings,
-                           screen_handlers,
-                           display, screen, NULL, event, keysym);
+  if (!handled)
+    handled = process_event (display->screen_bindings,
+                             display->n_screen_bindings,
+                             screen_handlers,
+                             display, screen, NULL, event, keysym);
  
   if (!all_keys_grabbed && !handled && window)
     handled = process_event (display->window_bindings,
diff --git a/src/core/prefs.c b/src/core/prefs.c
index 6d8f249..fdd5b08 100644
--- a/src/core/prefs.c
+++ b/src/core/prefs.c
@@ -57,6 +57,7 @@
 #define KEY_TERMINAL_DIR "/desktop/gnome/applications/terminal"
 #define KEY_TERMINAL_COMMAND KEY_TERMINAL_DIR "/exec"
 
+#define KEY_OVERLAY_KEY "/apps/metacity/general/overlay_key"
 #define KEY_SCREEN_BINDINGS_PREFIX "/apps/metacity/global_keybindings"
 #define KEY_WINDOW_BINDINGS_PREFIX "/apps/metacity/window_keybindings"
 #define KEY_LIST_BINDINGS_SUFFIX "_list"
@@ -1234,6 +1235,10 @@ change_notify (GConfClient    *client,
       if (update_workspace_name (key, str))
         queue_changed (META_PREF_WORKSPACE_NAMES);
     }
+  else if (g_str_equal (key, KEY_OVERLAY_KEY))
+    {
+      queue_changed (META_PREF_SCREEN_KEYBINDINGS);      
+    }
 #ifdef WITH_CLUTTER
   else if (g_str_equal (key, KEY_CLUTTER_PLUGINS) && !clutter_plugins_overridden)
     {
@@ -1892,6 +1897,8 @@ static MetaKeyPref window_bindings[] = {
   { NULL, NULL, FALSE }
 };
 
+static MetaKeyCombo overlay_key_combo = { 0, 0, 0 };
+
 #ifndef HAVE_GCONF
 typedef struct
 {
@@ -1926,13 +1933,44 @@ static MetaSimpleKeyMapping window_string_bindings[] = {
 
 #endif /* NOT HAVE_GCONF */
 
+/* These bindings are for modifiers alone, so they need special handling */
+static void
+init_special_screen_bindings (void)
+{
+#ifdef HAVE_GCONF
+  char *val;
+  GError *err = NULL;
+#endif
+  
+  /* Default values for bindings which are global, but take special handling */
+  meta_ui_parse_accelerator ("Super_L", &overlay_key_combo.keysym, 
+                             &overlay_key_combo.keycode, 
+                             &overlay_key_combo.modifiers);
+
+#ifdef HAVE_GCONF
+  val = gconf_client_get_string (default_client, KEY_OVERLAY_KEY, &err);
+  cleanup_error (&err);
+    
+  if (val && meta_ui_parse_accelerator (val, &overlay_key_combo.keysym, 
+                                     &overlay_key_combo.keycode, 
+                                     &overlay_key_combo.modifiers))
+    ;
+  else
+    {
+      meta_topic (META_DEBUG_KEYBINDINGS,
+                  "Failed to parse value for overlay_key\n");
+    }
+  g_free (val);
+#endif
+}
+
 static void
 init_bindings (void)
 {
 #ifdef HAVE_GCONF
   int i;
   GError *err;
-  
+
   i = 0;
   while (window_bindings[i].name)
     {
@@ -2016,9 +2054,13 @@ init_bindings (void)
 
       ++i;
     }
+
 #else /* HAVE_GCONF */
   int i = 0;
   int which = 0;
+  
+  init_global_bindings ();  
+  
   while (window_string_bindings[i].name)
     {
       if (window_string_bindings[i].keybinding == NULL)
@@ -2059,6 +2101,8 @@ init_bindings (void)
       ++i;
     }
 #endif /* HAVE_GCONF */
+  
+  init_special_screen_bindings ();  
 }
 
 static void
@@ -2784,6 +2828,12 @@ meta_prefs_get_window_bindings (const MetaKeyPref **bindings,
   *n_bindings = (int) G_N_ELEMENTS (window_bindings) - 1;
 }
 
+void 
+meta_prefs_get_overlay_binding (MetaKeyCombo *combo)
+{
+  *combo = overlay_key_combo;
+}
+
 MetaActionTitlebar
 meta_prefs_get_action_double_click_titlebar (void)
 {
diff --git a/src/core/screen.c b/src/core/screen.c
index 6cc42bf..7a43af9 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -2959,6 +2959,12 @@ meta_screen_get_screen_number (MetaScreen *screen)
   return screen->number;
 }
 
+/**
+ * Retrieve the display associated with screen.
+ * @screen: A #MetaScreen
+ * 
+ * Returns: (transfer none): Display 
+ */
 MetaDisplay *
 meta_screen_get_display (MetaScreen *screen)
 {
diff --git a/src/include/prefs.h b/src/include/prefs.h
index 0a40469..244a8dd 100644
--- a/src/include/prefs.h
+++ b/src/include/prefs.h
@@ -239,6 +239,8 @@ void meta_prefs_get_window_binding (const char          *name,
                                     unsigned int        *keysym,
                                     MetaVirtualModifier *modifiers);
 
+void meta_prefs_get_overlay_binding (MetaKeyCombo *combo);
+
 typedef enum
 {
   META_VISUAL_BELL_INVALID = 0,
diff --git a/src/metacity.schemas.in b/src/metacity.schemas.in
index 83da6fd..dd510be 100644
--- a/src/metacity.schemas.in
+++ b/src/metacity.schemas.in
@@ -9,6 +9,25 @@
     <!-- General preferences -->        
 
     <schema>
+      <key>/schemas/apps/metacity/general/overlay_key</key>
+      <applyto>/apps/metacity/general/overlay_key</applyto>
+      <owner>metacity</owner>
+      <type>string</type>
+      <default>&lt;Super_L&gt;</default>
+      <locale name="C">
+         <short>Modifier to use for extended window management operations</short>
+         <long>
+			This key will initiate the "overlay", which is a combination window
+			overview and application launching system.  The default is intended
+			to be the "Windows key" on PC hardware.  
+			
+			It's expected that this binding either the default or set to 
+			the empty string.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
       <key>/schemas/apps/metacity/general/mouse_button_modifier</key>
       <applyto>/apps/metacity/general/mouse_button_modifier</applyto>
       <owner>metacity</owner>
