GCC Code Coverage Report


Directory: ./
File: panels/color/cc-color-calibrate.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 378 0.0%
Functions: 0 32 0.0%
Branches: 0 161 0.0%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2012 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "config.h"
23
24 #include <colord-gtk.h>
25 #include <gio/gunixfdlist.h>
26 #include <glib/gi18n.h>
27 #include <glib-object.h>
28 #include <math.h>
29 #include <colord-session/cd-session.h>
30
31 #define GNOME_DESKTOP_USE_UNSTABLE_API
32 #include <gnome-rr/gnome-rr.h>
33
34 #include "cc-color-calibrate.h"
35
36 #define CALIBRATE_WINDOW_OPACITY 0.9
37
38 struct _CcColorCalibrate
39 {
40 GObject parent_instance;
41
42 CdDevice *device;
43 CdSensorCap device_kind;
44 CdSensor *sensor;
45 CdProfile *profile;
46 gchar *title;
47 GDBusProxy *proxy_helper;
48 GDBusProxy *proxy_inhibit;
49 GMainLoop *loop;
50 GnomeRROutput *output;
51 GnomeRRScreen *x11_screen;
52 GtkBuilder *builder;
53 GtkWindow *window;
54 GtkWidget *sample_widget;
55 guint gamma_size;
56 CdProfileQuality quality;
57 guint target_whitepoint; /* in Kelvin */
58 gdouble target_gamma;
59 gint inhibit_fd;
60 gint inhibit_cookie;
61 CdSessionError session_error_code;
62 };
63
64 #define CD_SESSION_ERROR cc_color_calibrate_error_quark()
65
66 #define COLORD_SETTINGS_SCHEMA "org.freedesktop.ColorHelper"
67
68 G_DEFINE_TYPE (CcColorCalibrate, cc_color_calibrate, G_TYPE_OBJECT)
69
70 static GQuark
71 cc_color_calibrate_error_quark (void)
72 {
73 static GQuark quark = 0;
74 if (!quark)
75 quark = g_quark_from_static_string ("CcColorCalibrateError");
76 return quark;
77 }
78
79 void
80 cc_color_calibrate_set_kind (CcColorCalibrate *calibrate,
81 CdSensorCap kind)
82 {
83 g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
84 calibrate->device_kind = kind;
85 }
86
87 void
88 cc_color_calibrate_set_temperature (CcColorCalibrate *calibrate,
89 guint temperature)
90 {
91 g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
92 g_return_if_fail (temperature < 10000);
93 calibrate->target_whitepoint = temperature;
94 }
95
96 void
97 cc_color_calibrate_set_quality (CcColorCalibrate *calibrate,
98 CdProfileQuality quality)
99 {
100 g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
101 calibrate->quality = quality;
102 }
103
104 CdProfileQuality
105 cc_color_calibrate_get_quality (CcColorCalibrate *calibrate)
106 {
107 g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), 0);
108 return calibrate->quality;
109 }
110
111 void
112 cc_color_calibrate_set_device (CcColorCalibrate *calibrate,
113 CdDevice *device)
114 {
115 g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
116 g_return_if_fail (CD_IS_DEVICE (device));
117 if (calibrate->device != NULL)
118 g_object_unref (calibrate->device);
119 calibrate->device = g_object_ref (device);
120 }
121
122 void
123 cc_color_calibrate_set_sensor (CcColorCalibrate *calibrate,
124 CdSensor *sensor)
125 {
126 g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
127 g_return_if_fail (CD_IS_SENSOR (sensor));
128 if (calibrate->sensor != NULL)
129 g_object_unref (calibrate->sensor);
130 calibrate->sensor = g_object_ref (sensor);
131 }
132
133 void
134 cc_color_calibrate_set_title (CcColorCalibrate *calibrate,
135 const gchar *title)
136 {
137 g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
138 g_return_if_fail (title != NULL);
139 g_free (calibrate->title);
140 calibrate->title = g_strdup (title);
141 }
142
143 CdProfile *
144 cc_color_calibrate_get_profile (CcColorCalibrate *calibrate)
145 {
146 g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), NULL);
147 return calibrate->profile;
148 }
149
150 static guint
151 _gnome_rr_output_get_gamma_size (GnomeRROutput *output)
152 {
153 GnomeRRCrtc *crtc;
154 gint len = 0;
155
156 crtc = gnome_rr_output_get_crtc (output);
157 if (crtc == NULL)
158 return 0;
159 gnome_rr_crtc_get_gamma (crtc,
160 &len,
161 NULL, NULL, NULL);
162 return (guint) len;
163 }
164
165 static gboolean
166 cc_color_calibrate_calib_setup_screen (CcColorCalibrate *calibrate,
167 const gchar *name,
168 GError **error)
169 {
170 gboolean ret = TRUE;
171
172 /* get screen */
173 calibrate->x11_screen = gnome_rr_screen_new (gdk_display_get_default (), error);
174 if (calibrate->x11_screen == NULL)
175 {
176 ret = FALSE;
177 goto out;
178 }
179
180 /* get the output */
181 calibrate->output = gnome_rr_screen_get_output_by_name (calibrate->x11_screen,
182 name);
183 if (calibrate->output == NULL)
184 {
185 ret = FALSE;
186 g_set_error_literal (error,
187 CD_SESSION_ERROR,
188 CD_SESSION_ERROR_INTERNAL,
189 "failed to get output");
190 goto out;
191 }
192
193 /* create a lookup table */
194 calibrate->gamma_size = _gnome_rr_output_get_gamma_size (calibrate->output);
195 if (calibrate->gamma_size == 0)
196 {
197 ret = FALSE;
198 g_set_error_literal (error,
199 CD_SESSION_ERROR,
200 CD_SESSION_ERROR_INTERNAL,
201 "gamma size is zero");
202 }
203 out:
204 return ret;
205 }
206
207 /**
208 * cc_color_calibrate_calib_set_output_gamma:
209 *
210 * Handle this here rather than in gnome-settings-daemon for two reasons:
211 *
212 * - We don't want to create a profile each time the video card gamma
213 * table is created, as that would mean ~15 DBus requests each time
214 * we get UpdateGamma from the session helper.
215 *
216 * - We only have 100ms to process the request before the next update
217 * could be scheduled.
218 **/
219 static gboolean
220 cc_color_calibrate_calib_set_output_gamma (CcColorCalibrate *calibrate,
221 GPtrArray *array,
222 GError **error)
223 {
224 CdColorRGB *p1;
225 CdColorRGB *p2;
226 CdColorRGB result;
227 gdouble mix;
228 GnomeRRCrtc *crtc;
229 g_autofree guint16 *blue = NULL;
230 g_autofree guint16 *green = NULL;
231 g_autofree guint16 *red = NULL;
232 guint i;
233
234 /* no length? */
235 if (array->len == 0)
236 {
237 g_set_error_literal (error,
238 CD_SESSION_ERROR,
239 CD_SESSION_ERROR_INTERNAL,
240 "no data in the CLUT array");
241 return FALSE;
242 }
243
244 /* convert to a type X understands of the right size */
245 red = g_new (guint16, calibrate->gamma_size);
246 green = g_new (guint16, calibrate->gamma_size);
247 blue = g_new (guint16, calibrate->gamma_size);
248 cd_color_rgb_set (&result, 1.0, 1.0, 1.0);
249 for (i = 0; i < calibrate->gamma_size; i++)
250 {
251 mix = (gdouble) (array->len - 1) /
252 (gdouble) (calibrate->gamma_size - 1) *
253 (gdouble) i;
254 p1 = g_ptr_array_index (array, (guint) floor (mix));
255 p2 = g_ptr_array_index (array, (guint) ceil (mix));
256 cd_color_rgb_interpolate (p1,
257 p2,
258 mix - (gint) mix,
259 &result);
260 red[i] = result.R * 0xffff;
261 green[i] = result.G * 0xffff;
262 blue[i] = result.B * 0xffff;
263 }
264
265 /* send to LUT */
266 crtc = gnome_rr_output_get_crtc (calibrate->output);
267 if (crtc == NULL)
268 {
269 g_set_error (error,
270 CD_SESSION_ERROR,
271 CD_SESSION_ERROR_INTERNAL,
272 "failed to get ctrc for %s",
273 gnome_rr_output_get_name (calibrate->output));
274 return FALSE;
275 }
276 gnome_rr_crtc_set_gamma (crtc, calibrate->gamma_size,
277 red, green, blue);
278 return TRUE;
279 }
280
281 static void
282 cc_color_calibrate_property_changed_cb (CcColorCalibrate *calibrate,
283 GVariant *changed_properties,
284 GStrv invalidated_properties)
285 {
286 gboolean ret;
287 GtkWidget *widget;
288 guint value;
289
290 ret = g_variant_lookup (changed_properties,
291 "Progress",
292 "u", &value);
293 if (ret)
294 {
295 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
296 "progressbar_status"));
297 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widget),
298 value / 100.0f);
299 }
300 }
301
302 static void
303 cc_color_calibrate_interaction_required (CcColorCalibrate *calibrate,
304 CdSessionInteraction code,
305 const gchar *message,
306 const gchar *image_path)
307 {
308 const gchar *message_transl;
309 gboolean show_button_start = FALSE;
310 GtkImage *img;
311 GtkLabel *label;
312 GtkWidget *widget;
313
314 /* the client helper does not ship an icon for this */
315 if (code == CD_SESSION_INTERACTION_SHUT_LAPTOP_LID)
316 image_path = "org.gnome.Settings-color-symbolic";
317
318 /* set image */
319 img = GTK_IMAGE (gtk_builder_get_object (calibrate->builder,
320 "image_status"));
321 if (image_path != NULL && image_path[0] != '\0')
322 {
323 g_autoptr(GdkPixbuf) pixbuf = NULL;
324
325 g_debug ("showing image %s", image_path);
326 pixbuf = gdk_pixbuf_new_from_file_at_size (image_path,
327 400, 400,
328 NULL);
329 if (pixbuf != NULL)
330 gtk_image_set_from_pixbuf (img, pixbuf);
331 gtk_widget_set_visible (GTK_WIDGET (img), TRUE);
332 gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), FALSE);
333 }
334 else
335 {
336 g_debug ("hiding image");
337 gtk_widget_set_visible (GTK_WIDGET (img), FALSE);
338 gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), TRUE);
339 }
340
341 /* set new status */
342 switch (code)
343 {
344 case CD_SESSION_INTERACTION_ATTACH_TO_SCREEN:
345 show_button_start = TRUE;
346 /* TRANSLATORS: The user has to attach the sensor to the screen */
347 message_transl = _("Place your calibration device over the square and press “Start”");
348 break;
349 case CD_SESSION_INTERACTION_MOVE_TO_CALIBRATION:
350 /* TRANSLATORS: Some calibration devices need the user to move a
351 * dial or switch manually. We also show a picture showing them
352 * what to do... */
353 message_transl = _("Move your calibration device to the calibrate position and press “Continue”");
354 break;
355 case CD_SESSION_INTERACTION_MOVE_TO_SURFACE:
356 /* TRANSLATORS: Some calibration devices need the user to move a
357 * dial or switch manually. We also show a picture showing them
358 * what to do... */
359 message_transl = _("Move your calibration device to the surface position and press “Continue”");
360 break;
361 case CD_SESSION_INTERACTION_SHUT_LAPTOP_LID:
362 /* TRANSLATORS: on some hardware e.g. Lenovo W700 the sensor
363 * is built into the palmrest and we need to fullscreen the
364 * sample widget and shut the lid. */
365 message_transl = _("Shut the laptop lid");
366 break;
367 default:
368 message_transl = message;
369 break;
370 }
371 label = GTK_LABEL (gtk_builder_get_object (calibrate->builder,
372 "label_status"));
373 gtk_label_set_label (label, message_transl);
374
375 /* show the correct button */
376 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
377 "button_start"));
378 gtk_widget_set_visible (widget, show_button_start);
379 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
380 "button_resume"));
381 gtk_widget_set_visible (widget, !show_button_start);
382 }
383
384 static const gchar *
385 cc_color_calibrate_get_error_translation (CdSessionError code)
386 {
387 const gchar *str = NULL;
388 switch (code)
389 {
390 case CD_SESSION_ERROR_FAILED_TO_FIND_DEVICE:
391 case CD_SESSION_ERROR_FAILED_TO_FIND_SENSOR:
392 case CD_SESSION_ERROR_INTERNAL:
393 case CD_SESSION_ERROR_INVALID_VALUE:
394 /* TRANSLATORS: We suck, the calibration failed and we have no
395 * good idea why or any suggestions */
396 str = _("An internal error occurred that could not be recovered.");
397 break;
398 case CD_SESSION_ERROR_FAILED_TO_FIND_TOOL:
399 /* TRANSLATORS: Some required-at-runtime tools were not
400 * installed, which should only affect insane distros */
401 str = _("Tools required for calibration are not installed.");
402 break;
403 case CD_SESSION_ERROR_FAILED_TO_GENERATE_PROFILE:
404 case CD_SESSION_ERROR_FAILED_TO_OPEN_PROFILE:
405 case CD_SESSION_ERROR_FAILED_TO_SAVE_PROFILE:
406 /* TRANSLATORS: The profile failed for some reason */
407 str = _("The profile could not be generated.");
408 break;
409 case CD_SESSION_ERROR_FAILED_TO_GET_WHITEPOINT:
410 /* TRANSLATORS: The user specified a whitepoint that was
411 * unobtainable with the hardware they've got -- see
412 * https://en.wikipedia.org/wiki/White_point for details */
413 str = _("The target whitepoint was not obtainable.");
414 break;
415 default:
416 break;
417 }
418 return str;
419 }
420
421 static void
422 cc_color_calibrate_finished (CcColorCalibrate *calibrate,
423 CdSessionError code,
424 const gchar *error_fallback)
425 {
426 GtkWidget *widget;
427 g_autoptr(GString) str = NULL;
428 const gchar *tmp;
429
430 /* save failure so we can get this after we've quit the loop */
431 calibrate->session_error_code = code;
432
433 /* show correct buttons */
434 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
435 "button_cancel"));
436 gtk_widget_set_visible (widget, FALSE);
437 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
438 "button_start"));
439 gtk_widget_set_visible (widget, FALSE);
440 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
441 "button_resume"));
442 gtk_widget_set_visible (widget, FALSE);
443 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
444 "button_done"));
445
446 str = g_string_new ("");
447 if (code == CD_SESSION_ERROR_NONE)
448 {
449 g_debug ("calibration succeeded");
450 /* TRANSLATORS: the display calibration process is finished */
451 g_string_append (str, _("Complete!"));
452 }
453 else
454 {
455 g_warning ("calibration failed with code %i: %s",
456 code, error_fallback);
457 /* TRANSLATORS: the display calibration failed, and we also show
458 * the translated (or untranslated) error string after this */
459 g_string_append (str, _("Calibration failed!"));
460 g_string_append (str, "\n\n");
461 tmp = cc_color_calibrate_get_error_translation (code);
462 g_string_append (str, tmp != NULL ? tmp : error_fallback);
463 }
464 g_string_append (str, "\n");
465 /* TRANSLATORS: The user can now remove the sensor from the screen */
466 g_string_append (str, _("You can remove the calibration device."));
467
468 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
469 "label_status"));
470 gtk_label_set_label (GTK_LABEL (widget), str->str);
471 }
472
473 static void
474 cc_color_calibrate_signal_cb (CcColorCalibrate *calibrate,
475 const gchar *sender_name,
476 const gchar *signal_name,
477 GVariant *parameters)
478 {
479 CdColorRGB color;
480 CdColorRGB *color_tmp;
481 const gchar *image = NULL;
482 const gchar *message;
483 const gchar *profile_path = NULL;
484 const gchar *str = NULL;
485 gboolean ret;
486 g_autoptr(GError) error = NULL;
487 GPtrArray *array = NULL;
488 GtkImage *img;
489 GtkLabel *label;
490 g_autoptr(GVariant) dict = NULL;
491
492 if (g_strcmp0 (signal_name, "Finished") == 0)
493 {
494 CdSessionError error_code;
495
496 g_variant_get (parameters, "(u@a{sv})",
497 &error_code,
498 &dict);
499 g_variant_lookup (dict, "ErrorDetails", "&s", &str);
500 ret = g_variant_lookup (dict, "ProfilePath", "&s", &profile_path);
501 if (ret)
502 calibrate->profile = cd_profile_new_with_object_path (profile_path);
503 cc_color_calibrate_finished (calibrate, error_code, str);
504 return;
505 }
506 if (g_strcmp0 (signal_name, "UpdateSample") == 0)
507 {
508 g_variant_get (parameters, "(ddd)",
509 &color.R,
510 &color.G,
511 &color.B);
512 img = GTK_IMAGE (gtk_builder_get_object (calibrate->builder,
513 "image_status"));
514 gtk_widget_set_visible (GTK_WIDGET (img), FALSE);
515 gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), TRUE);
516 cd_sample_widget_set_color (CD_SAMPLE_WIDGET (calibrate->sample_widget),
517 &color);
518
519 /* for Lenovo W700 and W520 laptops we almost fullscreen the
520 * sample widget as the device is actually embedded in the
521 * palmrest! */
522 if (cd_sensor_get_embedded (calibrate->sensor))
523 {
524 g_debug ("Making sample window larger for embedded sensor");
525 gtk_widget_set_size_request (calibrate->sample_widget, 1000, 600);
526 }
527
528 /* set the generic label too */
529 label = GTK_LABEL (gtk_builder_get_object (calibrate->builder,
530 "label_status"));
531 /* TRANSLATORS: The user has to be careful not to knock the
532 * display off the screen (although we do cope if this is
533 * detected early enough) */
534 gtk_label_set_label (label, _("Do not disturb the calibration device while in progress"));
535 return;
536 }
537 if (g_strcmp0 (signal_name, "InteractionRequired") == 0)
538 {
539 CdSessionInteraction code;
540
541 g_variant_get (parameters, "(u&s&s)",
542 &code,
543 &message,
544 &image);
545 g_debug ("Interaction required type %i: %s",
546 code, message);
547 cc_color_calibrate_interaction_required (calibrate,
548 code,
549 message,
550 image);
551 return;
552 }
553 if (g_strcmp0 (signal_name, "UpdateGamma") == 0)
554 {
555 g_autoptr(GVariantIter) iter = NULL;
556
557 g_variant_get (parameters,
558 "(a(ddd))",
559 &iter);
560 array = g_ptr_array_new_with_free_func (g_free);
561 while (g_variant_iter_loop (iter, "(ddd)",
562 &color.R,
563 &color.G,
564 &color.B))
565 {
566 color_tmp = cd_color_rgb_new ();
567 cd_color_rgb_copy (&color, color_tmp);
568 g_ptr_array_add (array, color_tmp);
569 }
570 ret = cc_color_calibrate_calib_set_output_gamma (calibrate,
571 array,
572 &error);
573 if (!ret)
574 {
575 g_warning ("failed to update gamma: %s",
576 error->message);
577 return;
578 }
579 return;
580 }
581 g_warning ("got unknown signal %s", signal_name);
582 }
583
584 static void
585 cc_color_calibrate_cancel (CcColorCalibrate *calibrate)
586 {
587 g_autoptr(GVariant) retval = NULL;
588 g_autoptr(GError) error = NULL;
589
590 /* cancel the calibration to ensure the helper quits */
591 retval = g_dbus_proxy_call_sync (calibrate->proxy_helper,
592 "Cancel",
593 NULL,
594 G_DBUS_CALL_FLAGS_NONE,
595 -1,
596 NULL,
597 &error);
598 if (retval == NULL)
599 g_warning ("Failed to send Cancel: %s", error->message);
600
601 /* return */
602 g_main_loop_quit (calibrate->loop);
603 }
604
605 static void
606 cc_color_calibrate_button_done_cb (CcColorCalibrate *calibrate)
607 {
608 g_main_loop_quit (calibrate->loop);
609 }
610
611 static void
612 cc_color_calibrate_button_start_cb (CcColorCalibrate *calibrate)
613 {
614 GtkWidget *widget;
615 g_autoptr(GError) error = NULL;
616 g_autoptr(GVariant) retval = NULL;
617
618 /* set correct buttons */
619 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
620 "button_start"));
621 gtk_widget_set_visible (widget, FALSE);
622 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
623 "button_resume"));
624 gtk_widget_set_visible (widget, FALSE);
625
626 /* continue */
627 retval = g_dbus_proxy_call_sync (calibrate->proxy_helper,
628 "Resume",
629 NULL,
630 G_DBUS_CALL_FLAGS_NONE,
631 -1,
632 NULL,
633 &error);
634 if (retval == NULL)
635 g_warning ("Failed to send Resume: %s", error->message);
636 }
637
638 static void
639 cc_color_calibrate_button_cancel_cb (CcColorCalibrate *calibrate)
640 {
641 cc_color_calibrate_cancel (calibrate);
642 }
643
644 #if 0
645 static gboolean
646 cc_color_calibrate_alpha_window_draw (CcColorCalibrate *calibrate, cairo_t *cr)
647 {
648 GtkWidget *widget;
649
650 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
651 "dialog_calibrate"));
652
653 if (gdk_screen_get_rgba_visual (gtk_widget_get_screen (widget)) &&
654 gdk_screen_is_composited (gtk_widget_get_screen (widget)))
655 {
656 /* transparent */
657 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, CALIBRATE_WINDOW_OPACITY);
658 }
659 else
660 {
661 /* opaque black */
662 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
663 }
664 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
665 cairo_paint (cr);
666 return FALSE;
667 }
668
669 static void
670 cc_color_calibrate_alpha_screen_changed_cb (CcColorCalibrate *calibrate)
671 {
672 GtkWidget *window;
673 GdkScreen *screen;
674 GdkVisual *visual;
675
676 window = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
677 "dialog_calibrate"));
678 screen = gtk_widget_get_screen (GTK_WIDGET (window));
679 visual = gdk_screen_get_rgba_visual (screen);
680 if (visual == NULL)
681 visual = gdk_screen_get_system_visual (screen);
682 gtk_widget_set_visual (GTK_WIDGET (window), visual);
683 }
684 #endif
685
686 static void
687 cc_color_calibrate_uninhibit (CcColorCalibrate *calibrate)
688 {
689 GtkApplication *application;
690
691 if (calibrate->inhibit_fd != -1)
692 {
693 close (calibrate->inhibit_fd);
694 calibrate->inhibit_fd = -1;
695 }
696
697 if (calibrate->inhibit_cookie != 0)
698 {
699 application = GTK_APPLICATION (g_application_get_default ());
700 gtk_application_uninhibit (application, calibrate->inhibit_cookie);
701 calibrate->inhibit_cookie = 0;
702 }
703 }
704
705 static void
706 cc_color_calibrate_inhibit (CcColorCalibrate *calibrate, GtkWindow *window)
707 {
708 g_autoptr(GError) error = NULL;
709 gint idx;
710 g_autoptr(GUnixFDList) fd_list = NULL;
711 g_autoptr(GVariant) retval = NULL;
712 GtkApplication *application;
713
714 /* inhibit basically everything we can */
715 application = GTK_APPLICATION (g_application_get_default ());
716 calibrate->inhibit_cookie = gtk_application_inhibit (application,
717 window,
718 GTK_APPLICATION_INHIBIT_LOGOUT |
719 GTK_APPLICATION_INHIBIT_SWITCH |
720 GTK_APPLICATION_INHIBIT_SUSPEND |
721 GTK_APPLICATION_INHIBIT_IDLE,
722 "Display calibration in progress");
723
724 /* tell logind to disallow the lid switch */
725 retval = g_dbus_proxy_call_with_unix_fd_list_sync (calibrate->proxy_inhibit,
726 "Inhibit",
727 g_variant_new ("(ssss)",
728 "shutdown:"
729 "sleep:"
730 "idle:"
731 "handle-lid-switch",
732 "Display Calibrator",
733 "Display calibration in progress",
734 "block"),
735 G_DBUS_CALL_FLAGS_NONE,
736 -1,
737 NULL,
738 &fd_list,
739 NULL,
740 &error);
741 if (retval == NULL)
742 {
743 g_warning ("Failed to send Inhibit: %s", error->message);
744 return;
745 }
746 g_variant_get (retval, "(h)", &idx);
747 calibrate->inhibit_fd = g_unix_fd_list_get (fd_list, idx, &error);
748 if (calibrate->inhibit_fd == -1)
749 {
750 g_warning ("Failed to receive system inhibitor fd: %s", error->message);
751 return;
752 }
753 g_debug ("System inhibitor fd is %d", calibrate->inhibit_fd);
754 }
755
756 gboolean
757 cc_color_calibrate_setup (CcColorCalibrate *calibrate,
758 GError **error)
759 {
760 gboolean ret = TRUE;
761
762 g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), FALSE);
763 g_return_val_if_fail (calibrate->device_kind != CD_SENSOR_CAP_UNKNOWN, FALSE);
764
765 /* use logind to disable system state idle */
766 calibrate->proxy_inhibit = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
767 G_DBUS_PROXY_FLAGS_NONE,
768 NULL,
769 "org.freedesktop.login1",
770 "/org/freedesktop/login1",
771 "org.freedesktop.login1.Manager",
772 NULL,
773 error);
774 if (calibrate->proxy_inhibit == NULL)
775 {
776 ret = FALSE;
777 goto out;
778 }
779
780 /* start the calibration session daemon */
781 calibrate->proxy_helper = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
782 G_DBUS_PROXY_FLAGS_NONE,
783 NULL,
784 CD_SESSION_DBUS_SERVICE,
785 CD_SESSION_DBUS_PATH,
786 CD_SESSION_DBUS_INTERFACE_DISPLAY,
787 NULL,
788 error);
789 if (calibrate->proxy_helper == NULL)
790 {
791 ret = FALSE;
792 goto out;
793 }
794 g_signal_connect_object (calibrate->proxy_helper,
795 "g-properties-changed",
796 G_CALLBACK (cc_color_calibrate_property_changed_cb),
797 calibrate, G_CONNECT_SWAPPED);
798 g_signal_connect_object (calibrate->proxy_helper,
799 "g-signal",
800 G_CALLBACK (cc_color_calibrate_signal_cb),
801 calibrate, G_CONNECT_SWAPPED);
802 out:
803 return ret;
804 }
805
806 gboolean
807 cc_color_calibrate_start (CcColorCalibrate *calibrate,
808 GtkWindow *parent,
809 GError **error)
810 {
811 const gchar *name;
812 GtkWidget *widget;
813 GtkWindow *window;
814 GVariantBuilder builder;
815 g_autoptr(GVariant) retval = NULL;
816
817 g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), FALSE);
818
819 /* get screen */
820 name = cd_device_get_metadata_item (calibrate->device,
821 CD_DEVICE_METADATA_XRANDR_NAME);
822 if (!cc_color_calibrate_calib_setup_screen (calibrate, name, error))
823 return FALSE;
824
825 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
826 g_variant_builder_add (&builder,
827 "{sv}",
828 "Quality",
829 g_variant_new_uint32 (calibrate->quality));
830 g_variant_builder_add (&builder,
831 "{sv}",
832 "Whitepoint",
833 g_variant_new_uint32 (calibrate->target_whitepoint));
834 g_variant_builder_add (&builder,
835 "{sv}",
836 "Gamma",
837 g_variant_new_double (calibrate->target_gamma));
838 g_variant_builder_add (&builder,
839 "{sv}",
840 "Title",
841 g_variant_new_string (calibrate->title));
842 g_variant_builder_add (&builder,
843 "{sv}",
844 "DeviceKind",
845 g_variant_new_uint32 (calibrate->device_kind));
846 retval = g_dbus_proxy_call_sync (calibrate->proxy_helper,
847 "Start",
848 g_variant_new ("(ssa{sv})",
849 cd_device_get_id (calibrate->device),
850 cd_sensor_get_id (calibrate->sensor),
851 &builder),
852 G_DBUS_CALL_FLAGS_NONE,
853 -1,
854 NULL,
855 error);
856 if (retval == NULL)
857 return FALSE;
858
859 /* set this above our parent */
860 window = GTK_WINDOW (gtk_builder_get_object (calibrate->builder,
861 "dialog_calibrate"));
862 gtk_window_set_modal (window, TRUE);
863 gtk_window_present (window);
864
865 /* show correct buttons */
866 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
867 "button_cancel"));
868 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
869 "button_start"));
870 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
871 "button_resume"));
872 gtk_widget_set_visible (widget, FALSE);
873 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
874 "button_done"));
875 gtk_widget_set_visible (widget, FALSE);
876
877 /* stop the computer from auto-suspending or turning off the screen */
878 cc_color_calibrate_inhibit (calibrate, parent);
879
880 g_main_loop_run (calibrate->loop);
881 gtk_window_close (window);
882
883 /* we can go idle now */
884 cc_color_calibrate_uninhibit (calibrate);
885
886 /* see if we failed */
887 if (calibrate->session_error_code != CD_SESSION_ERROR_NONE)
888 {
889 g_set_error_literal (error,
890 CD_SESSION_ERROR,
891 CD_SESSION_ERROR_INTERNAL,
892 "failed to calibrate");
893 return FALSE;
894 }
895
896 return TRUE;
897 }
898
899 static void
900 cc_color_calibrate_finalize (GObject *object)
901 {
902 CcColorCalibrate *calibrate = CC_COLOR_CALIBRATE (object);
903
904 g_clear_pointer (&calibrate->window, gtk_window_destroy);
905 g_clear_object (&calibrate->builder);
906 g_clear_object (&calibrate->device);
907 g_clear_object (&calibrate->proxy_helper);
908 g_clear_object (&calibrate->proxy_inhibit);
909 g_clear_object (&calibrate->sensor);
910 g_clear_object (&calibrate->x11_screen);
911 g_free (calibrate->title);
912 g_main_loop_unref (calibrate->loop);
913
914 G_OBJECT_CLASS (cc_color_calibrate_parent_class)->finalize (object);
915 }
916
917 static void
918 cc_color_calibrate_class_init (CcColorCalibrateClass *klass)
919 {
920 GObjectClass *object_class = G_OBJECT_CLASS (klass);
921 object_class->finalize = cc_color_calibrate_finalize;
922 }
923
924 static void
925 cc_color_calibrate_init (CcColorCalibrate *calibrate)
926 {
927 g_autoptr(GError) error = NULL;
928 gint retval;
929 g_autoptr(GSettings) settings = NULL;
930 GtkBox *box;
931 GtkWidget *widget;
932 GtkWindow *window;
933
934 calibrate->loop = g_main_loop_new (NULL, FALSE);
935 calibrate->inhibit_fd = -1;
936
937 /* load UI */
938 calibrate->builder = gtk_builder_new ();
939 retval = gtk_builder_add_from_resource (calibrate->builder,
940 "/org/gnome/control-center/color/cc-color-calibrate.ui",
941 &error);
942 if (retval == 0)
943 g_warning ("Could not load interface: %s", error->message);
944
945 /* add sample widget */
946 box = GTK_BOX (gtk_builder_get_object (calibrate->builder,
947 "vbox_status"));
948 calibrate->sample_widget = cd_sample_widget_new ();
949 gtk_widget_set_size_request (calibrate->sample_widget, 400, 400);
950 gtk_box_prepend (box, calibrate->sample_widget);
951 gtk_widget_set_vexpand (calibrate->sample_widget, FALSE);
952 gtk_widget_set_hexpand (calibrate->sample_widget, FALSE);
953
954 /* get defaults */
955 settings = g_settings_new (COLORD_SETTINGS_SCHEMA);
956 calibrate->target_whitepoint = g_settings_get_int (settings, "display-whitepoint");
957 calibrate->target_gamma = g_settings_get_double (settings, "display-gamma");
958
959 /* connect to buttons */
960 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
961 "button_start"));
962 g_signal_connect_object (widget, "clicked",
963 G_CALLBACK (cc_color_calibrate_button_start_cb), calibrate, G_CONNECT_SWAPPED);
964 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
965 "button_resume"));
966 g_signal_connect_object (widget, "clicked",
967 G_CALLBACK (cc_color_calibrate_button_start_cb), calibrate, G_CONNECT_SWAPPED);
968 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
969 "button_done"));
970 g_signal_connect_object (widget, "clicked",
971 G_CALLBACK (cc_color_calibrate_button_done_cb), calibrate, G_CONNECT_SWAPPED);
972 widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
973 "button_cancel"));
974 g_signal_connect_object (widget, "clicked",
975 G_CALLBACK (cc_color_calibrate_button_cancel_cb), calibrate, G_CONNECT_SWAPPED);
976 gtk_widget_set_visible (widget, TRUE);
977
978 /* setup the specialist calibration window */
979 window = GTK_WINDOW (gtk_builder_get_object (calibrate->builder,
980 "dialog_calibrate"));
981 calibrate->window = window;
982 }
983
984 CcColorCalibrate *
985 cc_color_calibrate_new (void)
986 {
987 CcColorCalibrate *calibrate;
988 calibrate = g_object_new (CC_TYPE_COLOR_CALIBRATE, NULL);
989 return CC_COLOR_CALIBRATE (calibrate);
990 }
991