diff --git a/plugins/xrandr/gsd-xrandr-manager.c b/plugins/xrandr/gsd-xrandr-manager.c index 620d783..ad5a914 100644 --- a/plugins/xrandr/gsd-xrandr-manager.c +++ b/plugins/xrandr/gsd-xrandr-manager.c @@ -35,7 +35,10 @@ #include #include #include -#include + +#define I_KNOW_THIS_IS_UNSTABLE_AND_ONLY_IN_FEDORA +#include +#include #ifdef HAVE_RANDR #include @@ -44,6 +47,22 @@ #include "gnome-settings-profile.h" #include "gsd-xrandr-manager.h" +#define GSD_XRANDR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_XRANDR_MANAGER, GsdXrandrManagerPrivate)) + +#define VIDEO_KEYSYM "XF86Display" + +struct GsdXrandrManagerPrivate +{ + /* Key code of the fn-F7 video key (XF86Display) */ + guint keycode; + RWScreen *rw_screen; + gboolean running; +}; + +enum { + PROP_0, +}; + 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); @@ -52,262 +71,113 @@ G_DEFINE_TYPE (GsdXrandrManager, gsd_xrandr_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; -#ifdef HAVE_RANDR -static int -get_rotation (GConfClient *client, - char *display, - int screen) +static GdkAtom +gnome_randr_atom (void) { - char *key; - int val; - GError *error; - - key = g_strdup_printf ("%s/%d/rotation", display, screen); - error = NULL; - val = gconf_client_get_int (client, key, &error); - g_free (key); - - if (error == NULL) { - return val; - } - - g_error_free (error); - - return 0; + return gdk_atom_intern ("_GNOME_RANDR_ATOM", FALSE); } -static int -get_resolution (GConfClient *client, - int screen, - char *keys[], - int *width, - int *height) +static Atom +gnome_randr_xatom (void) { - int i; - char *key; - char *val; - int w; - int h; - - val = NULL; - for (i = 0; keys[i] != NULL; i++) { - key = g_strdup_printf ("%s/%d/resolution", keys[i], screen); - val = gconf_client_get_string (client, key, NULL); - g_free (key); - - if (val != NULL) { - break; - } - } - - if (val == NULL) { - return -1; - } - - if (sscanf (val, "%dx%d", &w, &h) != 2) { - g_free (val); - return -1; - } - - g_free (val); - - *width = w; - *height = h; - - return i; + return gdk_x11_atom_to_xatom (gnome_randr_atom()); } -static int -get_rate (GConfClient *client, - char *display, - int screen) +static GdkFilterReturn +on_client_message (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) { - char *key; - int val; - GError *error; - - key = g_strdup_printf ("%s/%d/rate", display, screen); - error = NULL; - val = gconf_client_get_int (client, key, &error); - g_free (key); - - if (error == NULL) { - return val; + RWScreen *screen = data; + XEvent *ev = (XEvent *)xevent; + + if (ev->type == ClientMessage && + ev->xclient.message_type == gnome_randr_xatom()) { + + configuration_apply_stored (screen); + + return GDK_FILTER_REMOVE; } - - g_error_free (error); - - return 0; + + /* Pass the event on to GTK+ */ + return GDK_FILTER_CONTINUE; } -static int -find_closest_size (XRRScreenSize *sizes, - int nsizes, - int width, - int height) +static GdkFilterReturn +event_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) { - int closest; - int closest_width; - int closest_height; - int i; - - closest = 0; - closest_width = sizes[0].width; - closest_height = sizes[0].height; - for (i = 1; i < nsizes; i++) { - if (ABS (sizes[i].width - width) < ABS (closest_width - width) || - (sizes[i].width == closest_width && - ABS (sizes[i].height - height) < ABS (closest_height - height))) { - closest = i; - closest_width = sizes[i].width; - closest_height = sizes[i].height; - } + GsdXrandrManager *manager = data; + XEvent *xev = (XEvent *) xevent; + + if (!manager->priv->running) + return GDK_FILTER_CONTINUE; + + /* verify we have a key event */ + if (xev->xany.type != KeyPress && xev->xany.type != KeyRelease) + return GDK_FILTER_CONTINUE; + + if (xev->xkey.keycode == manager->priv->keycode) { + /* FIXME: here we should cycle between valid + * configurations, and save them + */ + configuration_apply_stored (manager->priv->rw_screen); + + return GDK_FILTER_CONTINUE; } - return closest; + return GDK_FILTER_CONTINUE; } -#endif /* HAVE_RANDR */ static void -apply_settings (GsdXrandrManager *manager) +on_randr_event (RWScreen *screen, gpointer data) { -#ifdef HAVE_RANDR - GdkDisplay *display; - Display *xdisplay; - int major; - int minor; - int event_base; - int error_base; - GConfClient *client; - int n_screens; - GdkScreen *screen; - GdkWindow *root_window; - int width; - int height; - int rate; - int rotation; -#ifdef HOST_NAME_MAX - char hostname[HOST_NAME_MAX + 1]; -#else - char hostname[256]; -#endif - char *specific_path; - char *keys[3]; - int i; - int residx; - - gnome_settings_profile_start (NULL); - - display = gdk_display_get_default (); - xdisplay = gdk_x11_display_get_xdisplay (display); - - /* Check if XRandR is supported on the display */ - if (!XRRQueryExtension (xdisplay, &event_base, &error_base) - || XRRQueryVersion (xdisplay, &major, &minor) == 0) { - goto out; - } - - if (major != 1 || minor < 1) { - g_message ("Display has unsupported version of XRandR (%d.%d), not setting resolution.", major, minor); - goto out; - } - - client = gconf_client_get_default (); - - i = 0; - specific_path = NULL; - if (gethostname (hostname, sizeof (hostname)) == 0) { - specific_path = g_strconcat ("/desktop/gnome/screen/", hostname, NULL); - keys[i++] = specific_path; - } - keys[i++] = "/desktop/gnome/screen/default"; - keys[i++] = NULL; - - n_screens = gdk_display_get_n_screens (display); - for (i = 0; i < n_screens; i++) { - screen = gdk_display_get_screen (display, i); - root_window = gdk_screen_get_root_window (screen); - residx = get_resolution (client, i, keys, &width, &height); - - if (residx != -1) { - XRRScreenSize *sizes; - int nsizes; - int j; - int closest; - short *rates; - int nrates; - int status; - int current_size; - short current_rate; - XRRScreenConfiguration *config; - Rotation current_rotation; - - config = XRRGetScreenInfo (xdisplay, gdk_x11_drawable_get_xid (GDK_DRAWABLE (root_window))); - - rate = get_rate (client, keys[residx], i); - - sizes = XRRConfigSizes (config, &nsizes); - closest = find_closest_size (sizes, nsizes, width, height); - - rates = XRRConfigRates (config, closest, &nrates); - for (j = 0; j < nrates; j++) { - if (rates[j] == rate) - break; - } - - /* Rate not supported, let X pick */ - if (j == nrates) - rate = 0; - - rotation = get_rotation (client, keys[residx], i); - if (rotation == 0) - rotation = RR_Rotate_0; - - current_size = XRRConfigCurrentConfiguration (config, ¤t_rotation); - current_rate = XRRConfigCurrentRate (config); - - if (closest != current_size || - rate != current_rate || - rotation != current_rotation) { - status = XRRSetScreenConfigAndRate (xdisplay, - config, - gdk_x11_drawable_get_xid (GDK_DRAWABLE (root_window)), - closest, - (Rotation) rotation, - rate, - GDK_CURRENT_TIME); - } - - XRRFreeScreenConfigInfo (config); - } - } - - g_free (specific_path); - - /* We need to make sure we process the screen resize event. */ - gdk_display_sync (display); + GsdXrandrManager *manager = data; - while (gtk_events_pending ()) { - gtk_main_iteration (); - } - - if (client != NULL) { - g_object_unref (client); - } - out: - gnome_settings_profile_end (NULL); - -#endif /* HAVE_RANDR */ + if (!manager->priv->running) + return; + + /* FIXME: Set up any new screens here */ } gboolean gsd_xrandr_manager_start (GsdXrandrManager *manager, GError **error) { - g_debug ("Starting xrandr manager"); - apply_settings (manager); + + g_debug ("Starting xrandr manager"); + if (manager->priv->rw_screen == NULL) { + g_set_error (error, 0, 0, "Failed to initialize XRandR extension"); + return FALSE; + } + + manager->priv->running = TRUE; + + if (manager->priv->keycode) { + gdk_error_trap_push (); + + XGrabKey (gdk_x11_get_default_xdisplay(), + manager->priv->keycode, AnyModifier, + gdk_x11_get_default_root_xwindow(), + True, GrabModeAsync, GrabModeAsync); + + gdk_flush (); + gdk_error_trap_pop (); + } + + configuration_apply_stored (manager->priv->rw_screen); + + gdk_window_add_filter (gdk_get_default_root_window(), + (GdkFilterFunc)event_filter, + manager); + + gdk_add_client_message_filter (gnome_randr_atom(), + on_client_message, + manager->priv->rw_screen); + return TRUE; } @@ -315,6 +185,16 @@ void gsd_xrandr_manager_stop (GsdXrandrManager *manager) { g_debug ("Stopping xrandr manager"); + + manager->priv->running = FALSE; + + gdk_error_trap_push (); + + XUngrabKey (gdk_x11_get_default_xdisplay(), + manager->priv->keycode, AnyModifier, + gdk_x11_get_default_root_xwindow()); + + gdk_error_trap_pop (); } static void @@ -388,11 +268,22 @@ gsd_xrandr_manager_class_init (GsdXrandrManagerClass *klass) object_class->constructor = gsd_xrandr_manager_constructor; object_class->dispose = gsd_xrandr_manager_dispose; object_class->finalize = gsd_xrandr_manager_finalize; + + g_type_class_add_private (klass, sizeof (GsdXrandrManagerPrivate)); } 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->rw_screen = rw_screen_new ( + gdk_screen_get_default(), on_randr_event, NULL); } static void @@ -405,6 +296,8 @@ gsd_xrandr_manager_finalize (GObject *object) xrandr_manager = GSD_XRANDR_MANAGER (object); + g_return_if_fail (xrandr_manager->priv != NULL); + G_OBJECT_CLASS (gsd_xrandr_manager_parent_class)->finalize (object); }