From 495aabfca8b1184afa5efdfef764f4a63d7e96d8 Mon Sep 17 00:00:00 2001
From: Federico Mena Quintero <federico@novell.com>
Date: Mon, 30 Nov 2009 16:33:22 -0600
Subject: [PATCH 1/3] Factor out function to get keycodes from keysym names

Also, make the 'keycode' field have a better name, 'switch_video_mode_keycode'.
---
 plugins/xrandr/gsd-xrandr-manager.c |   40 +++++++++++++++++++++-------------
 1 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/plugins/xrandr/gsd-xrandr-manager.c b/plugins/xrandr/gsd-xrandr-manager.c
index d278397..672cf99 100644
--- a/plugins/xrandr/gsd-xrandr-manager.c
+++ b/plugins/xrandr/gsd-xrandr-manager.c
@@ -87,8 +87,8 @@ struct GsdXrandrManagerPrivate
 {
         DBusGConnection *dbus_connection;
 
-        /* Key code of the fn-F7 video key (XF86Display) */
-        guint keycode;
+        /* Key code of the XF86Display key (Fn-F7 on Thinkpads, Fn-F4 on HP machines, etc.) */
+        guint switch_video_mode_keycode;
         GnomeRRScreen *rw_screen;
         gboolean running;
 
@@ -1004,7 +1004,7 @@ event_filter (GdkXEvent           *xevent,
         if (xev->xany.type != KeyPress && xev->xany.type != KeyRelease)
                 return GDK_FILTER_CONTINUE;
 
-        if (xev->xany.type == KeyPress && xev->xkey.keycode == manager->priv->keycode) {
+        if (xev->xany.type == KeyPress && xev->xkey.keycode == manager->priv->switch_video_mode_keycode) {
                 handle_fn_f7 (manager, xev->xkey.time);
 
                 return GDK_FILTER_CONTINUE;
@@ -1917,11 +1917,11 @@ gsd_xrandr_manager_start (GsdXrandrManager *manager,
                         (GConfClientNotifyFunc)on_config_changed,
                         manager, NULL, NULL);
 
-        if (manager->priv->keycode) {
+        if (manager->priv->switch_video_mode_keycode) {
                 gdk_error_trap_push ();
 
                 XGrabKey (gdk_x11_get_default_xdisplay(),
-                          manager->priv->keycode, AnyModifier,
+                          manager->priv->switch_video_mode_keycode, AnyModifier,
                           gdk_x11_get_default_root_xwindow(),
                           True, GrabModeAsync, GrabModeAsync);
 
@@ -1950,13 +1950,15 @@ gsd_xrandr_manager_stop (GsdXrandrManager *manager)
 
         manager->priv->running = FALSE;
 
-        gdk_error_trap_push ();
+        if (manager->priv->switch_video_mode_keycode) {
+                gdk_error_trap_push ();
 
-        XUngrabKey (gdk_x11_get_default_xdisplay(),
-                    manager->priv->keycode, AnyModifier,
-                    gdk_x11_get_default_root_xwindow());
+                XUngrabKey (gdk_x11_get_default_xdisplay(),
+                            manager->priv->switch_video_mode_keycode, AnyModifier,
+                            gdk_x11_get_default_root_xwindow());
 
-        gdk_error_trap_pop ();
+                gdk_error_trap_pop ();
+        }
 
         gdk_window_remove_filter (gdk_get_default_root_window (),
                                   (GdkFilterFunc) event_filter,
@@ -2065,16 +2067,24 @@ gsd_xrandr_manager_class_init (GsdXrandrManagerClass *klass)
         g_type_class_add_private (klass, sizeof (GsdXrandrManagerPrivate));
 }
 
+static guint
+get_keycode_for_keysym_name (const char *name)
+{
+        Display *dpy;
+        guint keyval;
+
+        dpy = gdk_x11_get_default_xdisplay ();
+
+        keyval = gdk_keyval_from_name (name);
+        return XKeysymToKeycode (dpy, keyval);
+}
+
 static void
 gsd_xrandr_manager_init (GsdXrandrManager *manager)
 {
-        Display *dpy = gdk_x11_get_default_xdisplay ();
-        guint keyval = gdk_keyval_from_name (VIDEO_KEYSYM);
-        guint keycode = XKeysymToKeycode (dpy, keyval);
-
         manager->priv = GSD_XRANDR_MANAGER_GET_PRIVATE (manager);
 
-        manager->priv->keycode = keycode;
+        manager->priv->switch_video_mode_keycode = get_keycode_for_keysym_name (VIDEO_KEYSYM);
 
         manager->priv->current_fn_f7_config = -1;
         manager->priv->fn_f7_configs = NULL;
-- 
1.6.0.2


From 7ace1162420b6037ade0ae3f17a5c7fc4fe80128 Mon Sep 17 00:00:00 2001
From: Federico Mena Quintero <federico@novell.com>
Date: Mon, 30 Nov 2009 16:36:41 -0600
Subject: [PATCH 2/3] Grab the XF86RotateWindows key as well

We will use this to rotate the tablet's main output when that key is pressed.

Signed-off-by: Federico Mena Quintero <federico@novell.com>
---
 plugins/xrandr/gsd-xrandr-manager.c |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/plugins/xrandr/gsd-xrandr-manager.c b/plugins/xrandr/gsd-xrandr-manager.c
index 672cf99..82856f0 100644
--- a/plugins/xrandr/gsd-xrandr-manager.c
+++ b/plugins/xrandr/gsd-xrandr-manager.c
@@ -66,6 +66,7 @@
 #define CONF_KEY "show_notification_icon"
 
 #define VIDEO_KEYSYM    "XF86Display"
+#define ROTATE_KEYSYM   "XF86RotateWindows"
 
 /* Number of seconds that the confirmation dialog will last before it resets the
  * RANDR configuration to its old state.
@@ -89,6 +90,10 @@ struct GsdXrandrManagerPrivate
 
         /* Key code of the XF86Display key (Fn-F7 on Thinkpads, Fn-F4 on HP machines, etc.) */
         guint switch_video_mode_keycode;
+
+        /* Key code of the XF86RotateWindows key (present on some tablets) */
+        guint rotate_windows_keycode;
+
         GnomeRRScreen *rw_screen;
         gboolean running;
 
@@ -1929,6 +1934,18 @@ gsd_xrandr_manager_start (GsdXrandrManager *manager,
                 gdk_error_trap_pop ();
         }
 
+        if (manager->priv->rotate_windows_keycode) {
+                gdk_error_trap_push ();
+
+                XGrabKey (gdk_x11_get_default_xdisplay(),
+                          manager->priv->rotate_windows_keycode, AnyModifier,
+                          gdk_x11_get_default_root_xwindow(),
+                          True, GrabModeAsync, GrabModeAsync);
+
+                gdk_flush ();
+                gdk_error_trap_pop ();
+        }
+
         show_timestamps_dialog (manager, "Startup");
         apply_stored_configuration_at_startup (manager, GDK_CURRENT_TIME); /* we don't have a real timestamp at startup anyway */
 
@@ -1960,6 +1977,16 @@ gsd_xrandr_manager_stop (GsdXrandrManager *manager)
                 gdk_error_trap_pop ();
         }
 
+        if (manager->priv->rotate_windows_keycode) {
+                gdk_error_trap_push ();
+
+                XUngrabKey (gdk_x11_get_default_xdisplay(),
+                            manager->priv->rotate_windows_keycode, AnyModifier,
+                            gdk_x11_get_default_root_xwindow());
+
+                gdk_error_trap_pop ();
+        }
+
         gdk_window_remove_filter (gdk_get_default_root_window (),
                                   (GdkFilterFunc) event_filter,
                                   manager);
@@ -2085,6 +2112,7 @@ gsd_xrandr_manager_init (GsdXrandrManager *manager)
         manager->priv = GSD_XRANDR_MANAGER_GET_PRIVATE (manager);
 
         manager->priv->switch_video_mode_keycode = get_keycode_for_keysym_name (VIDEO_KEYSYM);
+        manager->priv->rotate_windows_keycode = get_keycode_for_keysym_name (ROTATE_KEYSYM);
 
         manager->priv->current_fn_f7_config = -1;
         manager->priv->fn_f7_configs = NULL;
-- 
1.6.0.2


From ca1581a5ec52fe01f1ed482aa300c01d4753d131 Mon Sep 17 00:00:00 2001
From: Federico Mena Quintero <federico@novell.com>
Date: Mon, 30 Nov 2009 16:38:28 -0600
Subject: [PATCH 3/3] Handle the XF86RotateWindows hotkey by rotating the display

We find the laptop's built-in video output and rotate it to the next available rotation.

Signed-off-by: Federico Mena Quintero <federico@novell.com>

Also, don't assume that the configuration and the rr_screen come from
the GxdXrandrManager object.  That is the case only while the tray
icon's popup menu is active, but not when we are just handling the
XF86RotateWindows hotkey.

Signed-off-by: Federico Mena Quintero <federico@novell.com>
---
 plugins/xrandr/gsd-xrandr-manager.c |  152 +++++++++++++++++++++++++++++++---
 1 files changed, 138 insertions(+), 14 deletions(-)

diff --git a/plugins/xrandr/gsd-xrandr-manager.c b/plugins/xrandr/gsd-xrandr-manager.c
index 82856f0..8fa03be 100644
--- a/plugins/xrandr/gsd-xrandr-manager.c
+++ b/plugins/xrandr/gsd-xrandr-manager.c
@@ -112,6 +112,14 @@ struct GsdXrandrManagerPrivate
         guint32 last_config_timestamp;
 };
 
+static const GnomeRRRotation possible_rotations[] = {
+        GNOME_RR_ROTATION_0,
+        GNOME_RR_ROTATION_90,
+        GNOME_RR_ROTATION_180,
+        GNOME_RR_ROTATION_270
+        /* We don't allow REFLECT_X or REFLECT_Y for now, as gnome-display-properties doesn't allow them, either */
+};
+
 static void     gsd_xrandr_manager_class_init  (GsdXrandrManagerClass *klass);
 static void     gsd_xrandr_manager_init        (GsdXrandrManager      *xrandr_manager);
 static void     gsd_xrandr_manager_finalize    (GObject             *object);
@@ -120,6 +128,11 @@ static void error_message (GsdXrandrManager *mgr, const char *primary_text, GErr
 
 static void status_icon_popup_menu (GsdXrandrManager *manager, guint button, guint32 timestamp);
 static void run_display_capplet (GtkWidget *widget);
+static void get_allowed_rotations_for_output (GnomeRRConfig *config,
+                                              GnomeRRScreen *rr_screen,
+                                              GnomeOutputInfo *output,
+                                              int *out_num_rotations,
+                                              GnomeRRRotation *out_rotations);
 
 G_DEFINE_TYPE (GsdXrandrManager, gsd_xrandr_manager, G_TYPE_OBJECT)
 
@@ -994,6 +1007,118 @@ handle_fn_f7 (GsdXrandrManager *mgr, guint32 timestamp)
         g_debug ("done handling fn-f7");
 }
 
+static GnomeOutputInfo *
+get_laptop_output_info (GnomeRRConfig *config)
+{
+        int i;
+
+        for (i = 0; config->outputs[i] != NULL; i++) {
+                GnomeOutputInfo *info;
+
+                info = config->outputs[i];
+                if (is_laptop (info))
+                        return info;
+        }
+
+        return NULL;
+
+}
+
+static GnomeRRRotation
+get_next_rotation (GnomeRRRotation allowed_rotations, GnomeRRRotation current_rotation)
+{
+        int i;
+        int current_index;
+
+        /* First, find the index of the current rotation */
+
+        current_index = -1;
+
+        for (i = 0; i < G_N_ELEMENTS (possible_rotations); i++) {
+                GnomeRRRotation r;
+
+                r = possible_rotations[i];
+                if (r == current_rotation) {
+                        current_index = i;
+                        break;
+                }
+        }
+
+        if (current_index == -1) {
+                /* Huh, the current_rotation was not one of the supported rotations.  Bail out. */
+                return current_rotation;
+        }
+
+        /* Then, find the next rotation that is allowed */
+
+        i = (current_index + 1) % G_N_ELEMENTS (possible_rotations);
+
+        while (1) {
+                GnomeRRRotation r;
+
+                r = possible_rotations[i];
+                if (r == current_rotation) {
+                        /* We wrapped around and no other rotation is suported.  Bummer. */
+                        return current_rotation;
+                } else if (r & allowed_rotations)
+                        return r;
+
+                i = (i + 1) % G_N_ELEMENTS (possible_rotations);
+        }
+}
+
+/* We use this when the XF86RotateWindows key is pressed.  That key is present
+ * on some tablet PCs; they use it so that the user can rotate the tablet
+ * easily.
+ */
+static void
+handle_rotate_windows (GsdXrandrManager *mgr, guint32 timestamp)
+{
+        GsdXrandrManagerPrivate *priv = mgr->priv;
+        GnomeRRScreen *screen = priv->rw_screen;
+        GnomeRRConfig *current;
+        GError *error;
+        GnomeOutputInfo *rotatable_output_info;
+        int num_allowed_rotations;
+        GnomeRRRotation allowed_rotations;
+        GnomeRRRotation next_rotation;
+
+        g_debug ("Handling XF86RotateWindows");
+
+        /* Which output? */
+
+        current = gnome_rr_config_new_current (screen);
+
+        rotatable_output_info = get_laptop_output_info (current);
+        if (rotatable_output_info == NULL) {
+                g_debug ("No laptop outputs found to rotate; XF86RotateWindows key will do nothing");
+                goto out;
+        }
+
+        /* Which rotation? */
+
+        get_allowed_rotations_for_output (current, priv->rw_screen, rotatable_output_info, &num_allowed_rotations, &allowed_rotations);
+        next_rotation = get_next_rotation (allowed_rotations, rotatable_output_info->rotation);
+
+        if (next_rotation == rotatable_output_info->rotation) {
+                g_debug ("No rotations are supported other than the current one; XF86RotateWindows key will do nothing");
+                goto out;
+        }
+
+        /* Rotate */
+
+        rotatable_output_info->rotation = next_rotation;
+
+        error = NULL;
+        if (!gnome_rr_config_apply_with_time (current, screen, timestamp, &error)) {
+                error_message (mgr, _("Could not switch the monitor configuration"), error, NULL);
+                g_error_free (error);
+        }
+
+out:
+        gnome_rr_config_free (current);
+}
+
 static GdkFilterReturn
 event_filter (GdkXEvent           *xevent,
               GdkEvent            *event,
@@ -1009,8 +1134,11 @@ event_filter (GdkXEvent           *xevent,
         if (xev->xany.type != KeyPress && xev->xany.type != KeyRelease)
                 return GDK_FILTER_CONTINUE;
 
-        if (xev->xany.type == KeyPress && xev->xkey.keycode == manager->priv->switch_video_mode_keycode) {
-                handle_fn_f7 (manager, xev->xkey.time);
+        if (xev->xany.type == KeyPress) {
+                if (xev->xkey.keycode == manager->priv->switch_video_mode_keycode)
+                        handle_fn_f7 (manager, xev->xkey.time);
+                else if (xev->xkey.keycode == manager->priv->rotate_windows_keycode)
+                        handle_rotate_windows (manager, xev->xkey.time);
 
                 return GDK_FILTER_CONTINUE;
         }
@@ -1490,17 +1618,12 @@ make_menu_item_for_output_title (GsdXrandrManager *manager, GnomeOutputInfo *out
 }
 
 static void
-get_allowed_rotations_for_output (GsdXrandrManager *manager, GnomeOutputInfo *output, int *out_num_rotations, GnomeRRRotation *out_rotations)
+get_allowed_rotations_for_output (GnomeRRConfig *config,
+                                  GnomeRRScreen *rr_screen,
+                                  GnomeOutputInfo *output,
+                                  int *out_num_rotations,
+                                  GnomeRRRotation *out_rotations)
 {
-        static const GnomeRRRotation possible_rotations[] = {
-                GNOME_RR_ROTATION_0,
-                GNOME_RR_ROTATION_90,
-                GNOME_RR_ROTATION_180,
-                GNOME_RR_ROTATION_270
-                /* We don't allow REFLECT_X or REFLECT_Y for now, as gnome-display-properties doesn't allow them, either */
-        };
-
-        struct GsdXrandrManagerPrivate *priv = manager->priv;
         GnomeRRRotation current_rotation;
         int i;
 
@@ -1518,7 +1641,7 @@ get_allowed_rotations_for_output (GsdXrandrManager *manager, GnomeOutputInfo *ou
 
                 output->rotation = rotation_to_test;
 
-                if (gnome_rr_config_applicable (priv->configuration, priv->rw_screen, NULL)) { /* NULL-GError */
+                if (gnome_rr_config_applicable (config, rr_screen, NULL)) { /* NULL-GError */
                         (*out_num_rotations)++;
                         (*out_rotations) |= rotation_to_test;
                 }
@@ -1681,10 +1804,11 @@ add_items_for_rotations (GsdXrandrManager *manager, GnomeOutputInfo *output, Gno
 static void
 add_rotation_items_for_output (GsdXrandrManager *manager, GnomeOutputInfo *output)
 {
+        struct GsdXrandrManagerPrivate *priv = manager->priv;
         int num_rotations;
         GnomeRRRotation rotations;
 
-        get_allowed_rotations_for_output (manager, output, &num_rotations, &rotations);
+        get_allowed_rotations_for_output (priv->configuration, priv->rw_screen, output, &num_rotations, &rotations);
 
         if (num_rotations == 1)
                 add_unsupported_rotation_item (manager);
-- 
1.6.0.2

