| File: | capplets/display/xrandr-capplet.c |
| Warning: | line 1009, column 10 3rd function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* Monitor Settings. A preference panel for configuring monitors | |||
| 2 | * | |||
| 3 | * Copyright (C) 2007, 2008 Red Hat, Inc. | |||
| 4 | * | |||
| 5 | * This program is free software; you can redistribute it and/or modify | |||
| 6 | * it under the terms of the GNU General Public License as published by | |||
| 7 | * the Free Software Foundation; either version 2 of the License, or | |||
| 8 | * (at your option) any later version. | |||
| 9 | * | |||
| 10 | * This program is distributed in the hope that it will be useful, | |||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 13 | * GNU General Public License for more details. | |||
| 14 | * | |||
| 15 | * You should have received a copy of the GNU General Public License | |||
| 16 | * along with this program; if not, write to the Free Software | |||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |||
| 18 | * | |||
| 19 | * Author: Soren Sandmann <sandmann@redhat.com> | |||
| 20 | */ | |||
| 21 | ||||
| 22 | #include <config.h> | |||
| 23 | #include <string.h> | |||
| 24 | #include <stdlib.h> | |||
| 25 | #include <sys/wait.h> | |||
| 26 | ||||
| 27 | #include <ctk/ctk.h> | |||
| 28 | #include "scrollarea.h" | |||
| 29 | #define CAFE_DESKTOP_USE_UNSTABLE_API | |||
| 30 | #include <libcafe-desktop/cafe-desktop-utils.h> | |||
| 31 | #include <libcafe-desktop/cafe-rr.h> | |||
| 32 | #include <libcafe-desktop/cafe-rr-config.h> | |||
| 33 | #include <libcafe-desktop/cafe-rr-labeler.h> | |||
| 34 | #include <cdk/cdkx.h> | |||
| 35 | #include <X11/Xlib.h> | |||
| 36 | #include <glib/gi18n.h> | |||
| 37 | #include <gio/gio.h> | |||
| 38 | ||||
| 39 | #include "capplet-util.h" | |||
| 40 | ||||
| 41 | typedef struct App App; | |||
| 42 | typedef struct GrabInfo GrabInfo; | |||
| 43 | ||||
| 44 | struct App | |||
| 45 | { | |||
| 46 | CafeRRScreen *screen; | |||
| 47 | CafeRRConfig *current_configuration; | |||
| 48 | CafeRRLabeler *labeler; | |||
| 49 | CafeRROutputInfo *current_output; | |||
| 50 | ||||
| 51 | CtkWidget *dialog; | |||
| 52 | CtkWidget *current_monitor_event_box; | |||
| 53 | CtkWidget *current_monitor_label; | |||
| 54 | CtkWidget *monitor_on_radio; | |||
| 55 | CtkWidget *monitor_off_radio; | |||
| 56 | CtkWidget *resolution_combo; | |||
| 57 | CtkWidget *refresh_combo; | |||
| 58 | CtkWidget *rotation_combo; | |||
| 59 | CtkWidget *panel_checkbox; | |||
| 60 | CtkWidget *clone_checkbox; | |||
| 61 | CtkWidget *show_icon_checkbox; | |||
| 62 | CtkWidget *primary_button; | |||
| 63 | ||||
| 64 | /* We store the event timestamp when the Apply button is clicked */ | |||
| 65 | CtkWidget *apply_button; | |||
| 66 | guint32 apply_button_clicked_timestamp; | |||
| 67 | ||||
| 68 | CtkWidget *area; | |||
| 69 | gboolean ignore_gui_changes; | |||
| 70 | GSettings *settings; | |||
| 71 | ||||
| 72 | /* These are used while we are waiting for the ApplyConfiguration method to be executed over D-bus */ | |||
| 73 | GDBusConnection *connection; | |||
| 74 | GDBusProxy *proxy; | |||
| 75 | ||||
| 76 | enum { | |||
| 77 | APPLYING_VERSION_1, | |||
| 78 | APPLYING_VERSION_2 | |||
| 79 | } apply_configuration_state; | |||
| 80 | }; | |||
| 81 | ||||
| 82 | /* Response codes for custom buttons in the main dialog */ | |||
| 83 | enum { | |||
| 84 | RESPONSE_MAKE_DEFAULT = 1 | |||
| 85 | }; | |||
| 86 | ||||
| 87 | static void rebuild_gui (App *app); | |||
| 88 | static void on_clone_changed (CtkWidget *box, gpointer data); | |||
| 89 | static void on_rate_changed (CtkComboBox *box, gpointer data); | |||
| 90 | static gboolean output_overlaps (CafeRROutputInfo *output, CafeRRConfig *config); | |||
| 91 | static void select_current_output_from_dialog_position (App *app); | |||
| 92 | static void monitor_on_off_toggled_cb (CtkToggleButton *toggle, gpointer data); | |||
| 93 | static void get_geometry (CafeRROutputInfo *output, int *w, int *h); | |||
| 94 | static void apply_configuration_returned_cb (GObject *source_object, GAsyncResult *res, gpointer data); | |||
| 95 | static gboolean get_clone_size (CafeRRScreen *screen, int *width, int *height); | |||
| 96 | static gboolean output_info_supports_mode (App *app, CafeRROutputInfo *info, int width, int height); | |||
| 97 | ||||
| 98 | static void | |||
| 99 | error_message (App *app, const char *primary_text, const char *secondary_text) | |||
| 100 | { | |||
| 101 | CtkWidget *dialog; | |||
| 102 | ||||
| 103 | dialog = ctk_message_dialog_new ((app && app->dialog) ? CTK_WINDOW (app->dialog)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->dialog)), ((ctk_window_get_type ())))))) : NULL((void*)0), | |||
| 104 | CTK_DIALOG_MODAL | CTK_DIALOG_DESTROY_WITH_PARENT, | |||
| 105 | CTK_MESSAGE_ERROR, | |||
| 106 | CTK_BUTTONS_CLOSE, | |||
| 107 | "%s", primary_text); | |||
| 108 | ||||
| 109 | if (secondary_text) | |||
| 110 | ctk_message_dialog_format_secondary_text (CTK_MESSAGE_DIALOG (dialog)((((CtkMessageDialog*) (void *) g_type_check_instance_cast (( GTypeInstance*) ((dialog)), ((ctk_message_dialog_get_type ()) ))))), "%s", secondary_text); | |||
| 111 | ||||
| 112 | ctk_dialog_run (CTK_DIALOG (dialog)((((CtkDialog*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((dialog)), ((ctk_dialog_get_type ()))))))); | |||
| 113 | ctk_widget_destroy (dialog); | |||
| 114 | } | |||
| 115 | ||||
| 116 | static gboolean | |||
| 117 | do_free (gpointer data) | |||
| 118 | { | |||
| 119 | g_free (data); | |||
| 120 | return FALSE(0); | |||
| 121 | } | |||
| 122 | ||||
| 123 | static gchar * | |||
| 124 | idle_free (gchar *s) | |||
| 125 | { | |||
| 126 | g_idle_add (do_free, s); | |||
| 127 | ||||
| 128 | return s; | |||
| 129 | } | |||
| 130 | ||||
| 131 | static void | |||
| 132 | on_screen_changed (CafeRRScreen *scr, | |||
| 133 | gpointer data) | |||
| 134 | { | |||
| 135 | CafeRRConfig *current; | |||
| 136 | App *app = data; | |||
| 137 | ||||
| 138 | current = cafe_rr_config_new_current (app->screen, NULL((void*)0)); | |||
| 139 | ||||
| 140 | if (app->current_configuration) | |||
| 141 | g_object_unref (app->current_configuration); | |||
| 142 | ||||
| 143 | app->current_configuration = current; | |||
| 144 | app->current_output = NULL((void*)0); | |||
| 145 | ||||
| 146 | if (app->labeler) { | |||
| 147 | cafe_rr_labeler_hide (app->labeler); | |||
| 148 | g_object_unref (app->labeler); | |||
| 149 | } | |||
| 150 | ||||
| 151 | app->labeler = cafe_rr_labeler_new (app->current_configuration); | |||
| 152 | ||||
| 153 | select_current_output_from_dialog_position (app); | |||
| 154 | } | |||
| 155 | ||||
| 156 | static void | |||
| 157 | on_viewport_changed (FooScrollArea *scroll_area, | |||
| 158 | CdkRectangle *old_viewport, | |||
| 159 | CdkRectangle *new_viewport) | |||
| 160 | { | |||
| 161 | foo_scroll_area_set_size (scroll_area, | |||
| 162 | new_viewport->width, | |||
| 163 | new_viewport->height); | |||
| 164 | ||||
| 165 | foo_scroll_area_invalidate (scroll_area); | |||
| 166 | } | |||
| 167 | ||||
| 168 | static void | |||
| 169 | layout_set_font (PangoLayout *layout, const char *font) | |||
| 170 | { | |||
| 171 | PangoFontDescription *desc = | |||
| 172 | pango_font_description_from_string (font); | |||
| 173 | ||||
| 174 | if (desc) | |||
| 175 | { | |||
| 176 | pango_layout_set_font_description (layout, desc); | |||
| 177 | ||||
| 178 | pango_font_description_free (desc); | |||
| 179 | } | |||
| 180 | } | |||
| 181 | ||||
| 182 | static void | |||
| 183 | clear_combo (CtkWidget *widget) | |||
| 184 | { | |||
| 185 | CtkComboBox *box = CTK_COMBO_BOX (widget)((((CtkComboBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_combo_box_get_type ())))))); | |||
| 186 | CtkTreeModel *model = ctk_combo_box_get_model (box); | |||
| 187 | CtkListStore *store = CTK_LIST_STORE (model)((((CtkListStore*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((model)), ((ctk_list_store_get_type ())))))); | |||
| 188 | ||||
| 189 | ctk_list_store_clear (store); | |||
| 190 | } | |||
| 191 | ||||
| 192 | typedef struct | |||
| 193 | { | |||
| 194 | const char *text; | |||
| 195 | gboolean found; | |||
| 196 | CtkTreeIter iter; | |||
| 197 | } ForeachInfo; | |||
| 198 | ||||
| 199 | static gboolean | |||
| 200 | foreach (CtkTreeModel *model, | |||
| 201 | CtkTreePath *path, | |||
| 202 | CtkTreeIter *iter, | |||
| 203 | gpointer data) | |||
| 204 | { | |||
| 205 | ForeachInfo *info = data; | |||
| 206 | char *text = NULL((void*)0); | |||
| 207 | ||||
| 208 | ctk_tree_model_get (model, iter, 0, &text, -1); | |||
| 209 | ||||
| 210 | g_assert (text != NULL)do { if (text != ((void*)0)) ; else g_assertion_message_expr ( "display-properties", "xrandr-capplet.c", 210, ((const char*) (__func__)), "text != NULL"); } while (0); | |||
| 211 | ||||
| 212 | if (strcmp (info->text, text) == 0) | |||
| 213 | { | |||
| 214 | info->found = TRUE(!(0)); | |||
| 215 | info->iter = *iter; | |||
| 216 | return TRUE(!(0)); | |||
| 217 | } | |||
| 218 | ||||
| 219 | return FALSE(0); | |||
| 220 | } | |||
| 221 | ||||
| 222 | static void | |||
| 223 | add_key (CtkWidget *widget, | |||
| 224 | const char *text, | |||
| 225 | int width, int height, int rate, | |||
| 226 | CafeRRRotation rotation) | |||
| 227 | { | |||
| 228 | ForeachInfo info; | |||
| 229 | CtkComboBox *box = CTK_COMBO_BOX (widget)((((CtkComboBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_combo_box_get_type ())))))); | |||
| 230 | CtkTreeModel *model = ctk_combo_box_get_model (box); | |||
| 231 | CtkListStore *store = CTK_LIST_STORE (model)((((CtkListStore*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((model)), ((ctk_list_store_get_type ())))))); | |||
| 232 | ||||
| 233 | info.text = text; | |||
| 234 | info.found = FALSE(0); | |||
| 235 | ||||
| 236 | ctk_tree_model_foreach (model, foreach, &info); | |||
| 237 | ||||
| 238 | if (!info.found) | |||
| 239 | { | |||
| 240 | CtkTreeIter iter; | |||
| 241 | ctk_list_store_insert_with_values (store, &iter, -1, | |||
| 242 | 0, text, | |||
| 243 | 1, width, | |||
| 244 | 2, height, | |||
| 245 | 3, rate, | |||
| 246 | 4, width * height, | |||
| 247 | 5, rotation, | |||
| 248 | -1); | |||
| 249 | ||||
| 250 | } | |||
| 251 | } | |||
| 252 | ||||
| 253 | static gboolean | |||
| 254 | combo_select (CtkWidget *widget, const char *text) | |||
| 255 | { | |||
| 256 | CtkComboBox *box = CTK_COMBO_BOX (widget)((((CtkComboBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_combo_box_get_type ())))))); | |||
| 257 | CtkTreeModel *model = ctk_combo_box_get_model (box); | |||
| 258 | ForeachInfo info; | |||
| 259 | ||||
| 260 | info.text = text; | |||
| 261 | info.found = FALSE(0); | |||
| 262 | ||||
| 263 | ctk_tree_model_foreach (model, foreach, &info); | |||
| 264 | ||||
| 265 | if (!info.found) | |||
| 266 | return FALSE(0); | |||
| 267 | ||||
| 268 | ctk_combo_box_set_active_iter (box, &info.iter); | |||
| 269 | return TRUE(!(0)); | |||
| 270 | } | |||
| 271 | ||||
| 272 | static CafeRRMode ** | |||
| 273 | get_current_modes (App *app) | |||
| 274 | { | |||
| 275 | CafeRROutput *output; | |||
| 276 | ||||
| 277 | if (cafe_rr_config_get_clone (app->current_configuration)) | |||
| 278 | { | |||
| 279 | return cafe_rr_screen_list_clone_modes (app->screen); | |||
| 280 | } | |||
| 281 | else | |||
| 282 | { | |||
| 283 | if (!app->current_output) | |||
| 284 | return NULL((void*)0); | |||
| 285 | ||||
| 286 | output = cafe_rr_screen_get_output_by_name (app->screen, | |||
| 287 | cafe_rr_output_info_get_name (app->current_output)); | |||
| 288 | ||||
| 289 | if (!output) | |||
| 290 | return NULL((void*)0); | |||
| 291 | ||||
| 292 | return cafe_rr_output_list_modes (output); | |||
| 293 | } | |||
| 294 | } | |||
| 295 | ||||
| 296 | static void | |||
| 297 | rebuild_rotation_combo (App *app) | |||
| 298 | { | |||
| 299 | typedef struct | |||
| 300 | { | |||
| 301 | CafeRRRotation rotation; | |||
| 302 | const char * name; | |||
| 303 | } RotationInfo; | |||
| 304 | static const RotationInfo rotations[] = { | |||
| 305 | { CAFE_RR_ROTATION_0, N_("Normal")("Normal") }, | |||
| 306 | { CAFE_RR_ROTATION_90, N_("Left")("Left") }, | |||
| 307 | { CAFE_RR_ROTATION_270, N_("Right")("Right") }, | |||
| 308 | { CAFE_RR_ROTATION_180, N_("Upside Down")("Upside Down") }, | |||
| 309 | }; | |||
| 310 | const char *selection; | |||
| 311 | CafeRRRotation current; | |||
| 312 | int i; | |||
| 313 | ||||
| 314 | clear_combo (app->rotation_combo); | |||
| 315 | ||||
| 316 | ctk_widget_set_sensitive (app->rotation_combo, | |||
| 317 | app->current_output && cafe_rr_output_info_is_active (app->current_output)); | |||
| 318 | ||||
| 319 | if (!app->current_output) | |||
| 320 | return; | |||
| 321 | ||||
| 322 | current = cafe_rr_output_info_get_rotation (app->current_output); | |||
| 323 | ||||
| 324 | selection = NULL((void*)0); | |||
| 325 | for (i = 0; i < G_N_ELEMENTS (rotations)(sizeof (rotations) / sizeof ((rotations)[0])); ++i) | |||
| 326 | { | |||
| 327 | const RotationInfo *info = &(rotations[i]); | |||
| 328 | ||||
| 329 | cafe_rr_output_info_set_rotation (app->current_output, info->rotation); | |||
| 330 | ||||
| 331 | /* NULL-GError --- FIXME: we should say why this rotation is not available! */ | |||
| 332 | if (cafe_rr_config_applicable (app->current_configuration, app->screen, NULL((void*)0))) | |||
| 333 | { | |||
| 334 | add_key (app->rotation_combo, _(info->name)gettext (info->name), 0, 0, 0, info->rotation); | |||
| 335 | ||||
| 336 | if (info->rotation == current) | |||
| 337 | selection = _(info->name)gettext (info->name); | |||
| 338 | } | |||
| 339 | } | |||
| 340 | ||||
| 341 | cafe_rr_output_info_set_rotation (app->current_output, current); | |||
| 342 | ||||
| 343 | if (!(selection && combo_select (app->rotation_combo, selection))) | |||
| 344 | combo_select (app->rotation_combo, _("Normal")gettext ("Normal")); | |||
| 345 | } | |||
| 346 | ||||
| 347 | static char * | |||
| 348 | make_rate_string (int hz) | |||
| 349 | { | |||
| 350 | return g_strdup_printf (_("%d Hz")gettext ("%d Hz"), hz); | |||
| 351 | } | |||
| 352 | ||||
| 353 | static void | |||
| 354 | rebuild_rate_combo (App *app) | |||
| 355 | { | |||
| 356 | CafeRRMode **modes; | |||
| 357 | int best; | |||
| 358 | int i; | |||
| 359 | ||||
| 360 | clear_combo (app->refresh_combo); | |||
| 361 | ||||
| 362 | ctk_widget_set_sensitive ( | |||
| 363 | app->refresh_combo, app->current_output && cafe_rr_output_info_is_active (app->current_output)); | |||
| 364 | ||||
| 365 | if (!app->current_output | |||
| 366 | || !(modes = get_current_modes (app))) | |||
| 367 | return; | |||
| 368 | ||||
| 369 | best = -1; | |||
| 370 | for (i = 0; modes[i] != NULL((void*)0); ++i) | |||
| 371 | { | |||
| 372 | CafeRRMode *mode = modes[i]; | |||
| 373 | int width, height, rate; | |||
| 374 | int output_width, output_height; | |||
| 375 | ||||
| 376 | cafe_rr_output_info_get_geometry (app->current_output, NULL((void*)0), NULL((void*)0), &output_width, &output_height); | |||
| 377 | ||||
| 378 | width = cafe_rr_mode_get_width (mode); | |||
| 379 | height = cafe_rr_mode_get_height (mode); | |||
| 380 | rate = cafe_rr_mode_get_freq (mode); | |||
| 381 | ||||
| 382 | if (width == output_width && | |||
| 383 | height == output_height) | |||
| 384 | { | |||
| 385 | add_key (app->refresh_combo, | |||
| 386 | idle_free (make_rate_string (rate)), | |||
| 387 | 0, 0, rate, -1); | |||
| 388 | ||||
| 389 | if (rate > best) | |||
| 390 | best = rate; | |||
| 391 | } | |||
| 392 | } | |||
| 393 | ||||
| 394 | if (!combo_select (app->refresh_combo, idle_free (make_rate_string (cafe_rr_output_info_get_refresh_rate (app->current_output))))) | |||
| 395 | combo_select (app->refresh_combo, idle_free (make_rate_string (best))); | |||
| 396 | } | |||
| 397 | ||||
| 398 | static int | |||
| 399 | count_active_outputs (App *app) | |||
| 400 | { | |||
| 401 | int i, count = 0; | |||
| 402 | CafeRROutputInfo **outputs = cafe_rr_config_get_outputs (app->current_configuration); | |||
| 403 | ||||
| 404 | for (i = 0; outputs[i] != NULL((void*)0); ++i) | |||
| 405 | { | |||
| 406 | if (cafe_rr_output_info_is_active (outputs[i])) | |||
| 407 | count++; | |||
| 408 | } | |||
| 409 | ||||
| 410 | return count; | |||
| 411 | } | |||
| 412 | ||||
| 413 | /* Computes whether "Mirror Screens" (clone mode) is supported based on these criteria: | |||
| 414 | * | |||
| 415 | * 1. There is an available size for cloning. | |||
| 416 | * | |||
| 417 | * 2. There are 2 or more connected outputs that support that size. | |||
| 418 | */ | |||
| 419 | static gboolean | |||
| 420 | mirror_screens_is_supported (App *app) | |||
| 421 | { | |||
| 422 | int clone_width, clone_height; | |||
| 423 | gboolean have_clone_size; | |||
| 424 | gboolean mirror_is_supported; | |||
| 425 | ||||
| 426 | mirror_is_supported = FALSE(0); | |||
| 427 | ||||
| 428 | have_clone_size = get_clone_size (app->screen, &clone_width, &clone_height); | |||
| 429 | ||||
| 430 | if (have_clone_size) { | |||
| 431 | int i; | |||
| 432 | int num_outputs_with_clone_size; | |||
| 433 | CafeRROutputInfo **outputs = cafe_rr_config_get_outputs (app->current_configuration); | |||
| 434 | ||||
| 435 | num_outputs_with_clone_size = 0; | |||
| 436 | ||||
| 437 | for (i = 0; outputs[i] != NULL((void*)0); i++) | |||
| 438 | { | |||
| 439 | /* We count the connected outputs that support the clone size. It | |||
| 440 | * doesn't matter if those outputs aren't actually On currently; we | |||
| 441 | * will turn them on in on_clone_changed(). | |||
| 442 | */ | |||
| 443 | if (cafe_rr_output_info_is_connected (outputs[i]) && output_info_supports_mode (app, outputs[i], clone_width, clone_height)) | |||
| 444 | num_outputs_with_clone_size++; | |||
| 445 | } | |||
| 446 | ||||
| 447 | if (num_outputs_with_clone_size >= 2) | |||
| 448 | mirror_is_supported = TRUE(!(0)); | |||
| 449 | } | |||
| 450 | ||||
| 451 | return mirror_is_supported; | |||
| 452 | } | |||
| 453 | ||||
| 454 | static void | |||
| 455 | rebuild_mirror_screens (App *app) | |||
| 456 | { | |||
| 457 | gboolean mirror_is_active; | |||
| 458 | gboolean mirror_is_supported; | |||
| 459 | ||||
| 460 | g_signal_handlers_block_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app)g_signal_handlers_block_matched ((app->clone_checkbox), (GSignalMatchType ) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*) 0), (((GCallback) (on_clone_changed))), (app)); | |||
| 461 | ||||
| 462 | mirror_is_active = app->current_configuration && cafe_rr_config_get_clone (app->current_configuration); | |||
| 463 | ||||
| 464 | /* If mirror_is_active, then it *must* be possible to turn mirroring off */ | |||
| 465 | mirror_is_supported = mirror_is_active || mirror_screens_is_supported (app); | |||
| 466 | ||||
| 467 | ctk_toggle_button_set_active (CTK_TOGGLE_BUTTON (app->clone_checkbox)((((CtkToggleButton*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->clone_checkbox)), ((ctk_toggle_button_get_type ( ))))))), mirror_is_active); | |||
| 468 | ctk_widget_set_sensitive (app->clone_checkbox, mirror_is_supported); | |||
| 469 | ||||
| 470 | g_signal_handlers_unblock_by_func (app->clone_checkbox, G_CALLBACK (on_clone_changed), app)g_signal_handlers_unblock_matched ((app->clone_checkbox), ( GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA) , 0, 0, ((void*)0), (((GCallback) (on_clone_changed))), (app) ); | |||
| 471 | } | |||
| 472 | ||||
| 473 | static void | |||
| 474 | rebuild_current_monitor_label (App *app) | |||
| 475 | { | |||
| 476 | char *str, *tmp; | |||
| 477 | CdkRGBA color; | |||
| 478 | gboolean use_color; | |||
| 479 | ||||
| 480 | if (app->current_output) | |||
| 481 | { | |||
| 482 | if (cafe_rr_config_get_clone (app->current_configuration)) | |||
| 483 | tmp = g_strdup (_("Mirror Screens"))g_strdup_inline (gettext ("Mirror Screens")); | |||
| 484 | else | |||
| 485 | tmp = g_strdup_printf (_("Monitor: %s")gettext ("Monitor: %s"), cafe_rr_output_info_get_display_name (app->current_output)); | |||
| 486 | ||||
| 487 | str = g_strdup_printf ("<b>%s</b>", tmp); | |||
| 488 | cafe_rr_labeler_get_rgba_for_output (app->labeler, app->current_output, &color); | |||
| 489 | use_color = TRUE(!(0)); | |||
| 490 | g_free (tmp); | |||
| 491 | } | |||
| 492 | else | |||
| 493 | { | |||
| 494 | str = g_strdup_printf ("<b>%s</b>", _("Monitor")gettext ("Monitor")); | |||
| 495 | use_color = FALSE(0); | |||
| 496 | } | |||
| 497 | ||||
| 498 | ctk_label_set_markup (CTK_LABEL (app->current_monitor_label)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->current_monitor_label)), ((ctk_label_get_type () )))))), str); | |||
| 499 | g_free (str); | |||
| 500 | ||||
| 501 | if (use_color) | |||
| 502 | { | |||
| 503 | CdkRGBA black = { 0, 0, 0, 1.0 }; | |||
| 504 | ||||
| 505 | ctk_widget_override_background_color (app->current_monitor_event_box, ctk_widget_get_state_flags (app->current_monitor_event_box), &color); | |||
| 506 | ||||
| 507 | /* Make the label explicitly black. We don't want it to follow the | |||
| 508 | * theme's colors, since the label is always shown against a light | |||
| 509 | * pastel background. See bgo#556050 | |||
| 510 | */ | |||
| 511 | ctk_widget_override_color (app->current_monitor_label, ctk_widget_get_state_flags (app->current_monitor_label), &black); | |||
| 512 | } | |||
| 513 | ||||
| 514 | ctk_event_box_set_visible_window (CTK_EVENT_BOX (app->current_monitor_event_box)((((CtkEventBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->current_monitor_event_box)), ((ctk_event_box_get_type ())))))), use_color); | |||
| 515 | } | |||
| 516 | ||||
| 517 | static void | |||
| 518 | rebuild_on_off_radios (App *app) | |||
| 519 | { | |||
| 520 | gboolean sensitive; | |||
| 521 | gboolean on_active; | |||
| 522 | gboolean off_active; | |||
| 523 | ||||
| 524 | g_signal_handlers_block_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app)g_signal_handlers_block_matched ((app->monitor_on_radio), ( GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA) , 0, 0, ((void*)0), (((GCallback) (monitor_on_off_toggled_cb) )), (app)); | |||
| 525 | g_signal_handlers_block_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app)g_signal_handlers_block_matched ((app->monitor_off_radio), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (monitor_on_off_toggled_cb ))), (app)); | |||
| 526 | ||||
| 527 | sensitive = FALSE(0); | |||
| 528 | on_active = FALSE(0); | |||
| 529 | off_active = FALSE(0); | |||
| 530 | ||||
| 531 | if (!cafe_rr_config_get_clone (app->current_configuration) && app->current_output) | |||
| 532 | { | |||
| 533 | if (count_active_outputs (app) > 1 || !cafe_rr_output_info_is_active (app->current_output)) | |||
| 534 | sensitive = TRUE(!(0)); | |||
| 535 | else | |||
| 536 | sensitive = FALSE(0); | |||
| 537 | ||||
| 538 | on_active = cafe_rr_output_info_is_active (app->current_output); | |||
| 539 | off_active = !on_active; | |||
| 540 | } | |||
| 541 | ||||
| 542 | ctk_widget_set_sensitive (app->monitor_on_radio, sensitive); | |||
| 543 | ctk_widget_set_sensitive (app->monitor_off_radio, sensitive); | |||
| 544 | ||||
| 545 | ctk_toggle_button_set_active (CTK_TOGGLE_BUTTON (app->monitor_on_radio)((((CtkToggleButton*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->monitor_on_radio)), ((ctk_toggle_button_get_type ())))))), on_active); | |||
| 546 | ctk_toggle_button_set_active (CTK_TOGGLE_BUTTON (app->monitor_off_radio)((((CtkToggleButton*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->monitor_off_radio)), ((ctk_toggle_button_get_type ())))))), off_active); | |||
| 547 | ||||
| 548 | g_signal_handlers_unblock_by_func (app->monitor_on_radio, G_CALLBACK (monitor_on_off_toggled_cb), app)g_signal_handlers_unblock_matched ((app->monitor_on_radio) , (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (monitor_on_off_toggled_cb ))), (app)); | |||
| 549 | g_signal_handlers_unblock_by_func (app->monitor_off_radio, G_CALLBACK (monitor_on_off_toggled_cb), app)g_signal_handlers_unblock_matched ((app->monitor_off_radio ), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (((GCallback) (monitor_on_off_toggled_cb ))), (app)); | |||
| 550 | } | |||
| 551 | ||||
| 552 | static char * | |||
| 553 | make_resolution_string (int width, int height) | |||
| 554 | { | |||
| 555 | return g_strdup_printf (_("%d x %d")gettext ("%d x %d"), width, height); | |||
| 556 | } | |||
| 557 | ||||
| 558 | static void | |||
| 559 | find_best_mode (CafeRRMode **modes, int *out_width, int *out_height) | |||
| 560 | { | |||
| 561 | int i; | |||
| 562 | ||||
| 563 | *out_width = 0; | |||
| 564 | *out_height = 0; | |||
| 565 | ||||
| 566 | for (i = 0; modes[i] != NULL((void*)0); i++) | |||
| 567 | { | |||
| 568 | int w, h; | |||
| 569 | ||||
| 570 | w = cafe_rr_mode_get_width (modes[i]); | |||
| 571 | h = cafe_rr_mode_get_height (modes[i]); | |||
| 572 | ||||
| 573 | if (w * h > *out_width * *out_height) | |||
| 574 | { | |||
| 575 | *out_width = w; | |||
| 576 | *out_height = h; | |||
| 577 | } | |||
| 578 | } | |||
| 579 | } | |||
| 580 | ||||
| 581 | static void | |||
| 582 | rebuild_resolution_combo (App *app) | |||
| 583 | { | |||
| 584 | int i; | |||
| 585 | CafeRRMode **modes; | |||
| 586 | const char *current; | |||
| 587 | int output_width, output_height; | |||
| 588 | ||||
| 589 | clear_combo (app->resolution_combo); | |||
| 590 | ||||
| 591 | if (!(modes = get_current_modes (app)) | |||
| 592 | || !app->current_output | |||
| 593 | || !cafe_rr_output_info_is_active (app->current_output)) | |||
| 594 | { | |||
| 595 | ctk_widget_set_sensitive (app->resolution_combo, FALSE(0)); | |||
| 596 | return; | |||
| 597 | } | |||
| 598 | ||||
| 599 | g_assert (app->current_output != NULL)do { if (app->current_output != ((void*)0)) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c", 599, ((const char *) (__func__)), "app->current_output != NULL"); } while (0 ); | |||
| 600 | ||||
| 601 | cafe_rr_output_info_get_geometry (app->current_output, NULL((void*)0), NULL((void*)0), &output_width, &output_height); | |||
| 602 | g_assert (output_width != 0 && output_height != 0)do { if (output_width != 0 && output_height != 0) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c" , 602, ((const char*) (__func__)), "output_width != 0 && output_height != 0" ); } while (0); | |||
| 603 | ||||
| 604 | ctk_widget_set_sensitive (app->resolution_combo, TRUE(!(0))); | |||
| 605 | ||||
| 606 | for (i = 0; modes[i] != NULL((void*)0); ++i) | |||
| 607 | { | |||
| 608 | int width, height; | |||
| 609 | ||||
| 610 | width = cafe_rr_mode_get_width (modes[i]); | |||
| 611 | height = cafe_rr_mode_get_height (modes[i]); | |||
| 612 | ||||
| 613 | add_key (app->resolution_combo, | |||
| 614 | idle_free (make_resolution_string (width, height)), | |||
| 615 | width, height, 0, -1); | |||
| 616 | } | |||
| 617 | ||||
| 618 | current = idle_free (make_resolution_string (output_width, output_height)); | |||
| 619 | ||||
| 620 | if (!combo_select (app->resolution_combo, current)) | |||
| 621 | { | |||
| 622 | int best_w, best_h; | |||
| 623 | ||||
| 624 | find_best_mode (modes, &best_w, &best_h); | |||
| 625 | combo_select (app->resolution_combo, idle_free (make_resolution_string (best_w, best_h))); | |||
| 626 | } | |||
| 627 | } | |||
| 628 | ||||
| 629 | static void | |||
| 630 | rebuild_gui (App *app) | |||
| 631 | { | |||
| 632 | gboolean sensitive; | |||
| 633 | ||||
| 634 | /* We would break spectacularly if we recursed, so | |||
| 635 | * just assert if that happens | |||
| 636 | */ | |||
| 637 | g_assert (app->ignore_gui_changes == FALSE)do { if (app->ignore_gui_changes == (0)) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c", 637, ((const char *) (__func__)), "app->ignore_gui_changes == FALSE"); } while (0); | |||
| 638 | ||||
| 639 | app->ignore_gui_changes = TRUE(!(0)); | |||
| 640 | ||||
| 641 | sensitive = app->current_output ? TRUE(!(0)) : FALSE(0); | |||
| 642 | ||||
| 643 | #if 0 | |||
| 644 | g_debug ("rebuild gui, is on: %d", cafe_rr_output_info_is_active (app->current_output)); | |||
| 645 | #endif | |||
| 646 | ||||
| 647 | rebuild_mirror_screens (app); | |||
| 648 | rebuild_current_monitor_label (app); | |||
| 649 | rebuild_on_off_radios (app); | |||
| 650 | rebuild_resolution_combo (app); | |||
| 651 | rebuild_rate_combo (app); | |||
| 652 | rebuild_rotation_combo (app); | |||
| 653 | ||||
| 654 | #if 0 | |||
| 655 | g_debug ("sensitive: %d, on: %d", sensitive, cafe_rr_output_info_is_active (app->current_output)); | |||
| 656 | #endif | |||
| 657 | ctk_widget_set_sensitive (app->panel_checkbox, sensitive); | |||
| 658 | ||||
| 659 | ctk_widget_set_sensitive (app->primary_button, app->current_output && !cafe_rr_output_info_get_primary(app->current_output)); | |||
| 660 | ||||
| 661 | app->ignore_gui_changes = FALSE(0); | |||
| 662 | } | |||
| 663 | ||||
| 664 | static gboolean | |||
| 665 | get_mode (CtkWidget *widget, int *width, int *height, int *freq, CafeRRRotation *rot) | |||
| 666 | { | |||
| 667 | CtkTreeIter iter; | |||
| 668 | CtkTreeModel *model; | |||
| 669 | CtkComboBox *box = CTK_COMBO_BOX (widget)((((CtkComboBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_combo_box_get_type ())))))); | |||
| 670 | int dummy; | |||
| 671 | ||||
| 672 | if (!ctk_combo_box_get_active_iter (box, &iter)) | |||
| 673 | return FALSE(0); | |||
| 674 | ||||
| 675 | if (!width) | |||
| 676 | width = &dummy; | |||
| 677 | ||||
| 678 | if (!height) | |||
| 679 | height = &dummy; | |||
| 680 | ||||
| 681 | if (!freq) | |||
| 682 | freq = &dummy; | |||
| 683 | ||||
| 684 | if (!rot) | |||
| 685 | rot = (CafeRRRotation *)&dummy; | |||
| 686 | ||||
| 687 | model = ctk_combo_box_get_model (box); | |||
| 688 | ctk_tree_model_get (model, &iter, | |||
| 689 | 1, width, | |||
| 690 | 2, height, | |||
| 691 | 3, freq, | |||
| 692 | 5, rot, | |||
| 693 | -1); | |||
| 694 | ||||
| 695 | return TRUE(!(0)); | |||
| 696 | ||||
| 697 | } | |||
| 698 | ||||
| 699 | static void | |||
| 700 | on_rotation_changed (CtkComboBox *box, gpointer data) | |||
| 701 | { | |||
| 702 | App *app = data; | |||
| 703 | CafeRRRotation rotation; | |||
| 704 | ||||
| 705 | if (!app->current_output) | |||
| 706 | return; | |||
| 707 | ||||
| 708 | if (get_mode (app->rotation_combo, NULL((void*)0), NULL((void*)0), NULL((void*)0), &rotation)) | |||
| 709 | cafe_rr_output_info_set_rotation (app->current_output, rotation); | |||
| 710 | ||||
| 711 | foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ()))))))); | |||
| 712 | } | |||
| 713 | ||||
| 714 | static void | |||
| 715 | on_rate_changed (CtkComboBox *box, gpointer data) | |||
| 716 | { | |||
| 717 | App *app = data; | |||
| 718 | int rate; | |||
| 719 | ||||
| 720 | if (!app->current_output) | |||
| 721 | return; | |||
| 722 | ||||
| 723 | if (get_mode (app->refresh_combo, NULL((void*)0), NULL((void*)0), &rate, NULL((void*)0))) | |||
| 724 | cafe_rr_output_info_set_refresh_rate (app->current_output, rate); | |||
| 725 | ||||
| 726 | foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ()))))))); | |||
| 727 | } | |||
| 728 | ||||
| 729 | static void | |||
| 730 | select_resolution_for_current_output (App *app) | |||
| 731 | { | |||
| 732 | CafeRRMode **modes; | |||
| 733 | int width, height; | |||
| 734 | int x, y; | |||
| 735 | cafe_rr_output_info_get_geometry (app->current_output, &x, &y, NULL((void*)0), NULL((void*)0)); | |||
| 736 | ||||
| 737 | width = cafe_rr_output_info_get_preferred_width (app->current_output); | |||
| 738 | height = cafe_rr_output_info_get_preferred_height (app->current_output); | |||
| 739 | ||||
| 740 | if (width != 0 && height != 0) | |||
| 741 | { | |||
| 742 | cafe_rr_output_info_set_geometry (app->current_output, x, y, width, height); | |||
| 743 | return; | |||
| 744 | } | |||
| 745 | ||||
| 746 | modes = get_current_modes (app); | |||
| 747 | if (!modes) | |||
| 748 | return; | |||
| 749 | ||||
| 750 | find_best_mode (modes, &width, &height); | |||
| 751 | ||||
| 752 | cafe_rr_output_info_set_geometry (app->current_output, x, y, width, height); | |||
| 753 | } | |||
| 754 | ||||
| 755 | static void | |||
| 756 | monitor_on_off_toggled_cb (CtkToggleButton *toggle, gpointer data) | |||
| 757 | { | |||
| 758 | App *app = data; | |||
| 759 | gboolean is_on; | |||
| 760 | ||||
| 761 | if (!app->current_output) | |||
| 762 | return; | |||
| 763 | ||||
| 764 | if (!ctk_toggle_button_get_active (toggle)) | |||
| 765 | return; | |||
| 766 | ||||
| 767 | if (CTK_WIDGET (toggle)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toggle)), ((ctk_widget_get_type ())))))) == app->monitor_on_radio) | |||
| 768 | is_on = TRUE(!(0)); | |||
| 769 | else if (CTK_WIDGET (toggle)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((toggle)), ((ctk_widget_get_type ())))))) == app->monitor_off_radio) | |||
| 770 | is_on = FALSE(0); | |||
| 771 | else | |||
| 772 | { | |||
| 773 | g_assert_not_reached ()do { g_assertion_message_expr ("display-properties", "xrandr-capplet.c" , 773, ((const char*) (__func__)), ((void*)0)); } while (0); | |||
| 774 | return; | |||
| 775 | } | |||
| 776 | ||||
| 777 | cafe_rr_output_info_set_active (app->current_output, is_on); | |||
| 778 | ||||
| 779 | if (is_on) | |||
| 780 | select_resolution_for_current_output (app); /* The refresh rate will be picked in rebuild_rate_combo() */ | |||
| 781 | ||||
| 782 | rebuild_gui (app); | |||
| 783 | foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ()))))))); | |||
| 784 | } | |||
| 785 | ||||
| 786 | static void | |||
| 787 | realign_outputs_after_resolution_change (App *app, CafeRROutputInfo *output_that_changed, int old_width, int old_height) | |||
| 788 | { | |||
| 789 | /* We find the outputs that were below or to the right of the output that | |||
| 790 | * changed, and realign them; we also do that for outputs that shared the | |||
| 791 | * right/bottom edges with the output that changed. The outputs that are | |||
| 792 | * above or to the left of that output don't need to change. | |||
| 793 | */ | |||
| 794 | ||||
| 795 | int i; | |||
| 796 | int old_right_edge, old_bottom_edge; | |||
| 797 | int dx, dy; | |||
| 798 | int x, y, width, height; | |||
| 799 | CafeRROutputInfo **outputs; | |||
| 800 | ||||
| 801 | g_assert (app->current_configuration != NULL)do { if (app->current_configuration != ((void*)0)) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c", 801, ((const char *) (__func__)), "app->current_configuration != NULL"); } while (0); | |||
| 802 | ||||
| 803 | cafe_rr_output_info_get_geometry (output_that_changed, &x, &y, &width, &height); | |||
| 804 | if (width == old_width && height == old_height) | |||
| 805 | return; | |||
| 806 | ||||
| 807 | old_right_edge = x + old_width; | |||
| 808 | old_bottom_edge = y + old_height; | |||
| 809 | ||||
| 810 | dx = width - old_width; | |||
| 811 | dy = height - old_height; | |||
| 812 | ||||
| 813 | outputs = cafe_rr_config_get_outputs (app->current_configuration); | |||
| 814 | ||||
| 815 | for (i = 0; outputs[i] != NULL((void*)0); i++) | |||
| 816 | { | |||
| 817 | int output_x, output_y; | |||
| 818 | int output_width, output_height; | |||
| 819 | ||||
| 820 | if (outputs[i] == output_that_changed || cafe_rr_output_info_is_connected (outputs[i])) | |||
| 821 | continue; | |||
| 822 | ||||
| 823 | cafe_rr_output_info_get_geometry (outputs[i], &output_x, &output_y, &output_width, &output_height); | |||
| 824 | ||||
| 825 | if (output_x >= old_right_edge) | |||
| 826 | output_x += dx; | |||
| 827 | else if (output_x + output_width == old_right_edge) | |||
| 828 | output_x = x + width - output_width; | |||
| 829 | ||||
| 830 | ||||
| 831 | if (output_y >= old_bottom_edge) | |||
| 832 | output_y += dy; | |||
| 833 | else if (output_y + output_height == old_bottom_edge) | |||
| 834 | output_y = y + height - output_height; | |||
| 835 | ||||
| 836 | cafe_rr_output_info_set_geometry (outputs[i], output_x, output_y, output_width, output_height); | |||
| 837 | } | |||
| 838 | } | |||
| 839 | ||||
| 840 | static void | |||
| 841 | on_resolution_changed (CtkComboBox *box, gpointer data) | |||
| 842 | { | |||
| 843 | App *app = data; | |||
| 844 | int old_width, old_height; | |||
| 845 | int x, y; | |||
| 846 | int width; | |||
| 847 | int height; | |||
| 848 | ||||
| 849 | if (!app->current_output) | |||
| 850 | return; | |||
| 851 | ||||
| 852 | cafe_rr_output_info_get_geometry (app->current_output, &x, &y, &old_width, &old_height); | |||
| 853 | ||||
| 854 | if (get_mode (app->resolution_combo, &width, &height, NULL((void*)0), NULL((void*)0))) | |||
| 855 | { | |||
| 856 | cafe_rr_output_info_set_geometry (app->current_output, x, y, width, height); | |||
| 857 | ||||
| 858 | if (width == 0 || height == 0) | |||
| 859 | cafe_rr_output_info_set_active (app->current_output, FALSE(0)); | |||
| 860 | else | |||
| 861 | cafe_rr_output_info_set_active (app->current_output, TRUE(!(0))); | |||
| 862 | } | |||
| 863 | ||||
| 864 | realign_outputs_after_resolution_change (app, app->current_output, old_width, old_height); | |||
| 865 | ||||
| 866 | rebuild_rate_combo (app); | |||
| 867 | rebuild_rotation_combo (app); | |||
| 868 | ||||
| 869 | foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ()))))))); | |||
| 870 | } | |||
| 871 | ||||
| 872 | static void | |||
| 873 | lay_out_outputs_horizontally (App *app) | |||
| 874 | { | |||
| 875 | int i; | |||
| 876 | int x; | |||
| 877 | CafeRROutputInfo **outputs; | |||
| 878 | ||||
| 879 | /* Lay out all the monitors horizontally when "mirror screens" is turned | |||
| 880 | * off, to avoid having all of them overlapped initially. We put the | |||
| 881 | * outputs turned off on the right-hand side. | |||
| 882 | */ | |||
| 883 | ||||
| 884 | x = 0; | |||
| 885 | ||||
| 886 | /* First pass, all "on" outputs */ | |||
| 887 | outputs = cafe_rr_config_get_outputs (app->current_configuration); | |||
| 888 | ||||
| 889 | for (i = 0; outputs[i]; ++i) | |||
| 890 | { | |||
| 891 | int width, height; | |||
| 892 | if (cafe_rr_output_info_is_connected (outputs[i]) &&cafe_rr_output_info_is_active (outputs[i])) | |||
| 893 | { | |||
| 894 | cafe_rr_output_info_get_geometry (outputs[i], NULL((void*)0), NULL((void*)0), &width, &height); | |||
| 895 | cafe_rr_output_info_set_geometry (outputs[i], x, 0, width, height); | |||
| 896 | x += width; | |||
| 897 | } | |||
| 898 | } | |||
| 899 | ||||
| 900 | /* Second pass, all the black screens */ | |||
| 901 | ||||
| 902 | for (i = 0; outputs[i]; ++i) | |||
| 903 | { | |||
| 904 | int width, height; | |||
| 905 | if (!(cafe_rr_output_info_is_connected (outputs[i]) && cafe_rr_output_info_is_active (outputs[i]))) | |||
| 906 | { | |||
| 907 | cafe_rr_output_info_get_geometry (outputs[i], NULL((void*)0), NULL((void*)0), &width, &height); | |||
| 908 | cafe_rr_output_info_set_geometry (outputs[i], x, 0, width, height); | |||
| 909 | x += width; | |||
| 910 | } | |||
| 911 | } | |||
| 912 | ||||
| 913 | } | |||
| 914 | ||||
| 915 | /* FIXME: this function is copied from cafe-settings-daemon/plugins/xrandr/gsd-xrandr-manager.c. | |||
| 916 | * Do we need to put this function in cafe-desktop for public use? | |||
| 917 | */ | |||
| 918 | static gboolean | |||
| 919 | get_clone_size (CafeRRScreen *screen, int *width, int *height) | |||
| 920 | { | |||
| 921 | CafeRRMode **modes = cafe_rr_screen_list_clone_modes (screen); | |||
| 922 | int best_w, best_h; | |||
| 923 | int i; | |||
| 924 | ||||
| 925 | best_w = 0; | |||
| 926 | best_h = 0; | |||
| 927 | ||||
| 928 | for (i = 0; modes[i] != NULL((void*)0); ++i) { | |||
| 929 | CafeRRMode *mode = modes[i]; | |||
| 930 | int w, h; | |||
| 931 | ||||
| 932 | w = cafe_rr_mode_get_width (mode); | |||
| 933 | h = cafe_rr_mode_get_height (mode); | |||
| 934 | ||||
| 935 | if (w * h > best_w * best_h) { | |||
| 936 | best_w = w; | |||
| 937 | best_h = h; | |||
| 938 | } | |||
| 939 | } | |||
| 940 | ||||
| 941 | if (best_w
| |||
| 942 | if (width) | |||
| 943 | *width = best_w; | |||
| 944 | if (height) | |||
| 945 | *height = best_h; | |||
| 946 | ||||
| 947 | return TRUE(!(0)); | |||
| 948 | } | |||
| 949 | ||||
| 950 | return FALSE(0); | |||
| 951 | } | |||
| 952 | ||||
| 953 | static gboolean | |||
| 954 | output_info_supports_mode (App *app, CafeRROutputInfo *info, int width, int height) | |||
| 955 | { | |||
| 956 | CafeRROutput *output; | |||
| 957 | CafeRRMode **modes; | |||
| 958 | int i; | |||
| 959 | ||||
| 960 | if (!cafe_rr_output_info_is_connected (info)) | |||
| 961 | return FALSE(0); | |||
| 962 | ||||
| 963 | output = cafe_rr_screen_get_output_by_name (app->screen, cafe_rr_output_info_get_name (info)); | |||
| 964 | if (!output) | |||
| 965 | return FALSE(0); | |||
| 966 | ||||
| 967 | modes = cafe_rr_output_list_modes (output); | |||
| 968 | ||||
| 969 | for (i = 0; modes[i]; i++) { | |||
| 970 | if (cafe_rr_mode_get_width (modes[i]) == width | |||
| 971 | && cafe_rr_mode_get_height (modes[i]) == height) | |||
| 972 | return TRUE(!(0)); | |||
| 973 | } | |||
| 974 | ||||
| 975 | return FALSE(0); | |||
| 976 | } | |||
| 977 | ||||
| 978 | static void | |||
| 979 | on_clone_changed (CtkWidget *box, gpointer data) | |||
| 980 | { | |||
| 981 | App *app = data; | |||
| 982 | ||||
| 983 | cafe_rr_config_set_clone (app->current_configuration, ctk_toggle_button_get_active (CTK_TOGGLE_BUTTON (app->clone_checkbox)((((CtkToggleButton*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->clone_checkbox)), ((ctk_toggle_button_get_type ( ))))))))); | |||
| 984 | ||||
| 985 | if (cafe_rr_config_get_clone (app->current_configuration)) | |||
| ||||
| 986 | { | |||
| 987 | int i; | |||
| 988 | int width, height; | |||
| 989 | CafeRROutputInfo **outputs = cafe_rr_config_get_outputs (app->current_configuration); | |||
| 990 | ||||
| 991 | for (i = 0; outputs[i]; ++i) | |||
| 992 | { | |||
| 993 | if (cafe_rr_output_info_is_connected(outputs[i])) | |||
| 994 | { | |||
| 995 | app->current_output = outputs[i]; | |||
| 996 | break; | |||
| 997 | } | |||
| 998 | } | |||
| 999 | ||||
| 1000 | /* Turn on all the connected screens that support the best clone mode. | |||
| 1001 | * The user may hit "Mirror Screens", but he shouldn't have to turn on | |||
| 1002 | * all the required outputs as well. | |||
| 1003 | */ | |||
| 1004 | ||||
| 1005 | get_clone_size (app->screen, &width, &height); | |||
| 1006 | ||||
| 1007 | for (i = 0; outputs[i]; i++) { | |||
| 1008 | int x, y; | |||
| 1009 | if (output_info_supports_mode (app, outputs[i], width, height)) { | |||
| ||||
| 1010 | cafe_rr_output_info_set_active (outputs[i], TRUE(!(0))); | |||
| 1011 | cafe_rr_output_info_get_geometry (outputs[i], &x, &y, NULL((void*)0), NULL((void*)0)); | |||
| 1012 | cafe_rr_output_info_set_geometry (outputs[i], x, y, width, height); | |||
| 1013 | } | |||
| 1014 | } | |||
| 1015 | } | |||
| 1016 | else | |||
| 1017 | { | |||
| 1018 | if (output_overlaps (app->current_output, app->current_configuration)) | |||
| 1019 | lay_out_outputs_horizontally (app); | |||
| 1020 | } | |||
| 1021 | ||||
| 1022 | rebuild_gui (app); | |||
| 1023 | } | |||
| 1024 | ||||
| 1025 | static void | |||
| 1026 | get_geometry (CafeRROutputInfo *output, int *w, int *h) | |||
| 1027 | { | |||
| 1028 | CafeRRRotation rotation; | |||
| 1029 | ||||
| 1030 | if (cafe_rr_output_info_is_active (output)) | |||
| 1031 | { | |||
| 1032 | cafe_rr_output_info_get_geometry (output, NULL((void*)0), NULL((void*)0), w, h); | |||
| 1033 | } | |||
| 1034 | else | |||
| 1035 | { | |||
| 1036 | *h = cafe_rr_output_info_get_preferred_height (output); | |||
| 1037 | *w = cafe_rr_output_info_get_preferred_width (output); | |||
| 1038 | } | |||
| 1039 | rotation = cafe_rr_output_info_get_rotation (output); | |||
| 1040 | if ((rotation & CAFE_RR_ROTATION_90) || (rotation & CAFE_RR_ROTATION_270)) | |||
| 1041 | { | |||
| 1042 | int tmp; | |||
| 1043 | tmp = *h; | |||
| 1044 | *h = *w; | |||
| 1045 | *w = tmp; | |||
| 1046 | } | |||
| 1047 | } | |||
| 1048 | ||||
| 1049 | #define SPACE15 15 | |||
| 1050 | #define MARGIN15 15 | |||
| 1051 | ||||
| 1052 | static GList * | |||
| 1053 | list_connected_outputs (App *app, int *total_w, int *total_h) | |||
| 1054 | { | |||
| 1055 | int i, dummy; | |||
| 1056 | GList *result = NULL((void*)0); | |||
| 1057 | CafeRROutputInfo **outputs; | |||
| 1058 | ||||
| 1059 | if (!total_w) | |||
| 1060 | total_w = &dummy; | |||
| 1061 | if (!total_h) | |||
| 1062 | total_h = &dummy; | |||
| 1063 | ||||
| 1064 | *total_w = 0; | |||
| 1065 | *total_h = 0; | |||
| 1066 | ||||
| 1067 | outputs = cafe_rr_config_get_outputs(app->current_configuration); | |||
| 1068 | for (i = 0; outputs[i] != NULL((void*)0); ++i) | |||
| 1069 | { | |||
| 1070 | if (cafe_rr_output_info_is_connected (outputs[i])) | |||
| 1071 | { | |||
| 1072 | int w, h; | |||
| 1073 | ||||
| 1074 | result = g_list_prepend (result, outputs[i]); | |||
| 1075 | ||||
| 1076 | get_geometry (outputs[i], &w, &h); | |||
| 1077 | ||||
| 1078 | *total_w += w; | |||
| 1079 | *total_h += h; | |||
| 1080 | } | |||
| 1081 | } | |||
| 1082 | ||||
| 1083 | return g_list_reverse (result); | |||
| 1084 | } | |||
| 1085 | ||||
| 1086 | static int | |||
| 1087 | get_n_connected (App *app) | |||
| 1088 | { | |||
| 1089 | GList *connected_outputs = list_connected_outputs (app, NULL((void*)0), NULL((void*)0)); | |||
| 1090 | int n = g_list_length (connected_outputs); | |||
| 1091 | ||||
| 1092 | g_list_free (connected_outputs); | |||
| 1093 | ||||
| 1094 | return n; | |||
| 1095 | } | |||
| 1096 | ||||
| 1097 | static double | |||
| 1098 | compute_scale (App *app) | |||
| 1099 | { | |||
| 1100 | int available_w, available_h; | |||
| 1101 | int total_w, total_h; | |||
| 1102 | int n_monitors; | |||
| 1103 | CdkRectangle viewport; | |||
| 1104 | GList *connected_outputs; | |||
| 1105 | ||||
| 1106 | foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ())))))), &viewport); | |||
| 1107 | ||||
| 1108 | connected_outputs = list_connected_outputs (app, &total_w, &total_h); | |||
| 1109 | ||||
| 1110 | n_monitors = g_list_length (connected_outputs); | |||
| 1111 | ||||
| 1112 | g_list_free (connected_outputs); | |||
| 1113 | ||||
| 1114 | available_w = viewport.width - 2 * MARGIN15 - (n_monitors - 1) * SPACE15; | |||
| 1115 | available_h = viewport.height - 2 * MARGIN15 - (n_monitors - 1) * SPACE15; | |||
| 1116 | ||||
| 1117 | return MIN ((double)available_w / total_w, (double)available_h / total_h)((((double)available_w / total_w) < ((double)available_h / total_h)) ? ((double)available_w / total_w) : ((double)available_h / total_h)); | |||
| 1118 | } | |||
| 1119 | ||||
| 1120 | typedef struct Edge | |||
| 1121 | { | |||
| 1122 | CafeRROutputInfo *output; | |||
| 1123 | int x1, y1; | |||
| 1124 | int x2, y2; | |||
| 1125 | } Edge; | |||
| 1126 | ||||
| 1127 | typedef struct Snap | |||
| 1128 | { | |||
| 1129 | Edge *snapper; /* Edge that should be snapped */ | |||
| 1130 | Edge *snappee; | |||
| 1131 | int dy, dx; | |||
| 1132 | } Snap; | |||
| 1133 | ||||
| 1134 | static void | |||
| 1135 | add_edge (CafeRROutputInfo *output, int x1, int y1, int x2, int y2, GArray *edges) | |||
| 1136 | { | |||
| 1137 | Edge e; | |||
| 1138 | ||||
| 1139 | e.x1 = x1; | |||
| 1140 | e.x2 = x2; | |||
| 1141 | e.y1 = y1; | |||
| 1142 | e.y2 = y2; | |||
| 1143 | e.output = output; | |||
| 1144 | ||||
| 1145 | g_array_append_val (edges, e)g_array_append_vals (edges, &(e), 1); | |||
| 1146 | } | |||
| 1147 | ||||
| 1148 | static void | |||
| 1149 | list_edges_for_output (CafeRROutputInfo *output, GArray *edges) | |||
| 1150 | { | |||
| 1151 | int x, y, w, h; | |||
| 1152 | ||||
| 1153 | cafe_rr_output_info_get_geometry (output, &x, &y, &w, &h); | |||
| 1154 | get_geometry(output, &w, &h); // accounts for rotation | |||
| 1155 | ||||
| 1156 | /* Top, Bottom, Left, Right */ | |||
| 1157 | add_edge (output, x, y, x + w, y, edges); | |||
| 1158 | add_edge (output, x, y + h, x + w, y + h, edges); | |||
| 1159 | add_edge (output, x, y, x, y + h, edges); | |||
| 1160 | add_edge (output, x + w, y, x + w, y + h, edges); | |||
| 1161 | } | |||
| 1162 | ||||
| 1163 | static void | |||
| 1164 | list_edges (CafeRRConfig *config, GArray *edges) | |||
| 1165 | { | |||
| 1166 | int i; | |||
| 1167 | CafeRROutputInfo **outputs = cafe_rr_config_get_outputs (config); | |||
| 1168 | ||||
| 1169 | for (i = 0; outputs[i]; ++i) | |||
| 1170 | { | |||
| 1171 | if (cafe_rr_output_info_is_connected (outputs[i])) | |||
| 1172 | list_edges_for_output (outputs[i], edges); | |||
| 1173 | } | |||
| 1174 | } | |||
| 1175 | ||||
| 1176 | static gboolean | |||
| 1177 | overlap (int s1, int e1, int s2, int e2) | |||
| 1178 | { | |||
| 1179 | return (!(e1 < s2 || s1 >= e2)); | |||
| 1180 | } | |||
| 1181 | ||||
| 1182 | static gboolean | |||
| 1183 | horizontal_overlap (Edge *snapper, Edge *snappee) | |||
| 1184 | { | |||
| 1185 | if (snapper->y1 != snapper->y2 || snappee->y1 != snappee->y2) | |||
| 1186 | return FALSE(0); | |||
| 1187 | ||||
| 1188 | return overlap (snapper->x1, snapper->x2, snappee->x1, snappee->x2); | |||
| 1189 | } | |||
| 1190 | ||||
| 1191 | static gboolean | |||
| 1192 | vertical_overlap (Edge *snapper, Edge *snappee) | |||
| 1193 | { | |||
| 1194 | if (snapper->x1 != snapper->x2 || snappee->x1 != snappee->x2) | |||
| 1195 | return FALSE(0); | |||
| 1196 | ||||
| 1197 | return overlap (snapper->y1, snapper->y2, snappee->y1, snappee->y2); | |||
| 1198 | } | |||
| 1199 | ||||
| 1200 | static void | |||
| 1201 | add_snap (GArray *snaps, Snap snap) | |||
| 1202 | { | |||
| 1203 | if (ABS (snap.dx)(((snap.dx) < 0) ? -(snap.dx) : (snap.dx)) <= 200 || ABS (snap.dy)(((snap.dy) < 0) ? -(snap.dy) : (snap.dy)) <= 200) | |||
| 1204 | g_array_append_val (snaps, snap)g_array_append_vals (snaps, &(snap), 1); | |||
| 1205 | } | |||
| 1206 | ||||
| 1207 | static void | |||
| 1208 | add_edge_snaps (Edge *snapper, Edge *snappee, GArray *snaps) | |||
| 1209 | { | |||
| 1210 | Snap snap; | |||
| 1211 | ||||
| 1212 | snap.snapper = snapper; | |||
| 1213 | snap.snappee = snappee; | |||
| 1214 | ||||
| 1215 | if (horizontal_overlap (snapper, snappee)) | |||
| 1216 | { | |||
| 1217 | snap.dx = 0; | |||
| 1218 | snap.dy = snappee->y1 - snapper->y1; | |||
| 1219 | ||||
| 1220 | add_snap (snaps, snap); | |||
| 1221 | } | |||
| 1222 | else if (vertical_overlap (snapper, snappee)) | |||
| 1223 | { | |||
| 1224 | snap.dy = 0; | |||
| 1225 | snap.dx = snappee->x1 - snapper->x1; | |||
| 1226 | ||||
| 1227 | add_snap (snaps, snap); | |||
| 1228 | } | |||
| 1229 | ||||
| 1230 | /* Corner snaps */ | |||
| 1231 | /* 1->1 */ | |||
| 1232 | snap.dx = snappee->x1 - snapper->x1; | |||
| 1233 | snap.dy = snappee->y1 - snapper->y1; | |||
| 1234 | ||||
| 1235 | add_snap (snaps, snap); | |||
| 1236 | ||||
| 1237 | /* 1->2 */ | |||
| 1238 | snap.dx = snappee->x2 - snapper->x1; | |||
| 1239 | snap.dy = snappee->y2 - snapper->y1; | |||
| 1240 | ||||
| 1241 | add_snap (snaps, snap); | |||
| 1242 | ||||
| 1243 | /* 2->2 */ | |||
| 1244 | snap.dx = snappee->x2 - snapper->x2; | |||
| 1245 | snap.dy = snappee->y2 - snapper->y2; | |||
| 1246 | ||||
| 1247 | add_snap (snaps, snap); | |||
| 1248 | ||||
| 1249 | /* 2->1 */ | |||
| 1250 | snap.dx = snappee->x1 - snapper->x2; | |||
| 1251 | snap.dy = snappee->y1 - snapper->y2; | |||
| 1252 | ||||
| 1253 | add_snap (snaps, snap); | |||
| 1254 | } | |||
| 1255 | ||||
| 1256 | static void | |||
| 1257 | list_snaps (CafeRROutputInfo *output, GArray *edges, GArray *snaps) | |||
| 1258 | { | |||
| 1259 | int i; | |||
| 1260 | ||||
| 1261 | for (i = 0; i < edges->len; ++i) | |||
| 1262 | { | |||
| 1263 | Edge *output_edge = &(g_array_index (edges, Edge, i)(((Edge*) (void *) (edges)->data) [(i)])); | |||
| 1264 | ||||
| 1265 | if (output_edge->output == output) | |||
| 1266 | { | |||
| 1267 | int j; | |||
| 1268 | ||||
| 1269 | for (j = 0; j < edges->len; ++j) | |||
| 1270 | { | |||
| 1271 | Edge *edge = &(g_array_index (edges, Edge, j)(((Edge*) (void *) (edges)->data) [(j)])); | |||
| 1272 | ||||
| 1273 | if (edge->output != output) | |||
| 1274 | add_edge_snaps (output_edge, edge, snaps); | |||
| 1275 | } | |||
| 1276 | } | |||
| 1277 | } | |||
| 1278 | } | |||
| 1279 | ||||
| 1280 | #if 0 | |||
| 1281 | static void | |||
| 1282 | print_edge (Edge *edge) | |||
| 1283 | { | |||
| 1284 | g_debug ("(%d %d %d %d)", edge->x1, edge->y1, edge->x2, edge->y2); | |||
| 1285 | } | |||
| 1286 | #endif | |||
| 1287 | ||||
| 1288 | static gboolean | |||
| 1289 | corner_on_edge (int x, int y, Edge *e) | |||
| 1290 | { | |||
| 1291 | if (x == e->x1 && x == e->x2 && y >= e->y1 && y <= e->y2) | |||
| 1292 | return TRUE(!(0)); | |||
| 1293 | ||||
| 1294 | if (y == e->y1 && y == e->y2 && x >= e->x1 && x <= e->x2) | |||
| 1295 | return TRUE(!(0)); | |||
| 1296 | ||||
| 1297 | return FALSE(0); | |||
| 1298 | } | |||
| 1299 | ||||
| 1300 | static gboolean | |||
| 1301 | edges_align (Edge *e1, Edge *e2) | |||
| 1302 | { | |||
| 1303 | if (corner_on_edge (e1->x1, e1->y1, e2)) | |||
| 1304 | return TRUE(!(0)); | |||
| 1305 | ||||
| 1306 | if (corner_on_edge (e2->x1, e2->y1, e1)) | |||
| 1307 | return TRUE(!(0)); | |||
| 1308 | ||||
| 1309 | return FALSE(0); | |||
| 1310 | } | |||
| 1311 | ||||
| 1312 | static gboolean | |||
| 1313 | output_is_aligned (CafeRROutputInfo *output, GArray *edges) | |||
| 1314 | { | |||
| 1315 | gboolean result = FALSE(0); | |||
| 1316 | int i; | |||
| 1317 | ||||
| 1318 | for (i = 0; i < edges->len; ++i) | |||
| 1319 | { | |||
| 1320 | Edge *output_edge = &(g_array_index (edges, Edge, i)(((Edge*) (void *) (edges)->data) [(i)])); | |||
| 1321 | ||||
| 1322 | if (output_edge->output == output) | |||
| 1323 | { | |||
| 1324 | int j; | |||
| 1325 | ||||
| 1326 | for (j = 0; j < edges->len; ++j) | |||
| 1327 | { | |||
| 1328 | Edge *edge = &(g_array_index (edges, Edge, j)(((Edge*) (void *) (edges)->data) [(j)])); | |||
| 1329 | ||||
| 1330 | /* We are aligned if an output edge matches | |||
| 1331 | * an edge of another output | |||
| 1332 | */ | |||
| 1333 | if (edge->output != output_edge->output) | |||
| 1334 | { | |||
| 1335 | if (edges_align (output_edge, edge)) | |||
| 1336 | { | |||
| 1337 | result = TRUE(!(0)); | |||
| 1338 | goto done; | |||
| 1339 | } | |||
| 1340 | } | |||
| 1341 | } | |||
| 1342 | } | |||
| 1343 | } | |||
| 1344 | done: | |||
| 1345 | ||||
| 1346 | return result; | |||
| 1347 | } | |||
| 1348 | ||||
| 1349 | static void | |||
| 1350 | get_output_rect (CafeRROutputInfo *output, CdkRectangle *rect) | |||
| 1351 | { | |||
| 1352 | cafe_rr_output_info_get_geometry (output, &rect->x, &rect->y, &rect->width, &rect->height); | |||
| 1353 | get_geometry (output, &rect->width, &rect->height); // accounts for rotation | |||
| 1354 | } | |||
| 1355 | ||||
| 1356 | static gboolean | |||
| 1357 | output_overlaps (CafeRROutputInfo *output, CafeRRConfig *config) | |||
| 1358 | { | |||
| 1359 | int i; | |||
| 1360 | CdkRectangle output_rect; | |||
| 1361 | CafeRROutputInfo **outputs; | |||
| 1362 | ||||
| 1363 | get_output_rect (output, &output_rect); | |||
| 1364 | ||||
| 1365 | outputs = cafe_rr_config_get_outputs (config); | |||
| 1366 | for (i = 0; outputs[i]; ++i) | |||
| 1367 | { | |||
| 1368 | if (outputs[i] != output && cafe_rr_output_info_is_connected (outputs[i])) | |||
| 1369 | { | |||
| 1370 | CdkRectangle other_rect; | |||
| 1371 | ||||
| 1372 | get_output_rect (outputs[i], &other_rect); | |||
| 1373 | if (cdk_rectangle_intersect (&output_rect, &other_rect, NULL((void*)0))) | |||
| 1374 | return TRUE(!(0)); | |||
| 1375 | } | |||
| 1376 | } | |||
| 1377 | ||||
| 1378 | return FALSE(0); | |||
| 1379 | } | |||
| 1380 | ||||
| 1381 | static gboolean | |||
| 1382 | cafe_rr_config_is_aligned (CafeRRConfig *config, GArray *edges) | |||
| 1383 | { | |||
| 1384 | int i; | |||
| 1385 | gboolean result = TRUE(!(0)); | |||
| 1386 | CafeRROutputInfo **outputs; | |||
| 1387 | ||||
| 1388 | outputs = cafe_rr_config_get_outputs(config); | |||
| 1389 | for (i = 0; outputs[i]; ++i) | |||
| 1390 | { | |||
| 1391 | if (cafe_rr_output_info_is_connected (outputs[i])) | |||
| 1392 | { | |||
| 1393 | if (!output_is_aligned (outputs[i], edges)) | |||
| 1394 | return FALSE(0); | |||
| 1395 | ||||
| 1396 | if (output_overlaps (outputs[i], config)) | |||
| 1397 | return FALSE(0); | |||
| 1398 | } | |||
| 1399 | } | |||
| 1400 | ||||
| 1401 | return result; | |||
| 1402 | } | |||
| 1403 | ||||
| 1404 | struct GrabInfo | |||
| 1405 | { | |||
| 1406 | int grab_x; | |||
| 1407 | int grab_y; | |||
| 1408 | int output_x; | |||
| 1409 | int output_y; | |||
| 1410 | }; | |||
| 1411 | ||||
| 1412 | static gboolean | |||
| 1413 | is_corner_snap (const Snap *s) | |||
| 1414 | { | |||
| 1415 | return s->dx != 0 && s->dy != 0; | |||
| 1416 | } | |||
| 1417 | ||||
| 1418 | static int | |||
| 1419 | compare_snaps (gconstpointer v1, gconstpointer v2) | |||
| 1420 | { | |||
| 1421 | const Snap *s1 = v1; | |||
| 1422 | const Snap *s2 = v2; | |||
| 1423 | int sv1 = MAX (ABS (s1->dx), ABS (s1->dy))((((((s1->dx) < 0) ? -(s1->dx) : (s1->dx))) > ( (((s1->dy) < 0) ? -(s1->dy) : (s1->dy)))) ? ((((s1 ->dx) < 0) ? -(s1->dx) : (s1->dx))) : ((((s1-> dy) < 0) ? -(s1->dy) : (s1->dy)))); | |||
| 1424 | int sv2 = MAX (ABS (s2->dx), ABS (s2->dy))((((((s2->dx) < 0) ? -(s2->dx) : (s2->dx))) > ( (((s2->dy) < 0) ? -(s2->dy) : (s2->dy)))) ? ((((s2 ->dx) < 0) ? -(s2->dx) : (s2->dx))) : ((((s2-> dy) < 0) ? -(s2->dy) : (s2->dy)))); | |||
| 1425 | int d; | |||
| 1426 | ||||
| 1427 | d = sv1 - sv2; | |||
| 1428 | ||||
| 1429 | /* This snapping algorithm is good enough for rock'n'roll, but | |||
| 1430 | * this is probably a better: | |||
| 1431 | * | |||
| 1432 | * First do a horizontal/vertical snap, then | |||
| 1433 | * with the new coordinates from that snap, | |||
| 1434 | * do a corner snap. | |||
| 1435 | * | |||
| 1436 | * Right now, it's confusing that corner snapping | |||
| 1437 | * depends on the distance in an axis that you can't actually see. | |||
| 1438 | * | |||
| 1439 | */ | |||
| 1440 | if (d == 0) | |||
| 1441 | { | |||
| 1442 | if (is_corner_snap (s1) && !is_corner_snap (s2)) | |||
| 1443 | return -1; | |||
| 1444 | else if (is_corner_snap (s2) && !is_corner_snap (s1)) | |||
| 1445 | return 1; | |||
| 1446 | else | |||
| 1447 | return 0; | |||
| 1448 | } | |||
| 1449 | else | |||
| 1450 | { | |||
| 1451 | return d; | |||
| 1452 | } | |||
| 1453 | } | |||
| 1454 | ||||
| 1455 | /* Sets a mouse cursor for a widget's window. As a hack, you can pass | |||
| 1456 | * CDK_BLANK_CURSOR to mean "set the cursor to NULL" (i.e. reset the widget's | |||
| 1457 | * window's cursor to its default). | |||
| 1458 | */ | |||
| 1459 | static void | |||
| 1460 | set_cursor (CtkWidget *widget, CdkCursorType type) | |||
| 1461 | { | |||
| 1462 | CdkCursor *cursor; | |||
| 1463 | CdkWindow *window; | |||
| 1464 | ||||
| 1465 | if (type == CDK_BLANK_CURSOR) | |||
| 1466 | cursor = NULL((void*)0); | |||
| 1467 | else | |||
| 1468 | cursor = cdk_cursor_new_for_display (ctk_widget_get_display (widget), type); | |||
| 1469 | ||||
| 1470 | window = ctk_widget_get_window (widget); | |||
| 1471 | ||||
| 1472 | if (window) | |||
| 1473 | cdk_window_set_cursor (window, cursor); | |||
| 1474 | ||||
| 1475 | if (cursor) | |||
| 1476 | g_object_unref (cursor); | |||
| 1477 | } | |||
| 1478 | ||||
| 1479 | static void | |||
| 1480 | set_monitors_tooltip (App *app, gboolean is_dragging) | |||
| 1481 | { | |||
| 1482 | const char *text; | |||
| 1483 | ||||
| 1484 | if (is_dragging) | |||
| 1485 | text = NULL((void*)0); | |||
| 1486 | else | |||
| 1487 | text = _("Select a monitor to change its properties; drag it to rearrange its placement.")gettext ("Select a monitor to change its properties; drag it to rearrange its placement." ); | |||
| 1488 | ||||
| 1489 | ctk_widget_set_tooltip_text (app->area, text); | |||
| 1490 | } | |||
| 1491 | ||||
| 1492 | static void | |||
| 1493 | on_output_event (FooScrollArea *area, | |||
| 1494 | FooScrollAreaEvent *event, | |||
| 1495 | gpointer data) | |||
| 1496 | { | |||
| 1497 | CafeRROutputInfo *output = data; | |||
| 1498 | App *app = g_object_get_data (G_OBJECT (area)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((area)), (((GType) ((20) << (2)))))))), "app"); | |||
| 1499 | ||||
| 1500 | /* If the mouse is inside the outputs, set the cursor to "you can move me". See | |||
| 1501 | * on_canvas_event() for where we reset the cursor to the default if it | |||
| 1502 | * exits the outputs' area. | |||
| 1503 | */ | |||
| 1504 | if (!cafe_rr_config_get_clone (app->current_configuration) && get_n_connected (app) > 1) | |||
| 1505 | set_cursor (CTK_WIDGET (area)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((area)), ((ctk_widget_get_type ())))))), CDK_FLEUR); | |||
| 1506 | ||||
| 1507 | if (event->type == FOO_BUTTON_PRESS) | |||
| 1508 | { | |||
| 1509 | GrabInfo *info; | |||
| 1510 | ||||
| 1511 | app->current_output = output; | |||
| 1512 | ||||
| 1513 | rebuild_gui (app); | |||
| 1514 | set_monitors_tooltip (app, TRUE(!(0))); | |||
| 1515 | ||||
| 1516 | if (!cafe_rr_config_get_clone (app->current_configuration) && get_n_connected (app) > 1) | |||
| 1517 | { | |||
| 1518 | int output_x, output_y; | |||
| 1519 | cafe_rr_output_info_get_geometry (output, &output_x, &output_y, NULL((void*)0), NULL((void*)0)); | |||
| 1520 | ||||
| 1521 | foo_scroll_area_begin_grab (area, on_output_event, data); | |||
| 1522 | ||||
| 1523 | info = g_new0 (GrabInfo, 1)((GrabInfo *) g_malloc0_n ((1), sizeof (GrabInfo))); | |||
| 1524 | info->grab_x = event->x; | |||
| 1525 | info->grab_y = event->y; | |||
| 1526 | info->output_x = output_x; | |||
| 1527 | info->output_y = output_y; | |||
| 1528 | ||||
| 1529 | g_object_set_data (G_OBJECT (output)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((output)), (((GType) ((20) << (2)))))))), "grab-info", info); | |||
| 1530 | } | |||
| 1531 | ||||
| 1532 | foo_scroll_area_invalidate (area); | |||
| 1533 | } | |||
| 1534 | else | |||
| 1535 | { | |||
| 1536 | if (foo_scroll_area_is_grabbed (area)) | |||
| 1537 | { | |||
| 1538 | GrabInfo *info = g_object_get_data (G_OBJECT (output)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((output)), (((GType) ((20) << (2)))))))), "grab-info"); | |||
| 1539 | double scale = compute_scale (app); | |||
| 1540 | int old_x, old_y; | |||
| 1541 | int width, height; | |||
| 1542 | int new_x, new_y; | |||
| 1543 | int i; | |||
| 1544 | GArray *edges, *snaps, *new_edges; | |||
| 1545 | ||||
| 1546 | cafe_rr_output_info_get_geometry (output, &old_x, &old_y, &width, &height); | |||
| 1547 | new_x = info->output_x + (event->x - info->grab_x) / scale; | |||
| 1548 | new_y = info->output_y + (event->y - info->grab_y) / scale; | |||
| 1549 | ||||
| 1550 | cafe_rr_output_info_set_geometry (output, new_x, new_y, width, height); | |||
| 1551 | ||||
| 1552 | edges = g_array_new (TRUE(!(0)), TRUE(!(0)), sizeof (Edge)); | |||
| 1553 | snaps = g_array_new (TRUE(!(0)), TRUE(!(0)), sizeof (Snap)); | |||
| 1554 | new_edges = g_array_new (TRUE(!(0)), TRUE(!(0)), sizeof (Edge)); | |||
| 1555 | ||||
| 1556 | list_edges (app->current_configuration, edges); | |||
| 1557 | list_snaps (output, edges, snaps); | |||
| 1558 | ||||
| 1559 | g_array_sort (snaps, compare_snaps); | |||
| 1560 | ||||
| 1561 | cafe_rr_output_info_set_geometry (output, new_x, new_y, width, height); | |||
| 1562 | ||||
| 1563 | for (i = 0; i < snaps->len; ++i) | |||
| 1564 | { | |||
| 1565 | Snap *snap = &(g_array_index (snaps, Snap, i)(((Snap*) (void *) (snaps)->data) [(i)])); | |||
| 1566 | GArray *new_edges = g_array_new (TRUE(!(0)), TRUE(!(0)), sizeof (Edge)); | |||
| 1567 | ||||
| 1568 | cafe_rr_output_info_set_geometry (output, new_x + snap->dx, new_y + snap->dy, width, height); | |||
| 1569 | ||||
| 1570 | g_array_set_size (new_edges, 0); | |||
| 1571 | list_edges (app->current_configuration, new_edges); | |||
| 1572 | ||||
| 1573 | if (cafe_rr_config_is_aligned (app->current_configuration, new_edges)) | |||
| 1574 | { | |||
| 1575 | g_array_free (new_edges, TRUE(!(0))); | |||
| 1576 | break; | |||
| 1577 | } | |||
| 1578 | else | |||
| 1579 | { | |||
| 1580 | cafe_rr_output_info_set_geometry (output, info->output_x, info->output_y, width, height); | |||
| 1581 | } | |||
| 1582 | } | |||
| 1583 | ||||
| 1584 | g_array_free (new_edges, TRUE(!(0))); | |||
| 1585 | g_array_free (snaps, TRUE(!(0))); | |||
| 1586 | g_array_free (edges, TRUE(!(0))); | |||
| 1587 | ||||
| 1588 | if (event->type == FOO_BUTTON_RELEASE) | |||
| 1589 | { | |||
| 1590 | foo_scroll_area_end_grab (area); | |||
| 1591 | set_monitors_tooltip (app, FALSE(0)); | |||
| 1592 | ||||
| 1593 | g_free (g_object_get_data (G_OBJECT (output)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((output)), (((GType) ((20) << (2)))))))), "grab-info")); | |||
| 1594 | g_object_set_data (G_OBJECT (output)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((output)), (((GType) ((20) << (2)))))))), "grab-info", NULL((void*)0)); | |||
| 1595 | ||||
| 1596 | #if 0 | |||
| 1597 | g_debug ("new position: %d %d %d %d", output->x, output->y, output->width, output->height); | |||
| 1598 | #endif | |||
| 1599 | } | |||
| 1600 | ||||
| 1601 | foo_scroll_area_invalidate (area); | |||
| 1602 | } | |||
| 1603 | } | |||
| 1604 | } | |||
| 1605 | ||||
| 1606 | static void | |||
| 1607 | on_canvas_event (FooScrollArea *area, | |||
| 1608 | FooScrollAreaEvent *event, | |||
| 1609 | gpointer data) | |||
| 1610 | { | |||
| 1611 | /* If the mouse exits the outputs, reset the cursor to the default. See | |||
| 1612 | * on_output_event() for where we set the cursor to the movement cursor if | |||
| 1613 | * it is over one of the outputs. | |||
| 1614 | */ | |||
| 1615 | set_cursor (CTK_WIDGET (area)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((area)), ((ctk_widget_get_type ())))))), CDK_BLANK_CURSOR); | |||
| 1616 | } | |||
| 1617 | ||||
| 1618 | static PangoLayout * | |||
| 1619 | get_display_name (App *app, | |||
| 1620 | CafeRROutputInfo *output) | |||
| 1621 | { | |||
| 1622 | char *text; | |||
| 1623 | PangoLayout * layout; | |||
| 1624 | ||||
| 1625 | if (cafe_rr_config_get_clone (app->current_configuration)) { | |||
| 1626 | /* Translators: this is the feature where what you see on your laptop's | |||
| 1627 | * screen is the same as your external monitor. Here, "Mirror" is being | |||
| 1628 | * used as an adjective, not as a verb. For example, the Spanish | |||
| 1629 | * translation could be "Pantallas en Espejo", *not* "Espejar Pantallas". | |||
| 1630 | */ | |||
| 1631 | text = g_strdup_printf (_("Mirror Screens")gettext ("Mirror Screens")); | |||
| 1632 | } | |||
| 1633 | else { | |||
| 1634 | text = g_strdup_printf ("<b>%s</b>\n<small>%s</small>", cafe_rr_output_info_get_display_name (output), cafe_rr_output_info_get_name (output)); | |||
| 1635 | } | |||
| 1636 | layout = ctk_widget_create_pango_layout (CTK_WIDGET (app->area)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((ctk_widget_get_type ())))))), text); | |||
| 1637 | pango_layout_set_markup (layout, text, -1); | |||
| 1638 | g_free (text); | |||
| 1639 | pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); | |||
| 1640 | return layout; | |||
| 1641 | } | |||
| 1642 | ||||
| 1643 | static void | |||
| 1644 | paint_background (FooScrollArea *area, | |||
| 1645 | cairo_t *cr) | |||
| 1646 | { | |||
| 1647 | CdkRectangle viewport; | |||
| 1648 | CtkWidget *widget; | |||
| 1649 | CtkStyleContext *widget_style; | |||
| 1650 | CdkRGBA *base_color = NULL((void*)0); | |||
| 1651 | CdkRGBA dark_color; | |||
| 1652 | ||||
| 1653 | widget = CTK_WIDGET (area)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((area)), ((ctk_widget_get_type ())))))); | |||
| 1654 | ||||
| 1655 | foo_scroll_area_get_viewport (area, &viewport); | |||
| 1656 | ||||
| 1657 | widget_style = ctk_widget_get_style_context (widget); | |||
| 1658 | ||||
| 1659 | ctk_style_context_save (widget_style); | |||
| 1660 | ctk_style_context_set_state (widget_style, CTK_STATE_FLAG_SELECTED); | |||
| 1661 | ctk_style_context_get (widget_style, | |||
| 1662 | ctk_style_context_get_state (widget_style), | |||
| 1663 | CTK_STYLE_PROPERTY_BACKGROUND_COLOR"background-color", &base_color, | |||
| 1664 | NULL((void*)0)); | |||
| 1665 | ctk_style_context_restore (widget_style); | |||
| 1666 | cdk_cairo_set_source_rgba(cr, base_color); | |||
| 1667 | cdk_rgba_free (base_color); | |||
| 1668 | ||||
| 1669 | cairo_rectangle (cr, | |||
| 1670 | viewport.x, viewport.y, | |||
| 1671 | viewport.width, viewport.height); | |||
| 1672 | ||||
| 1673 | cairo_fill_preserve (cr); | |||
| 1674 | ||||
| 1675 | foo_scroll_area_add_input_from_fill (area, cr, on_canvas_event, NULL((void*)0)); | |||
| 1676 | ||||
| 1677 | ctk_style_context_save (widget_style); | |||
| 1678 | ctk_style_context_set_state (widget_style, CTK_STATE_FLAG_SELECTED); | |||
| 1679 | cafe_desktop_ctk_style_get_dark_color (widget_style, | |||
| 1680 | ctk_style_context_get_state (widget_style), | |||
| 1681 | &dark_color); | |||
| 1682 | ctk_style_context_restore (widget_style); | |||
| 1683 | cdk_cairo_set_source_rgba (cr, &dark_color); | |||
| 1684 | ||||
| 1685 | cairo_stroke (cr); | |||
| 1686 | } | |||
| 1687 | ||||
| 1688 | static void | |||
| 1689 | paint_output (App *app, cairo_t *cr, int i) | |||
| 1690 | { | |||
| 1691 | int w, h; | |||
| 1692 | double scale = compute_scale (app); | |||
| 1693 | double x, y; | |||
| 1694 | int output_x, output_y; | |||
| 1695 | CafeRRRotation rotation; | |||
| 1696 | int total_w, total_h; | |||
| 1697 | GList *connected_outputs = list_connected_outputs (app, &total_w, &total_h); | |||
| 1698 | CafeRROutputInfo *output = g_list_nth_data (connected_outputs, i); | |||
| 1699 | PangoLayout *layout = get_display_name (app, output); | |||
| 1700 | PangoRectangle ink_extent, log_extent; | |||
| 1701 | CdkRectangle viewport; | |||
| 1702 | CdkRGBA output_color; | |||
| 1703 | double r, g, b; | |||
| 1704 | double available_w; | |||
| 1705 | double factor; | |||
| 1706 | ||||
| 1707 | cairo_save (cr); | |||
| 1708 | ||||
| 1709 | foo_scroll_area_get_viewport (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ())))))), &viewport); | |||
| 1710 | ||||
| 1711 | get_geometry (output, &w, &h); | |||
| 1712 | ||||
| 1713 | #if 0 | |||
| 1714 | g_debug ("%s (%p) geometry %d %d %d", output->name, output, | |||
| 1715 | w, h, output->rate); | |||
| 1716 | #endif | |||
| 1717 | ||||
| 1718 | viewport.height -= 2 * MARGIN15; | |||
| 1719 | viewport.width -= 2 * MARGIN15; | |||
| 1720 | ||||
| 1721 | cafe_rr_output_info_get_geometry (output, &output_x, &output_y, NULL((void*)0), NULL((void*)0)); | |||
| 1722 | x = output_x * scale + MARGIN15 + (viewport.width - total_w * scale) / 2.0; | |||
| 1723 | y = output_y * scale + MARGIN15 + (viewport.height - total_h * scale) / 2.0; | |||
| 1724 | ||||
| 1725 | #if 0 | |||
| 1726 | g_debug ("scaled: %f %f", x, y); | |||
| 1727 | ||||
| 1728 | g_debug ("scale: %f", scale); | |||
| 1729 | ||||
| 1730 | g_debug ("%f %f %f %f", x, y, w * scale + 0.5, h * scale + 0.5); | |||
| 1731 | #endif | |||
| 1732 | ||||
| 1733 | cairo_translate (cr, | |||
| 1734 | x + (w * scale + 0.5) / 2, | |||
| 1735 | y + (h * scale + 0.5) / 2); | |||
| 1736 | ||||
| 1737 | /* rotation is already applied in get_geometry */ | |||
| 1738 | ||||
| 1739 | rotation = cafe_rr_output_info_get_rotation (output); | |||
| 1740 | if (rotation & CAFE_RR_REFLECT_X) | |||
| 1741 | cairo_scale (cr, -1, 1); | |||
| 1742 | ||||
| 1743 | if (rotation & CAFE_RR_REFLECT_Y) | |||
| 1744 | cairo_scale (cr, 1, -1); | |||
| 1745 | ||||
| 1746 | cairo_translate (cr, | |||
| 1747 | - x - (w * scale + 0.5) / 2, | |||
| 1748 | - y - (h * scale + 0.5) / 2); | |||
| 1749 | ||||
| 1750 | ||||
| 1751 | cairo_rectangle (cr, x, y, w * scale + 0.5, h * scale + 0.5); | |||
| 1752 | cairo_clip_preserve (cr); | |||
| 1753 | ||||
| 1754 | cafe_rr_labeler_get_rgba_for_output (app->labeler, output, &output_color); | |||
| 1755 | r = output_color.red; | |||
| 1756 | g = output_color.green; | |||
| 1757 | b = output_color.blue; | |||
| 1758 | ||||
| 1759 | if (!cafe_rr_output_info_is_active (output)) | |||
| 1760 | { | |||
| 1761 | /* If the output is turned off, just darken the selected color */ | |||
| 1762 | r *= 0.2; | |||
| 1763 | g *= 0.2; | |||
| 1764 | b *= 0.2; | |||
| 1765 | } | |||
| 1766 | ||||
| 1767 | cairo_set_source_rgba (cr, r, g, b, 1.0); | |||
| 1768 | ||||
| 1769 | foo_scroll_area_add_input_from_fill (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ())))))), | |||
| 1770 | cr, on_output_event, output); | |||
| 1771 | cairo_fill (cr); | |||
| 1772 | ||||
| 1773 | if (output == app->current_output) | |||
| 1774 | { | |||
| 1775 | cairo_rectangle (cr, x + 2, y + 2, w * scale + 0.5 - 4, h * scale + 0.5 - 4); | |||
| 1776 | ||||
| 1777 | cairo_set_line_width (cr, 4); | |||
| 1778 | cairo_set_source_rgba (cr, 0.33, 0.43, 0.57, 1.0); | |||
| 1779 | cairo_stroke (cr); | |||
| 1780 | } | |||
| 1781 | ||||
| 1782 | cairo_rectangle (cr, x + 0.5, y + 0.5, w * scale + 0.5 - 1, h * scale + 0.5 - 1); | |||
| 1783 | ||||
| 1784 | cairo_set_line_width (cr, 1); | |||
| 1785 | cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); | |||
| 1786 | ||||
| 1787 | cairo_stroke (cr); | |||
| 1788 | cairo_set_line_width (cr, 2); | |||
| 1789 | ||||
| 1790 | layout_set_font (layout, "Sans 12"); | |||
| 1791 | pango_layout_get_pixel_extents (layout, &ink_extent, &log_extent); | |||
| 1792 | ||||
| 1793 | available_w = w * scale + 0.5 - 6; /* Same as the inner rectangle's width, minus 1 pixel of padding on each side */ | |||
| 1794 | if (available_w < ink_extent.width) | |||
| 1795 | factor = available_w / ink_extent.width; | |||
| 1796 | else | |||
| 1797 | factor = 1.0; | |||
| 1798 | ||||
| 1799 | cairo_move_to (cr, | |||
| 1800 | x + ((w * scale + 0.5) - factor * log_extent.width) / 2, | |||
| 1801 | y + ((h * scale + 0.5) - factor * log_extent.height) / 2); | |||
| 1802 | ||||
| 1803 | cairo_scale (cr, factor, factor); | |||
| 1804 | ||||
| 1805 | if (cafe_rr_output_info_is_active (output)) | |||
| 1806 | cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); | |||
| 1807 | else | |||
| 1808 | cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); | |||
| 1809 | ||||
| 1810 | pango_cairo_show_layout (cr, layout); | |||
| 1811 | ||||
| 1812 | cairo_restore (cr); | |||
| 1813 | ||||
| 1814 | g_object_unref (layout); | |||
| 1815 | } | |||
| 1816 | ||||
| 1817 | static void | |||
| 1818 | on_area_paint (FooScrollArea *area, | |||
| 1819 | cairo_t *cr, | |||
| 1820 | gpointer data) | |||
| 1821 | { | |||
| 1822 | App *app = data; | |||
| 1823 | GList *connected_outputs = NULL((void*)0); | |||
| 1824 | GList *list; | |||
| 1825 | ||||
| 1826 | paint_background (area, cr); | |||
| 1827 | ||||
| 1828 | if (!app->current_configuration) | |||
| 1829 | return; | |||
| 1830 | ||||
| 1831 | connected_outputs = list_connected_outputs (app, NULL((void*)0), NULL((void*)0)); | |||
| 1832 | ||||
| 1833 | #if 0 | |||
| 1834 | double scale; | |||
| 1835 | scale = compute_scale (app); | |||
| 1836 | g_debug ("scale: %f", scale); | |||
| 1837 | #endif | |||
| 1838 | ||||
| 1839 | for (list = connected_outputs; list != NULL((void*)0); list = list->next) | |||
| 1840 | { | |||
| 1841 | paint_output (app, cr, g_list_position (connected_outputs, list)); | |||
| 1842 | ||||
| 1843 | if (cafe_rr_config_get_clone (app->current_configuration)) | |||
| 1844 | break; | |||
| 1845 | } | |||
| 1846 | } | |||
| 1847 | ||||
| 1848 | static void | |||
| 1849 | make_text_combo (CtkWidget *widget, int sort_column) | |||
| 1850 | { | |||
| 1851 | CtkComboBox *box = CTK_COMBO_BOX (widget)((((CtkComboBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_combo_box_get_type ())))))); | |||
| 1852 | CtkListStore *store = ctk_list_store_new ( | |||
| 1853 | 6, | |||
| 1854 | G_TYPE_STRING((GType) ((16) << (2))), /* Text */ | |||
| 1855 | G_TYPE_INT((GType) ((6) << (2))), /* Width */ | |||
| 1856 | G_TYPE_INT((GType) ((6) << (2))), /* Height */ | |||
| 1857 | G_TYPE_INT((GType) ((6) << (2))), /* Frequency */ | |||
| 1858 | G_TYPE_INT((GType) ((6) << (2))), /* Width * Height */ | |||
| 1859 | G_TYPE_INT((GType) ((6) << (2)))); /* Rotation */ | |||
| 1860 | ||||
| 1861 | CtkCellRenderer *cell; | |||
| 1862 | ||||
| 1863 | ctk_cell_layout_clear (CTK_CELL_LAYOUT (widget)((((CtkCellLayout*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_cell_layout_get_type ()))))))); | |||
| 1864 | ||||
| 1865 | ctk_combo_box_set_model (box, CTK_TREE_MODEL (store)((((CtkTreeModel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((store)), ((ctk_tree_model_get_type ()))))))); | |||
| 1866 | ||||
| 1867 | cell = ctk_cell_renderer_text_new (); | |||
| 1868 | ctk_cell_layout_pack_start (CTK_CELL_LAYOUT (box)((((CtkCellLayout*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_cell_layout_get_type ())))))), cell, TRUE(!(0))); | |||
| 1869 | ctk_cell_layout_set_attributes (CTK_CELL_LAYOUT (box)((((CtkCellLayout*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_cell_layout_get_type ())))))), cell, | |||
| 1870 | "text", 0, | |||
| 1871 | NULL((void*)0)); | |||
| 1872 | ||||
| 1873 | if (sort_column != -1) | |||
| 1874 | { | |||
| 1875 | ctk_tree_sortable_set_sort_column_id (CTK_TREE_SORTABLE (store)((((CtkTreeSortable*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((store)), ((ctk_tree_sortable_get_type ())))))), | |||
| 1876 | sort_column, | |||
| 1877 | CTK_SORT_DESCENDING); | |||
| 1878 | } | |||
| 1879 | } | |||
| 1880 | ||||
| 1881 | static void | |||
| 1882 | compute_virtual_size_for_configuration (CafeRRConfig *config, int *ret_width, int *ret_height) | |||
| 1883 | { | |||
| 1884 | int i; | |||
| 1885 | int width, height; | |||
| 1886 | int output_x, output_y, output_width, output_height; | |||
| 1887 | CafeRROutputInfo **outputs; | |||
| 1888 | ||||
| 1889 | width = height = 0; | |||
| 1890 | ||||
| 1891 | outputs = cafe_rr_config_get_outputs (config); | |||
| 1892 | for (i = 0; outputs[i] != NULL((void*)0); i++) | |||
| 1893 | { | |||
| 1894 | if (cafe_rr_output_info_is_active (outputs[i])) | |||
| 1895 | { | |||
| 1896 | cafe_rr_output_info_get_geometry (outputs[i], &output_x, &output_y, &output_width, &output_height); | |||
| 1897 | width = MAX (width, output_x + output_width)(((width) > (output_x + output_width)) ? (width) : (output_x + output_width)); | |||
| 1898 | height = MAX (height, output_y + output_height)(((height) > (output_y + output_height)) ? (height) : (output_y + output_height)); | |||
| 1899 | } | |||
| 1900 | } | |||
| 1901 | ||||
| 1902 | *ret_width = width; | |||
| 1903 | *ret_height = height; | |||
| 1904 | } | |||
| 1905 | ||||
| 1906 | static void | |||
| 1907 | check_required_virtual_size (App *app) | |||
| 1908 | { | |||
| 1909 | int req_width, req_height; | |||
| 1910 | int min_width, max_width; | |||
| 1911 | int min_height, max_height; | |||
| 1912 | ||||
| 1913 | compute_virtual_size_for_configuration (app->current_configuration, &req_width, &req_height); | |||
| 1914 | ||||
| 1915 | cafe_rr_screen_get_ranges (app->screen, &min_width, &max_width, &min_height, &max_height); | |||
| 1916 | ||||
| 1917 | #if 0 | |||
| 1918 | g_debug ("X Server supports:"); | |||
| 1919 | g_debug ("min_width = %d, max_width = %d", min_width, max_width); | |||
| 1920 | g_debug ("min_height = %d, max_height = %d", min_height, max_height); | |||
| 1921 | ||||
| 1922 | g_debug ("Requesting size of %dx%d", req_width, req_height); | |||
| 1923 | #endif | |||
| 1924 | ||||
| 1925 | if (!(min_width <= req_width && req_width <= max_width | |||
| 1926 | && min_height <= req_height && req_height <= max_height)) | |||
| 1927 | { | |||
| 1928 | /* FIXME: present a useful dialog, maybe even before the user tries to Apply */ | |||
| 1929 | #if 0 | |||
| 1930 | g_debug ("Your X server needs a larger Virtual size!"); | |||
| 1931 | #endif | |||
| 1932 | } | |||
| 1933 | } | |||
| 1934 | ||||
| 1935 | static void | |||
| 1936 | begin_version2_apply_configuration (App *app, CdkWindow *parent_window, guint32 timestamp) | |||
| 1937 | { | |||
| 1938 | XID parent_window_xid; | |||
| 1939 | GError *error = NULL((void*)0); | |||
| 1940 | ||||
| 1941 | parent_window_xid = CDK_WINDOW_XID (parent_window)(cdk_x11_window_get_xid (parent_window)); | |||
| 1942 | ||||
| 1943 | app->proxy = g_dbus_proxy_new_sync (app->connection, | |||
| 1944 | G_DBUS_PROXY_FLAGS_NONE, | |||
| 1945 | NULL((void*)0), | |||
| 1946 | "org.cafe.SettingsDaemon", | |||
| 1947 | "/org/cafe/SettingsDaemon/XRANDR", | |||
| 1948 | "org.cafe.SettingsDaemon.XRANDR_2", | |||
| 1949 | NULL((void*)0), | |||
| 1950 | &error); | |||
| 1951 | if (app->proxy == NULL((void*)0)) { | |||
| 1952 | g_warning ("Failed to get dbus connection: %s", error->message); | |||
| 1953 | g_error_free (error); | |||
| 1954 | } else { | |||
| 1955 | app->apply_configuration_state = APPLYING_VERSION_2; | |||
| 1956 | g_dbus_proxy_call (app->proxy, | |||
| 1957 | "ApplyConfiguration", | |||
| 1958 | g_variant_new ("(xx)", parent_window_xid, timestamp), | |||
| 1959 | G_DBUS_CALL_FLAGS_NONE, | |||
| 1960 | -1, | |||
| 1961 | NULL((void*)0), | |||
| 1962 | (GAsyncReadyCallback) apply_configuration_returned_cb, | |||
| 1963 | app); | |||
| 1964 | } | |||
| 1965 | } | |||
| 1966 | ||||
| 1967 | static void | |||
| 1968 | begin_version1_apply_configuration (App *app) | |||
| 1969 | { | |||
| 1970 | GError *error = NULL((void*)0); | |||
| 1971 | ||||
| 1972 | app->proxy = g_dbus_proxy_new_sync (app->connection, | |||
| 1973 | G_DBUS_PROXY_FLAGS_NONE, | |||
| 1974 | NULL((void*)0), | |||
| 1975 | "org.cafe.SettingsDaemon", | |||
| 1976 | "/org/cafe/SettingsDaemon/XRANDR", | |||
| 1977 | "org.cafe.SettingsDaemon.XRANDR", | |||
| 1978 | NULL((void*)0), | |||
| 1979 | &error); | |||
| 1980 | if (app->proxy == NULL((void*)0)) { | |||
| 1981 | g_warning ("Failed to get dbus connection: %s", error->message); | |||
| 1982 | g_error_free (error); | |||
| 1983 | } else { | |||
| 1984 | app->apply_configuration_state = APPLYING_VERSION_1; | |||
| 1985 | g_dbus_proxy_call (app->proxy, | |||
| 1986 | "ApplyConfiguration", | |||
| 1987 | g_variant_new ("()"), | |||
| 1988 | G_DBUS_CALL_FLAGS_NONE, | |||
| 1989 | -1, | |||
| 1990 | NULL((void*)0), | |||
| 1991 | (GAsyncReadyCallback) apply_configuration_returned_cb, | |||
| 1992 | app); | |||
| 1993 | } | |||
| 1994 | } | |||
| 1995 | ||||
| 1996 | static void | |||
| 1997 | ensure_current_configuration_is_saved (void) | |||
| 1998 | { | |||
| 1999 | CafeRRScreen *rr_screen; | |||
| 2000 | CafeRRConfig *rr_config; | |||
| 2001 | ||||
| 2002 | /* Normally, cafe_rr_config_save() creates a backup file based on the | |||
| 2003 | * old monitors.xml. However, if *that* file didn't exist, there is | |||
| 2004 | * nothing from which to create a backup. So, here we'll save the | |||
| 2005 | * current/unchanged configuration and then let our caller call | |||
| 2006 | * cafe_rr_config_save() again with the new/changed configuration, so | |||
| 2007 | * that there *will* be a backup file in the end. | |||
| 2008 | */ | |||
| 2009 | ||||
| 2010 | rr_screen = cafe_rr_screen_new (cdk_screen_get_default (), NULL((void*)0)); /* NULL-GError */ | |||
| 2011 | if (!rr_screen) | |||
| 2012 | return; | |||
| 2013 | ||||
| 2014 | rr_config = cafe_rr_config_new_current (rr_screen, NULL((void*)0)); | |||
| 2015 | cafe_rr_config_save (rr_config, NULL((void*)0)); /* NULL-GError */ | |||
| 2016 | ||||
| 2017 | g_object_unref (rr_config); | |||
| 2018 | g_object_unref (rr_screen); | |||
| 2019 | } | |||
| 2020 | ||||
| 2021 | /* Callback for g_dbus_proxy_call() */ | |||
| 2022 | static void | |||
| 2023 | apply_configuration_returned_cb (GObject *source_object, | |||
| 2024 | GAsyncResult *res, | |||
| 2025 | gpointer data) | |||
| 2026 | { | |||
| 2027 | GVariant *variant; | |||
| 2028 | GError *error; | |||
| 2029 | App *app = data; | |||
| 2030 | ||||
| 2031 | error = NULL((void*)0); | |||
| 2032 | ||||
| 2033 | if (app->proxy == NULL((void*)0)) | |||
| 2034 | return; | |||
| 2035 | ||||
| 2036 | variant = g_dbus_proxy_call_finish (app->proxy, res, &error); | |||
| 2037 | if (variant == NULL((void*)0)) { | |||
| 2038 | if (app->apply_configuration_state == APPLYING_VERSION_2 | |||
| 2039 | && g_error_matches (error, G_DBUS_ERRORg_dbus_error_quark(), G_DBUS_ERROR_UNKNOWN_METHOD)) { | |||
| 2040 | g_error_free (error); | |||
| 2041 | ||||
| 2042 | g_object_unref (app->proxy); | |||
| 2043 | app->proxy = NULL((void*)0); | |||
| 2044 | ||||
| 2045 | begin_version1_apply_configuration (app); | |||
| 2046 | return; | |||
| 2047 | } else { | |||
| 2048 | /* We don't pop up an error message; cafe-settings-daemon already does that | |||
| 2049 | * in case the selected RANDR configuration could not be applied. | |||
| 2050 | */ | |||
| 2051 | g_error_free (error); | |||
| 2052 | } | |||
| 2053 | } | |||
| 2054 | ||||
| 2055 | g_object_unref (app->proxy); | |||
| 2056 | app->proxy = NULL((void*)0); | |||
| 2057 | ||||
| 2058 | g_object_unref (app->connection); | |||
| 2059 | app->connection = NULL((void*)0); | |||
| 2060 | ||||
| 2061 | ctk_widget_set_sensitive (app->dialog, TRUE(!(0))); | |||
| 2062 | } | |||
| 2063 | ||||
| 2064 | static gboolean | |||
| 2065 | sanitize_and_save_configuration (App *app) | |||
| 2066 | { | |||
| 2067 | GError *error; | |||
| 2068 | ||||
| 2069 | cafe_rr_config_sanitize (app->current_configuration); | |||
| 2070 | ||||
| 2071 | check_required_virtual_size (app); | |||
| 2072 | ||||
| 2073 | foo_scroll_area_invalidate (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ()))))))); | |||
| 2074 | ||||
| 2075 | ensure_current_configuration_is_saved (); | |||
| 2076 | ||||
| 2077 | error = NULL((void*)0); | |||
| 2078 | if (!cafe_rr_config_save (app->current_configuration, &error)) | |||
| 2079 | { | |||
| 2080 | error_message (app, _("Could not save the monitor configuration")gettext ("Could not save the monitor configuration"), error->message); | |||
| 2081 | g_error_free (error); | |||
| 2082 | return FALSE(0); | |||
| 2083 | } | |||
| 2084 | ||||
| 2085 | return TRUE(!(0)); | |||
| 2086 | } | |||
| 2087 | ||||
| 2088 | static void | |||
| 2089 | apply (App *app) | |||
| 2090 | { | |||
| 2091 | GError *error = NULL((void*)0); | |||
| 2092 | ||||
| 2093 | if (!sanitize_and_save_configuration (app)) | |||
| 2094 | return; | |||
| 2095 | ||||
| 2096 | g_assert (app->connection == NULL)do { if (app->connection == ((void*)0)) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c", 2096, ((const char *) (__func__)), "app->connection == NULL"); } while (0); | |||
| 2097 | g_assert (app->proxy == NULL)do { if (app->proxy == ((void*)0)) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c", 2097, ((const char *) (__func__)), "app->proxy == NULL"); } while (0); | |||
| 2098 | ||||
| 2099 | app->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL((void*)0), &error); | |||
| 2100 | if (app->connection == NULL((void*)0)) { | |||
| 2101 | error_message (app, _("Could not get session bus while applying display configuration")gettext ("Could not get session bus while applying display configuration" ), error->message); | |||
| 2102 | g_error_free (error); | |||
| 2103 | return; | |||
| 2104 | } | |||
| 2105 | ||||
| 2106 | ctk_widget_set_sensitive (app->dialog, FALSE(0)); | |||
| 2107 | ||||
| 2108 | begin_version2_apply_configuration (app, ctk_widget_get_window (app->dialog), app->apply_button_clicked_timestamp); | |||
| 2109 | } | |||
| 2110 | ||||
| 2111 | static void | |||
| 2112 | on_detect_displays (CtkWidget *widget, gpointer data) | |||
| 2113 | { | |||
| 2114 | App *app = data; | |||
| 2115 | GError *error; | |||
| 2116 | ||||
| 2117 | error = NULL((void*)0); | |||
| 2118 | if (!cafe_rr_screen_refresh (app->screen, &error)) { | |||
| 2119 | if (error) { | |||
| 2120 | error_message (app, _("Could not detect displays")gettext ("Could not detect displays"), error->message); | |||
| 2121 | g_error_free (error); | |||
| 2122 | } | |||
| 2123 | } | |||
| 2124 | } | |||
| 2125 | ||||
| 2126 | static void | |||
| 2127 | set_primary (CtkWidget *widget, gpointer data) | |||
| 2128 | { | |||
| 2129 | App *app = data; | |||
| 2130 | int i; | |||
| 2131 | CafeRROutputInfo **outputs; | |||
| 2132 | ||||
| 2133 | if (!app->current_output) | |||
| 2134 | return; | |||
| 2135 | ||||
| 2136 | outputs = cafe_rr_config_get_outputs (app->current_configuration); | |||
| 2137 | for (i=0; outputs[i]!=NULL((void*)0); i++) { | |||
| 2138 | cafe_rr_output_info_set_primary (outputs[i], outputs[i] == app->current_output); | |||
| 2139 | } | |||
| 2140 | ||||
| 2141 | ctk_widget_set_sensitive (app->primary_button, !cafe_rr_output_info_get_primary(app->current_output)); | |||
| 2142 | } | |||
| 2143 | ||||
| 2144 | #define CSD_XRANDR_SCHEMA"org.cafe.SettingsDaemon.plugins.xrandr" "org.cafe.SettingsDaemon.plugins.xrandr" | |||
| 2145 | #define SHOW_ICON_KEY"show-notification-icon" "show-notification-icon" | |||
| 2146 | #define DEFAULT_CONFIGURATION_FILE_KEY"default-configuration-file" "default-configuration-file" | |||
| 2147 | ||||
| 2148 | static void | |||
| 2149 | on_show_icon_toggled (CtkWidget *widget, gpointer data) | |||
| 2150 | { | |||
| 2151 | CtkToggleButton *tb = CTK_TOGGLE_BUTTON (widget)((((CtkToggleButton*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((widget)), ((ctk_toggle_button_get_type ())))))); | |||
| 2152 | App *app = data; | |||
| 2153 | ||||
| 2154 | g_settings_set_boolean (app->settings, SHOW_ICON_KEY"show-notification-icon", | |||
| 2155 | ctk_toggle_button_get_active (tb)); | |||
| 2156 | } | |||
| 2157 | ||||
| 2158 | static CafeRROutputInfo * | |||
| 2159 | get_nearest_output (CafeRRConfig *configuration, int x, int y) | |||
| 2160 | { | |||
| 2161 | int i; | |||
| 2162 | int nearest_index; | |||
| 2163 | int nearest_dist; | |||
| 2164 | CafeRROutputInfo **outputs; | |||
| 2165 | ||||
| 2166 | nearest_index = -1; | |||
| 2167 | nearest_dist = G_MAXINT2147483647; | |||
| 2168 | ||||
| 2169 | outputs = cafe_rr_config_get_outputs (configuration); | |||
| 2170 | for (i = 0; outputs[i] != NULL((void*)0); i++) | |||
| 2171 | { | |||
| 2172 | int dist_x, dist_y; | |||
| 2173 | int output_x, output_y, output_width, output_height; | |||
| 2174 | ||||
| 2175 | if (!(cafe_rr_output_info_is_connected(outputs[i]) && cafe_rr_output_info_is_active (outputs[i]))) | |||
| 2176 | continue; | |||
| 2177 | ||||
| 2178 | cafe_rr_output_info_get_geometry (outputs[i], &output_x, &output_y, &output_width, &output_height); | |||
| 2179 | if (x < output_x) | |||
| 2180 | dist_x = output_x - x; | |||
| 2181 | else if (x >= output_x + output_width) | |||
| 2182 | dist_x = x - (output_x + output_width) + 1; | |||
| 2183 | else | |||
| 2184 | dist_x = 0; | |||
| 2185 | ||||
| 2186 | if (y < output_y) | |||
| 2187 | dist_y = output_y - y; | |||
| 2188 | else if (y >= output_y + output_height) | |||
| 2189 | dist_y = y - (output_y + output_height) + 1; | |||
| 2190 | else | |||
| 2191 | dist_y = 0; | |||
| 2192 | ||||
| 2193 | if (MIN (dist_x, dist_y)(((dist_x) < (dist_y)) ? (dist_x) : (dist_y)) < nearest_dist) | |||
| 2194 | { | |||
| 2195 | nearest_dist = MIN (dist_x, dist_y)(((dist_x) < (dist_y)) ? (dist_x) : (dist_y)); | |||
| 2196 | nearest_index = i; | |||
| 2197 | } | |||
| 2198 | } | |||
| 2199 | ||||
| 2200 | if (nearest_index != -1) | |||
| 2201 | return outputs[nearest_index]; | |||
| 2202 | else | |||
| 2203 | return NULL((void*)0); | |||
| 2204 | ||||
| 2205 | } | |||
| 2206 | ||||
| 2207 | /* Gets the output that contains the largest intersection with the window. | |||
| 2208 | * Logic stolen from cdk_screen_get_monitor_at_window(). | |||
| 2209 | */ | |||
| 2210 | static CafeRROutputInfo * | |||
| 2211 | get_output_for_window (CafeRRConfig *configuration, CdkWindow *window) | |||
| 2212 | { | |||
| 2213 | CdkRectangle win_rect; | |||
| 2214 | int i; | |||
| 2215 | int largest_area; | |||
| 2216 | int largest_index; | |||
| 2217 | CafeRROutputInfo **outputs; | |||
| 2218 | ||||
| 2219 | cdk_window_get_geometry (window, &win_rect.x, &win_rect.y, &win_rect.width, &win_rect.height); | |||
| 2220 | cdk_window_get_origin (window, &win_rect.x, &win_rect.y); | |||
| 2221 | ||||
| 2222 | largest_area = 0; | |||
| 2223 | largest_index = -1; | |||
| 2224 | ||||
| 2225 | outputs = cafe_rr_config_get_outputs (configuration); | |||
| 2226 | for (i = 0; outputs[i] != NULL((void*)0); i++) | |||
| 2227 | { | |||
| 2228 | CdkRectangle output_rect, intersection; | |||
| 2229 | ||||
| 2230 | cafe_rr_output_info_get_geometry (outputs[i], &output_rect.x, &output_rect.y, &output_rect.width, &output_rect.height); | |||
| 2231 | ||||
| 2232 | if (cafe_rr_output_info_is_connected (outputs[i]) && cdk_rectangle_intersect (&win_rect, &output_rect, &intersection)) | |||
| 2233 | { | |||
| 2234 | int area; | |||
| 2235 | ||||
| 2236 | area = intersection.width * intersection.height; | |||
| 2237 | if (area > largest_area) | |||
| 2238 | { | |||
| 2239 | largest_area = area; | |||
| 2240 | largest_index = i; | |||
| 2241 | } | |||
| 2242 | } | |||
| 2243 | } | |||
| 2244 | ||||
| 2245 | if (largest_index != -1) | |||
| 2246 | return outputs[largest_index]; | |||
| 2247 | else | |||
| 2248 | return get_nearest_output (configuration, | |||
| 2249 | win_rect.x + win_rect.width / 2, | |||
| 2250 | win_rect.y + win_rect.height / 2); | |||
| 2251 | } | |||
| 2252 | ||||
| 2253 | /* We select the current output, i.e. select the one being edited, based on | |||
| 2254 | * which output is showing the configuration dialog. | |||
| 2255 | */ | |||
| 2256 | static void | |||
| 2257 | select_current_output_from_dialog_position (App *app) | |||
| 2258 | { | |||
| 2259 | if (ctk_widget_get_realized (app->dialog)) | |||
| 2260 | app->current_output = get_output_for_window (app->current_configuration, ctk_widget_get_window (app->dialog)); | |||
| 2261 | else | |||
| 2262 | app->current_output = NULL((void*)0); | |||
| 2263 | ||||
| 2264 | rebuild_gui (app); | |||
| 2265 | } | |||
| 2266 | ||||
| 2267 | /* This is a CtkWidget::map-event handler. We wait for the display-properties | |||
| 2268 | * dialog to be mapped, and then we select the output which corresponds to the | |||
| 2269 | * monitor on which the dialog is being shown. | |||
| 2270 | */ | |||
| 2271 | static gboolean | |||
| 2272 | dialog_map_event_cb (CtkWidget *widget, CdkEventAny *event, gpointer data) | |||
| 2273 | { | |||
| 2274 | App *app = data; | |||
| 2275 | ||||
| 2276 | select_current_output_from_dialog_position (app); | |||
| 2277 | return FALSE(0); | |||
| 2278 | } | |||
| 2279 | ||||
| 2280 | static void | |||
| 2281 | apply_button_clicked_cb (CtkButton *button, gpointer data) | |||
| 2282 | { | |||
| 2283 | App *app = data; | |||
| 2284 | ||||
| 2285 | /* We simply store the timestamp at which the Apply button was clicked. | |||
| 2286 | * We'll just wait for the dialog to return from ctk_dialog_run(), and | |||
| 2287 | * *then* use the timestamp when applying the RANDR configuration. | |||
| 2288 | */ | |||
| 2289 | ||||
| 2290 | app->apply_button_clicked_timestamp = ctk_get_current_event_time (); | |||
| 2291 | } | |||
| 2292 | ||||
| 2293 | static void | |||
| 2294 | success_dialog_for_make_default (App *app) | |||
| 2295 | { | |||
| 2296 | CtkWidget *dialog; | |||
| 2297 | ||||
| 2298 | dialog = ctk_message_dialog_new (CTK_WINDOW (app->dialog)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->dialog)), ((ctk_window_get_type ())))))), | |||
| 2299 | CTK_DIALOG_MODAL, | |||
| 2300 | CTK_MESSAGE_INFO, | |||
| 2301 | CTK_BUTTONS_OK, | |||
| 2302 | _("The monitor configuration has been saved")gettext ("The monitor configuration has been saved")); | |||
| 2303 | ctk_message_dialog_format_secondary_text (CTK_MESSAGE_DIALOG (dialog)((((CtkMessageDialog*) (void *) g_type_check_instance_cast (( GTypeInstance*) ((dialog)), ((ctk_message_dialog_get_type ()) ))))), | |||
| 2304 | _("This configuration will be used the next time someone logs in.")gettext ("This configuration will be used the next time someone logs in." )); | |||
| 2305 | ||||
| 2306 | ctk_dialog_run (CTK_DIALOG (dialog)((((CtkDialog*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((dialog)), ((ctk_dialog_get_type ()))))))); | |||
| 2307 | ctk_widget_destroy (dialog); | |||
| 2308 | } | |||
| 2309 | ||||
| 2310 | static void | |||
| 2311 | error_dialog_for_make_default (App *app, const char *error_text) | |||
| 2312 | { | |||
| 2313 | error_message (app, _("Could not set the default configuration for monitors")gettext ("Could not set the default configuration for monitors" ), error_text); | |||
| 2314 | } | |||
| 2315 | ||||
| 2316 | static void | |||
| 2317 | make_default (App *app) | |||
| 2318 | { | |||
| 2319 | char *command_line; | |||
| 2320 | char *source_filename; | |||
| 2321 | char *dest_filename; | |||
| 2322 | char *dest_basename; | |||
| 2323 | char *std_error; | |||
| 2324 | gint exit_status; | |||
| 2325 | GError *error; | |||
| 2326 | ||||
| 2327 | if (!sanitize_and_save_configuration (app)) | |||
| 2328 | return; | |||
| 2329 | ||||
| 2330 | dest_filename = g_settings_get_string (app->settings, DEFAULT_CONFIGURATION_FILE_KEY"default-configuration-file"); | |||
| 2331 | if (!dest_filename) | |||
| 2332 | return; /* FIXME: present an error? */ | |||
| 2333 | ||||
| 2334 | dest_basename = g_path_get_basename (dest_filename); | |||
| 2335 | ||||
| 2336 | source_filename = cafe_rr_config_get_intended_filename (); | |||
| 2337 | ||||
| 2338 | command_line = g_strdup_printf ("pkexec %s/cafe-display-properties-install-systemwide %s %s", | |||
| 2339 | SBINDIR"/usr/sbin", | |||
| 2340 | source_filename, | |||
| 2341 | dest_basename); | |||
| 2342 | ||||
| 2343 | error = NULL((void*)0); | |||
| 2344 | if (!g_spawn_command_line_sync (command_line, NULL((void*)0), &std_error, &exit_status, &error)) | |||
| 2345 | { | |||
| 2346 | error_dialog_for_make_default (app, error->message); | |||
| 2347 | g_error_free (error); | |||
| 2348 | } | |||
| 2349 | else if (!WIFEXITED (exit_status)(((exit_status) & 0x7f) == 0) || WEXITSTATUS (exit_status)(((exit_status) & 0xff00) >> 8) != 0) | |||
| 2350 | { | |||
| 2351 | error_dialog_for_make_default (app, std_error); | |||
| 2352 | } | |||
| 2353 | else | |||
| 2354 | { | |||
| 2355 | success_dialog_for_make_default (app); | |||
| 2356 | } | |||
| 2357 | ||||
| 2358 | g_free (std_error); | |||
| 2359 | g_free (dest_filename); | |||
| 2360 | g_free (dest_basename); | |||
| 2361 | g_free (source_filename); | |||
| 2362 | g_free (command_line); | |||
| 2363 | } | |||
| 2364 | ||||
| 2365 | static CtkWidget* | |||
| 2366 | _ctk_builder_get_widget (CtkBuilder *builder, const gchar *name) | |||
| 2367 | { | |||
| 2368 | return CTK_WIDGET (ctk_builder_get_object (builder, name))((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((ctk_builder_get_object (builder, name))), ((ctk_widget_get_type ())))))); | |||
| 2369 | } | |||
| 2370 | ||||
| 2371 | static void | |||
| 2372 | run_application (App *app) | |||
| 2373 | { | |||
| 2374 | CtkBuilder *builder; | |||
| 2375 | CtkWidget *align; | |||
| 2376 | GError *error = NULL((void*)0); | |||
| 2377 | ||||
| 2378 | builder = ctk_builder_new (); | |||
| 2379 | ||||
| 2380 | if (ctk_builder_add_from_resource (builder, "/org/cafe/ccc/display/display-capplet.ui", &error) == 0) | |||
| 2381 | { | |||
| 2382 | g_warning ("Could not parse UI definition: %s", error->message); | |||
| 2383 | g_error_free (error); | |||
| 2384 | g_object_unref (builder); | |||
| 2385 | return; | |||
| 2386 | } | |||
| 2387 | ||||
| 2388 | app->screen = cafe_rr_screen_new (cdk_screen_get_default (), &error); | |||
| 2389 | g_signal_connect (app->screen, "changed", G_CALLBACK (on_screen_changed), app)g_signal_connect_data ((app->screen), ("changed"), (((GCallback ) (on_screen_changed))), (app), ((void*)0), (GConnectFlags) 0 ); | |||
| 2390 | if (!app->screen) | |||
| 2391 | { | |||
| 2392 | error_message (NULL((void*)0), _("Could not get screen information")gettext ("Could not get screen information"), error->message); | |||
| 2393 | g_error_free (error); | |||
| 2394 | g_object_unref (builder); | |||
| 2395 | return; | |||
| 2396 | } | |||
| 2397 | ||||
| 2398 | app->settings = g_settings_new (CSD_XRANDR_SCHEMA"org.cafe.SettingsDaemon.plugins.xrandr"); | |||
| 2399 | ||||
| 2400 | app->dialog = _ctk_builder_get_widget (builder, "dialog"); | |||
| 2401 | g_signal_connect_after (app->dialog, "map-event",g_signal_connect_data ((app->dialog), ("map-event"), (((GCallback ) (dialog_map_event_cb))), (app), ((void*)0), G_CONNECT_AFTER ) | |||
| 2402 | G_CALLBACK (dialog_map_event_cb), app)g_signal_connect_data ((app->dialog), ("map-event"), (((GCallback ) (dialog_map_event_cb))), (app), ((void*)0), G_CONNECT_AFTER ); | |||
| 2403 | ||||
| 2404 | ctk_window_set_default_icon_name ("preferences-desktop-display"); | |||
| 2405 | ctk_window_set_icon_name (CTK_WINDOW (app->dialog)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->dialog)), ((ctk_window_get_type ())))))), | |||
| 2406 | "preferences-desktop-display"); | |||
| 2407 | ||||
| 2408 | app->current_monitor_event_box = _ctk_builder_get_widget (builder, | |||
| 2409 | "current_monitor_event_box"); | |||
| 2410 | app->current_monitor_label = _ctk_builder_get_widget (builder, | |||
| 2411 | "current_monitor_label"); | |||
| 2412 | ||||
| 2413 | app->monitor_on_radio = _ctk_builder_get_widget (builder, | |||
| 2414 | "monitor_on_radio"); | |||
| 2415 | app->monitor_off_radio = _ctk_builder_get_widget (builder, | |||
| 2416 | "monitor_off_radio"); | |||
| 2417 | g_signal_connect (app->monitor_on_radio, "toggled",g_signal_connect_data ((app->monitor_on_radio), ("toggled" ), (((GCallback) (monitor_on_off_toggled_cb))), (app), ((void *)0), (GConnectFlags) 0) | |||
| 2418 | G_CALLBACK (monitor_on_off_toggled_cb), app)g_signal_connect_data ((app->monitor_on_radio), ("toggled" ), (((GCallback) (monitor_on_off_toggled_cb))), (app), ((void *)0), (GConnectFlags) 0); | |||
| 2419 | g_signal_connect (app->monitor_off_radio, "toggled",g_signal_connect_data ((app->monitor_off_radio), ("toggled" ), (((GCallback) (monitor_on_off_toggled_cb))), (app), ((void *)0), (GConnectFlags) 0) | |||
| 2420 | G_CALLBACK (monitor_on_off_toggled_cb), app)g_signal_connect_data ((app->monitor_off_radio), ("toggled" ), (((GCallback) (monitor_on_off_toggled_cb))), (app), ((void *)0), (GConnectFlags) 0); | |||
| 2421 | ||||
| 2422 | app->resolution_combo = _ctk_builder_get_widget (builder, | |||
| 2423 | "resolution_combo"); | |||
| 2424 | g_signal_connect (app->resolution_combo, "changed",g_signal_connect_data ((app->resolution_combo), ("changed" ), (((GCallback) (on_resolution_changed))), (app), ((void*)0) , (GConnectFlags) 0) | |||
| 2425 | G_CALLBACK (on_resolution_changed), app)g_signal_connect_data ((app->resolution_combo), ("changed" ), (((GCallback) (on_resolution_changed))), (app), ((void*)0) , (GConnectFlags) 0); | |||
| 2426 | ||||
| 2427 | app->refresh_combo = _ctk_builder_get_widget (builder, "refresh_combo"); | |||
| 2428 | g_signal_connect (app->refresh_combo, "changed",g_signal_connect_data ((app->refresh_combo), ("changed"), ( ((GCallback) (on_rate_changed))), (app), ((void*)0), (GConnectFlags ) 0) | |||
| 2429 | G_CALLBACK (on_rate_changed), app)g_signal_connect_data ((app->refresh_combo), ("changed"), ( ((GCallback) (on_rate_changed))), (app), ((void*)0), (GConnectFlags ) 0); | |||
| 2430 | ||||
| 2431 | app->rotation_combo = _ctk_builder_get_widget (builder, "rotation_combo"); | |||
| 2432 | g_signal_connect (app->rotation_combo, "changed",g_signal_connect_data ((app->rotation_combo), ("changed"), (((GCallback) (on_rotation_changed))), (app), ((void*)0), (GConnectFlags ) 0) | |||
| 2433 | G_CALLBACK (on_rotation_changed), app)g_signal_connect_data ((app->rotation_combo), ("changed"), (((GCallback) (on_rotation_changed))), (app), ((void*)0), (GConnectFlags ) 0); | |||
| 2434 | ||||
| 2435 | app->clone_checkbox = _ctk_builder_get_widget (builder, "clone_checkbox"); | |||
| 2436 | g_signal_connect (app->clone_checkbox, "toggled",g_signal_connect_data ((app->clone_checkbox), ("toggled"), (((GCallback) (on_clone_changed))), (app), ((void*)0), (GConnectFlags ) 0) | |||
| 2437 | G_CALLBACK (on_clone_changed), app)g_signal_connect_data ((app->clone_checkbox), ("toggled"), (((GCallback) (on_clone_changed))), (app), ((void*)0), (GConnectFlags ) 0); | |||
| 2438 | ||||
| 2439 | g_signal_connect (_ctk_builder_get_widget (builder, "detect_displays_button"),g_signal_connect_data ((_ctk_builder_get_widget (builder, "detect_displays_button" )), ("clicked"), (((GCallback) (on_detect_displays))), (app), ((void*)0), (GConnectFlags) 0) | |||
| 2440 | "clicked", G_CALLBACK (on_detect_displays), app)g_signal_connect_data ((_ctk_builder_get_widget (builder, "detect_displays_button" )), ("clicked"), (((GCallback) (on_detect_displays))), (app), ((void*)0), (GConnectFlags) 0); | |||
| 2441 | ||||
| 2442 | app->primary_button = _ctk_builder_get_widget (builder, "primary_button"); | |||
| 2443 | ||||
| 2444 | g_signal_connect (app->primary_button, "clicked", G_CALLBACK (set_primary), app)g_signal_connect_data ((app->primary_button), ("clicked"), (((GCallback) (set_primary))), (app), ((void*)0), (GConnectFlags ) 0); | |||
| 2445 | ||||
| 2446 | app->show_icon_checkbox = _ctk_builder_get_widget (builder, | |||
| 2447 | "show_notification_icon"); | |||
| 2448 | ||||
| 2449 | ctk_toggle_button_set_active (CTK_TOGGLE_BUTTON (app->show_icon_checkbox)((((CtkToggleButton*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->show_icon_checkbox)), ((ctk_toggle_button_get_type ())))))), | |||
| 2450 | g_settings_get_boolean (app->settings, SHOW_ICON_KEY"show-notification-icon")); | |||
| 2451 | ||||
| 2452 | g_signal_connect (app->show_icon_checkbox, "toggled", G_CALLBACK (on_show_icon_toggled), app)g_signal_connect_data ((app->show_icon_checkbox), ("toggled" ), (((GCallback) (on_show_icon_toggled))), (app), ((void*)0), (GConnectFlags) 0); | |||
| 2453 | ||||
| 2454 | app->panel_checkbox = _ctk_builder_get_widget (builder, "panel_checkbox"); | |||
| 2455 | ||||
| 2456 | make_text_combo (app->resolution_combo, 4); | |||
| 2457 | make_text_combo (app->refresh_combo, 3); | |||
| 2458 | make_text_combo (app->rotation_combo, -1); | |||
| 2459 | ||||
| 2460 | g_assert (app->panel_checkbox)do { if (app->panel_checkbox) ; else g_assertion_message_expr ("display-properties", "xrandr-capplet.c", 2460, ((const char *) (__func__)), "app->panel_checkbox"); } while (0); | |||
| 2461 | ||||
| 2462 | /* Scroll Area */ | |||
| 2463 | app->area = (CtkWidget *)foo_scroll_area_new (); | |||
| 2464 | ||||
| 2465 | g_object_set_data (G_OBJECT (app->area)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), (((GType) ((20) << (2)))))))), "app", app); | |||
| 2466 | ||||
| 2467 | set_monitors_tooltip (app, FALSE(0)); | |||
| 2468 | ||||
| 2469 | /* FIXME: this should be computed dynamically */ | |||
| 2470 | foo_scroll_area_set_min_size (FOO_SCROLL_AREA (app->area)((((FooScrollArea*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->area)), ((foo_scroll_area_get_type ())))))), -1, 200); | |||
| 2471 | ctk_widget_show (app->area); | |||
| 2472 | g_signal_connect (app->area, "paint",g_signal_connect_data ((app->area), ("paint"), (((GCallback ) (on_area_paint))), (app), ((void*)0), (GConnectFlags) 0) | |||
| 2473 | G_CALLBACK (on_area_paint), app)g_signal_connect_data ((app->area), ("paint"), (((GCallback ) (on_area_paint))), (app), ((void*)0), (GConnectFlags) 0); | |||
| 2474 | g_signal_connect (app->area, "viewport_changed",g_signal_connect_data ((app->area), ("viewport_changed"), ( ((GCallback) (on_viewport_changed))), (app), ((void*)0), (GConnectFlags ) 0) | |||
| 2475 | G_CALLBACK (on_viewport_changed), app)g_signal_connect_data ((app->area), ("viewport_changed"), ( ((GCallback) (on_viewport_changed))), (app), ((void*)0), (GConnectFlags ) 0); | |||
| 2476 | ||||
| 2477 | align = _ctk_builder_get_widget (builder, "align"); | |||
| 2478 | ||||
| 2479 | ctk_container_add (CTK_CONTAINER (align)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((align)), ((ctk_container_get_type ())))))), app->area); | |||
| 2480 | ||||
| 2481 | app->apply_button = _ctk_builder_get_widget (builder, "apply_button"); | |||
| 2482 | g_signal_connect (app->apply_button, "clicked",g_signal_connect_data ((app->apply_button), ("clicked"), ( ((GCallback) (apply_button_clicked_cb))), (app), ((void*)0), ( GConnectFlags) 0) | |||
| 2483 | G_CALLBACK (apply_button_clicked_cb), app)g_signal_connect_data ((app->apply_button), ("clicked"), ( ((GCallback) (apply_button_clicked_cb))), (app), ((void*)0), ( GConnectFlags) 0); | |||
| 2484 | ||||
| 2485 | on_screen_changed (app->screen, app); | |||
| 2486 | ||||
| 2487 | g_object_unref (builder); | |||
| 2488 | ||||
| 2489 | restart: | |||
| 2490 | switch (ctk_dialog_run (CTK_DIALOG (app->dialog)((((CtkDialog*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->dialog)), ((ctk_dialog_get_type ())))))))) | |||
| 2491 | { | |||
| 2492 | default: | |||
| 2493 | /* Fall Through */ | |||
| 2494 | case CTK_RESPONSE_DELETE_EVENT: | |||
| 2495 | case CTK_RESPONSE_CLOSE: | |||
| 2496 | #if 0 | |||
| 2497 | g_debug ("Close"); | |||
| 2498 | #endif | |||
| 2499 | break; | |||
| 2500 | ||||
| 2501 | case CTK_RESPONSE_HELP: | |||
| 2502 | ctk_show_uri_on_window (CTK_WINDOW (app->dialog)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((app->dialog)), ((ctk_window_get_type ())))))), | |||
| 2503 | "help:cafe-user-guide/goscustdesk-70", | |||
| 2504 | ctk_get_current_event_time (), | |||
| 2505 | &error); | |||
| 2506 | if (error) | |||
| 2507 | { | |||
| 2508 | error_message (app, _("Could not open help content")gettext ("Could not open help content"), error->message); | |||
| 2509 | g_error_free (error); | |||
| 2510 | } | |||
| 2511 | goto restart; | |||
| 2512 | break; | |||
| 2513 | ||||
| 2514 | case CTK_RESPONSE_APPLY: | |||
| 2515 | apply (app); | |||
| 2516 | goto restart; | |||
| 2517 | break; | |||
| 2518 | ||||
| 2519 | case RESPONSE_MAKE_DEFAULT: | |||
| 2520 | make_default (app); | |||
| 2521 | goto restart; | |||
| 2522 | break; | |||
| 2523 | } | |||
| 2524 | ||||
| 2525 | ctk_widget_destroy (app->dialog); | |||
| 2526 | g_object_unref (app->screen); | |||
| 2527 | g_object_unref (app->settings); | |||
| 2528 | } | |||
| 2529 | ||||
| 2530 | int | |||
| 2531 | main (int argc, char **argv) | |||
| 2532 | { | |||
| 2533 | App *app; | |||
| 2534 | ||||
| 2535 | capplet_init (NULL((void*)0), &argc, &argv); | |||
| 2536 | ||||
| 2537 | app = g_new0 (App, 1)((App *) g_malloc0_n ((1), sizeof (App))); | |||
| 2538 | ||||
| 2539 | g_object_set (ctk_settings_get_default (), "ctk-button-images", TRUE(!(0)), NULL((void*)0)); | |||
| 2540 | ||||
| 2541 | run_application (app); | |||
| 2542 | ||||
| 2543 | g_free (app); | |||
| 2544 | ||||
| 2545 | return 0; | |||
| 2546 | } |