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 | } |