diff options
Diffstat (limited to 'plugins/xrandr/gsd-xrandr-manager.c')
-rw-r--r-- | plugins/xrandr/gsd-xrandr-manager.c | 2584 |
1 files changed, 0 insertions, 2584 deletions
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 <[email protected]> - * 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 <sys/types.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> - -#include <locale.h> - -#include <glib.h> -#include <glib/gi18n.h> -#include <gdk/gdk.h> -#include <gdk/gdkx.h> -#include <gtk/gtk.h> -#include <mateconf/mateconf-client.h> -#include <dbus/dbus-glib.h> - -#define MATE_DESKTOP_USE_UNSTABLE_API - -#include <libmateui/mate-rr-config.h> -#include <libmateui/mate-rr.h> -#include <libmateui/mate-rr-labeler.h> - -#ifdef HAVE_LIBMATENOTIFY -#include <libmatenotify/notify.h> -#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, - "<span weight=\"bold\" size=\"larger\">" - "Display configuration could not be run" - "</span>\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 ("<b>%s</b>", 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 ("<i>%s</i>", _("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); -} |