From e46b4adef5c6c6805b3ca6dbfbe99a4299252514 Mon Sep 17 00:00:00 2001 From: haxar Date: Tue, 21 Feb 2012 20:14:01 -0800 Subject: gsd to msd complete rename patch by NiceandGently; file rename commit --- plugins/xrandr/gsd-xrandr-16.png | Bin 613 -> 0 bytes plugins/xrandr/gsd-xrandr-22.png | Bin 866 -> 0 bytes plugins/xrandr/gsd-xrandr-24.png | Bin 909 -> 0 bytes plugins/xrandr/gsd-xrandr-32.png | Bin 1602 -> 0 bytes plugins/xrandr/gsd-xrandr-manager.c | 2584 --------------------------------- plugins/xrandr/gsd-xrandr-manager.h | 61 - plugins/xrandr/gsd-xrandr-manager.xml | 23 - plugins/xrandr/gsd-xrandr-plugin.c | 104 -- plugins/xrandr/gsd-xrandr-plugin.h | 63 - plugins/xrandr/gsd-xrandr.svg | 470 ------ plugins/xrandr/msd-xrandr-16.png | Bin 0 -> 613 bytes plugins/xrandr/msd-xrandr-22.png | Bin 0 -> 866 bytes plugins/xrandr/msd-xrandr-24.png | Bin 0 -> 909 bytes plugins/xrandr/msd-xrandr-32.png | Bin 0 -> 1602 bytes plugins/xrandr/msd-xrandr-manager.c | 2584 +++++++++++++++++++++++++++++++++ plugins/xrandr/msd-xrandr-manager.h | 61 + plugins/xrandr/msd-xrandr-manager.xml | 23 + plugins/xrandr/msd-xrandr-plugin.c | 104 ++ plugins/xrandr/msd-xrandr-plugin.h | 63 + plugins/xrandr/msd-xrandr.svg | 470 ++++++ 20 files changed, 3305 insertions(+), 3305 deletions(-) delete mode 100644 plugins/xrandr/gsd-xrandr-16.png delete mode 100644 plugins/xrandr/gsd-xrandr-22.png delete mode 100644 plugins/xrandr/gsd-xrandr-24.png delete mode 100644 plugins/xrandr/gsd-xrandr-32.png delete mode 100644 plugins/xrandr/gsd-xrandr-manager.c delete mode 100644 plugins/xrandr/gsd-xrandr-manager.h delete mode 100644 plugins/xrandr/gsd-xrandr-manager.xml delete mode 100644 plugins/xrandr/gsd-xrandr-plugin.c delete mode 100644 plugins/xrandr/gsd-xrandr-plugin.h delete mode 100644 plugins/xrandr/gsd-xrandr.svg create mode 100644 plugins/xrandr/msd-xrandr-16.png create mode 100644 plugins/xrandr/msd-xrandr-22.png create mode 100644 plugins/xrandr/msd-xrandr-24.png create mode 100644 plugins/xrandr/msd-xrandr-32.png create mode 100644 plugins/xrandr/msd-xrandr-manager.c create mode 100644 plugins/xrandr/msd-xrandr-manager.h create mode 100644 plugins/xrandr/msd-xrandr-manager.xml create mode 100644 plugins/xrandr/msd-xrandr-plugin.c create mode 100644 plugins/xrandr/msd-xrandr-plugin.h create mode 100644 plugins/xrandr/msd-xrandr.svg (limited to 'plugins/xrandr') diff --git a/plugins/xrandr/gsd-xrandr-16.png b/plugins/xrandr/gsd-xrandr-16.png deleted file mode 100644 index f996ddf..0000000 Binary files a/plugins/xrandr/gsd-xrandr-16.png and /dev/null differ diff --git a/plugins/xrandr/gsd-xrandr-22.png b/plugins/xrandr/gsd-xrandr-22.png deleted file mode 100644 index cc47eec..0000000 Binary files a/plugins/xrandr/gsd-xrandr-22.png and /dev/null differ diff --git a/plugins/xrandr/gsd-xrandr-24.png b/plugins/xrandr/gsd-xrandr-24.png deleted file mode 100644 index 49b4e12..0000000 Binary files a/plugins/xrandr/gsd-xrandr-24.png and /dev/null differ diff --git a/plugins/xrandr/gsd-xrandr-32.png b/plugins/xrandr/gsd-xrandr-32.png deleted file mode 100644 index 95de3ea..0000000 Binary files a/plugins/xrandr/gsd-xrandr-32.png and /dev/null differ diff --git a/plugins/xrandr/gsd-xrandr-manager.c b/plugins/xrandr/gsd-xrandr-manager.c deleted file mode 100644 index dc00be1..0000000 --- a/plugins/xrandr/gsd-xrandr-manager.c +++ /dev/null @@ -1,2584 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2007 William Jon McCann - * Copyright (C) 2007, 2008 Red Hat, Inc - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#define MATE_DESKTOP_USE_UNSTABLE_API - -#include -#include -#include - -#ifdef HAVE_LIBMATENOTIFY -#include -#endif - -#include "mate-settings-profile.h" -#include "msd-xrandr-manager.h" - -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX 255 -#endif - -#define MSD_XRANDR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManagerPrivate)) - -#define CONF_DIR "/apps/mate_settings_daemon/xrandr" -#define CONF_KEY_SHOW_NOTIFICATION_ICON (CONF_DIR "/show_notification_icon") -#define CONF_KEY_TURN_ON_EXTERNAL_MONITORS_AT_STARTUP (CONF_DIR "/turn_on_external_monitors_at_startup") -#define CONF_KEY_TURN_ON_LAPTOP_MONITOR_AT_STARTUP (CONF_DIR "/turn_on_laptop_monitor_at_startup") -#define CONF_KEY_DEFAULT_CONFIGURATION_FILE (CONF_DIR "/default_configuration_file") - -#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. - */ -#define CONFIRMATION_DIALOG_SECONDS 30 - -/* name of the icon files (msd-xrandr.svg, etc.) */ -#define MSD_XRANDR_ICON_NAME "msd-xrandr" - -/* executable of the control center's display configuration capplet */ -#define MSD_XRANDR_DISPLAY_CAPPLET "mate-control-center display" - -#define MSD_DBUS_PATH "/org/mate/SettingsDaemon" -#define MSD_DBUS_NAME "org.mate.SettingsDaemon" -#define MSD_XRANDR_DBUS_PATH MSD_DBUS_PATH "/XRANDR" -#define MSD_XRANDR_DBUS_NAME MSD_DBUS_NAME ".XRANDR" - -struct MsdXrandrManagerPrivate -{ - DBusGConnection *dbus_connection; - - /* 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; - - MateRRScreen *rw_screen; - gboolean running; - - GtkStatusIcon *status_icon; - GtkWidget *popup_menu; - MateRRConfig *configuration; - MateRRLabeler *labeler; - MateConfClient *client; - int notify_id; - - /* fn-F7 status */ - int current_fn_f7_config; /* -1 if no configs */ - MateRRConfig **fn_f7_configs; /* NULL terminated, NULL if there are no configs */ - - /* Last time at which we got a "screen got reconfigured" event; see on_randr_event() */ - guint32 last_config_timestamp; -}; - -static const MateRRRotation possible_rotations[] = { - MATE_RR_ROTATION_0, - MATE_RR_ROTATION_90, - MATE_RR_ROTATION_180, - MATE_RR_ROTATION_270 - /* We don't allow REFLECT_X or REFLECT_Y for now, as mate-display-properties doesn't allow them, either */ -}; - -static void msd_xrandr_manager_class_init (MsdXrandrManagerClass *klass); -static void msd_xrandr_manager_init (MsdXrandrManager *xrandr_manager); -static void msd_xrandr_manager_finalize (GObject *object); - -static void error_message (MsdXrandrManager *mgr, const char *primary_text, GError *error_to_display, const char *secondary_text); - -static void status_icon_popup_menu (MsdXrandrManager *manager, guint button, guint32 timestamp); -static void run_display_capplet (GtkWidget *widget); -static void get_allowed_rotations_for_output (MateRRConfig *config, - MateRRScreen *rr_screen, - MateOutputInfo *output, - int *out_num_rotations, - MateRRRotation *out_rotations); - -G_DEFINE_TYPE (MsdXrandrManager, msd_xrandr_manager, G_TYPE_OBJECT) - -static gpointer manager_object = NULL; - -static FILE *log_file; - -static void -log_open (void) -{ - char *toggle_filename; - char *log_filename; - struct stat st; - - if (log_file) - return; - - toggle_filename = g_build_filename (g_get_home_dir (), "msd-debug-randr", NULL); - log_filename = g_build_filename (g_get_home_dir (), "msd-debug-randr.log", NULL); - - if (stat (toggle_filename, &st) != 0) - goto out; - - log_file = fopen (log_filename, "a"); - - if (log_file && ftell (log_file) == 0) - fprintf (log_file, "To keep this log from being created, please rm ~/msd-debug-randr\n"); - -out: - g_free (toggle_filename); - g_free (log_filename); -} - -static void -log_close (void) -{ - if (log_file) { - fclose (log_file); - log_file = NULL; - } -} - -static void -log_msg (const char *format, ...) -{ - if (log_file) { - va_list args; - - va_start (args, format); - vfprintf (log_file, format, args); - va_end (args); - } -} - -static void -log_output (MateOutputInfo *output) -{ - log_msg (" %s: ", output->name ? output->name : "unknown"); - - if (output->connected) { - if (output->on) { - log_msg ("%dx%d@%d +%d+%d", - output->width, - output->height, - output->rate, - output->x, - output->y); - } else - log_msg ("off"); - } else - log_msg ("disconnected"); - - if (output->display_name) - log_msg (" (%s)", output->display_name); - - if (output->primary) - log_msg (" (primary output)"); - - log_msg ("\n"); -} - -static void -log_configuration (MateRRConfig *config) -{ - int i; - - log_msg (" cloned: %s\n", config->clone ? "yes" : "no"); - - for (i = 0; config->outputs[i] != NULL; i++) - log_output (config->outputs[i]); - - if (i == 0) - log_msg (" no outputs!\n"); -} - -static char -timestamp_relationship (guint32 a, guint32 b) -{ - if (a < b) - return '<'; - else if (a > b) - return '>'; - else - return '='; -} - -static void -log_screen (MateRRScreen *screen) -{ - MateRRConfig *config; - int min_w, min_h, max_w, max_h; - guint32 change_timestamp, config_timestamp; - - if (!log_file) - return; - - config = mate_rr_config_new_current (screen); - - mate_rr_screen_get_ranges (screen, &min_w, &max_w, &min_h, &max_h); - mate_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp); - - log_msg (" Screen min(%d, %d), max(%d, %d), change=%u %c config=%u\n", - min_w, min_h, - max_w, max_h, - change_timestamp, - timestamp_relationship (change_timestamp, config_timestamp), - config_timestamp); - - log_configuration (config); - mate_rr_config_free (config); -} - -static void -log_configurations (MateRRConfig **configs) -{ - int i; - - if (!configs) { - log_msg (" No configurations\n"); - return; - } - - for (i = 0; configs[i]; i++) { - log_msg (" Configuration %d\n", i); - log_configuration (configs[i]); - } -} - -static void -show_timestamps_dialog (MsdXrandrManager *manager, const char *msg) -{ -#if 1 - return; -#else - struct MsdXrandrManagerPrivate *priv = manager->priv; - GtkWidget *dialog; - guint32 change_timestamp, config_timestamp; - static int serial; - - mate_rr_screen_get_timestamps (priv->rw_screen, &change_timestamp, &config_timestamp); - - dialog = gtk_message_dialog_new (NULL, - 0, - GTK_MESSAGE_INFO, - GTK_BUTTONS_CLOSE, - "RANDR timestamps (%d):\n%s\nchange: %u\nconfig: %u", - serial++, - msg, - change_timestamp, - config_timestamp); - g_signal_connect (dialog, "response", - G_CALLBACK (gtk_widget_destroy), NULL); - gtk_widget_show (dialog); -#endif -} - -/* This function centralizes the use of mate_rr_config_apply_from_filename_with_time(). - * - * Optionally filters out MATE_RR_ERROR_NO_MATCHING_CONFIG from - * mate_rr_config_apply_from_filename_with_time(), since that is not usually an error. - */ -static gboolean -apply_configuration_from_filename (MsdXrandrManager *manager, - const char *filename, - gboolean no_matching_config_is_an_error, - guint32 timestamp, - GError **error) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - GError *my_error; - gboolean success; - char *str; - - str = g_strdup_printf ("Applying %s with timestamp %d", filename, timestamp); - show_timestamps_dialog (manager, str); - g_free (str); - - my_error = NULL; - success = mate_rr_config_apply_from_filename_with_time (priv->rw_screen, filename, timestamp, &my_error); - if (success) - return TRUE; - - if (g_error_matches (my_error, MATE_RR_ERROR, MATE_RR_ERROR_NO_MATCHING_CONFIG)) { - if (no_matching_config_is_an_error) - goto fail; - - /* This is not an error; the user probably changed his monitors - * and so they don't match any of the stored configurations. - */ - g_error_free (my_error); - return TRUE; - } - -fail: - g_propagate_error (error, my_error); - return FALSE; -} - -/* This function centralizes the use of mate_rr_config_apply_with_time(). - * - * Applies a configuration and displays an error message if an error happens. - * We just return whether setting the configuration succeeded. - */ -static gboolean -apply_configuration_and_display_error (MsdXrandrManager *manager, MateRRConfig *config, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = manager->priv; - GError *error; - gboolean success; - - error = NULL; - success = mate_rr_config_apply_with_time (config, priv->rw_screen, timestamp, &error); - if (!success) { - log_msg ("Could not switch to the following configuration (timestamp %u): %s\n", timestamp, error->message); - log_configuration (config); - error_message (manager, _("Could not switch the monitor configuration"), error, NULL); - g_error_free (error); - } - - return success; -} - -static void -restore_backup_configuration_without_messages (const char *backup_filename, const char *intended_filename) -{ - backup_filename = mate_rr_config_get_backup_filename (); - rename (backup_filename, intended_filename); -} - -static void -restore_backup_configuration (MsdXrandrManager *manager, const char *backup_filename, const char *intended_filename, guint32 timestamp) -{ - int saved_errno; - - if (rename (backup_filename, intended_filename) == 0) { - GError *error; - - error = NULL; - if (!apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, &error)) { - error_message (manager, _("Could not restore the display's configuration"), error, NULL); - - if (error) - g_error_free (error); - } - - return; - } - - saved_errno = errno; - - /* ENOENT means the original file didn't exist. That is *not* an error; - * the backup was not created because there wasn't even an original - * monitors.xml (such as on a first-time login). Note that *here* there - * is a "didn't work" monitors.xml, so we must delete that one. - */ - if (saved_errno == ENOENT) - unlink (intended_filename); - else { - char *msg; - - msg = g_strdup_printf ("Could not rename %s to %s: %s", - backup_filename, intended_filename, - g_strerror (saved_errno)); - error_message (manager, - _("Could not restore the display's configuration from a backup"), - NULL, - msg); - g_free (msg); - } - - unlink (backup_filename); -} - -typedef struct { - MsdXrandrManager *manager; - GtkWidget *dialog; - - int countdown; - int response_id; -} TimeoutDialog; - -static void -print_countdown_text (TimeoutDialog *timeout) -{ - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (timeout->dialog), - ngettext ("The display will be reset to its previous configuration in %d second", - "The display will be reset to its previous configuration in %d seconds", - timeout->countdown), - timeout->countdown); -} - -static gboolean -timeout_cb (gpointer data) -{ - TimeoutDialog *timeout = data; - - timeout->countdown--; - - if (timeout->countdown == 0) { - timeout->response_id = GTK_RESPONSE_CANCEL; - gtk_main_quit (); - } else { - print_countdown_text (timeout); - } - - return TRUE; -} - -static void -timeout_response_cb (GtkDialog *dialog, int response_id, gpointer data) -{ - TimeoutDialog *timeout = data; - - if (response_id == GTK_RESPONSE_DELETE_EVENT) { - /* The user closed the dialog or pressed ESC, revert */ - timeout->response_id = GTK_RESPONSE_CANCEL; - } else - timeout->response_id = response_id; - - gtk_main_quit (); -} - -static gboolean -user_says_things_are_ok (MsdXrandrManager *manager, GdkWindow *parent_window) -{ - TimeoutDialog timeout; - guint timeout_id; - - timeout.manager = manager; - - timeout.dialog = gtk_message_dialog_new (NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - _("Does the display look OK?")); - - timeout.countdown = CONFIRMATION_DIALOG_SECONDS; - - print_countdown_text (&timeout); - - gtk_dialog_add_button (GTK_DIALOG (timeout.dialog), _("_Restore Previous Configuration"), GTK_RESPONSE_CANCEL); - gtk_dialog_add_button (GTK_DIALOG (timeout.dialog), _("_Keep This Configuration"), GTK_RESPONSE_ACCEPT); - gtk_dialog_set_default_response (GTK_DIALOG (timeout.dialog), GTK_RESPONSE_ACCEPT); /* ah, the optimism */ - - g_signal_connect (timeout.dialog, "response", - G_CALLBACK (timeout_response_cb), - &timeout); - - gtk_widget_realize (timeout.dialog); - - if (parent_window) - gdk_window_set_transient_for (gtk_widget_get_window (timeout.dialog), parent_window); - - gtk_widget_show_all (timeout.dialog); - /* We don't use g_timeout_add_seconds() since we actually care that the user sees "real" second ticks in the dialog */ - timeout_id = g_timeout_add (1000, - timeout_cb, - &timeout); - gtk_main (); - - gtk_widget_destroy (timeout.dialog); - g_source_remove (timeout_id); - - if (timeout.response_id == GTK_RESPONSE_ACCEPT) - return TRUE; - else - return FALSE; -} - -struct confirmation { - MsdXrandrManager *manager; - GdkWindow *parent_window; - guint32 timestamp; -}; - -static gboolean -confirm_with_user_idle_cb (gpointer data) -{ - struct confirmation *confirmation = data; - char *backup_filename; - char *intended_filename; - - backup_filename = mate_rr_config_get_backup_filename (); - intended_filename = mate_rr_config_get_intended_filename (); - - if (user_says_things_are_ok (confirmation->manager, confirmation->parent_window)) - unlink (backup_filename); - else - restore_backup_configuration (confirmation->manager, backup_filename, intended_filename, confirmation->timestamp); - - g_free (confirmation); - - return FALSE; -} - -static void -queue_confirmation_by_user (MsdXrandrManager *manager, GdkWindow *parent_window, guint32 timestamp) -{ - struct confirmation *confirmation; - - confirmation = g_new (struct confirmation, 1); - confirmation->manager = manager; - confirmation->parent_window = parent_window; - confirmation->timestamp = timestamp; - - g_idle_add (confirm_with_user_idle_cb, confirmation); -} - -static gboolean -try_to_apply_intended_configuration (MsdXrandrManager *manager, GdkWindow *parent_window, guint32 timestamp, GError **error) -{ - char *backup_filename; - char *intended_filename; - gboolean result; - - /* Try to apply the intended configuration */ - - backup_filename = mate_rr_config_get_backup_filename (); - intended_filename = mate_rr_config_get_intended_filename (); - - result = apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, error); - if (!result) { - error_message (manager, _("The selected configuration for displays could not be applied"), error ? *error : NULL, NULL); - restore_backup_configuration_without_messages (backup_filename, intended_filename); - goto out; - } else { - /* We need to return as quickly as possible, so instead of - * confirming with the user right here, we do it in an idle - * handler. The caller only expects a status for "could you - * change the RANDR configuration?", not "is the user OK with it - * as well?". - */ - queue_confirmation_by_user (manager, parent_window, timestamp); - } - -out: - g_free (backup_filename); - g_free (intended_filename); - - return result; -} - -/* DBus method for org.mate.SettingsDaemon.XRANDR ApplyConfiguration; see msd-xrandr-manager.xml for the interface definition */ -static gboolean -msd_xrandr_manager_apply_configuration (MsdXrandrManager *manager, - GError **error) -{ - return try_to_apply_intended_configuration (manager, NULL, GDK_CURRENT_TIME, error); -} - -/* DBus method for org.mate.SettingsDaemon.XRANDR_2 ApplyConfiguration; see msd-xrandr-manager.xml for the interface definition */ -static gboolean -msd_xrandr_manager_2_apply_configuration (MsdXrandrManager *manager, - gint64 parent_window_id, - gint64 timestamp, - GError **error) -{ - GdkWindow *parent_window; - gboolean result; - - if (parent_window_id != 0) - parent_window = gdk_window_foreign_new_for_display (gdk_display_get_default (), (GdkNativeWindow) parent_window_id); - else - parent_window = NULL; - - result = try_to_apply_intended_configuration (manager, parent_window, (guint32) timestamp, error); - - if (parent_window) - g_object_unref (parent_window); - - return result; -} - -/* We include this after the definition of msd_xrandr_manager_apply_configuration() so the prototype will already exist */ -#include "msd-xrandr-manager-glue.h" - -static gboolean -is_laptop (MateRRScreen *screen, MateOutputInfo *output) -{ - MateRROutput *rr_output; - - rr_output = mate_rr_screen_get_output_by_name (screen, output->name); - return mate_rr_output_is_laptop (rr_output); -} - -static gboolean -get_clone_size (MateRRScreen *screen, int *width, int *height) -{ - MateRRMode **modes = mate_rr_screen_list_clone_modes (screen); - int best_w, best_h; - int i; - - best_w = 0; - best_h = 0; - - for (i = 0; modes[i] != NULL; ++i) { - MateRRMode *mode = modes[i]; - int w, h; - - w = mate_rr_mode_get_width (mode); - h = mate_rr_mode_get_height (mode); - - if (w * h > best_w * best_h) { - best_w = w; - best_h = h; - } - } - - if (best_w > 0 && best_h > 0) { - if (width) - *width = best_w; - if (height) - *height = best_h; - - return TRUE; - } - - return FALSE; -} - -static void -print_output (MateOutputInfo *info) -{ - g_print (" Output: %s attached to %s\n", info->display_name, info->name); - g_print (" status: %s\n", info->on ? "on" : "off"); - g_print (" width: %d\n", info->width); - g_print (" height: %d\n", info->height); - g_print (" rate: %d\n", info->rate); - g_print (" position: %d %d\n", info->x, info->y); -} - -static void -print_configuration (MateRRConfig *config, const char *header) -{ - int i; - - g_print ("=== %s Configuration ===\n", header); - if (!config) { - g_print (" none\n"); - return; - } - - for (i = 0; config->outputs[i] != NULL; ++i) - print_output (config->outputs[i]); -} - -static gboolean -config_is_all_off (MateRRConfig *config) -{ - int j; - - for (j = 0; config->outputs[j] != NULL; ++j) { - if (config->outputs[j]->on) { - return FALSE; - } - } - - return TRUE; -} - -static MateRRConfig * -make_clone_setup (MateRRScreen *screen) -{ - MateRRConfig *result; - int width, height; - int i; - - if (!get_clone_size (screen, &width, &height)) - return NULL; - - result = mate_rr_config_new_current (screen); - - for (i = 0; result->outputs[i] != NULL; ++i) { - MateOutputInfo *info = result->outputs[i]; - - info->on = FALSE; - if (info->connected) { - MateRROutput *output = - mate_rr_screen_get_output_by_name (screen, info->name); - MateRRMode **modes = mate_rr_output_list_modes (output); - int j; - int best_rate = 0; - - for (j = 0; modes[j] != NULL; ++j) { - MateRRMode *mode = modes[j]; - int w, h; - - w = mate_rr_mode_get_width (mode); - h = mate_rr_mode_get_height (mode); - - if (w == width && h == height) { - int r = mate_rr_mode_get_freq (mode); - if (r > best_rate) - best_rate = r; - } - } - - if (best_rate > 0) { - info->on = TRUE; - info->width = width; - info->height = height; - info->rate = best_rate; - info->rotation = MATE_RR_ROTATION_0; - info->x = 0; - info->y = 0; - } - } - } - - if (config_is_all_off (result)) { - mate_rr_config_free (result); - result = NULL; - } - - print_configuration (result, "clone setup"); - - return result; -} - -static MateRRMode * -find_best_mode (MateRROutput *output) -{ - MateRRMode *preferred; - MateRRMode **modes; - int best_size; - int best_width, best_height, best_rate; - int i; - MateRRMode *best_mode; - - preferred = mate_rr_output_get_preferred_mode (output); - if (preferred) - return preferred; - - modes = mate_rr_output_list_modes (output); - if (!modes) - return NULL; - - best_size = best_width = best_height = best_rate = 0; - best_mode = NULL; - - for (i = 0; modes[i] != NULL; i++) { - int w, h, r; - int size; - - w = mate_rr_mode_get_width (modes[i]); - h = mate_rr_mode_get_height (modes[i]); - r = mate_rr_mode_get_freq (modes[i]); - - size = w * h; - - if (size > best_size) { - best_size = size; - best_width = w; - best_height = h; - best_rate = r; - best_mode = modes[i]; - } else if (size == best_size) { - if (r > best_rate) { - best_rate = r; - best_mode = modes[i]; - } - } - } - - return best_mode; -} - -static gboolean -turn_on (MateRRScreen *screen, - MateOutputInfo *info, - int x, int y) -{ - MateRROutput *output = mate_rr_screen_get_output_by_name (screen, info->name); - MateRRMode *mode = find_best_mode (output); - - if (mode) { - info->on = TRUE; - info->x = x; - info->y = y; - info->width = mate_rr_mode_get_width (mode); - info->height = mate_rr_mode_get_height (mode); - info->rotation = MATE_RR_ROTATION_0; - info->rate = mate_rr_mode_get_freq (mode); - - return TRUE; - } - - return FALSE; -} - -static MateRRConfig * -make_laptop_setup (MateRRScreen *screen) -{ - /* Turn on the laptop, disable everything else */ - MateRRConfig *result = mate_rr_config_new_current (screen); - int i; - - for (i = 0; result->outputs[i] != NULL; ++i) { - MateOutputInfo *info = result->outputs[i]; - - if (is_laptop (screen, info)) { - if (!turn_on (screen, info, 0, 0)) { - mate_rr_config_free (result); - result = NULL; - break; - } - } - else { - info->on = FALSE; - } - } - - if (config_is_all_off (result)) { - mate_rr_config_free (result); - result = NULL; - } - - print_configuration (result, "Laptop setup"); - - /* FIXME - Maybe we should return NULL if there is more than - * one connected "laptop" screen? - */ - return result; - -} - -static int -turn_on_and_get_rightmost_offset (MateRRScreen *screen, MateOutputInfo *info, int x) -{ - if (turn_on (screen, info, x, 0)) - x += info->width; - - return x; -} - -static MateRRConfig * -make_xinerama_setup (MateRRScreen *screen) -{ - /* Turn on everything that has a preferred mode, and - * position it from left to right - */ - MateRRConfig *result = mate_rr_config_new_current (screen); - int i; - int x; - - x = 0; - for (i = 0; result->outputs[i] != NULL; ++i) { - MateOutputInfo *info = result->outputs[i]; - - if (is_laptop (screen, info)) - x = turn_on_and_get_rightmost_offset (screen, info, x); - } - - for (i = 0; result->outputs[i] != NULL; ++i) { - MateOutputInfo *info = result->outputs[i]; - - if (info->connected && !is_laptop (screen, info)) - x = turn_on_and_get_rightmost_offset (screen, info, x); - } - - if (config_is_all_off (result)) { - mate_rr_config_free (result); - result = NULL; - } - - print_configuration (result, "xinerama setup"); - - return result; -} - -static MateRRConfig * -make_other_setup (MateRRScreen *screen) -{ - /* Turn off all laptops, and make all external monitors clone - * from (0, 0) - */ - - MateRRConfig *result = mate_rr_config_new_current (screen); - int i; - - for (i = 0; result->outputs[i] != NULL; ++i) { - MateOutputInfo *info = result->outputs[i]; - - if (is_laptop (screen, info)) { - info->on = FALSE; - } - else { - if (info->connected) - turn_on (screen, info, 0, 0); - } - } - - if (config_is_all_off (result)) { - mate_rr_config_free (result); - result = NULL; - } - - print_configuration (result, "other setup"); - - return result; -} - -static GPtrArray * -sanitize (MsdXrandrManager *manager, GPtrArray *array) -{ - int i; - GPtrArray *new; - - g_debug ("before sanitizing"); - - for (i = 0; i < array->len; ++i) { - if (array->pdata[i]) { - print_configuration (array->pdata[i], "before"); - } - } - - - /* Remove configurations that are duplicates of - * configurations earlier in the cycle - */ - for (i = 0; i < array->len; i++) { - int j; - - for (j = i + 1; j < array->len; j++) { - MateRRConfig *this = array->pdata[j]; - MateRRConfig *other = array->pdata[i]; - - if (this && other && mate_rr_config_equal (this, other)) { - g_debug ("removing duplicate configuration"); - mate_rr_config_free (this); - array->pdata[j] = NULL; - break; - } - } - } - - for (i = 0; i < array->len; ++i) { - MateRRConfig *config = array->pdata[i]; - - if (config && config_is_all_off (config)) { - g_debug ("removing configuration as all outputs are off"); - mate_rr_config_free (array->pdata[i]); - array->pdata[i] = NULL; - } - } - - /* Do a final sanitization pass. This will remove configurations that - * don't fit in the framebuffer's Virtual size. - */ - - for (i = 0; i < array->len; i++) { - MateRRConfig *config = array->pdata[i]; - - if (config) { - GError *error; - - error = NULL; - if (!mate_rr_config_applicable (config, manager->priv->rw_screen, &error)) { /* NULL-GError */ - g_debug ("removing configuration which is not applicable because %s", error->message); - g_error_free (error); - - mate_rr_config_free (config); - array->pdata[i] = NULL; - } - } - } - - /* Remove NULL configurations */ - new = g_ptr_array_new (); - - for (i = 0; i < array->len; ++i) { - if (array->pdata[i]) { - g_ptr_array_add (new, array->pdata[i]); - print_configuration (array->pdata[i], "Final"); - } - } - - if (new->len > 0) { - g_ptr_array_add (new, NULL); - } else { - g_ptr_array_free (new, TRUE); - new = NULL; - } - - g_ptr_array_free (array, TRUE); - - return new; -} - -static void -generate_fn_f7_configs (MsdXrandrManager *mgr) -{ - GPtrArray *array = g_ptr_array_new (); - MateRRScreen *screen = mgr->priv->rw_screen; - - g_debug ("Generating configurations"); - - /* Free any existing list of configurations */ - if (mgr->priv->fn_f7_configs) { - int i; - - for (i = 0; mgr->priv->fn_f7_configs[i] != NULL; ++i) - mate_rr_config_free (mgr->priv->fn_f7_configs[i]); - g_free (mgr->priv->fn_f7_configs); - - mgr->priv->fn_f7_configs = NULL; - mgr->priv->current_fn_f7_config = -1; - } - - g_ptr_array_add (array, mate_rr_config_new_current (screen)); - g_ptr_array_add (array, make_clone_setup (screen)); - g_ptr_array_add (array, make_xinerama_setup (screen)); - g_ptr_array_add (array, make_laptop_setup (screen)); - g_ptr_array_add (array, make_other_setup (screen)); - - array = sanitize (mgr, array); - - if (array) { - mgr->priv->fn_f7_configs = (MateRRConfig **)g_ptr_array_free (array, FALSE); - mgr->priv->current_fn_f7_config = 0; - } -} - -static void -error_message (MsdXrandrManager *mgr, const char *primary_text, GError *error_to_display, const char *secondary_text) -{ -#ifdef HAVE_LIBMATENOTIFY - MsdXrandrManagerPrivate *priv = mgr->priv; - NotifyNotification *notification; - - g_assert (error_to_display == NULL || secondary_text == NULL); - - if (priv->status_icon) - notification = notify_notification_new_with_status_icon (primary_text, - error_to_display ? error_to_display->message : secondary_text, - MSD_XRANDR_ICON_NAME, - priv->status_icon); - else - notification = notify_notification_new (primary_text, - error_to_display ? error_to_display->message : secondary_text, - MSD_XRANDR_ICON_NAME, - NULL); - - notify_notification_show (notification, NULL); /* NULL-GError */ -#else - GtkWidget *dialog; - - dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, - "%s", primary_text); - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", - error_to_display ? error_to_display->message : secondary_text); - - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); -#endif /* HAVE_LIBMATENOTIFY */ -} - -static void -handle_fn_f7 (MsdXrandrManager *mgr, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = mgr->priv; - MateRRScreen *screen = priv->rw_screen; - MateRRConfig *current; - GError *error; - - /* Theory of fn-F7 operation - * - * We maintain a datastructure "fn_f7_status", that contains - * a list of MateRRConfig's. Each of the MateRRConfigs has a - * mode (or "off") for each connected output. - * - * When the user hits fn-F7, we cycle to the next MateRRConfig - * in the data structure. If the data structure does not exist, it - * is generated. If the configs in the data structure do not match - * the current hardware reality, it is regenerated. - * - */ - g_debug ("Handling fn-f7"); - - log_open (); - log_msg ("Handling XF86Display hotkey - timestamp %u\n", timestamp); - - error = NULL; - if (!mate_rr_screen_refresh (screen, &error) && error) { - char *str; - - str = g_strdup_printf (_("Could not refresh the screen information: %s"), error->message); - g_error_free (error); - - log_msg ("%s\n", str); - error_message (mgr, str, NULL, _("Trying to switch the monitor configuration anyway.")); - g_free (str); - } - - if (!priv->fn_f7_configs) { - log_msg ("Generating stock configurations:\n"); - generate_fn_f7_configs (mgr); - log_configurations (priv->fn_f7_configs); - } - - current = mate_rr_config_new_current (screen); - - if (priv->fn_f7_configs && - (!mate_rr_config_match (current, priv->fn_f7_configs[0]) || - !mate_rr_config_equal (current, priv->fn_f7_configs[mgr->priv->current_fn_f7_config]))) { - /* Our view of the world is incorrect, so regenerate the - * configurations - */ - generate_fn_f7_configs (mgr); - log_msg ("Regenerated stock configurations:\n"); - log_configurations (priv->fn_f7_configs); - } - - mate_rr_config_free (current); - - if (priv->fn_f7_configs) { - guint32 server_timestamp; - gboolean success; - - mgr->priv->current_fn_f7_config++; - - if (priv->fn_f7_configs[mgr->priv->current_fn_f7_config] == NULL) - mgr->priv->current_fn_f7_config = 0; - - g_debug ("cycling to next configuration (%d)", mgr->priv->current_fn_f7_config); - - print_configuration (priv->fn_f7_configs[mgr->priv->current_fn_f7_config], "new config"); - - g_debug ("applying"); - - /* See https://bugzilla.gnome.org/show_bug.cgi?id=610482 - * - * Sometimes we'll get two rapid XF86Display keypress events, - * but their timestamps will be out of order with respect to the - * RANDR timestamps. This *may* be due to stupid BIOSes sending - * out display-switch keystrokes "to make Windows work". - * - * The X server will error out if the timestamp provided is - * older than a previous change configuration timestamp. We - * assume here that we do want this event to go through still, - * since kernel timestamps may be skewed wrt the X server. - */ - mate_rr_screen_get_timestamps (screen, NULL, &server_timestamp); - if (timestamp < server_timestamp) - timestamp = server_timestamp; - - success = apply_configuration_and_display_error (mgr, priv->fn_f7_configs[mgr->priv->current_fn_f7_config], timestamp); - - if (success) { - log_msg ("Successfully switched to configuration (timestamp %u):\n", timestamp); - log_configuration (priv->fn_f7_configs[mgr->priv->current_fn_f7_config]); - } - } - else { - g_debug ("no configurations generated"); - } - - log_close (); - - g_debug ("done handling fn-f7"); -} - -static MateOutputInfo * -get_laptop_output_info (MateRRScreen *screen, MateRRConfig *config) -{ - int i; - - for (i = 0; config->outputs[i] != NULL; i++) { - MateOutputInfo *info; - - info = config->outputs[i]; - if (is_laptop (screen, info)) - return info; - } - - return NULL; - -} - -static MateRRRotation -get_next_rotation (MateRRRotation allowed_rotations, MateRRRotation 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++) { - MateRRRotation 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) { - MateRRRotation 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 (MsdXrandrManager *mgr, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = mgr->priv; - MateRRScreen *screen = priv->rw_screen; - MateRRConfig *current; - MateOutputInfo *rotatable_output_info; - int num_allowed_rotations; - MateRRRotation allowed_rotations; - MateRRRotation next_rotation; - - g_debug ("Handling XF86RotateWindows"); - - /* Which output? */ - - current = mate_rr_config_new_current (screen); - - rotatable_output_info = get_laptop_output_info (screen, 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; - - apply_configuration_and_display_error (mgr, current, timestamp); - -out: - mate_rr_config_free (current); -} - -static GdkFilterReturn -event_filter (GdkXEvent *xevent, - GdkEvent *event, - gpointer data) -{ - MsdXrandrManager *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->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; - } - - return GDK_FILTER_CONTINUE; -} - -static void -refresh_tray_icon_menu_if_active (MsdXrandrManager *manager, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = manager->priv; - - if (priv->popup_menu) { - gtk_menu_shell_cancel (GTK_MENU_SHELL (priv->popup_menu)); /* status_icon_popup_menu_selection_done_cb() will free everything */ - status_icon_popup_menu (manager, 0, timestamp); - } -} - -static void -auto_configure_outputs (MsdXrandrManager *manager, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = manager->priv; - MateRRConfig *config; - int i; - GList *just_turned_on; - GList *l; - int x; - GError *error; - gboolean applicable; - - config = mate_rr_config_new_current (priv->rw_screen); - - /* For outputs that are connected and on (i.e. they have a CRTC assigned - * to them, so they are getting a signal), we leave them as they are - * with their current modes. - * - * For other outputs, we will turn on connected-but-off outputs and turn - * off disconnected-but-on outputs. - * - * FIXME: If an output remained connected+on, it would be nice to ensure - * that the output's CRTCs still has a reasonable mode (think of - * changing one monitor for another with different capabilities). - */ - - just_turned_on = NULL; - - for (i = 0; config->outputs[i] != NULL; i++) { - MateOutputInfo *output = config->outputs[i]; - - if (output->connected && !output->on) { - output->on = TRUE; - output->rotation = MATE_RR_ROTATION_0; - just_turned_on = g_list_prepend (just_turned_on, GINT_TO_POINTER (i)); - } else if (!output->connected && output->on) - output->on = FALSE; - } - - /* Now, lay out the outputs from left to right. Put first the outputs - * which remained on; put last the outputs that were newly turned on. - */ - - x = 0; - - /* First, outputs that remained on */ - - for (i = 0; config->outputs[i] != NULL; i++) { - MateOutputInfo *output = config->outputs[i]; - - if (g_list_find (just_turned_on, GINT_TO_POINTER (i))) - continue; - - if (output->on) { - g_assert (output->connected); - - output->x = x; - output->y = 0; - - x += output->width; - } - } - - /* Second, outputs that were newly-turned on */ - - for (l = just_turned_on; l; l = l->next) { - MateOutputInfo *output; - - i = GPOINTER_TO_INT (l->data); - output = config->outputs[i]; - - g_assert (output->on && output->connected); - - output->x = x; - output->y = 0; - - /* since the output was off, use its preferred width/height (it doesn't have a real width/height yet) */ - output->width = output->pref_width; - output->height = output->pref_height; - - x += output->width; - } - - /* Check if we have a large enough framebuffer size. If not, turn off - * outputs from right to left until we reach a usable size. - */ - - just_turned_on = g_list_reverse (just_turned_on); /* now the outputs here are from right to left */ - - l = just_turned_on; - while (1) { - MateOutputInfo *output; - gboolean is_bounds_error; - - error = NULL; - applicable = mate_rr_config_applicable (config, priv->rw_screen, &error); - - if (applicable) - break; - - is_bounds_error = g_error_matches (error, MATE_RR_ERROR, MATE_RR_ERROR_BOUNDS_ERROR); - g_error_free (error); - - if (!is_bounds_error) - break; - - if (l) { - i = GPOINTER_TO_INT (l->data); - l = l->next; - - output = config->outputs[i]; - output->on = FALSE; - } else - break; - } - - /* Apply the configuration! */ - - if (applicable) - apply_configuration_and_display_error (manager, config, timestamp); - - g_list_free (just_turned_on); - mate_rr_config_free (config); - - /* Finally, even though we did a best-effort job in sanitizing the - * outputs, we don't know the physical layout of the monitors. We'll - * start the display capplet so that the user can tweak things to his - * liking. - */ - -#if 0 - /* FIXME: This is disabled for now. The capplet is not a single-instance application. - * If you do this: - * - * 1. Start the display capplet - * - * 2. Plug an extra monitor - * - * 3. Hit the "Detect displays" button - * - * Then we will get a RANDR event because X re-probes the outputs. We don't want to - * start up a second display capplet right there! - */ - - run_display_capplet (NULL); -#endif -} - -static void -apply_color_profiles (void) -{ - gboolean ret; - GError *error = NULL; - - /* run the mate-color-manager apply program */ - ret = g_spawn_command_line_async (BINDIR "/gcm-apply", &error); - if (!ret) { - /* only print the warning if the binary is installed */ - if (error->code != G_SPAWN_ERROR_NOENT) { - g_warning ("failed to apply color profiles: %s", error->message); - } - g_error_free (error); - } -} - -static void -on_randr_event (MateRRScreen *screen, gpointer data) -{ - MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); - MsdXrandrManagerPrivate *priv = manager->priv; - guint32 change_timestamp, config_timestamp; - - if (!priv->running) - return; - - mate_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp); - - log_open (); - log_msg ("Got RANDR event with timestamps change=%u %c config=%u\n", - change_timestamp, - timestamp_relationship (change_timestamp, config_timestamp), - config_timestamp); - - if (change_timestamp >= config_timestamp) { - /* The event is due to an explicit configuration change. - * - * If the change was performed by us, then we need to do nothing. - * - * If the change was done by some other X client, we don't need - * to do anything, either; the screen is already configured. - */ - show_timestamps_dialog (manager, "ignoring since change > config"); - log_msg (" Ignoring event since change >= config\n"); - } else { - /* Here, config_timestamp > change_timestamp. This means that - * the screen got reconfigured because of hotplug/unplug; the X - * server is just notifying us, and we need to configure the - * outputs in a sane way. - */ - - char *intended_filename; - GError *error; - gboolean success; - - show_timestamps_dialog (manager, "need to deal with reconfiguration, as config > change"); - - intended_filename = mate_rr_config_get_intended_filename (); - - error = NULL; - success = apply_configuration_from_filename (manager, intended_filename, TRUE, config_timestamp, &error); - g_free (intended_filename); - - if (!success) { - /* We don't bother checking the error type. - * - * Both G_FILE_ERROR_NOENT and - * MATE_RR_ERROR_NO_MATCHING_CONFIG would mean, "there - * was no configuration to apply, or none that matched - * the current outputs", and in that case we need to run - * our fallback. - * - * Any other error means "we couldn't do the smart thing - * of using a previously- saved configuration, anyway, - * for some other reason. In that case, we also need to - * run our fallback to avoid leaving the user with a - * bogus configuration. - */ - - if (error) - g_error_free (error); - - if (config_timestamp != priv->last_config_timestamp) { - priv->last_config_timestamp = config_timestamp; - auto_configure_outputs (manager, config_timestamp); - log_msg (" Automatically configured outputs to deal with event\n"); - } else - log_msg (" Ignored event as old and new config timestamps are the same\n"); - } else - log_msg ("Applied stored configuration to deal with event\n"); - } - - /* poke mate-color-manager */ - apply_color_profiles (); - - refresh_tray_icon_menu_if_active (manager, MAX (change_timestamp, config_timestamp)); - - log_close (); -} - -static void -run_display_capplet (GtkWidget *widget) -{ - GdkScreen *screen; - GError *error; - - if (widget) - screen = gtk_widget_get_screen (widget); - else - screen = gdk_screen_get_default (); - - error = NULL; - if (!gdk_spawn_command_line_on_screen (screen, MSD_XRANDR_DISPLAY_CAPPLET, &error)) { - GtkWidget *dialog; - - dialog = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, - "" - "Display configuration could not be run" - "\n\n" - "%s", error->message); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - - g_error_free (error); - } -} - -static void -popup_menu_configure_display_cb (GtkMenuItem *item, gpointer data) -{ - run_display_capplet (GTK_WIDGET (item)); -} - -static void -status_icon_popup_menu_selection_done_cb (GtkMenuShell *menu_shell, gpointer data) -{ - MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); - struct MsdXrandrManagerPrivate *priv = manager->priv; - - gtk_widget_destroy (priv->popup_menu); - priv->popup_menu = NULL; - - mate_rr_labeler_hide (priv->labeler); - g_object_unref (priv->labeler); - priv->labeler = NULL; - - mate_rr_config_free (priv->configuration); - priv->configuration = NULL; -} - -#define OUTPUT_TITLE_ITEM_BORDER 2 -#define OUTPUT_TITLE_ITEM_PADDING 4 - -/* This is an expose-event hander for the title label for each MateRROutput. - * We want each title to have a colored background, so we paint that background, then - * return FALSE to let GtkLabel expose itself (i.e. paint the label's text), and then - * we have a signal_connect_after handler as well. See the comments below - * to see why that "after" handler is needed. - */ -static gboolean -output_title_label_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) -{ - MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); - struct MsdXrandrManagerPrivate *priv = manager->priv; - MateOutputInfo *output; - GdkColor color; - cairo_t *cr; - GtkAllocation allocation; - - g_assert (GTK_IS_LABEL (widget)); - - output = g_object_get_data (G_OBJECT (widget), "output"); - g_assert (output != NULL); - - g_assert (priv->labeler != NULL); - - /* Draw a black rectangular border, filled with the color that corresponds to this output */ - - mate_rr_labeler_get_color_for_output (priv->labeler, output, &color); - - cr = gdk_cairo_create (gtk_widget_get_window (widget)); - - cairo_set_source_rgb (cr, 0, 0, 0); - cairo_set_line_width (cr, OUTPUT_TITLE_ITEM_BORDER); - gtk_widget_get_allocation (widget, &allocation); - cairo_rectangle (cr, - allocation.x + OUTPUT_TITLE_ITEM_BORDER / 2.0, - allocation.y + OUTPUT_TITLE_ITEM_BORDER / 2.0, - allocation.width - OUTPUT_TITLE_ITEM_BORDER, - allocation.height - OUTPUT_TITLE_ITEM_BORDER); - cairo_stroke (cr); - - gdk_cairo_set_source_color (cr, &color); - cairo_rectangle (cr, - allocation.x + OUTPUT_TITLE_ITEM_BORDER, - allocation.y + OUTPUT_TITLE_ITEM_BORDER, - allocation.width - 2 * OUTPUT_TITLE_ITEM_BORDER, - allocation.height - 2 * OUTPUT_TITLE_ITEM_BORDER); - - cairo_fill (cr); - - /* We want the label to always show up as if it were sensitive - * ("style->fg[GTK_STATE_NORMAL]"), even though the label is insensitive - * due to being inside an insensitive menu item. So, here we have a - * HACK in which we frob the label's state directly. GtkLabel's expose - * handler will be run after this function, so it will think that the - * label is in GTK_STATE_NORMAL. We reset the label's state back to - * insensitive in output_title_label_after_expose_event_cb(). - * - * Yay for fucking with GTK+'s internals. - */ - - gtk_widget_set_state (widget, GTK_STATE_NORMAL); - - return FALSE; -} - -/* See the comment in output_title_event_box_expose_event_cb() about this funny label widget */ -static gboolean -output_title_label_after_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) -{ - g_assert (GTK_IS_LABEL (widget)); - gtk_widget_set_state (widget, GTK_STATE_INSENSITIVE); - - return FALSE; -} - -static void -title_item_size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer data) -{ - /* When GtkMenu does size_request on its items, it asks them for their "toggle size", - * which will be non-zero when there are check/radio items. GtkMenu remembers - * the largest of those sizes. During the size_allocate pass, GtkMenu calls - * gtk_menu_item_toggle_size_allocate() with that value, to tell the menu item - * that it should later paint its child a bit to the right of its edge. - * - * However, we want the "title" menu items for each RANDR output to span the *whole* - * allocation of the menu item, not just the "allocation minus toggle" area. - * - * So, we let the menu item size_allocate itself as usual, but this - * callback gets run afterward. Here we hack a toggle size of 0 into - * the menu item, and size_allocate it by hand *again*. We also need to - * avoid recursing into this function. - */ - - g_assert (GTK_IS_MENU_ITEM (widget)); - - gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (widget), 0); - - g_signal_handlers_block_by_func (widget, title_item_size_allocate_cb, NULL); - - /* Sigh. There is no way to turn on GTK_ALLOC_NEEDED outside of GTK+ - * itself; also, since calling size_allocate on a widget with the same - * allcation is a no-op, we need to allocate with a "different" size - * first. - */ - - allocation->width++; - gtk_widget_size_allocate (widget, allocation); - - allocation->width--; - gtk_widget_size_allocate (widget, allocation); - - g_signal_handlers_unblock_by_func (widget, title_item_size_allocate_cb, NULL); -} - -static GtkWidget * -make_menu_item_for_output_title (MsdXrandrManager *manager, MateOutputInfo *output) -{ - GtkWidget *item; - GtkWidget *label; - char *str; - GdkColor black = { 0, 0, 0, 0 }; - - item = gtk_menu_item_new (); - - g_signal_connect (item, "size-allocate", - G_CALLBACK (title_item_size_allocate_cb), NULL); - - str = g_markup_printf_escaped ("%s", output->display_name); - label = gtk_label_new (NULL); - gtk_label_set_markup (GTK_LABEL (label), str); - g_free (str); - - /* Make the label explicitly black. We don't want it to follow the - * theme's colors, since the label is always shown against a light - * pastel background. See bgo#556050 - */ - gtk_widget_modify_fg (label, gtk_widget_get_state (label), &black); - - /* Add padding around the label to fit the box that we'll draw for color-coding */ - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_misc_set_padding (GTK_MISC (label), - OUTPUT_TITLE_ITEM_BORDER + OUTPUT_TITLE_ITEM_PADDING, - OUTPUT_TITLE_ITEM_BORDER + OUTPUT_TITLE_ITEM_PADDING); - - gtk_container_add (GTK_CONTAINER (item), label); - - /* We want to paint a colored box as the background of the label, so we connect - * to its expose-event signal. See the comment in *** to see why need to connect - * to the label both 'before' and 'after'. - */ - g_signal_connect (label, "expose-event", - G_CALLBACK (output_title_label_expose_event_cb), manager); - g_signal_connect_after (label, "expose-event", - G_CALLBACK (output_title_label_after_expose_event_cb), manager); - - g_object_set_data (G_OBJECT (label), "output", output); - - gtk_widget_set_sensitive (item, FALSE); /* the title is not selectable */ - gtk_widget_show_all (item); - - return item; -} - -static void -get_allowed_rotations_for_output (MateRRConfig *config, - MateRRScreen *rr_screen, - MateOutputInfo *output, - int *out_num_rotations, - MateRRRotation *out_rotations) -{ - MateRRRotation current_rotation; - int i; - - *out_num_rotations = 0; - *out_rotations = 0; - - current_rotation = output->rotation; - - /* Yay for brute force */ - - for (i = 0; i < G_N_ELEMENTS (possible_rotations); i++) { - MateRRRotation rotation_to_test; - - rotation_to_test = possible_rotations[i]; - - output->rotation = rotation_to_test; - - if (mate_rr_config_applicable (config, rr_screen, NULL)) { /* NULL-GError */ - (*out_num_rotations)++; - (*out_rotations) |= rotation_to_test; - } - } - - output->rotation = current_rotation; - - if (*out_num_rotations == 0 || *out_rotations == 0) { - g_warning ("Huh, output %p says it doesn't support any rotations, and yet it has a current rotation?", output); - *out_num_rotations = 1; - *out_rotations = output->rotation; - } -} - -static void -add_unsupported_rotation_item (MsdXrandrManager *manager) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - GtkWidget *item; - GtkWidget *label; - gchar *markup; - - item = gtk_menu_item_new (); - - label = gtk_label_new (NULL); - markup = g_strdup_printf ("%s", _("Rotation not supported")); - gtk_label_set_markup (GTK_LABEL (label), markup); - g_free (markup); - gtk_container_add (GTK_CONTAINER (item), label); - - gtk_widget_show_all (item); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); -} - -static void -ensure_current_configuration_is_saved (void) -{ - MateRRScreen *rr_screen; - MateRRConfig *rr_config; - - /* Normally, mate_rr_config_save() creates a backup file based on the - * old monitors.xml. However, if *that* file didn't exist, there is - * nothing from which to create a backup. So, here we'll save the - * current/unchanged configuration and then let our caller call - * mate_rr_config_save() again with the new/changed configuration, so - * that there *will* be a backup file in the end. - */ - - rr_screen = mate_rr_screen_new (gdk_screen_get_default (), NULL, NULL, NULL); /* NULL-GError */ - if (!rr_screen) - return; - - rr_config = mate_rr_config_new_current (rr_screen); - mate_rr_config_save (rr_config, NULL); /* NULL-GError */ - - mate_rr_config_free (rr_config); - mate_rr_screen_destroy (rr_screen); -} - -static void -output_rotation_item_activate_cb (GtkCheckMenuItem *item, gpointer data) -{ - MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); - struct MsdXrandrManagerPrivate *priv = manager->priv; - MateOutputInfo *output; - MateRRRotation rotation; - GError *error; - - /* Not interested in deselected items */ - if (!gtk_check_menu_item_get_active (item)) - return; - - ensure_current_configuration_is_saved (); - - output = g_object_get_data (G_OBJECT (item), "output"); - rotation = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "rotation")); - - output->rotation = rotation; - - error = NULL; - if (!mate_rr_config_save (priv->configuration, &error)) { - error_message (manager, _("Could not save monitor configuration"), error, NULL); - if (error) - g_error_free (error); - - return; - } - - try_to_apply_intended_configuration (manager, NULL, gtk_get_current_event_time (), NULL); /* NULL-GError */ -} - -static void -add_items_for_rotations (MsdXrandrManager *manager, MateOutputInfo *output, MateRRRotation allowed_rotations) -{ - typedef struct { - MateRRRotation rotation; - const char * name; - } RotationInfo; - static const RotationInfo rotations[] = { - { MATE_RR_ROTATION_0, N_("Normal") }, - { MATE_RR_ROTATION_90, N_("Left") }, - { MATE_RR_ROTATION_270, N_("Right") }, - { MATE_RR_ROTATION_180, N_("Upside Down") }, - /* We don't allow REFLECT_X or REFLECT_Y for now, as mate-display-properties doesn't allow them, either */ - }; - - struct MsdXrandrManagerPrivate *priv = manager->priv; - int i; - GSList *group; - GtkWidget *active_item; - gulong active_item_activate_id; - - group = NULL; - active_item = NULL; - active_item_activate_id = 0; - - for (i = 0; i < G_N_ELEMENTS (rotations); i++) { - MateRRRotation rot; - GtkWidget *item; - gulong activate_id; - - rot = rotations[i].rotation; - - if ((allowed_rotations & rot) == 0) { - /* don't display items for rotations which are - * unavailable. Their availability is not under the - * user's control, anyway. - */ - continue; - } - - item = gtk_radio_menu_item_new_with_label (group, _(rotations[i].name)); - gtk_widget_show_all (item); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); - - g_object_set_data (G_OBJECT (item), "output", output); - g_object_set_data (G_OBJECT (item), "rotation", GINT_TO_POINTER (rot)); - - activate_id = g_signal_connect (item, "activate", - G_CALLBACK (output_rotation_item_activate_cb), manager); - - group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item)); - - if (rot == output->rotation) { - active_item = item; - active_item_activate_id = activate_id; - } - } - - if (active_item) { - /* Block the signal temporarily so our callback won't be called; - * we are just setting up the UI. - */ - g_signal_handler_block (active_item, active_item_activate_id); - - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (active_item), TRUE); - - g_signal_handler_unblock (active_item, active_item_activate_id); - } - -} - -static void -add_rotation_items_for_output (MsdXrandrManager *manager, MateOutputInfo *output) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - int num_rotations; - MateRRRotation rotations; - - get_allowed_rotations_for_output (priv->configuration, priv->rw_screen, output, &num_rotations, &rotations); - - if (num_rotations == 1) - add_unsupported_rotation_item (manager); - else - add_items_for_rotations (manager, output, rotations); -} - -static void -add_menu_items_for_output (MsdXrandrManager *manager, MateOutputInfo *output) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - GtkWidget *item; - - item = make_menu_item_for_output_title (manager, output); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); - - add_rotation_items_for_output (manager, output); -} - -static void -add_menu_items_for_outputs (MsdXrandrManager *manager) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - int i; - - for (i = 0; priv->configuration->outputs[i] != NULL; i++) { - if (priv->configuration->outputs[i]->connected) - add_menu_items_for_output (manager, priv->configuration->outputs[i]); - } -} - -static void -status_icon_popup_menu (MsdXrandrManager *manager, guint button, guint32 timestamp) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - GtkWidget *item; - - g_assert (priv->configuration == NULL); - priv->configuration = mate_rr_config_new_current (priv->rw_screen); - - g_assert (priv->labeler == NULL); - priv->labeler = mate_rr_labeler_new (priv->configuration); - - g_assert (priv->popup_menu == NULL); - priv->popup_menu = gtk_menu_new (); - - add_menu_items_for_outputs (manager); - - item = gtk_separator_menu_item_new (); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); - - item = gtk_menu_item_new_with_mnemonic (_("_Configure Display Settingsā€¦")); - g_signal_connect (item, "activate", - G_CALLBACK (popup_menu_configure_display_cb), manager); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); - - g_signal_connect (priv->popup_menu, "selection-done", - G_CALLBACK (status_icon_popup_menu_selection_done_cb), manager); - - gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL, - gtk_status_icon_position_menu, - priv->status_icon, button, timestamp); -} - -static void -status_icon_activate_cb (GtkStatusIcon *status_icon, gpointer data) -{ - MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); - - /* Suck; we don't get a proper button/timestamp */ - status_icon_popup_menu (manager, 0, gtk_get_current_event_time ()); -} - -static void -status_icon_popup_menu_cb (GtkStatusIcon *status_icon, guint button, guint32 timestamp, gpointer data) -{ - MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); - - status_icon_popup_menu (manager, button, timestamp); -} - -static void -status_icon_start (MsdXrandrManager *manager) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - - /* Ideally, we should detect if we are on a tablet and only display - * the icon in that case. - */ - if (!priv->status_icon) { - priv->status_icon = gtk_status_icon_new_from_icon_name (MSD_XRANDR_ICON_NAME); - gtk_status_icon_set_tooltip_text (priv->status_icon, _("Configure display settings")); - - g_signal_connect (priv->status_icon, "activate", - G_CALLBACK (status_icon_activate_cb), manager); - g_signal_connect (priv->status_icon, "popup-menu", - G_CALLBACK (status_icon_popup_menu_cb), manager); - } -} - -static void -status_icon_stop (MsdXrandrManager *manager) -{ - struct MsdXrandrManagerPrivate *priv = manager->priv; - - if (priv->status_icon) { - g_signal_handlers_disconnect_by_func ( - priv->status_icon, G_CALLBACK (status_icon_activate_cb), manager); - g_signal_handlers_disconnect_by_func ( - priv->status_icon, G_CALLBACK (status_icon_popup_menu_cb), manager); - - /* hide the icon before unreffing it; otherwise we will leak - whitespace in the notification area due to a bug in there */ - gtk_status_icon_set_visible (priv->status_icon, FALSE); - g_object_unref (priv->status_icon); - priv->status_icon = NULL; - } -} - -static void -start_or_stop_icon (MsdXrandrManager *manager) -{ - if (mateconf_client_get_bool (manager->priv->client, CONF_KEY_SHOW_NOTIFICATION_ICON, NULL)) { - status_icon_start (manager); - } - else { - status_icon_stop (manager); - } -} - -static void -on_config_changed (MateConfClient *client, - guint cnxn_id, - MateConfEntry *entry, - MsdXrandrManager *manager) -{ - if (strcmp (entry->key, CONF_KEY_SHOW_NOTIFICATION_ICON) == 0) - start_or_stop_icon (manager); -} - -static gboolean -apply_intended_configuration (MsdXrandrManager *manager, const char *intended_filename, guint32 timestamp) -{ - GError *my_error; - gboolean result; - - my_error = NULL; - result = apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, &my_error); - if (!result) { - if (my_error) { - if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) - error_message (manager, _("Could not apply the stored configuration for monitors"), my_error, NULL); - - g_error_free (my_error); - } - } - - return result; -} - -static void -apply_default_boot_configuration (MsdXrandrManager *mgr, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = mgr->priv; - MateRRScreen *screen = priv->rw_screen; - MateRRConfig *config; - gboolean turn_on_external, turn_on_laptop; - - turn_on_external = - mateconf_client_get_bool (mgr->priv->client, CONF_KEY_TURN_ON_EXTERNAL_MONITORS_AT_STARTUP, NULL); - turn_on_laptop = - mateconf_client_get_bool (mgr->priv->client, CONF_KEY_TURN_ON_LAPTOP_MONITOR_AT_STARTUP, NULL); - - if (turn_on_external && turn_on_laptop) - config = make_clone_setup (screen); - else if (!turn_on_external && turn_on_laptop) - config = make_laptop_setup (screen); - else if (turn_on_external && !turn_on_laptop) - config = make_other_setup (screen); - else - config = make_laptop_setup (screen); - - if (config) { - apply_configuration_and_display_error (mgr, config, timestamp); - mate_rr_config_free (config); - } -} - -static gboolean -apply_stored_configuration_at_startup (MsdXrandrManager *manager, guint32 timestamp) -{ - GError *my_error; - gboolean success; - char *backup_filename; - char *intended_filename; - - backup_filename = mate_rr_config_get_backup_filename (); - intended_filename = mate_rr_config_get_intended_filename (); - - /* 1. See if there was a "saved" configuration. If there is one, it means - * that the user had selected to change the display configuration, but the - * machine crashed. In that case, we'll apply *that* configuration and save it on top of the - * "intended" one. - */ - - my_error = NULL; - - success = apply_configuration_from_filename (manager, backup_filename, FALSE, timestamp, &my_error); - if (success) { - /* The backup configuration existed, and could be applied - * successfully, so we must restore it on top of the - * failed/intended one. - */ - restore_backup_configuration (manager, backup_filename, intended_filename, timestamp); - goto out; - } - - if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - /* Epic fail: there (probably) was a backup configuration, but - * we could not apply it. The only thing we can do is delete - * the backup configuration. Let's hope that the user doesn't - * get left with an unusable display... - */ - - unlink (backup_filename); - goto out; - } - - /* 2. There was no backup configuration! This means we are - * good. Apply the intended configuration instead. - */ - - success = apply_intended_configuration (manager, intended_filename, timestamp); - -out: - - if (my_error) - g_error_free (my_error); - - g_free (backup_filename); - g_free (intended_filename); - - return success; -} - -static gboolean -apply_default_configuration_from_file (MsdXrandrManager *manager, guint32 timestamp) -{ - MsdXrandrManagerPrivate *priv = manager->priv; - char *default_config_filename; - gboolean result; - - default_config_filename = mateconf_client_get_string (priv->client, CONF_KEY_DEFAULT_CONFIGURATION_FILE, NULL); - if (!default_config_filename) - return FALSE; - - result = apply_configuration_from_filename (manager, default_config_filename, TRUE, timestamp, NULL); - - g_free (default_config_filename); - return result; -} - -gboolean -msd_xrandr_manager_start (MsdXrandrManager *manager, - GError **error) -{ - g_debug ("Starting xrandr manager"); - mate_settings_profile_start (NULL); - - log_open (); - log_msg ("------------------------------------------------------------\nSTARTING XRANDR PLUGIN\n"); - - manager->priv->rw_screen = mate_rr_screen_new ( - gdk_screen_get_default (), on_randr_event, manager, error); - - if (manager->priv->rw_screen == NULL) { - log_msg ("Could not initialize the RANDR plugin%s%s\n", - (error && *error) ? ": " : "", - (error && *error) ? (*error)->message : ""); - log_close (); - return FALSE; - } - - log_msg ("State of screen at startup:\n"); - log_screen (manager->priv->rw_screen); - - manager->priv->running = TRUE; - manager->priv->client = mateconf_client_get_default (); - - g_assert (manager->priv->notify_id == 0); - - mateconf_client_add_dir (manager->priv->client, CONF_DIR, - MATECONF_CLIENT_PRELOAD_ONELEVEL, - NULL); - - manager->priv->notify_id = - mateconf_client_notify_add ( - manager->priv->client, CONF_DIR, - (MateConfClientNotifyFunc)on_config_changed, - manager, NULL, NULL); - - if (manager->priv->switch_video_mode_keycode) { - gdk_error_trap_push (); - - XGrabKey (gdk_x11_get_default_xdisplay(), - manager->priv->switch_video_mode_keycode, AnyModifier, - gdk_x11_get_default_root_xwindow(), - True, GrabModeAsync, GrabModeAsync); - - gdk_flush (); - 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"); - if (!apply_stored_configuration_at_startup (manager, GDK_CURRENT_TIME)) /* we don't have a real timestamp at startup anyway */ - if (!apply_default_configuration_from_file (manager, GDK_CURRENT_TIME)) - apply_default_boot_configuration (manager, GDK_CURRENT_TIME); - - log_msg ("State of screen after initial configuration:\n"); - log_screen (manager->priv->rw_screen); - - gdk_window_add_filter (gdk_get_default_root_window(), - (GdkFilterFunc)event_filter, - manager); - - start_or_stop_icon (manager); - - log_close (); - - mate_settings_profile_end (NULL); - - return TRUE; -} - -void -msd_xrandr_manager_stop (MsdXrandrManager *manager) -{ - g_debug ("Stopping xrandr manager"); - - manager->priv->running = FALSE; - - if (manager->priv->switch_video_mode_keycode) { - gdk_error_trap_push (); - - XUngrabKey (gdk_x11_get_default_xdisplay(), - manager->priv->switch_video_mode_keycode, AnyModifier, - gdk_x11_get_default_root_xwindow()); - - 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); - - if (manager->priv->notify_id != 0) { - mateconf_client_remove_dir (manager->priv->client, - CONF_DIR, NULL); - mateconf_client_notify_remove (manager->priv->client, - manager->priv->notify_id); - manager->priv->notify_id = 0; - } - - if (manager->priv->client != NULL) { - g_object_unref (manager->priv->client); - manager->priv->client = NULL; - } - - if (manager->priv->rw_screen != NULL) { - mate_rr_screen_destroy (manager->priv->rw_screen); - manager->priv->rw_screen = NULL; - } - - if (manager->priv->dbus_connection != NULL) { - dbus_g_connection_unref (manager->priv->dbus_connection); - manager->priv->dbus_connection = NULL; - } - - status_icon_stop (manager); - - log_open (); - log_msg ("STOPPING XRANDR PLUGIN\n------------------------------------------------------------\n"); - log_close (); -} - -static void -msd_xrandr_manager_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MsdXrandrManager *self; - - self = MSD_XRANDR_MANAGER (object); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -msd_xrandr_manager_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MsdXrandrManager *self; - - self = MSD_XRANDR_MANAGER (object); - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GObject * -msd_xrandr_manager_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties) -{ - MsdXrandrManager *xrandr_manager; - MsdXrandrManagerClass *klass; - - klass = MSD_XRANDR_MANAGER_CLASS (g_type_class_peek (MSD_TYPE_XRANDR_MANAGER)); - - xrandr_manager = MSD_XRANDR_MANAGER (G_OBJECT_CLASS (msd_xrandr_manager_parent_class)->constructor (type, - n_construct_properties, - construct_properties)); - - return G_OBJECT (xrandr_manager); -} - -static void -msd_xrandr_manager_dispose (GObject *object) -{ - MsdXrandrManager *xrandr_manager; - - xrandr_manager = MSD_XRANDR_MANAGER (object); - - G_OBJECT_CLASS (msd_xrandr_manager_parent_class)->dispose (object); -} - -static void -msd_xrandr_manager_class_init (MsdXrandrManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->get_property = msd_xrandr_manager_get_property; - object_class->set_property = msd_xrandr_manager_set_property; - object_class->constructor = msd_xrandr_manager_constructor; - object_class->dispose = msd_xrandr_manager_dispose; - object_class->finalize = msd_xrandr_manager_finalize; - - dbus_g_object_type_install_info (MSD_TYPE_XRANDR_MANAGER, &dbus_glib_msd_xrandr_manager_object_info); - - g_type_class_add_private (klass, sizeof (MsdXrandrManagerPrivate)); -} - -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 -msd_xrandr_manager_init (MsdXrandrManager *manager) -{ - manager->priv = MSD_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; -} - -static void -msd_xrandr_manager_finalize (GObject *object) -{ - MsdXrandrManager *xrandr_manager; - - g_return_if_fail (object != NULL); - g_return_if_fail (MSD_IS_XRANDR_MANAGER (object)); - - xrandr_manager = MSD_XRANDR_MANAGER (object); - - g_return_if_fail (xrandr_manager->priv != NULL); - - G_OBJECT_CLASS (msd_xrandr_manager_parent_class)->finalize (object); -} - -static gboolean -register_manager_dbus (MsdXrandrManager *manager) -{ - GError *error = NULL; - - manager->priv->dbus_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (manager->priv->dbus_connection == NULL) { - if (error != NULL) { - g_warning ("Error getting session bus: %s", error->message); - g_error_free (error); - } - return FALSE; - } - - /* Hmm, should we do this in msd_xrandr_manager_start()? */ - dbus_g_connection_register_g_object (manager->priv->dbus_connection, MSD_XRANDR_DBUS_PATH, G_OBJECT (manager)); - - return TRUE; -} - -MsdXrandrManager * -msd_xrandr_manager_new (void) -{ - if (manager_object != NULL) { - g_object_ref (manager_object); - } else { - manager_object = g_object_new (MSD_TYPE_XRANDR_MANAGER, NULL); - g_object_add_weak_pointer (manager_object, - (gpointer *) &manager_object); - - if (!register_manager_dbus (manager_object)) { - g_object_unref (manager_object); - return NULL; - } - } - - return MSD_XRANDR_MANAGER (manager_object); -} diff --git a/plugins/xrandr/gsd-xrandr-manager.h b/plugins/xrandr/gsd-xrandr-manager.h deleted file mode 100644 index dbb5dff..0000000 --- a/plugins/xrandr/gsd-xrandr-manager.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2007 William Jon McCann - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#ifndef __MSD_XRANDR_MANAGER_H -#define __MSD_XRANDR_MANAGER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define MSD_TYPE_XRANDR_MANAGER (msd_xrandr_manager_get_type ()) -#define MSD_XRANDR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManager)) -#define MSD_XRANDR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManagerClass)) -#define MSD_IS_XRANDR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_XRANDR_MANAGER)) -#define MSD_IS_XRANDR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_XRANDR_MANAGER)) -#define MSD_XRANDR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManagerClass)) - -typedef struct MsdXrandrManagerPrivate MsdXrandrManagerPrivate; - -typedef struct -{ - GObject parent; - MsdXrandrManagerPrivate *priv; -} MsdXrandrManager; - -typedef struct -{ - GObjectClass parent_class; -} MsdXrandrManagerClass; - -GType msd_xrandr_manager_get_type (void); - -MsdXrandrManager * msd_xrandr_manager_new (void); -gboolean msd_xrandr_manager_start (MsdXrandrManager *manager, - GError **error); -void msd_xrandr_manager_stop (MsdXrandrManager *manager); - -#ifdef __cplusplus -} -#endif - -#endif /* __MSD_XRANDR_MANAGER_H */ diff --git a/plugins/xrandr/gsd-xrandr-manager.xml b/plugins/xrandr/gsd-xrandr-manager.xml deleted file mode 100644 index c82a594..0000000 --- a/plugins/xrandr/gsd-xrandr-manager.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/xrandr/gsd-xrandr-plugin.c b/plugins/xrandr/gsd-xrandr-plugin.c deleted file mode 100644 index 9e78124..0000000 --- a/plugins/xrandr/gsd-xrandr-plugin.c +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2007 William Jon McCann - * - * 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 - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#include "config.h" - -#include -#include - -#include "mate-settings-plugin.h" -#include "msd-xrandr-plugin.h" -#include "msd-xrandr-manager.h" - -struct MsdXrandrPluginPrivate { - MsdXrandrManager *manager; -}; - -#define MSD_XRANDR_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPluginPrivate)) - -MATE_SETTINGS_PLUGIN_REGISTER (MsdXrandrPlugin, msd_xrandr_plugin) - -static void -msd_xrandr_plugin_init (MsdXrandrPlugin *plugin) -{ - plugin->priv = MSD_XRANDR_PLUGIN_GET_PRIVATE (plugin); - - g_debug ("MsdXrandrPlugin initializing"); - - plugin->priv->manager = msd_xrandr_manager_new (); -} - -static void -msd_xrandr_plugin_finalize (GObject *object) -{ - MsdXrandrPlugin *plugin; - - g_return_if_fail (object != NULL); - g_return_if_fail (MSD_IS_XRANDR_PLUGIN (object)); - - g_debug ("MsdXrandrPlugin finalizing"); - - plugin = MSD_XRANDR_PLUGIN (object); - - g_return_if_fail (plugin->priv != NULL); - - if (plugin->priv->manager != NULL) { - g_object_unref (plugin->priv->manager); - } - - G_OBJECT_CLASS (msd_xrandr_plugin_parent_class)->finalize (object); -} - -static void -impl_activate (MateSettingsPlugin *plugin) -{ - gboolean res; - GError *error; - - g_debug ("Activating xrandr plugin"); - - error = NULL; - res = msd_xrandr_manager_start (MSD_XRANDR_PLUGIN (plugin)->priv->manager, &error); - if (! res) { - g_warning ("Unable to start xrandr manager: %s", error->message); - g_error_free (error); - } -} - -static void -impl_deactivate (MateSettingsPlugin *plugin) -{ - g_debug ("Deactivating xrandr plugin"); - msd_xrandr_manager_stop (MSD_XRANDR_PLUGIN (plugin)->priv->manager); -} - -static void -msd_xrandr_plugin_class_init (MsdXrandrPluginClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - MateSettingsPluginClass *plugin_class = MATE_SETTINGS_PLUGIN_CLASS (klass); - - object_class->finalize = msd_xrandr_plugin_finalize; - - plugin_class->activate = impl_activate; - plugin_class->deactivate = impl_deactivate; - - g_type_class_add_private (klass, sizeof (MsdXrandrPluginPrivate)); -} diff --git a/plugins/xrandr/gsd-xrandr-plugin.h b/plugins/xrandr/gsd-xrandr-plugin.h deleted file mode 100644 index 62e742e..0000000 --- a/plugins/xrandr/gsd-xrandr-plugin.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- - * - * Copyright (C) 2007 William Jon McCann - * - * 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 - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#ifndef __MSD_XRANDR_PLUGIN_H__ -#define __MSD_XRANDR_PLUGIN_H__ - -#include -#include -#include - -#include "mate-settings-plugin.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define MSD_TYPE_XRANDR_PLUGIN (msd_xrandr_plugin_get_type ()) -#define MSD_XRANDR_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPlugin)) -#define MSD_XRANDR_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPluginClass)) -#define MSD_IS_XRANDR_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_XRANDR_PLUGIN)) -#define MSD_IS_XRANDR_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_XRANDR_PLUGIN)) -#define MSD_XRANDR_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPluginClass)) - -typedef struct MsdXrandrPluginPrivate MsdXrandrPluginPrivate; - -typedef struct -{ - MateSettingsPlugin parent; - MsdXrandrPluginPrivate *priv; -} MsdXrandrPlugin; - -typedef struct -{ - MateSettingsPluginClass parent_class; -} MsdXrandrPluginClass; - -GType msd_xrandr_plugin_get_type (void) G_GNUC_CONST; - -/* All the plugins must implement this function */ -G_MODULE_EXPORT GType register_mate_settings_plugin (GTypeModule *module); - -#ifdef __cplusplus -} -#endif - -#endif /* __MSD_XRANDR_PLUGIN_H__ */ diff --git a/plugins/xrandr/gsd-xrandr.svg b/plugins/xrandr/gsd-xrandr.svg deleted file mode 100644 index 0679b6b..0000000 --- a/plugins/xrandr/gsd-xrandr.svg +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Change Resolution - - - Jakub Steiner - - - - - - display - resolution - video - - - - - Andreas Nilsson -Luca Ferretti <elle.uca@libero.it> - - - - http://www.gnome.org - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/xrandr/msd-xrandr-16.png b/plugins/xrandr/msd-xrandr-16.png new file mode 100644 index 0000000..f996ddf Binary files /dev/null and b/plugins/xrandr/msd-xrandr-16.png differ diff --git a/plugins/xrandr/msd-xrandr-22.png b/plugins/xrandr/msd-xrandr-22.png new file mode 100644 index 0000000..cc47eec Binary files /dev/null and b/plugins/xrandr/msd-xrandr-22.png differ diff --git a/plugins/xrandr/msd-xrandr-24.png b/plugins/xrandr/msd-xrandr-24.png new file mode 100644 index 0000000..49b4e12 Binary files /dev/null and b/plugins/xrandr/msd-xrandr-24.png differ diff --git a/plugins/xrandr/msd-xrandr-32.png b/plugins/xrandr/msd-xrandr-32.png new file mode 100644 index 0000000..95de3ea Binary files /dev/null and b/plugins/xrandr/msd-xrandr-32.png differ diff --git a/plugins/xrandr/msd-xrandr-manager.c b/plugins/xrandr/msd-xrandr-manager.c new file mode 100644 index 0000000..dc00be1 --- /dev/null +++ b/plugins/xrandr/msd-xrandr-manager.c @@ -0,0 +1,2584 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * Copyright (C) 2007, 2008 Red Hat, Inc + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MATE_DESKTOP_USE_UNSTABLE_API + +#include +#include +#include + +#ifdef HAVE_LIBMATENOTIFY +#include +#endif + +#include "mate-settings-profile.h" +#include "msd-xrandr-manager.h" + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +#define MSD_XRANDR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManagerPrivate)) + +#define CONF_DIR "/apps/mate_settings_daemon/xrandr" +#define CONF_KEY_SHOW_NOTIFICATION_ICON (CONF_DIR "/show_notification_icon") +#define CONF_KEY_TURN_ON_EXTERNAL_MONITORS_AT_STARTUP (CONF_DIR "/turn_on_external_monitors_at_startup") +#define CONF_KEY_TURN_ON_LAPTOP_MONITOR_AT_STARTUP (CONF_DIR "/turn_on_laptop_monitor_at_startup") +#define CONF_KEY_DEFAULT_CONFIGURATION_FILE (CONF_DIR "/default_configuration_file") + +#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. + */ +#define CONFIRMATION_DIALOG_SECONDS 30 + +/* name of the icon files (msd-xrandr.svg, etc.) */ +#define MSD_XRANDR_ICON_NAME "msd-xrandr" + +/* executable of the control center's display configuration capplet */ +#define MSD_XRANDR_DISPLAY_CAPPLET "mate-control-center display" + +#define MSD_DBUS_PATH "/org/mate/SettingsDaemon" +#define MSD_DBUS_NAME "org.mate.SettingsDaemon" +#define MSD_XRANDR_DBUS_PATH MSD_DBUS_PATH "/XRANDR" +#define MSD_XRANDR_DBUS_NAME MSD_DBUS_NAME ".XRANDR" + +struct MsdXrandrManagerPrivate +{ + DBusGConnection *dbus_connection; + + /* 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; + + MateRRScreen *rw_screen; + gboolean running; + + GtkStatusIcon *status_icon; + GtkWidget *popup_menu; + MateRRConfig *configuration; + MateRRLabeler *labeler; + MateConfClient *client; + int notify_id; + + /* fn-F7 status */ + int current_fn_f7_config; /* -1 if no configs */ + MateRRConfig **fn_f7_configs; /* NULL terminated, NULL if there are no configs */ + + /* Last time at which we got a "screen got reconfigured" event; see on_randr_event() */ + guint32 last_config_timestamp; +}; + +static const MateRRRotation possible_rotations[] = { + MATE_RR_ROTATION_0, + MATE_RR_ROTATION_90, + MATE_RR_ROTATION_180, + MATE_RR_ROTATION_270 + /* We don't allow REFLECT_X or REFLECT_Y for now, as mate-display-properties doesn't allow them, either */ +}; + +static void msd_xrandr_manager_class_init (MsdXrandrManagerClass *klass); +static void msd_xrandr_manager_init (MsdXrandrManager *xrandr_manager); +static void msd_xrandr_manager_finalize (GObject *object); + +static void error_message (MsdXrandrManager *mgr, const char *primary_text, GError *error_to_display, const char *secondary_text); + +static void status_icon_popup_menu (MsdXrandrManager *manager, guint button, guint32 timestamp); +static void run_display_capplet (GtkWidget *widget); +static void get_allowed_rotations_for_output (MateRRConfig *config, + MateRRScreen *rr_screen, + MateOutputInfo *output, + int *out_num_rotations, + MateRRRotation *out_rotations); + +G_DEFINE_TYPE (MsdXrandrManager, msd_xrandr_manager, G_TYPE_OBJECT) + +static gpointer manager_object = NULL; + +static FILE *log_file; + +static void +log_open (void) +{ + char *toggle_filename; + char *log_filename; + struct stat st; + + if (log_file) + return; + + toggle_filename = g_build_filename (g_get_home_dir (), "msd-debug-randr", NULL); + log_filename = g_build_filename (g_get_home_dir (), "msd-debug-randr.log", NULL); + + if (stat (toggle_filename, &st) != 0) + goto out; + + log_file = fopen (log_filename, "a"); + + if (log_file && ftell (log_file) == 0) + fprintf (log_file, "To keep this log from being created, please rm ~/msd-debug-randr\n"); + +out: + g_free (toggle_filename); + g_free (log_filename); +} + +static void +log_close (void) +{ + if (log_file) { + fclose (log_file); + log_file = NULL; + } +} + +static void +log_msg (const char *format, ...) +{ + if (log_file) { + va_list args; + + va_start (args, format); + vfprintf (log_file, format, args); + va_end (args); + } +} + +static void +log_output (MateOutputInfo *output) +{ + log_msg (" %s: ", output->name ? output->name : "unknown"); + + if (output->connected) { + if (output->on) { + log_msg ("%dx%d@%d +%d+%d", + output->width, + output->height, + output->rate, + output->x, + output->y); + } else + log_msg ("off"); + } else + log_msg ("disconnected"); + + if (output->display_name) + log_msg (" (%s)", output->display_name); + + if (output->primary) + log_msg (" (primary output)"); + + log_msg ("\n"); +} + +static void +log_configuration (MateRRConfig *config) +{ + int i; + + log_msg (" cloned: %s\n", config->clone ? "yes" : "no"); + + for (i = 0; config->outputs[i] != NULL; i++) + log_output (config->outputs[i]); + + if (i == 0) + log_msg (" no outputs!\n"); +} + +static char +timestamp_relationship (guint32 a, guint32 b) +{ + if (a < b) + return '<'; + else if (a > b) + return '>'; + else + return '='; +} + +static void +log_screen (MateRRScreen *screen) +{ + MateRRConfig *config; + int min_w, min_h, max_w, max_h; + guint32 change_timestamp, config_timestamp; + + if (!log_file) + return; + + config = mate_rr_config_new_current (screen); + + mate_rr_screen_get_ranges (screen, &min_w, &max_w, &min_h, &max_h); + mate_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp); + + log_msg (" Screen min(%d, %d), max(%d, %d), change=%u %c config=%u\n", + min_w, min_h, + max_w, max_h, + change_timestamp, + timestamp_relationship (change_timestamp, config_timestamp), + config_timestamp); + + log_configuration (config); + mate_rr_config_free (config); +} + +static void +log_configurations (MateRRConfig **configs) +{ + int i; + + if (!configs) { + log_msg (" No configurations\n"); + return; + } + + for (i = 0; configs[i]; i++) { + log_msg (" Configuration %d\n", i); + log_configuration (configs[i]); + } +} + +static void +show_timestamps_dialog (MsdXrandrManager *manager, const char *msg) +{ +#if 1 + return; +#else + struct MsdXrandrManagerPrivate *priv = manager->priv; + GtkWidget *dialog; + guint32 change_timestamp, config_timestamp; + static int serial; + + mate_rr_screen_get_timestamps (priv->rw_screen, &change_timestamp, &config_timestamp); + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + "RANDR timestamps (%d):\n%s\nchange: %u\nconfig: %u", + serial++, + msg, + change_timestamp, + config_timestamp); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); +#endif +} + +/* This function centralizes the use of mate_rr_config_apply_from_filename_with_time(). + * + * Optionally filters out MATE_RR_ERROR_NO_MATCHING_CONFIG from + * mate_rr_config_apply_from_filename_with_time(), since that is not usually an error. + */ +static gboolean +apply_configuration_from_filename (MsdXrandrManager *manager, + const char *filename, + gboolean no_matching_config_is_an_error, + guint32 timestamp, + GError **error) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + GError *my_error; + gboolean success; + char *str; + + str = g_strdup_printf ("Applying %s with timestamp %d", filename, timestamp); + show_timestamps_dialog (manager, str); + g_free (str); + + my_error = NULL; + success = mate_rr_config_apply_from_filename_with_time (priv->rw_screen, filename, timestamp, &my_error); + if (success) + return TRUE; + + if (g_error_matches (my_error, MATE_RR_ERROR, MATE_RR_ERROR_NO_MATCHING_CONFIG)) { + if (no_matching_config_is_an_error) + goto fail; + + /* This is not an error; the user probably changed his monitors + * and so they don't match any of the stored configurations. + */ + g_error_free (my_error); + return TRUE; + } + +fail: + g_propagate_error (error, my_error); + return FALSE; +} + +/* This function centralizes the use of mate_rr_config_apply_with_time(). + * + * Applies a configuration and displays an error message if an error happens. + * We just return whether setting the configuration succeeded. + */ +static gboolean +apply_configuration_and_display_error (MsdXrandrManager *manager, MateRRConfig *config, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = manager->priv; + GError *error; + gboolean success; + + error = NULL; + success = mate_rr_config_apply_with_time (config, priv->rw_screen, timestamp, &error); + if (!success) { + log_msg ("Could not switch to the following configuration (timestamp %u): %s\n", timestamp, error->message); + log_configuration (config); + error_message (manager, _("Could not switch the monitor configuration"), error, NULL); + g_error_free (error); + } + + return success; +} + +static void +restore_backup_configuration_without_messages (const char *backup_filename, const char *intended_filename) +{ + backup_filename = mate_rr_config_get_backup_filename (); + rename (backup_filename, intended_filename); +} + +static void +restore_backup_configuration (MsdXrandrManager *manager, const char *backup_filename, const char *intended_filename, guint32 timestamp) +{ + int saved_errno; + + if (rename (backup_filename, intended_filename) == 0) { + GError *error; + + error = NULL; + if (!apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, &error)) { + error_message (manager, _("Could not restore the display's configuration"), error, NULL); + + if (error) + g_error_free (error); + } + + return; + } + + saved_errno = errno; + + /* ENOENT means the original file didn't exist. That is *not* an error; + * the backup was not created because there wasn't even an original + * monitors.xml (such as on a first-time login). Note that *here* there + * is a "didn't work" monitors.xml, so we must delete that one. + */ + if (saved_errno == ENOENT) + unlink (intended_filename); + else { + char *msg; + + msg = g_strdup_printf ("Could not rename %s to %s: %s", + backup_filename, intended_filename, + g_strerror (saved_errno)); + error_message (manager, + _("Could not restore the display's configuration from a backup"), + NULL, + msg); + g_free (msg); + } + + unlink (backup_filename); +} + +typedef struct { + MsdXrandrManager *manager; + GtkWidget *dialog; + + int countdown; + int response_id; +} TimeoutDialog; + +static void +print_countdown_text (TimeoutDialog *timeout) +{ + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (timeout->dialog), + ngettext ("The display will be reset to its previous configuration in %d second", + "The display will be reset to its previous configuration in %d seconds", + timeout->countdown), + timeout->countdown); +} + +static gboolean +timeout_cb (gpointer data) +{ + TimeoutDialog *timeout = data; + + timeout->countdown--; + + if (timeout->countdown == 0) { + timeout->response_id = GTK_RESPONSE_CANCEL; + gtk_main_quit (); + } else { + print_countdown_text (timeout); + } + + return TRUE; +} + +static void +timeout_response_cb (GtkDialog *dialog, int response_id, gpointer data) +{ + TimeoutDialog *timeout = data; + + if (response_id == GTK_RESPONSE_DELETE_EVENT) { + /* The user closed the dialog or pressed ESC, revert */ + timeout->response_id = GTK_RESPONSE_CANCEL; + } else + timeout->response_id = response_id; + + gtk_main_quit (); +} + +static gboolean +user_says_things_are_ok (MsdXrandrManager *manager, GdkWindow *parent_window) +{ + TimeoutDialog timeout; + guint timeout_id; + + timeout.manager = manager; + + timeout.dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Does the display look OK?")); + + timeout.countdown = CONFIRMATION_DIALOG_SECONDS; + + print_countdown_text (&timeout); + + gtk_dialog_add_button (GTK_DIALOG (timeout.dialog), _("_Restore Previous Configuration"), GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (timeout.dialog), _("_Keep This Configuration"), GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (GTK_DIALOG (timeout.dialog), GTK_RESPONSE_ACCEPT); /* ah, the optimism */ + + g_signal_connect (timeout.dialog, "response", + G_CALLBACK (timeout_response_cb), + &timeout); + + gtk_widget_realize (timeout.dialog); + + if (parent_window) + gdk_window_set_transient_for (gtk_widget_get_window (timeout.dialog), parent_window); + + gtk_widget_show_all (timeout.dialog); + /* We don't use g_timeout_add_seconds() since we actually care that the user sees "real" second ticks in the dialog */ + timeout_id = g_timeout_add (1000, + timeout_cb, + &timeout); + gtk_main (); + + gtk_widget_destroy (timeout.dialog); + g_source_remove (timeout_id); + + if (timeout.response_id == GTK_RESPONSE_ACCEPT) + return TRUE; + else + return FALSE; +} + +struct confirmation { + MsdXrandrManager *manager; + GdkWindow *parent_window; + guint32 timestamp; +}; + +static gboolean +confirm_with_user_idle_cb (gpointer data) +{ + struct confirmation *confirmation = data; + char *backup_filename; + char *intended_filename; + + backup_filename = mate_rr_config_get_backup_filename (); + intended_filename = mate_rr_config_get_intended_filename (); + + if (user_says_things_are_ok (confirmation->manager, confirmation->parent_window)) + unlink (backup_filename); + else + restore_backup_configuration (confirmation->manager, backup_filename, intended_filename, confirmation->timestamp); + + g_free (confirmation); + + return FALSE; +} + +static void +queue_confirmation_by_user (MsdXrandrManager *manager, GdkWindow *parent_window, guint32 timestamp) +{ + struct confirmation *confirmation; + + confirmation = g_new (struct confirmation, 1); + confirmation->manager = manager; + confirmation->parent_window = parent_window; + confirmation->timestamp = timestamp; + + g_idle_add (confirm_with_user_idle_cb, confirmation); +} + +static gboolean +try_to_apply_intended_configuration (MsdXrandrManager *manager, GdkWindow *parent_window, guint32 timestamp, GError **error) +{ + char *backup_filename; + char *intended_filename; + gboolean result; + + /* Try to apply the intended configuration */ + + backup_filename = mate_rr_config_get_backup_filename (); + intended_filename = mate_rr_config_get_intended_filename (); + + result = apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, error); + if (!result) { + error_message (manager, _("The selected configuration for displays could not be applied"), error ? *error : NULL, NULL); + restore_backup_configuration_without_messages (backup_filename, intended_filename); + goto out; + } else { + /* We need to return as quickly as possible, so instead of + * confirming with the user right here, we do it in an idle + * handler. The caller only expects a status for "could you + * change the RANDR configuration?", not "is the user OK with it + * as well?". + */ + queue_confirmation_by_user (manager, parent_window, timestamp); + } + +out: + g_free (backup_filename); + g_free (intended_filename); + + return result; +} + +/* DBus method for org.mate.SettingsDaemon.XRANDR ApplyConfiguration; see msd-xrandr-manager.xml for the interface definition */ +static gboolean +msd_xrandr_manager_apply_configuration (MsdXrandrManager *manager, + GError **error) +{ + return try_to_apply_intended_configuration (manager, NULL, GDK_CURRENT_TIME, error); +} + +/* DBus method for org.mate.SettingsDaemon.XRANDR_2 ApplyConfiguration; see msd-xrandr-manager.xml for the interface definition */ +static gboolean +msd_xrandr_manager_2_apply_configuration (MsdXrandrManager *manager, + gint64 parent_window_id, + gint64 timestamp, + GError **error) +{ + GdkWindow *parent_window; + gboolean result; + + if (parent_window_id != 0) + parent_window = gdk_window_foreign_new_for_display (gdk_display_get_default (), (GdkNativeWindow) parent_window_id); + else + parent_window = NULL; + + result = try_to_apply_intended_configuration (manager, parent_window, (guint32) timestamp, error); + + if (parent_window) + g_object_unref (parent_window); + + return result; +} + +/* We include this after the definition of msd_xrandr_manager_apply_configuration() so the prototype will already exist */ +#include "msd-xrandr-manager-glue.h" + +static gboolean +is_laptop (MateRRScreen *screen, MateOutputInfo *output) +{ + MateRROutput *rr_output; + + rr_output = mate_rr_screen_get_output_by_name (screen, output->name); + return mate_rr_output_is_laptop (rr_output); +} + +static gboolean +get_clone_size (MateRRScreen *screen, int *width, int *height) +{ + MateRRMode **modes = mate_rr_screen_list_clone_modes (screen); + int best_w, best_h; + int i; + + best_w = 0; + best_h = 0; + + for (i = 0; modes[i] != NULL; ++i) { + MateRRMode *mode = modes[i]; + int w, h; + + w = mate_rr_mode_get_width (mode); + h = mate_rr_mode_get_height (mode); + + if (w * h > best_w * best_h) { + best_w = w; + best_h = h; + } + } + + if (best_w > 0 && best_h > 0) { + if (width) + *width = best_w; + if (height) + *height = best_h; + + return TRUE; + } + + return FALSE; +} + +static void +print_output (MateOutputInfo *info) +{ + g_print (" Output: %s attached to %s\n", info->display_name, info->name); + g_print (" status: %s\n", info->on ? "on" : "off"); + g_print (" width: %d\n", info->width); + g_print (" height: %d\n", info->height); + g_print (" rate: %d\n", info->rate); + g_print (" position: %d %d\n", info->x, info->y); +} + +static void +print_configuration (MateRRConfig *config, const char *header) +{ + int i; + + g_print ("=== %s Configuration ===\n", header); + if (!config) { + g_print (" none\n"); + return; + } + + for (i = 0; config->outputs[i] != NULL; ++i) + print_output (config->outputs[i]); +} + +static gboolean +config_is_all_off (MateRRConfig *config) +{ + int j; + + for (j = 0; config->outputs[j] != NULL; ++j) { + if (config->outputs[j]->on) { + return FALSE; + } + } + + return TRUE; +} + +static MateRRConfig * +make_clone_setup (MateRRScreen *screen) +{ + MateRRConfig *result; + int width, height; + int i; + + if (!get_clone_size (screen, &width, &height)) + return NULL; + + result = mate_rr_config_new_current (screen); + + for (i = 0; result->outputs[i] != NULL; ++i) { + MateOutputInfo *info = result->outputs[i]; + + info->on = FALSE; + if (info->connected) { + MateRROutput *output = + mate_rr_screen_get_output_by_name (screen, info->name); + MateRRMode **modes = mate_rr_output_list_modes (output); + int j; + int best_rate = 0; + + for (j = 0; modes[j] != NULL; ++j) { + MateRRMode *mode = modes[j]; + int w, h; + + w = mate_rr_mode_get_width (mode); + h = mate_rr_mode_get_height (mode); + + if (w == width && h == height) { + int r = mate_rr_mode_get_freq (mode); + if (r > best_rate) + best_rate = r; + } + } + + if (best_rate > 0) { + info->on = TRUE; + info->width = width; + info->height = height; + info->rate = best_rate; + info->rotation = MATE_RR_ROTATION_0; + info->x = 0; + info->y = 0; + } + } + } + + if (config_is_all_off (result)) { + mate_rr_config_free (result); + result = NULL; + } + + print_configuration (result, "clone setup"); + + return result; +} + +static MateRRMode * +find_best_mode (MateRROutput *output) +{ + MateRRMode *preferred; + MateRRMode **modes; + int best_size; + int best_width, best_height, best_rate; + int i; + MateRRMode *best_mode; + + preferred = mate_rr_output_get_preferred_mode (output); + if (preferred) + return preferred; + + modes = mate_rr_output_list_modes (output); + if (!modes) + return NULL; + + best_size = best_width = best_height = best_rate = 0; + best_mode = NULL; + + for (i = 0; modes[i] != NULL; i++) { + int w, h, r; + int size; + + w = mate_rr_mode_get_width (modes[i]); + h = mate_rr_mode_get_height (modes[i]); + r = mate_rr_mode_get_freq (modes[i]); + + size = w * h; + + if (size > best_size) { + best_size = size; + best_width = w; + best_height = h; + best_rate = r; + best_mode = modes[i]; + } else if (size == best_size) { + if (r > best_rate) { + best_rate = r; + best_mode = modes[i]; + } + } + } + + return best_mode; +} + +static gboolean +turn_on (MateRRScreen *screen, + MateOutputInfo *info, + int x, int y) +{ + MateRROutput *output = mate_rr_screen_get_output_by_name (screen, info->name); + MateRRMode *mode = find_best_mode (output); + + if (mode) { + info->on = TRUE; + info->x = x; + info->y = y; + info->width = mate_rr_mode_get_width (mode); + info->height = mate_rr_mode_get_height (mode); + info->rotation = MATE_RR_ROTATION_0; + info->rate = mate_rr_mode_get_freq (mode); + + return TRUE; + } + + return FALSE; +} + +static MateRRConfig * +make_laptop_setup (MateRRScreen *screen) +{ + /* Turn on the laptop, disable everything else */ + MateRRConfig *result = mate_rr_config_new_current (screen); + int i; + + for (i = 0; result->outputs[i] != NULL; ++i) { + MateOutputInfo *info = result->outputs[i]; + + if (is_laptop (screen, info)) { + if (!turn_on (screen, info, 0, 0)) { + mate_rr_config_free (result); + result = NULL; + break; + } + } + else { + info->on = FALSE; + } + } + + if (config_is_all_off (result)) { + mate_rr_config_free (result); + result = NULL; + } + + print_configuration (result, "Laptop setup"); + + /* FIXME - Maybe we should return NULL if there is more than + * one connected "laptop" screen? + */ + return result; + +} + +static int +turn_on_and_get_rightmost_offset (MateRRScreen *screen, MateOutputInfo *info, int x) +{ + if (turn_on (screen, info, x, 0)) + x += info->width; + + return x; +} + +static MateRRConfig * +make_xinerama_setup (MateRRScreen *screen) +{ + /* Turn on everything that has a preferred mode, and + * position it from left to right + */ + MateRRConfig *result = mate_rr_config_new_current (screen); + int i; + int x; + + x = 0; + for (i = 0; result->outputs[i] != NULL; ++i) { + MateOutputInfo *info = result->outputs[i]; + + if (is_laptop (screen, info)) + x = turn_on_and_get_rightmost_offset (screen, info, x); + } + + for (i = 0; result->outputs[i] != NULL; ++i) { + MateOutputInfo *info = result->outputs[i]; + + if (info->connected && !is_laptop (screen, info)) + x = turn_on_and_get_rightmost_offset (screen, info, x); + } + + if (config_is_all_off (result)) { + mate_rr_config_free (result); + result = NULL; + } + + print_configuration (result, "xinerama setup"); + + return result; +} + +static MateRRConfig * +make_other_setup (MateRRScreen *screen) +{ + /* Turn off all laptops, and make all external monitors clone + * from (0, 0) + */ + + MateRRConfig *result = mate_rr_config_new_current (screen); + int i; + + for (i = 0; result->outputs[i] != NULL; ++i) { + MateOutputInfo *info = result->outputs[i]; + + if (is_laptop (screen, info)) { + info->on = FALSE; + } + else { + if (info->connected) + turn_on (screen, info, 0, 0); + } + } + + if (config_is_all_off (result)) { + mate_rr_config_free (result); + result = NULL; + } + + print_configuration (result, "other setup"); + + return result; +} + +static GPtrArray * +sanitize (MsdXrandrManager *manager, GPtrArray *array) +{ + int i; + GPtrArray *new; + + g_debug ("before sanitizing"); + + for (i = 0; i < array->len; ++i) { + if (array->pdata[i]) { + print_configuration (array->pdata[i], "before"); + } + } + + + /* Remove configurations that are duplicates of + * configurations earlier in the cycle + */ + for (i = 0; i < array->len; i++) { + int j; + + for (j = i + 1; j < array->len; j++) { + MateRRConfig *this = array->pdata[j]; + MateRRConfig *other = array->pdata[i]; + + if (this && other && mate_rr_config_equal (this, other)) { + g_debug ("removing duplicate configuration"); + mate_rr_config_free (this); + array->pdata[j] = NULL; + break; + } + } + } + + for (i = 0; i < array->len; ++i) { + MateRRConfig *config = array->pdata[i]; + + if (config && config_is_all_off (config)) { + g_debug ("removing configuration as all outputs are off"); + mate_rr_config_free (array->pdata[i]); + array->pdata[i] = NULL; + } + } + + /* Do a final sanitization pass. This will remove configurations that + * don't fit in the framebuffer's Virtual size. + */ + + for (i = 0; i < array->len; i++) { + MateRRConfig *config = array->pdata[i]; + + if (config) { + GError *error; + + error = NULL; + if (!mate_rr_config_applicable (config, manager->priv->rw_screen, &error)) { /* NULL-GError */ + g_debug ("removing configuration which is not applicable because %s", error->message); + g_error_free (error); + + mate_rr_config_free (config); + array->pdata[i] = NULL; + } + } + } + + /* Remove NULL configurations */ + new = g_ptr_array_new (); + + for (i = 0; i < array->len; ++i) { + if (array->pdata[i]) { + g_ptr_array_add (new, array->pdata[i]); + print_configuration (array->pdata[i], "Final"); + } + } + + if (new->len > 0) { + g_ptr_array_add (new, NULL); + } else { + g_ptr_array_free (new, TRUE); + new = NULL; + } + + g_ptr_array_free (array, TRUE); + + return new; +} + +static void +generate_fn_f7_configs (MsdXrandrManager *mgr) +{ + GPtrArray *array = g_ptr_array_new (); + MateRRScreen *screen = mgr->priv->rw_screen; + + g_debug ("Generating configurations"); + + /* Free any existing list of configurations */ + if (mgr->priv->fn_f7_configs) { + int i; + + for (i = 0; mgr->priv->fn_f7_configs[i] != NULL; ++i) + mate_rr_config_free (mgr->priv->fn_f7_configs[i]); + g_free (mgr->priv->fn_f7_configs); + + mgr->priv->fn_f7_configs = NULL; + mgr->priv->current_fn_f7_config = -1; + } + + g_ptr_array_add (array, mate_rr_config_new_current (screen)); + g_ptr_array_add (array, make_clone_setup (screen)); + g_ptr_array_add (array, make_xinerama_setup (screen)); + g_ptr_array_add (array, make_laptop_setup (screen)); + g_ptr_array_add (array, make_other_setup (screen)); + + array = sanitize (mgr, array); + + if (array) { + mgr->priv->fn_f7_configs = (MateRRConfig **)g_ptr_array_free (array, FALSE); + mgr->priv->current_fn_f7_config = 0; + } +} + +static void +error_message (MsdXrandrManager *mgr, const char *primary_text, GError *error_to_display, const char *secondary_text) +{ +#ifdef HAVE_LIBMATENOTIFY + MsdXrandrManagerPrivate *priv = mgr->priv; + NotifyNotification *notification; + + g_assert (error_to_display == NULL || secondary_text == NULL); + + if (priv->status_icon) + notification = notify_notification_new_with_status_icon (primary_text, + error_to_display ? error_to_display->message : secondary_text, + MSD_XRANDR_ICON_NAME, + priv->status_icon); + else + notification = notify_notification_new (primary_text, + error_to_display ? error_to_display->message : secondary_text, + MSD_XRANDR_ICON_NAME, + NULL); + + notify_notification_show (notification, NULL); /* NULL-GError */ +#else + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "%s", primary_text); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", + error_to_display ? error_to_display->message : secondary_text); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +#endif /* HAVE_LIBMATENOTIFY */ +} + +static void +handle_fn_f7 (MsdXrandrManager *mgr, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = mgr->priv; + MateRRScreen *screen = priv->rw_screen; + MateRRConfig *current; + GError *error; + + /* Theory of fn-F7 operation + * + * We maintain a datastructure "fn_f7_status", that contains + * a list of MateRRConfig's. Each of the MateRRConfigs has a + * mode (or "off") for each connected output. + * + * When the user hits fn-F7, we cycle to the next MateRRConfig + * in the data structure. If the data structure does not exist, it + * is generated. If the configs in the data structure do not match + * the current hardware reality, it is regenerated. + * + */ + g_debug ("Handling fn-f7"); + + log_open (); + log_msg ("Handling XF86Display hotkey - timestamp %u\n", timestamp); + + error = NULL; + if (!mate_rr_screen_refresh (screen, &error) && error) { + char *str; + + str = g_strdup_printf (_("Could not refresh the screen information: %s"), error->message); + g_error_free (error); + + log_msg ("%s\n", str); + error_message (mgr, str, NULL, _("Trying to switch the monitor configuration anyway.")); + g_free (str); + } + + if (!priv->fn_f7_configs) { + log_msg ("Generating stock configurations:\n"); + generate_fn_f7_configs (mgr); + log_configurations (priv->fn_f7_configs); + } + + current = mate_rr_config_new_current (screen); + + if (priv->fn_f7_configs && + (!mate_rr_config_match (current, priv->fn_f7_configs[0]) || + !mate_rr_config_equal (current, priv->fn_f7_configs[mgr->priv->current_fn_f7_config]))) { + /* Our view of the world is incorrect, so regenerate the + * configurations + */ + generate_fn_f7_configs (mgr); + log_msg ("Regenerated stock configurations:\n"); + log_configurations (priv->fn_f7_configs); + } + + mate_rr_config_free (current); + + if (priv->fn_f7_configs) { + guint32 server_timestamp; + gboolean success; + + mgr->priv->current_fn_f7_config++; + + if (priv->fn_f7_configs[mgr->priv->current_fn_f7_config] == NULL) + mgr->priv->current_fn_f7_config = 0; + + g_debug ("cycling to next configuration (%d)", mgr->priv->current_fn_f7_config); + + print_configuration (priv->fn_f7_configs[mgr->priv->current_fn_f7_config], "new config"); + + g_debug ("applying"); + + /* See https://bugzilla.gnome.org/show_bug.cgi?id=610482 + * + * Sometimes we'll get two rapid XF86Display keypress events, + * but their timestamps will be out of order with respect to the + * RANDR timestamps. This *may* be due to stupid BIOSes sending + * out display-switch keystrokes "to make Windows work". + * + * The X server will error out if the timestamp provided is + * older than a previous change configuration timestamp. We + * assume here that we do want this event to go through still, + * since kernel timestamps may be skewed wrt the X server. + */ + mate_rr_screen_get_timestamps (screen, NULL, &server_timestamp); + if (timestamp < server_timestamp) + timestamp = server_timestamp; + + success = apply_configuration_and_display_error (mgr, priv->fn_f7_configs[mgr->priv->current_fn_f7_config], timestamp); + + if (success) { + log_msg ("Successfully switched to configuration (timestamp %u):\n", timestamp); + log_configuration (priv->fn_f7_configs[mgr->priv->current_fn_f7_config]); + } + } + else { + g_debug ("no configurations generated"); + } + + log_close (); + + g_debug ("done handling fn-f7"); +} + +static MateOutputInfo * +get_laptop_output_info (MateRRScreen *screen, MateRRConfig *config) +{ + int i; + + for (i = 0; config->outputs[i] != NULL; i++) { + MateOutputInfo *info; + + info = config->outputs[i]; + if (is_laptop (screen, info)) + return info; + } + + return NULL; + +} + +static MateRRRotation +get_next_rotation (MateRRRotation allowed_rotations, MateRRRotation 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++) { + MateRRRotation 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) { + MateRRRotation 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 (MsdXrandrManager *mgr, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = mgr->priv; + MateRRScreen *screen = priv->rw_screen; + MateRRConfig *current; + MateOutputInfo *rotatable_output_info; + int num_allowed_rotations; + MateRRRotation allowed_rotations; + MateRRRotation next_rotation; + + g_debug ("Handling XF86RotateWindows"); + + /* Which output? */ + + current = mate_rr_config_new_current (screen); + + rotatable_output_info = get_laptop_output_info (screen, 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; + + apply_configuration_and_display_error (mgr, current, timestamp); + +out: + mate_rr_config_free (current); +} + +static GdkFilterReturn +event_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + MsdXrandrManager *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->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; + } + + return GDK_FILTER_CONTINUE; +} + +static void +refresh_tray_icon_menu_if_active (MsdXrandrManager *manager, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = manager->priv; + + if (priv->popup_menu) { + gtk_menu_shell_cancel (GTK_MENU_SHELL (priv->popup_menu)); /* status_icon_popup_menu_selection_done_cb() will free everything */ + status_icon_popup_menu (manager, 0, timestamp); + } +} + +static void +auto_configure_outputs (MsdXrandrManager *manager, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = manager->priv; + MateRRConfig *config; + int i; + GList *just_turned_on; + GList *l; + int x; + GError *error; + gboolean applicable; + + config = mate_rr_config_new_current (priv->rw_screen); + + /* For outputs that are connected and on (i.e. they have a CRTC assigned + * to them, so they are getting a signal), we leave them as they are + * with their current modes. + * + * For other outputs, we will turn on connected-but-off outputs and turn + * off disconnected-but-on outputs. + * + * FIXME: If an output remained connected+on, it would be nice to ensure + * that the output's CRTCs still has a reasonable mode (think of + * changing one monitor for another with different capabilities). + */ + + just_turned_on = NULL; + + for (i = 0; config->outputs[i] != NULL; i++) { + MateOutputInfo *output = config->outputs[i]; + + if (output->connected && !output->on) { + output->on = TRUE; + output->rotation = MATE_RR_ROTATION_0; + just_turned_on = g_list_prepend (just_turned_on, GINT_TO_POINTER (i)); + } else if (!output->connected && output->on) + output->on = FALSE; + } + + /* Now, lay out the outputs from left to right. Put first the outputs + * which remained on; put last the outputs that were newly turned on. + */ + + x = 0; + + /* First, outputs that remained on */ + + for (i = 0; config->outputs[i] != NULL; i++) { + MateOutputInfo *output = config->outputs[i]; + + if (g_list_find (just_turned_on, GINT_TO_POINTER (i))) + continue; + + if (output->on) { + g_assert (output->connected); + + output->x = x; + output->y = 0; + + x += output->width; + } + } + + /* Second, outputs that were newly-turned on */ + + for (l = just_turned_on; l; l = l->next) { + MateOutputInfo *output; + + i = GPOINTER_TO_INT (l->data); + output = config->outputs[i]; + + g_assert (output->on && output->connected); + + output->x = x; + output->y = 0; + + /* since the output was off, use its preferred width/height (it doesn't have a real width/height yet) */ + output->width = output->pref_width; + output->height = output->pref_height; + + x += output->width; + } + + /* Check if we have a large enough framebuffer size. If not, turn off + * outputs from right to left until we reach a usable size. + */ + + just_turned_on = g_list_reverse (just_turned_on); /* now the outputs here are from right to left */ + + l = just_turned_on; + while (1) { + MateOutputInfo *output; + gboolean is_bounds_error; + + error = NULL; + applicable = mate_rr_config_applicable (config, priv->rw_screen, &error); + + if (applicable) + break; + + is_bounds_error = g_error_matches (error, MATE_RR_ERROR, MATE_RR_ERROR_BOUNDS_ERROR); + g_error_free (error); + + if (!is_bounds_error) + break; + + if (l) { + i = GPOINTER_TO_INT (l->data); + l = l->next; + + output = config->outputs[i]; + output->on = FALSE; + } else + break; + } + + /* Apply the configuration! */ + + if (applicable) + apply_configuration_and_display_error (manager, config, timestamp); + + g_list_free (just_turned_on); + mate_rr_config_free (config); + + /* Finally, even though we did a best-effort job in sanitizing the + * outputs, we don't know the physical layout of the monitors. We'll + * start the display capplet so that the user can tweak things to his + * liking. + */ + +#if 0 + /* FIXME: This is disabled for now. The capplet is not a single-instance application. + * If you do this: + * + * 1. Start the display capplet + * + * 2. Plug an extra monitor + * + * 3. Hit the "Detect displays" button + * + * Then we will get a RANDR event because X re-probes the outputs. We don't want to + * start up a second display capplet right there! + */ + + run_display_capplet (NULL); +#endif +} + +static void +apply_color_profiles (void) +{ + gboolean ret; + GError *error = NULL; + + /* run the mate-color-manager apply program */ + ret = g_spawn_command_line_async (BINDIR "/gcm-apply", &error); + if (!ret) { + /* only print the warning if the binary is installed */ + if (error->code != G_SPAWN_ERROR_NOENT) { + g_warning ("failed to apply color profiles: %s", error->message); + } + g_error_free (error); + } +} + +static void +on_randr_event (MateRRScreen *screen, gpointer data) +{ + MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); + MsdXrandrManagerPrivate *priv = manager->priv; + guint32 change_timestamp, config_timestamp; + + if (!priv->running) + return; + + mate_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp); + + log_open (); + log_msg ("Got RANDR event with timestamps change=%u %c config=%u\n", + change_timestamp, + timestamp_relationship (change_timestamp, config_timestamp), + config_timestamp); + + if (change_timestamp >= config_timestamp) { + /* The event is due to an explicit configuration change. + * + * If the change was performed by us, then we need to do nothing. + * + * If the change was done by some other X client, we don't need + * to do anything, either; the screen is already configured. + */ + show_timestamps_dialog (manager, "ignoring since change > config"); + log_msg (" Ignoring event since change >= config\n"); + } else { + /* Here, config_timestamp > change_timestamp. This means that + * the screen got reconfigured because of hotplug/unplug; the X + * server is just notifying us, and we need to configure the + * outputs in a sane way. + */ + + char *intended_filename; + GError *error; + gboolean success; + + show_timestamps_dialog (manager, "need to deal with reconfiguration, as config > change"); + + intended_filename = mate_rr_config_get_intended_filename (); + + error = NULL; + success = apply_configuration_from_filename (manager, intended_filename, TRUE, config_timestamp, &error); + g_free (intended_filename); + + if (!success) { + /* We don't bother checking the error type. + * + * Both G_FILE_ERROR_NOENT and + * MATE_RR_ERROR_NO_MATCHING_CONFIG would mean, "there + * was no configuration to apply, or none that matched + * the current outputs", and in that case we need to run + * our fallback. + * + * Any other error means "we couldn't do the smart thing + * of using a previously- saved configuration, anyway, + * for some other reason. In that case, we also need to + * run our fallback to avoid leaving the user with a + * bogus configuration. + */ + + if (error) + g_error_free (error); + + if (config_timestamp != priv->last_config_timestamp) { + priv->last_config_timestamp = config_timestamp; + auto_configure_outputs (manager, config_timestamp); + log_msg (" Automatically configured outputs to deal with event\n"); + } else + log_msg (" Ignored event as old and new config timestamps are the same\n"); + } else + log_msg ("Applied stored configuration to deal with event\n"); + } + + /* poke mate-color-manager */ + apply_color_profiles (); + + refresh_tray_icon_menu_if_active (manager, MAX (change_timestamp, config_timestamp)); + + log_close (); +} + +static void +run_display_capplet (GtkWidget *widget) +{ + GdkScreen *screen; + GError *error; + + if (widget) + screen = gtk_widget_get_screen (widget); + else + screen = gdk_screen_get_default (); + + error = NULL; + if (!gdk_spawn_command_line_on_screen (screen, MSD_XRANDR_DISPLAY_CAPPLET, &error)) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + "" + "Display configuration could not be run" + "\n\n" + "%s", error->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_error_free (error); + } +} + +static void +popup_menu_configure_display_cb (GtkMenuItem *item, gpointer data) +{ + run_display_capplet (GTK_WIDGET (item)); +} + +static void +status_icon_popup_menu_selection_done_cb (GtkMenuShell *menu_shell, gpointer data) +{ + MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); + struct MsdXrandrManagerPrivate *priv = manager->priv; + + gtk_widget_destroy (priv->popup_menu); + priv->popup_menu = NULL; + + mate_rr_labeler_hide (priv->labeler); + g_object_unref (priv->labeler); + priv->labeler = NULL; + + mate_rr_config_free (priv->configuration); + priv->configuration = NULL; +} + +#define OUTPUT_TITLE_ITEM_BORDER 2 +#define OUTPUT_TITLE_ITEM_PADDING 4 + +/* This is an expose-event hander for the title label for each MateRROutput. + * We want each title to have a colored background, so we paint that background, then + * return FALSE to let GtkLabel expose itself (i.e. paint the label's text), and then + * we have a signal_connect_after handler as well. See the comments below + * to see why that "after" handler is needed. + */ +static gboolean +output_title_label_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); + struct MsdXrandrManagerPrivate *priv = manager->priv; + MateOutputInfo *output; + GdkColor color; + cairo_t *cr; + GtkAllocation allocation; + + g_assert (GTK_IS_LABEL (widget)); + + output = g_object_get_data (G_OBJECT (widget), "output"); + g_assert (output != NULL); + + g_assert (priv->labeler != NULL); + + /* Draw a black rectangular border, filled with the color that corresponds to this output */ + + mate_rr_labeler_get_color_for_output (priv->labeler, output, &color); + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_line_width (cr, OUTPUT_TITLE_ITEM_BORDER); + gtk_widget_get_allocation (widget, &allocation); + cairo_rectangle (cr, + allocation.x + OUTPUT_TITLE_ITEM_BORDER / 2.0, + allocation.y + OUTPUT_TITLE_ITEM_BORDER / 2.0, + allocation.width - OUTPUT_TITLE_ITEM_BORDER, + allocation.height - OUTPUT_TITLE_ITEM_BORDER); + cairo_stroke (cr); + + gdk_cairo_set_source_color (cr, &color); + cairo_rectangle (cr, + allocation.x + OUTPUT_TITLE_ITEM_BORDER, + allocation.y + OUTPUT_TITLE_ITEM_BORDER, + allocation.width - 2 * OUTPUT_TITLE_ITEM_BORDER, + allocation.height - 2 * OUTPUT_TITLE_ITEM_BORDER); + + cairo_fill (cr); + + /* We want the label to always show up as if it were sensitive + * ("style->fg[GTK_STATE_NORMAL]"), even though the label is insensitive + * due to being inside an insensitive menu item. So, here we have a + * HACK in which we frob the label's state directly. GtkLabel's expose + * handler will be run after this function, so it will think that the + * label is in GTK_STATE_NORMAL. We reset the label's state back to + * insensitive in output_title_label_after_expose_event_cb(). + * + * Yay for fucking with GTK+'s internals. + */ + + gtk_widget_set_state (widget, GTK_STATE_NORMAL); + + return FALSE; +} + +/* See the comment in output_title_event_box_expose_event_cb() about this funny label widget */ +static gboolean +output_title_label_after_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) +{ + g_assert (GTK_IS_LABEL (widget)); + gtk_widget_set_state (widget, GTK_STATE_INSENSITIVE); + + return FALSE; +} + +static void +title_item_size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer data) +{ + /* When GtkMenu does size_request on its items, it asks them for their "toggle size", + * which will be non-zero when there are check/radio items. GtkMenu remembers + * the largest of those sizes. During the size_allocate pass, GtkMenu calls + * gtk_menu_item_toggle_size_allocate() with that value, to tell the menu item + * that it should later paint its child a bit to the right of its edge. + * + * However, we want the "title" menu items for each RANDR output to span the *whole* + * allocation of the menu item, not just the "allocation minus toggle" area. + * + * So, we let the menu item size_allocate itself as usual, but this + * callback gets run afterward. Here we hack a toggle size of 0 into + * the menu item, and size_allocate it by hand *again*. We also need to + * avoid recursing into this function. + */ + + g_assert (GTK_IS_MENU_ITEM (widget)); + + gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (widget), 0); + + g_signal_handlers_block_by_func (widget, title_item_size_allocate_cb, NULL); + + /* Sigh. There is no way to turn on GTK_ALLOC_NEEDED outside of GTK+ + * itself; also, since calling size_allocate on a widget with the same + * allcation is a no-op, we need to allocate with a "different" size + * first. + */ + + allocation->width++; + gtk_widget_size_allocate (widget, allocation); + + allocation->width--; + gtk_widget_size_allocate (widget, allocation); + + g_signal_handlers_unblock_by_func (widget, title_item_size_allocate_cb, NULL); +} + +static GtkWidget * +make_menu_item_for_output_title (MsdXrandrManager *manager, MateOutputInfo *output) +{ + GtkWidget *item; + GtkWidget *label; + char *str; + GdkColor black = { 0, 0, 0, 0 }; + + item = gtk_menu_item_new (); + + g_signal_connect (item, "size-allocate", + G_CALLBACK (title_item_size_allocate_cb), NULL); + + str = g_markup_printf_escaped ("%s", output->display_name); + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), str); + g_free (str); + + /* Make the label explicitly black. We don't want it to follow the + * theme's colors, since the label is always shown against a light + * pastel background. See bgo#556050 + */ + gtk_widget_modify_fg (label, gtk_widget_get_state (label), &black); + + /* Add padding around the label to fit the box that we'll draw for color-coding */ + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_misc_set_padding (GTK_MISC (label), + OUTPUT_TITLE_ITEM_BORDER + OUTPUT_TITLE_ITEM_PADDING, + OUTPUT_TITLE_ITEM_BORDER + OUTPUT_TITLE_ITEM_PADDING); + + gtk_container_add (GTK_CONTAINER (item), label); + + /* We want to paint a colored box as the background of the label, so we connect + * to its expose-event signal. See the comment in *** to see why need to connect + * to the label both 'before' and 'after'. + */ + g_signal_connect (label, "expose-event", + G_CALLBACK (output_title_label_expose_event_cb), manager); + g_signal_connect_after (label, "expose-event", + G_CALLBACK (output_title_label_after_expose_event_cb), manager); + + g_object_set_data (G_OBJECT (label), "output", output); + + gtk_widget_set_sensitive (item, FALSE); /* the title is not selectable */ + gtk_widget_show_all (item); + + return item; +} + +static void +get_allowed_rotations_for_output (MateRRConfig *config, + MateRRScreen *rr_screen, + MateOutputInfo *output, + int *out_num_rotations, + MateRRRotation *out_rotations) +{ + MateRRRotation current_rotation; + int i; + + *out_num_rotations = 0; + *out_rotations = 0; + + current_rotation = output->rotation; + + /* Yay for brute force */ + + for (i = 0; i < G_N_ELEMENTS (possible_rotations); i++) { + MateRRRotation rotation_to_test; + + rotation_to_test = possible_rotations[i]; + + output->rotation = rotation_to_test; + + if (mate_rr_config_applicable (config, rr_screen, NULL)) { /* NULL-GError */ + (*out_num_rotations)++; + (*out_rotations) |= rotation_to_test; + } + } + + output->rotation = current_rotation; + + if (*out_num_rotations == 0 || *out_rotations == 0) { + g_warning ("Huh, output %p says it doesn't support any rotations, and yet it has a current rotation?", output); + *out_num_rotations = 1; + *out_rotations = output->rotation; + } +} + +static void +add_unsupported_rotation_item (MsdXrandrManager *manager) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + GtkWidget *item; + GtkWidget *label; + gchar *markup; + + item = gtk_menu_item_new (); + + label = gtk_label_new (NULL); + markup = g_strdup_printf ("%s", _("Rotation not supported")); + gtk_label_set_markup (GTK_LABEL (label), markup); + g_free (markup); + gtk_container_add (GTK_CONTAINER (item), label); + + gtk_widget_show_all (item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); +} + +static void +ensure_current_configuration_is_saved (void) +{ + MateRRScreen *rr_screen; + MateRRConfig *rr_config; + + /* Normally, mate_rr_config_save() creates a backup file based on the + * old monitors.xml. However, if *that* file didn't exist, there is + * nothing from which to create a backup. So, here we'll save the + * current/unchanged configuration and then let our caller call + * mate_rr_config_save() again with the new/changed configuration, so + * that there *will* be a backup file in the end. + */ + + rr_screen = mate_rr_screen_new (gdk_screen_get_default (), NULL, NULL, NULL); /* NULL-GError */ + if (!rr_screen) + return; + + rr_config = mate_rr_config_new_current (rr_screen); + mate_rr_config_save (rr_config, NULL); /* NULL-GError */ + + mate_rr_config_free (rr_config); + mate_rr_screen_destroy (rr_screen); +} + +static void +output_rotation_item_activate_cb (GtkCheckMenuItem *item, gpointer data) +{ + MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); + struct MsdXrandrManagerPrivate *priv = manager->priv; + MateOutputInfo *output; + MateRRRotation rotation; + GError *error; + + /* Not interested in deselected items */ + if (!gtk_check_menu_item_get_active (item)) + return; + + ensure_current_configuration_is_saved (); + + output = g_object_get_data (G_OBJECT (item), "output"); + rotation = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "rotation")); + + output->rotation = rotation; + + error = NULL; + if (!mate_rr_config_save (priv->configuration, &error)) { + error_message (manager, _("Could not save monitor configuration"), error, NULL); + if (error) + g_error_free (error); + + return; + } + + try_to_apply_intended_configuration (manager, NULL, gtk_get_current_event_time (), NULL); /* NULL-GError */ +} + +static void +add_items_for_rotations (MsdXrandrManager *manager, MateOutputInfo *output, MateRRRotation allowed_rotations) +{ + typedef struct { + MateRRRotation rotation; + const char * name; + } RotationInfo; + static const RotationInfo rotations[] = { + { MATE_RR_ROTATION_0, N_("Normal") }, + { MATE_RR_ROTATION_90, N_("Left") }, + { MATE_RR_ROTATION_270, N_("Right") }, + { MATE_RR_ROTATION_180, N_("Upside Down") }, + /* We don't allow REFLECT_X or REFLECT_Y for now, as mate-display-properties doesn't allow them, either */ + }; + + struct MsdXrandrManagerPrivate *priv = manager->priv; + int i; + GSList *group; + GtkWidget *active_item; + gulong active_item_activate_id; + + group = NULL; + active_item = NULL; + active_item_activate_id = 0; + + for (i = 0; i < G_N_ELEMENTS (rotations); i++) { + MateRRRotation rot; + GtkWidget *item; + gulong activate_id; + + rot = rotations[i].rotation; + + if ((allowed_rotations & rot) == 0) { + /* don't display items for rotations which are + * unavailable. Their availability is not under the + * user's control, anyway. + */ + continue; + } + + item = gtk_radio_menu_item_new_with_label (group, _(rotations[i].name)); + gtk_widget_show_all (item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); + + g_object_set_data (G_OBJECT (item), "output", output); + g_object_set_data (G_OBJECT (item), "rotation", GINT_TO_POINTER (rot)); + + activate_id = g_signal_connect (item, "activate", + G_CALLBACK (output_rotation_item_activate_cb), manager); + + group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item)); + + if (rot == output->rotation) { + active_item = item; + active_item_activate_id = activate_id; + } + } + + if (active_item) { + /* Block the signal temporarily so our callback won't be called; + * we are just setting up the UI. + */ + g_signal_handler_block (active_item, active_item_activate_id); + + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (active_item), TRUE); + + g_signal_handler_unblock (active_item, active_item_activate_id); + } + +} + +static void +add_rotation_items_for_output (MsdXrandrManager *manager, MateOutputInfo *output) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + int num_rotations; + MateRRRotation rotations; + + get_allowed_rotations_for_output (priv->configuration, priv->rw_screen, output, &num_rotations, &rotations); + + if (num_rotations == 1) + add_unsupported_rotation_item (manager); + else + add_items_for_rotations (manager, output, rotations); +} + +static void +add_menu_items_for_output (MsdXrandrManager *manager, MateOutputInfo *output) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + GtkWidget *item; + + item = make_menu_item_for_output_title (manager, output); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); + + add_rotation_items_for_output (manager, output); +} + +static void +add_menu_items_for_outputs (MsdXrandrManager *manager) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + int i; + + for (i = 0; priv->configuration->outputs[i] != NULL; i++) { + if (priv->configuration->outputs[i]->connected) + add_menu_items_for_output (manager, priv->configuration->outputs[i]); + } +} + +static void +status_icon_popup_menu (MsdXrandrManager *manager, guint button, guint32 timestamp) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + GtkWidget *item; + + g_assert (priv->configuration == NULL); + priv->configuration = mate_rr_config_new_current (priv->rw_screen); + + g_assert (priv->labeler == NULL); + priv->labeler = mate_rr_labeler_new (priv->configuration); + + g_assert (priv->popup_menu == NULL); + priv->popup_menu = gtk_menu_new (); + + add_menu_items_for_outputs (manager); + + item = gtk_separator_menu_item_new (); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Configure Display Settingsā€¦")); + g_signal_connect (item, "activate", + G_CALLBACK (popup_menu_configure_display_cb), manager); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); + + g_signal_connect (priv->popup_menu, "selection-done", + G_CALLBACK (status_icon_popup_menu_selection_done_cb), manager); + + gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL, + gtk_status_icon_position_menu, + priv->status_icon, button, timestamp); +} + +static void +status_icon_activate_cb (GtkStatusIcon *status_icon, gpointer data) +{ + MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); + + /* Suck; we don't get a proper button/timestamp */ + status_icon_popup_menu (manager, 0, gtk_get_current_event_time ()); +} + +static void +status_icon_popup_menu_cb (GtkStatusIcon *status_icon, guint button, guint32 timestamp, gpointer data) +{ + MsdXrandrManager *manager = MSD_XRANDR_MANAGER (data); + + status_icon_popup_menu (manager, button, timestamp); +} + +static void +status_icon_start (MsdXrandrManager *manager) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + + /* Ideally, we should detect if we are on a tablet and only display + * the icon in that case. + */ + if (!priv->status_icon) { + priv->status_icon = gtk_status_icon_new_from_icon_name (MSD_XRANDR_ICON_NAME); + gtk_status_icon_set_tooltip_text (priv->status_icon, _("Configure display settings")); + + g_signal_connect (priv->status_icon, "activate", + G_CALLBACK (status_icon_activate_cb), manager); + g_signal_connect (priv->status_icon, "popup-menu", + G_CALLBACK (status_icon_popup_menu_cb), manager); + } +} + +static void +status_icon_stop (MsdXrandrManager *manager) +{ + struct MsdXrandrManagerPrivate *priv = manager->priv; + + if (priv->status_icon) { + g_signal_handlers_disconnect_by_func ( + priv->status_icon, G_CALLBACK (status_icon_activate_cb), manager); + g_signal_handlers_disconnect_by_func ( + priv->status_icon, G_CALLBACK (status_icon_popup_menu_cb), manager); + + /* hide the icon before unreffing it; otherwise we will leak + whitespace in the notification area due to a bug in there */ + gtk_status_icon_set_visible (priv->status_icon, FALSE); + g_object_unref (priv->status_icon); + priv->status_icon = NULL; + } +} + +static void +start_or_stop_icon (MsdXrandrManager *manager) +{ + if (mateconf_client_get_bool (manager->priv->client, CONF_KEY_SHOW_NOTIFICATION_ICON, NULL)) { + status_icon_start (manager); + } + else { + status_icon_stop (manager); + } +} + +static void +on_config_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MsdXrandrManager *manager) +{ + if (strcmp (entry->key, CONF_KEY_SHOW_NOTIFICATION_ICON) == 0) + start_or_stop_icon (manager); +} + +static gboolean +apply_intended_configuration (MsdXrandrManager *manager, const char *intended_filename, guint32 timestamp) +{ + GError *my_error; + gboolean result; + + my_error = NULL; + result = apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, &my_error); + if (!result) { + if (my_error) { + if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + error_message (manager, _("Could not apply the stored configuration for monitors"), my_error, NULL); + + g_error_free (my_error); + } + } + + return result; +} + +static void +apply_default_boot_configuration (MsdXrandrManager *mgr, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = mgr->priv; + MateRRScreen *screen = priv->rw_screen; + MateRRConfig *config; + gboolean turn_on_external, turn_on_laptop; + + turn_on_external = + mateconf_client_get_bool (mgr->priv->client, CONF_KEY_TURN_ON_EXTERNAL_MONITORS_AT_STARTUP, NULL); + turn_on_laptop = + mateconf_client_get_bool (mgr->priv->client, CONF_KEY_TURN_ON_LAPTOP_MONITOR_AT_STARTUP, NULL); + + if (turn_on_external && turn_on_laptop) + config = make_clone_setup (screen); + else if (!turn_on_external && turn_on_laptop) + config = make_laptop_setup (screen); + else if (turn_on_external && !turn_on_laptop) + config = make_other_setup (screen); + else + config = make_laptop_setup (screen); + + if (config) { + apply_configuration_and_display_error (mgr, config, timestamp); + mate_rr_config_free (config); + } +} + +static gboolean +apply_stored_configuration_at_startup (MsdXrandrManager *manager, guint32 timestamp) +{ + GError *my_error; + gboolean success; + char *backup_filename; + char *intended_filename; + + backup_filename = mate_rr_config_get_backup_filename (); + intended_filename = mate_rr_config_get_intended_filename (); + + /* 1. See if there was a "saved" configuration. If there is one, it means + * that the user had selected to change the display configuration, but the + * machine crashed. In that case, we'll apply *that* configuration and save it on top of the + * "intended" one. + */ + + my_error = NULL; + + success = apply_configuration_from_filename (manager, backup_filename, FALSE, timestamp, &my_error); + if (success) { + /* The backup configuration existed, and could be applied + * successfully, so we must restore it on top of the + * failed/intended one. + */ + restore_backup_configuration (manager, backup_filename, intended_filename, timestamp); + goto out; + } + + if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + /* Epic fail: there (probably) was a backup configuration, but + * we could not apply it. The only thing we can do is delete + * the backup configuration. Let's hope that the user doesn't + * get left with an unusable display... + */ + + unlink (backup_filename); + goto out; + } + + /* 2. There was no backup configuration! This means we are + * good. Apply the intended configuration instead. + */ + + success = apply_intended_configuration (manager, intended_filename, timestamp); + +out: + + if (my_error) + g_error_free (my_error); + + g_free (backup_filename); + g_free (intended_filename); + + return success; +} + +static gboolean +apply_default_configuration_from_file (MsdXrandrManager *manager, guint32 timestamp) +{ + MsdXrandrManagerPrivate *priv = manager->priv; + char *default_config_filename; + gboolean result; + + default_config_filename = mateconf_client_get_string (priv->client, CONF_KEY_DEFAULT_CONFIGURATION_FILE, NULL); + if (!default_config_filename) + return FALSE; + + result = apply_configuration_from_filename (manager, default_config_filename, TRUE, timestamp, NULL); + + g_free (default_config_filename); + return result; +} + +gboolean +msd_xrandr_manager_start (MsdXrandrManager *manager, + GError **error) +{ + g_debug ("Starting xrandr manager"); + mate_settings_profile_start (NULL); + + log_open (); + log_msg ("------------------------------------------------------------\nSTARTING XRANDR PLUGIN\n"); + + manager->priv->rw_screen = mate_rr_screen_new ( + gdk_screen_get_default (), on_randr_event, manager, error); + + if (manager->priv->rw_screen == NULL) { + log_msg ("Could not initialize the RANDR plugin%s%s\n", + (error && *error) ? ": " : "", + (error && *error) ? (*error)->message : ""); + log_close (); + return FALSE; + } + + log_msg ("State of screen at startup:\n"); + log_screen (manager->priv->rw_screen); + + manager->priv->running = TRUE; + manager->priv->client = mateconf_client_get_default (); + + g_assert (manager->priv->notify_id == 0); + + mateconf_client_add_dir (manager->priv->client, CONF_DIR, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + + manager->priv->notify_id = + mateconf_client_notify_add ( + manager->priv->client, CONF_DIR, + (MateConfClientNotifyFunc)on_config_changed, + manager, NULL, NULL); + + if (manager->priv->switch_video_mode_keycode) { + gdk_error_trap_push (); + + XGrabKey (gdk_x11_get_default_xdisplay(), + manager->priv->switch_video_mode_keycode, AnyModifier, + gdk_x11_get_default_root_xwindow(), + True, GrabModeAsync, GrabModeAsync); + + gdk_flush (); + 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"); + if (!apply_stored_configuration_at_startup (manager, GDK_CURRENT_TIME)) /* we don't have a real timestamp at startup anyway */ + if (!apply_default_configuration_from_file (manager, GDK_CURRENT_TIME)) + apply_default_boot_configuration (manager, GDK_CURRENT_TIME); + + log_msg ("State of screen after initial configuration:\n"); + log_screen (manager->priv->rw_screen); + + gdk_window_add_filter (gdk_get_default_root_window(), + (GdkFilterFunc)event_filter, + manager); + + start_or_stop_icon (manager); + + log_close (); + + mate_settings_profile_end (NULL); + + return TRUE; +} + +void +msd_xrandr_manager_stop (MsdXrandrManager *manager) +{ + g_debug ("Stopping xrandr manager"); + + manager->priv->running = FALSE; + + if (manager->priv->switch_video_mode_keycode) { + gdk_error_trap_push (); + + XUngrabKey (gdk_x11_get_default_xdisplay(), + manager->priv->switch_video_mode_keycode, AnyModifier, + gdk_x11_get_default_root_xwindow()); + + 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); + + if (manager->priv->notify_id != 0) { + mateconf_client_remove_dir (manager->priv->client, + CONF_DIR, NULL); + mateconf_client_notify_remove (manager->priv->client, + manager->priv->notify_id); + manager->priv->notify_id = 0; + } + + if (manager->priv->client != NULL) { + g_object_unref (manager->priv->client); + manager->priv->client = NULL; + } + + if (manager->priv->rw_screen != NULL) { + mate_rr_screen_destroy (manager->priv->rw_screen); + manager->priv->rw_screen = NULL; + } + + if (manager->priv->dbus_connection != NULL) { + dbus_g_connection_unref (manager->priv->dbus_connection); + manager->priv->dbus_connection = NULL; + } + + status_icon_stop (manager); + + log_open (); + log_msg ("STOPPING XRANDR PLUGIN\n------------------------------------------------------------\n"); + log_close (); +} + +static void +msd_xrandr_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MsdXrandrManager *self; + + self = MSD_XRANDR_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +msd_xrandr_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MsdXrandrManager *self; + + self = MSD_XRANDR_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +msd_xrandr_manager_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + MsdXrandrManager *xrandr_manager; + MsdXrandrManagerClass *klass; + + klass = MSD_XRANDR_MANAGER_CLASS (g_type_class_peek (MSD_TYPE_XRANDR_MANAGER)); + + xrandr_manager = MSD_XRANDR_MANAGER (G_OBJECT_CLASS (msd_xrandr_manager_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (xrandr_manager); +} + +static void +msd_xrandr_manager_dispose (GObject *object) +{ + MsdXrandrManager *xrandr_manager; + + xrandr_manager = MSD_XRANDR_MANAGER (object); + + G_OBJECT_CLASS (msd_xrandr_manager_parent_class)->dispose (object); +} + +static void +msd_xrandr_manager_class_init (MsdXrandrManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = msd_xrandr_manager_get_property; + object_class->set_property = msd_xrandr_manager_set_property; + object_class->constructor = msd_xrandr_manager_constructor; + object_class->dispose = msd_xrandr_manager_dispose; + object_class->finalize = msd_xrandr_manager_finalize; + + dbus_g_object_type_install_info (MSD_TYPE_XRANDR_MANAGER, &dbus_glib_msd_xrandr_manager_object_info); + + g_type_class_add_private (klass, sizeof (MsdXrandrManagerPrivate)); +} + +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 +msd_xrandr_manager_init (MsdXrandrManager *manager) +{ + manager->priv = MSD_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; +} + +static void +msd_xrandr_manager_finalize (GObject *object) +{ + MsdXrandrManager *xrandr_manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (MSD_IS_XRANDR_MANAGER (object)); + + xrandr_manager = MSD_XRANDR_MANAGER (object); + + g_return_if_fail (xrandr_manager->priv != NULL); + + G_OBJECT_CLASS (msd_xrandr_manager_parent_class)->finalize (object); +} + +static gboolean +register_manager_dbus (MsdXrandrManager *manager) +{ + GError *error = NULL; + + manager->priv->dbus_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (manager->priv->dbus_connection == NULL) { + if (error != NULL) { + g_warning ("Error getting session bus: %s", error->message); + g_error_free (error); + } + return FALSE; + } + + /* Hmm, should we do this in msd_xrandr_manager_start()? */ + dbus_g_connection_register_g_object (manager->priv->dbus_connection, MSD_XRANDR_DBUS_PATH, G_OBJECT (manager)); + + return TRUE; +} + +MsdXrandrManager * +msd_xrandr_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (MSD_TYPE_XRANDR_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + + if (!register_manager_dbus (manager_object)) { + g_object_unref (manager_object); + return NULL; + } + } + + return MSD_XRANDR_MANAGER (manager_object); +} diff --git a/plugins/xrandr/msd-xrandr-manager.h b/plugins/xrandr/msd-xrandr-manager.h new file mode 100644 index 0000000..dbb5dff --- /dev/null +++ b/plugins/xrandr/msd-xrandr-manager.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __MSD_XRANDR_MANAGER_H +#define __MSD_XRANDR_MANAGER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MSD_TYPE_XRANDR_MANAGER (msd_xrandr_manager_get_type ()) +#define MSD_XRANDR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManager)) +#define MSD_XRANDR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManagerClass)) +#define MSD_IS_XRANDR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_XRANDR_MANAGER)) +#define MSD_IS_XRANDR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_XRANDR_MANAGER)) +#define MSD_XRANDR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_XRANDR_MANAGER, MsdXrandrManagerClass)) + +typedef struct MsdXrandrManagerPrivate MsdXrandrManagerPrivate; + +typedef struct +{ + GObject parent; + MsdXrandrManagerPrivate *priv; +} MsdXrandrManager; + +typedef struct +{ + GObjectClass parent_class; +} MsdXrandrManagerClass; + +GType msd_xrandr_manager_get_type (void); + +MsdXrandrManager * msd_xrandr_manager_new (void); +gboolean msd_xrandr_manager_start (MsdXrandrManager *manager, + GError **error); +void msd_xrandr_manager_stop (MsdXrandrManager *manager); + +#ifdef __cplusplus +} +#endif + +#endif /* __MSD_XRANDR_MANAGER_H */ diff --git a/plugins/xrandr/msd-xrandr-manager.xml b/plugins/xrandr/msd-xrandr-manager.xml new file mode 100644 index 0000000..c82a594 --- /dev/null +++ b/plugins/xrandr/msd-xrandr-manager.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/xrandr/msd-xrandr-plugin.c b/plugins/xrandr/msd-xrandr-plugin.c new file mode 100644 index 0000000..9e78124 --- /dev/null +++ b/plugins/xrandr/msd-xrandr-plugin.c @@ -0,0 +1,104 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include +#include + +#include "mate-settings-plugin.h" +#include "msd-xrandr-plugin.h" +#include "msd-xrandr-manager.h" + +struct MsdXrandrPluginPrivate { + MsdXrandrManager *manager; +}; + +#define MSD_XRANDR_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPluginPrivate)) + +MATE_SETTINGS_PLUGIN_REGISTER (MsdXrandrPlugin, msd_xrandr_plugin) + +static void +msd_xrandr_plugin_init (MsdXrandrPlugin *plugin) +{ + plugin->priv = MSD_XRANDR_PLUGIN_GET_PRIVATE (plugin); + + g_debug ("MsdXrandrPlugin initializing"); + + plugin->priv->manager = msd_xrandr_manager_new (); +} + +static void +msd_xrandr_plugin_finalize (GObject *object) +{ + MsdXrandrPlugin *plugin; + + g_return_if_fail (object != NULL); + g_return_if_fail (MSD_IS_XRANDR_PLUGIN (object)); + + g_debug ("MsdXrandrPlugin finalizing"); + + plugin = MSD_XRANDR_PLUGIN (object); + + g_return_if_fail (plugin->priv != NULL); + + if (plugin->priv->manager != NULL) { + g_object_unref (plugin->priv->manager); + } + + G_OBJECT_CLASS (msd_xrandr_plugin_parent_class)->finalize (object); +} + +static void +impl_activate (MateSettingsPlugin *plugin) +{ + gboolean res; + GError *error; + + g_debug ("Activating xrandr plugin"); + + error = NULL; + res = msd_xrandr_manager_start (MSD_XRANDR_PLUGIN (plugin)->priv->manager, &error); + if (! res) { + g_warning ("Unable to start xrandr manager: %s", error->message); + g_error_free (error); + } +} + +static void +impl_deactivate (MateSettingsPlugin *plugin) +{ + g_debug ("Deactivating xrandr plugin"); + msd_xrandr_manager_stop (MSD_XRANDR_PLUGIN (plugin)->priv->manager); +} + +static void +msd_xrandr_plugin_class_init (MsdXrandrPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MateSettingsPluginClass *plugin_class = MATE_SETTINGS_PLUGIN_CLASS (klass); + + object_class->finalize = msd_xrandr_plugin_finalize; + + plugin_class->activate = impl_activate; + plugin_class->deactivate = impl_deactivate; + + g_type_class_add_private (klass, sizeof (MsdXrandrPluginPrivate)); +} diff --git a/plugins/xrandr/msd-xrandr-plugin.h b/plugins/xrandr/msd-xrandr-plugin.h new file mode 100644 index 0000000..62e742e --- /dev/null +++ b/plugins/xrandr/msd-xrandr-plugin.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __MSD_XRANDR_PLUGIN_H__ +#define __MSD_XRANDR_PLUGIN_H__ + +#include +#include +#include + +#include "mate-settings-plugin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MSD_TYPE_XRANDR_PLUGIN (msd_xrandr_plugin_get_type ()) +#define MSD_XRANDR_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPlugin)) +#define MSD_XRANDR_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPluginClass)) +#define MSD_IS_XRANDR_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MSD_TYPE_XRANDR_PLUGIN)) +#define MSD_IS_XRANDR_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MSD_TYPE_XRANDR_PLUGIN)) +#define MSD_XRANDR_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MSD_TYPE_XRANDR_PLUGIN, MsdXrandrPluginClass)) + +typedef struct MsdXrandrPluginPrivate MsdXrandrPluginPrivate; + +typedef struct +{ + MateSettingsPlugin parent; + MsdXrandrPluginPrivate *priv; +} MsdXrandrPlugin; + +typedef struct +{ + MateSettingsPluginClass parent_class; +} MsdXrandrPluginClass; + +GType msd_xrandr_plugin_get_type (void) G_GNUC_CONST; + +/* All the plugins must implement this function */ +G_MODULE_EXPORT GType register_mate_settings_plugin (GTypeModule *module); + +#ifdef __cplusplus +} +#endif + +#endif /* __MSD_XRANDR_PLUGIN_H__ */ diff --git a/plugins/xrandr/msd-xrandr.svg b/plugins/xrandr/msd-xrandr.svg new file mode 100644 index 0000000..0679b6b --- /dev/null +++ b/plugins/xrandr/msd-xrandr.svg @@ -0,0 +1,470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Change Resolution + + + Jakub Steiner + + + + + + display + resolution + video + + + + + Andreas Nilsson +Luca Ferretti <elle.uca@libero.it> + + + + http://www.gnome.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1