diff options
Diffstat (limited to 'panels/color/cc-color-calibrate.c')
-rw-r--r-- | panels/color/cc-color-calibrate.c | 993 |
1 files changed, 993 insertions, 0 deletions
diff --git a/panels/color/cc-color-calibrate.c b/panels/color/cc-color-calibrate.c new file mode 100644 index 0000000..dc9d0f0 --- /dev/null +++ b/panels/color/cc-color-calibrate.c @@ -0,0 +1,993 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <colord-gtk.h> +#include <gio/gunixfdlist.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <math.h> +#include <colord-session/cd-session.h> + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <gnome-rr/gnome-rr.h> + +#include "cc-color-calibrate.h" + +#define CALIBRATE_WINDOW_OPACITY 0.9 + +struct _CcColorCalibrate +{ + GObject parent_instance; + + CdDevice *device; + CdSensorCap device_kind; + CdSensor *sensor; + CdProfile *profile; + gchar *title; + GDBusProxy *proxy_helper; + GDBusProxy *proxy_inhibit; + GMainLoop *loop; + GnomeRROutput *output; + GnomeRRScreen *x11_screen; + GtkBuilder *builder; + GtkWindow *window; + GtkWidget *sample_widget; + guint gamma_size; + CdProfileQuality quality; + guint target_whitepoint; /* in Kelvin */ + gdouble target_gamma; + gint inhibit_fd; + gint inhibit_cookie; + CdSessionError session_error_code; +}; + +#define CD_SESSION_ERROR cc_color_calibrate_error_quark() + +#define COLORD_SETTINGS_SCHEMA "org.freedesktop.ColorHelper" + +G_DEFINE_TYPE (CcColorCalibrate, cc_color_calibrate, G_TYPE_OBJECT) + +static GQuark +cc_color_calibrate_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("CcColorCalibrateError"); + return quark; +} + +void +cc_color_calibrate_set_kind (CcColorCalibrate *calibrate, + CdSensorCap kind) +{ + g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate)); + calibrate->device_kind = kind; +} + +void +cc_color_calibrate_set_temperature (CcColorCalibrate *calibrate, + guint temperature) +{ + g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate)); + g_return_if_fail (temperature < 10000); + calibrate->target_whitepoint = temperature; +} + +void +cc_color_calibrate_set_quality (CcColorCalibrate *calibrate, + CdProfileQuality quality) +{ + g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate)); + calibrate->quality = quality; +} + +CdProfileQuality +cc_color_calibrate_get_quality (CcColorCalibrate *calibrate) +{ + g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), 0); + return calibrate->quality; +} + +void +cc_color_calibrate_set_device (CcColorCalibrate *calibrate, + CdDevice *device) +{ + g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate)); + g_return_if_fail (CD_IS_DEVICE (device)); + if (calibrate->device != NULL) + g_object_unref (calibrate->device); + calibrate->device = g_object_ref (device); +} + +void +cc_color_calibrate_set_sensor (CcColorCalibrate *calibrate, + CdSensor *sensor) +{ + g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate)); + g_return_if_fail (CD_IS_SENSOR (sensor)); + if (calibrate->sensor != NULL) + g_object_unref (calibrate->sensor); + calibrate->sensor = g_object_ref (sensor); +} + +void +cc_color_calibrate_set_title (CcColorCalibrate *calibrate, + const gchar *title) +{ + g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate)); + g_return_if_fail (title != NULL); + g_free (calibrate->title); + calibrate->title = g_strdup (title); +} + +CdProfile * +cc_color_calibrate_get_profile (CcColorCalibrate *calibrate) +{ + g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), NULL); + return calibrate->profile; +} + +static guint +_gnome_rr_output_get_gamma_size (GnomeRROutput *output) +{ + GnomeRRCrtc *crtc; + gint len = 0; + + crtc = gnome_rr_output_get_crtc (output); + if (crtc == NULL) + return 0; + gnome_rr_crtc_get_gamma (crtc, + &len, + NULL, NULL, NULL); + return (guint) len; +} + +static gboolean +cc_color_calibrate_calib_setup_screen (CcColorCalibrate *calibrate, + const gchar *name, + GError **error) +{ + gboolean ret = TRUE; + + /* get screen */ + calibrate->x11_screen = gnome_rr_screen_new (gdk_display_get_default (), error); + if (calibrate->x11_screen == NULL) + { + ret = FALSE; + goto out; + } + + /* get the output */ + calibrate->output = gnome_rr_screen_get_output_by_name (calibrate->x11_screen, + name); + if (calibrate->output == NULL) + { + ret = FALSE; + g_set_error_literal (error, + CD_SESSION_ERROR, + CD_SESSION_ERROR_INTERNAL, + "failed to get output"); + goto out; + } + + /* create a lookup table */ + calibrate->gamma_size = _gnome_rr_output_get_gamma_size (calibrate->output); + if (calibrate->gamma_size == 0) + { + ret = FALSE; + g_set_error_literal (error, + CD_SESSION_ERROR, + CD_SESSION_ERROR_INTERNAL, + "gamma size is zero"); + } +out: + return ret; +} + +/** + * cc_color_calibrate_calib_set_output_gamma: + * + * Handle this here rather than in gnome-settings-daemon for two reasons: + * + * - We don't want to create a profile each time the video card gamma + * table is created, as that would mean ~15 DBus requests each time + * we get UpdateGamma from the session helper. + * + * - We only have 100ms to process the request before the next update + * could be scheduled. + **/ +static gboolean +cc_color_calibrate_calib_set_output_gamma (CcColorCalibrate *calibrate, + GPtrArray *array, + GError **error) +{ + CdColorRGB *p1; + CdColorRGB *p2; + CdColorRGB result; + gdouble mix; + GnomeRRCrtc *crtc; + g_autofree guint16 *blue = NULL; + g_autofree guint16 *green = NULL; + g_autofree guint16 *red = NULL; + guint i; + + /* no length? */ + if (array->len == 0) + { + g_set_error_literal (error, + CD_SESSION_ERROR, + CD_SESSION_ERROR_INTERNAL, + "no data in the CLUT array"); + return FALSE; + } + + /* convert to a type X understands of the right size */ + red = g_new (guint16, calibrate->gamma_size); + green = g_new (guint16, calibrate->gamma_size); + blue = g_new (guint16, calibrate->gamma_size); + cd_color_rgb_set (&result, 1.0, 1.0, 1.0); + for (i = 0; i < calibrate->gamma_size; i++) + { + mix = (gdouble) (array->len - 1) / + (gdouble) (calibrate->gamma_size - 1) * + (gdouble) i; + p1 = g_ptr_array_index (array, (guint) floor (mix)); + p2 = g_ptr_array_index (array, (guint) ceil (mix)); + cd_color_rgb_interpolate (p1, + p2, + mix - (gint) mix, + &result); + red[i] = result.R * 0xffff; + green[i] = result.G * 0xffff; + blue[i] = result.B * 0xffff; + } + + /* send to LUT */ + crtc = gnome_rr_output_get_crtc (calibrate->output); + if (crtc == NULL) + { + g_set_error (error, + CD_SESSION_ERROR, + CD_SESSION_ERROR_INTERNAL, + "failed to get ctrc for %s", + gnome_rr_output_get_name (calibrate->output)); + return FALSE; + } + gnome_rr_crtc_set_gamma (crtc, calibrate->gamma_size, + red, green, blue); + return TRUE; +} + +static void +cc_color_calibrate_property_changed_cb (CcColorCalibrate *calibrate, + GVariant *changed_properties, + GStrv invalidated_properties) +{ + gboolean ret; + GtkWidget *widget; + guint value; + + ret = g_variant_lookup (changed_properties, + "Progress", + "u", &value); + if (ret) + { + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "progressbar_status")); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widget), + value / 100.0f); + } +} + +static void +cc_color_calibrate_interaction_required (CcColorCalibrate *calibrate, + CdSessionInteraction code, + const gchar *message, + const gchar *image_path) +{ + const gchar *message_transl; + gboolean show_button_start = FALSE; + GtkImage *img; + GtkLabel *label; + GtkWidget *widget; + + /* the client helper does not ship an icon for this */ + if (code == CD_SESSION_INTERACTION_SHUT_LAPTOP_LID) + image_path = "preferences-color-symbolic"; + + /* set image */ + img = GTK_IMAGE (gtk_builder_get_object (calibrate->builder, + "image_status")); + if (image_path != NULL && image_path[0] != '\0') + { + g_autoptr(GdkPixbuf) pixbuf = NULL; + + g_debug ("showing image %s", image_path); + pixbuf = gdk_pixbuf_new_from_file_at_size (image_path, + 400, 400, + NULL); + if (pixbuf != NULL) + gtk_image_set_from_pixbuf (img, pixbuf); + gtk_widget_set_visible (GTK_WIDGET (img), TRUE); + gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), FALSE); + } + else + { + g_debug ("hiding image"); + gtk_widget_set_visible (GTK_WIDGET (img), FALSE); + gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), TRUE); + } + + /* set new status */ + switch (code) + { + case CD_SESSION_INTERACTION_ATTACH_TO_SCREEN: + show_button_start = TRUE; + /* TRANSLATORS: The user has to attach the sensor to the screen */ + message_transl = _("Place your calibration device over the square and press “Start”"); + break; + case CD_SESSION_INTERACTION_MOVE_TO_CALIBRATION: + /* TRANSLATORS: Some calibration devices need the user to move a + * dial or switch manually. We also show a picture showing them + * what to do... */ + message_transl = _("Move your calibration device to the calibrate position and press “Continue”"); + break; + case CD_SESSION_INTERACTION_MOVE_TO_SURFACE: + /* TRANSLATORS: Some calibration devices need the user to move a + * dial or switch manually. We also show a picture showing them + * what to do... */ + message_transl = _("Move your calibration device to the surface position and press “Continue”"); + break; + case CD_SESSION_INTERACTION_SHUT_LAPTOP_LID: + /* TRANSLATORS: on some hardware e.g. Lenovo W700 the sensor + * is built into the palmrest and we need to fullscreen the + * sample widget and shut the lid. */ + message_transl = _("Shut the laptop lid"); + break; + default: + message_transl = message; + break; + } + label = GTK_LABEL (gtk_builder_get_object (calibrate->builder, + "label_status")); + gtk_label_set_label (label, message_transl); + + /* show the correct button */ + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_start")); + gtk_widget_set_visible (widget, show_button_start); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_resume")); + gtk_widget_set_visible (widget, !show_button_start); +} + +static const gchar * +cc_color_calibrate_get_error_translation (CdSessionError code) +{ + const gchar *str = NULL; + switch (code) + { + case CD_SESSION_ERROR_FAILED_TO_FIND_DEVICE: + case CD_SESSION_ERROR_FAILED_TO_FIND_SENSOR: + case CD_SESSION_ERROR_INTERNAL: + case CD_SESSION_ERROR_INVALID_VALUE: + /* TRANSLATORS: We suck, the calibration failed and we have no + * good idea why or any suggestions */ + str = _("An internal error occurred that could not be recovered."); + break; + case CD_SESSION_ERROR_FAILED_TO_FIND_TOOL: + /* TRANSLATORS: Some required-at-runtime tools were not + * installed, which should only affect insane distros */ + str = _("Tools required for calibration are not installed."); + break; + case CD_SESSION_ERROR_FAILED_TO_GENERATE_PROFILE: + case CD_SESSION_ERROR_FAILED_TO_OPEN_PROFILE: + case CD_SESSION_ERROR_FAILED_TO_SAVE_PROFILE: + /* TRANSLATORS: The profile failed for some reason */ + str = _("The profile could not be generated."); + break; + case CD_SESSION_ERROR_FAILED_TO_GET_WHITEPOINT: + /* TRANSLATORS: The user specified a whitepoint that was + * unobtainable with the hardware they've got -- see + * https://en.wikipedia.org/wiki/White_point for details */ + str = _("The target whitepoint was not obtainable."); + break; + default: + break; + } + return str; +} + +static void +cc_color_calibrate_finished (CcColorCalibrate *calibrate, + CdSessionError code, + const gchar *error_fallback) +{ + GtkWidget *widget; + g_autoptr(GString) str = NULL; + const gchar *tmp; + + /* save failure so we can get this after we've quit the loop */ + calibrate->session_error_code = code; + + /* show correct buttons */ + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_cancel")); + gtk_widget_set_visible (widget, FALSE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_start")); + gtk_widget_set_visible (widget, FALSE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_resume")); + gtk_widget_set_visible (widget, FALSE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_done")); + gtk_widget_set_visible (widget, TRUE); + + str = g_string_new (""); + if (code == CD_SESSION_ERROR_NONE) + { + g_debug ("calibration succeeded"); + /* TRANSLATORS: the display calibration process is finished */ + g_string_append (str, _("Complete!")); + } + else + { + g_warning ("calibration failed with code %i: %s", + code, error_fallback); + /* TRANSLATORS: the display calibration failed, and we also show + * the translated (or untranslated) error string after this */ + g_string_append (str, _("Calibration failed!")); + g_string_append (str, "\n\n"); + tmp = cc_color_calibrate_get_error_translation (code); + g_string_append (str, tmp != NULL ? tmp : error_fallback); + } + g_string_append (str, "\n"); + /* TRANSLATORS: The user can now remove the sensor from the screen */ + g_string_append (str, _("You can remove the calibration device.")); + + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "label_status")); + gtk_label_set_label (GTK_LABEL (widget), str->str); +} + +static void +cc_color_calibrate_signal_cb (CcColorCalibrate *calibrate, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters) +{ + CdColorRGB color; + CdColorRGB *color_tmp; + const gchar *image = NULL; + const gchar *message; + const gchar *profile_path = NULL; + const gchar *str = NULL; + gboolean ret; + g_autoptr(GError) error = NULL; + GPtrArray *array = NULL; + GtkImage *img; + GtkLabel *label; + g_autoptr(GVariant) dict = NULL; + + if (g_strcmp0 (signal_name, "Finished") == 0) + { + CdSessionError error_code; + + g_variant_get (parameters, "(u@a{sv})", + &error_code, + &dict); + g_variant_lookup (dict, "ErrorDetails", "&s", &str); + ret = g_variant_lookup (dict, "ProfilePath", "&s", &profile_path); + if (ret) + calibrate->profile = cd_profile_new_with_object_path (profile_path); + cc_color_calibrate_finished (calibrate, error_code, str); + return; + } + if (g_strcmp0 (signal_name, "UpdateSample") == 0) + { + g_variant_get (parameters, "(ddd)", + &color.R, + &color.G, + &color.B); + img = GTK_IMAGE (gtk_builder_get_object (calibrate->builder, + "image_status")); + gtk_widget_set_visible (GTK_WIDGET (img), FALSE); + gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), TRUE); + cd_sample_widget_set_color (CD_SAMPLE_WIDGET (calibrate->sample_widget), + &color); + + /* for Lenovo W700 and W520 laptops we almost fullscreen the + * sample widget as the device is actually embedded in the + * palmrest! */ + if (cd_sensor_get_embedded (calibrate->sensor)) + { + g_debug ("Making sample window larger for embedded sensor"); + gtk_widget_set_size_request (calibrate->sample_widget, 1000, 600); + } + + /* set the generic label too */ + label = GTK_LABEL (gtk_builder_get_object (calibrate->builder, + "label_status")); + /* TRANSLATORS: The user has to be careful not to knock the + * display off the screen (although we do cope if this is + * detected early enough) */ + gtk_label_set_label (label, _("Do not disturb the calibration device while in progress")); + return; + } + if (g_strcmp0 (signal_name, "InteractionRequired") == 0) + { + CdSessionInteraction code; + + g_variant_get (parameters, "(u&s&s)", + &code, + &message, + &image); + g_debug ("Interaction required type %i: %s", + code, message); + cc_color_calibrate_interaction_required (calibrate, + code, + message, + image); + return; + } + if (g_strcmp0 (signal_name, "UpdateGamma") == 0) + { + g_autoptr(GVariantIter) iter = NULL; + + g_variant_get (parameters, + "(a(ddd))", + &iter); + array = g_ptr_array_new_with_free_func (g_free); + while (g_variant_iter_loop (iter, "(ddd)", + &color.R, + &color.G, + &color.B)) + { + color_tmp = cd_color_rgb_new (); + cd_color_rgb_copy (&color, color_tmp); + g_ptr_array_add (array, color_tmp); + } + ret = cc_color_calibrate_calib_set_output_gamma (calibrate, + array, + &error); + if (!ret) + { + g_warning ("failed to update gamma: %s", + error->message); + return; + } + return; + } + g_warning ("got unknown signal %s", signal_name); +} + +static void +cc_color_calibrate_cancel (CcColorCalibrate *calibrate) +{ + g_autoptr(GVariant) retval = NULL; + g_autoptr(GError) error = NULL; + + /* cancel the calibration to ensure the helper quits */ + retval = g_dbus_proxy_call_sync (calibrate->proxy_helper, + "Cancel", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (retval == NULL) + g_warning ("Failed to send Cancel: %s", error->message); + + /* return */ + g_main_loop_quit (calibrate->loop); +} + +static void +cc_color_calibrate_button_done_cb (CcColorCalibrate *calibrate) +{ + g_main_loop_quit (calibrate->loop); +} + +static void +cc_color_calibrate_button_start_cb (CcColorCalibrate *calibrate) +{ + GtkWidget *widget; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) retval = NULL; + + /* set correct buttons */ + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_start")); + gtk_widget_set_visible (widget, FALSE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_resume")); + gtk_widget_set_visible (widget, FALSE); + + /* continue */ + retval = g_dbus_proxy_call_sync (calibrate->proxy_helper, + "Resume", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (retval == NULL) + g_warning ("Failed to send Resume: %s", error->message); +} + +static void +cc_color_calibrate_button_cancel_cb (CcColorCalibrate *calibrate) +{ + cc_color_calibrate_cancel (calibrate); +} + +#if 0 +static gboolean +cc_color_calibrate_alpha_window_draw (CcColorCalibrate *calibrate, cairo_t *cr) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "dialog_calibrate")); + + if (gdk_screen_get_rgba_visual (gtk_widget_get_screen (widget)) && + gdk_screen_is_composited (gtk_widget_get_screen (widget))) + { + /* transparent */ + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, CALIBRATE_WINDOW_OPACITY); + } + else + { + /* opaque black */ + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + } + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + return FALSE; +} + +static void +cc_color_calibrate_alpha_screen_changed_cb (CcColorCalibrate *calibrate) +{ + GtkWidget *window; + GdkScreen *screen; + GdkVisual *visual; + + window = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "dialog_calibrate")); + screen = gtk_widget_get_screen (GTK_WIDGET (window)); + visual = gdk_screen_get_rgba_visual (screen); + if (visual == NULL) + visual = gdk_screen_get_system_visual (screen); + gtk_widget_set_visual (GTK_WIDGET (window), visual); +} +#endif + +static void +cc_color_calibrate_uninhibit (CcColorCalibrate *calibrate) +{ + GtkApplication *application; + + if (calibrate->inhibit_fd != -1) + { + close (calibrate->inhibit_fd); + calibrate->inhibit_fd = -1; + } + + if (calibrate->inhibit_cookie != 0) + { + application = GTK_APPLICATION (g_application_get_default ()); + gtk_application_uninhibit (application, calibrate->inhibit_cookie); + calibrate->inhibit_cookie = 0; + } +} + +static void +cc_color_calibrate_inhibit (CcColorCalibrate *calibrate, GtkWindow *window) +{ + g_autoptr(GError) error = NULL; + gint idx; + g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(GVariant) retval = NULL; + GtkApplication *application; + + /* inhibit basically everything we can */ + application = GTK_APPLICATION (g_application_get_default ()); + calibrate->inhibit_cookie = gtk_application_inhibit (application, + window, + GTK_APPLICATION_INHIBIT_LOGOUT | + GTK_APPLICATION_INHIBIT_SWITCH | + GTK_APPLICATION_INHIBIT_SUSPEND | + GTK_APPLICATION_INHIBIT_IDLE, + "Display calibration in progress"); + + /* tell logind to disallow the lid switch */ + retval = g_dbus_proxy_call_with_unix_fd_list_sync (calibrate->proxy_inhibit, + "Inhibit", + g_variant_new ("(ssss)", + "shutdown:" + "sleep:" + "idle:" + "handle-lid-switch", + "Display Calibrator", + "Display calibration in progress", + "block"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &fd_list, + NULL, + &error); + if (retval == NULL) + { + g_warning ("Failed to send Inhibit: %s", error->message); + return; + } + g_variant_get (retval, "(h)", &idx); + calibrate->inhibit_fd = g_unix_fd_list_get (fd_list, idx, &error); + if (calibrate->inhibit_fd == -1) + { + g_warning ("Failed to receive system inhibitor fd: %s", error->message); + return; + } + g_debug ("System inhibitor fd is %d", calibrate->inhibit_fd); +} + +gboolean +cc_color_calibrate_setup (CcColorCalibrate *calibrate, + GError **error) +{ + gboolean ret = TRUE; + + g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), FALSE); + g_return_val_if_fail (calibrate->device_kind != CD_SENSOR_CAP_UNKNOWN, FALSE); + + /* use logind to disable system state idle */ + calibrate->proxy_inhibit = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + NULL, + error); + if (calibrate->proxy_inhibit == NULL) + { + ret = FALSE; + goto out; + } + + /* start the calibration session daemon */ + calibrate->proxy_helper = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + CD_SESSION_DBUS_SERVICE, + CD_SESSION_DBUS_PATH, + CD_SESSION_DBUS_INTERFACE_DISPLAY, + NULL, + error); + if (calibrate->proxy_helper == NULL) + { + ret = FALSE; + goto out; + } + g_signal_connect_object (calibrate->proxy_helper, + "g-properties-changed", + G_CALLBACK (cc_color_calibrate_property_changed_cb), + calibrate, G_CONNECT_SWAPPED); + g_signal_connect_object (calibrate->proxy_helper, + "g-signal", + G_CALLBACK (cc_color_calibrate_signal_cb), + calibrate, G_CONNECT_SWAPPED); +out: + return ret; +} + +gboolean +cc_color_calibrate_start (CcColorCalibrate *calibrate, + GtkWindow *parent, + GError **error) +{ + const gchar *name; + GtkWidget *widget; + GtkWindow *window; + GVariantBuilder builder; + g_autoptr(GVariant) retval = NULL; + + g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), FALSE); + + /* get screen */ + name = cd_device_get_metadata_item (calibrate->device, + CD_DEVICE_METADATA_XRANDR_NAME); + if (!cc_color_calibrate_calib_setup_screen (calibrate, name, error)) + return FALSE; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add (&builder, + "{sv}", + "Quality", + g_variant_new_uint32 (calibrate->quality)); + g_variant_builder_add (&builder, + "{sv}", + "Whitepoint", + g_variant_new_uint32 (calibrate->target_whitepoint)); + g_variant_builder_add (&builder, + "{sv}", + "Gamma", + g_variant_new_double (calibrate->target_gamma)); + g_variant_builder_add (&builder, + "{sv}", + "Title", + g_variant_new_string (calibrate->title)); + g_variant_builder_add (&builder, + "{sv}", + "DeviceKind", + g_variant_new_uint32 (calibrate->device_kind)); + retval = g_dbus_proxy_call_sync (calibrate->proxy_helper, + "Start", + g_variant_new ("(ssa{sv})", + cd_device_get_id (calibrate->device), + cd_sensor_get_id (calibrate->sensor), + &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (retval == NULL) + return FALSE; + + /* set this above our parent */ + window = GTK_WINDOW (gtk_builder_get_object (calibrate->builder, + "dialog_calibrate")); + gtk_window_set_modal (window, TRUE); + gtk_widget_show (GTK_WIDGET (window)); + + /* show correct buttons */ + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_cancel")); + gtk_widget_set_visible (widget, TRUE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_start")); + gtk_widget_set_visible (widget, TRUE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_resume")); + gtk_widget_set_visible (widget, FALSE); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_done")); + gtk_widget_set_visible (widget, FALSE); + + /* stop the computer from auto-suspending or turning off the screen */ + cc_color_calibrate_inhibit (calibrate, parent); + + g_main_loop_run (calibrate->loop); + gtk_widget_hide (GTK_WIDGET (window)); + + /* we can go idle now */ + cc_color_calibrate_uninhibit (calibrate); + + /* see if we failed */ + if (calibrate->session_error_code != CD_SESSION_ERROR_NONE) + { + g_set_error_literal (error, + CD_SESSION_ERROR, + CD_SESSION_ERROR_INTERNAL, + "failed to calibrate"); + return FALSE; + } + + return TRUE; +} + +static void +cc_color_calibrate_finalize (GObject *object) +{ + CcColorCalibrate *calibrate = CC_COLOR_CALIBRATE (object); + + g_clear_pointer (&calibrate->window, gtk_window_destroy); + g_clear_object (&calibrate->builder); + g_clear_object (&calibrate->device); + g_clear_object (&calibrate->proxy_helper); + g_clear_object (&calibrate->proxy_inhibit); + g_clear_object (&calibrate->sensor); + g_clear_object (&calibrate->x11_screen); + g_free (calibrate->title); + g_main_loop_unref (calibrate->loop); + + G_OBJECT_CLASS (cc_color_calibrate_parent_class)->finalize (object); +} + +static void +cc_color_calibrate_class_init (CcColorCalibrateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = cc_color_calibrate_finalize; +} + +static void +cc_color_calibrate_init (CcColorCalibrate *calibrate) +{ + g_autoptr(GError) error = NULL; + gint retval; + g_autoptr(GSettings) settings = NULL; + GtkBox *box; + GtkWidget *widget; + GtkWindow *window; + + calibrate->loop = g_main_loop_new (NULL, FALSE); + calibrate->inhibit_fd = -1; + + /* load UI */ + calibrate->builder = gtk_builder_new (); + retval = gtk_builder_add_from_resource (calibrate->builder, + "/org/gnome/control-center/color/cc-color-calibrate.ui", + &error); + if (retval == 0) + g_warning ("Could not load interface: %s", error->message); + + /* add sample widget */ + box = GTK_BOX (gtk_builder_get_object (calibrate->builder, + "vbox_status")); + calibrate->sample_widget = cd_sample_widget_new (); + gtk_widget_set_size_request (calibrate->sample_widget, 400, 400); + gtk_box_prepend (box, calibrate->sample_widget); + gtk_widget_set_vexpand (calibrate->sample_widget, FALSE); + gtk_widget_set_hexpand (calibrate->sample_widget, FALSE); + + /* get defaults */ + settings = g_settings_new (COLORD_SETTINGS_SCHEMA); + calibrate->target_whitepoint = g_settings_get_int (settings, "display-whitepoint"); + calibrate->target_gamma = g_settings_get_double (settings, "display-gamma"); + + /* connect to buttons */ + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_start")); + g_signal_connect_object (widget, "clicked", + G_CALLBACK (cc_color_calibrate_button_start_cb), calibrate, G_CONNECT_SWAPPED); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_resume")); + g_signal_connect_object (widget, "clicked", + G_CALLBACK (cc_color_calibrate_button_start_cb), calibrate, G_CONNECT_SWAPPED); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_done")); + g_signal_connect_object (widget, "clicked", + G_CALLBACK (cc_color_calibrate_button_done_cb), calibrate, G_CONNECT_SWAPPED); + widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder, + "button_cancel")); + g_signal_connect_object (widget, "clicked", + G_CALLBACK (cc_color_calibrate_button_cancel_cb), calibrate, G_CONNECT_SWAPPED); + gtk_widget_show (widget); + + /* setup the specialist calibration window */ + window = GTK_WINDOW (gtk_builder_get_object (calibrate->builder, + "dialog_calibrate")); + calibrate->window = window; +} + +CcColorCalibrate * +cc_color_calibrate_new (void) +{ + CcColorCalibrate *calibrate; + calibrate = g_object_new (CC_TYPE_COLOR_CALIBRATE, NULL); + return CC_COLOR_CALIBRATE (calibrate); +} |