872 lines
28 KiB
C
872 lines
28 KiB
C
/* cc-display-settings.c
|
||
*
|
||
* Copyright (C) 2007, 2008, 2018, 2019 Red Hat, Inc.
|
||
* Copyright (C) 2013 Intel, Inc.
|
||
*
|
||
* Written by: Benjamin Berg <bberg@redhat.com>
|
||
*
|
||
* 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, 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, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include <float.h>
|
||
#include <glib/gi18n.h>
|
||
#include <float.h>
|
||
#include <math.h>
|
||
#include "cc-display-settings.h"
|
||
#include "cc-display-config.h"
|
||
|
||
#define MAX_SCALE_BUTTONS 5
|
||
|
||
struct _CcDisplaySettings
|
||
{
|
||
AdwBin object;
|
||
|
||
gboolean updating;
|
||
gboolean num_scales;
|
||
gboolean folded;
|
||
guint idle_udpate_id;
|
||
|
||
gboolean has_accelerometer;
|
||
CcDisplayConfig *config;
|
||
CcDisplayMonitor *selected_output;
|
||
|
||
GListModel *orientation_list;
|
||
GListStore *refresh_rate_list;
|
||
GListStore *resolution_list;
|
||
GListModel *scale_list;
|
||
|
||
GtkWidget *orientation_row;
|
||
GtkWidget *refresh_rate_row;
|
||
GtkWidget *resolution_row;
|
||
GtkWidget *scale_bbox;
|
||
GtkWidget *scale_buttons_row;
|
||
GtkWidget *scale_combo_row;
|
||
GtkWidget *underscanning_row;
|
||
GtkWidget *underscanning_switch;
|
||
};
|
||
|
||
typedef struct _CcDisplaySettings CcDisplaySettings;
|
||
|
||
enum {
|
||
PROP_0,
|
||
PROP_HAS_ACCELEROMETER,
|
||
PROP_CONFIG,
|
||
PROP_SELECTED_OUTPUT,
|
||
PROP_LAST
|
||
};
|
||
|
||
G_DEFINE_TYPE (CcDisplaySettings, cc_display_settings, ADW_TYPE_BIN)
|
||
|
||
static GParamSpec *props[PROP_LAST];
|
||
|
||
static void on_scale_btn_active_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self);
|
||
|
||
|
||
static gboolean
|
||
should_show_rotation (CcDisplaySettings *self)
|
||
{
|
||
gboolean supports_rotation;
|
||
|
||
supports_rotation = cc_display_monitor_supports_rotation (self->selected_output,
|
||
CC_DISPLAY_ROTATION_90 |
|
||
CC_DISPLAY_ROTATION_180 |
|
||
CC_DISPLAY_ROTATION_270);
|
||
|
||
/* Doesn't support rotation at all */
|
||
if (!supports_rotation)
|
||
return FALSE;
|
||
|
||
/* We can always rotate displays that aren't builtin */
|
||
if (!cc_display_monitor_is_builtin (self->selected_output))
|
||
return TRUE;
|
||
|
||
/* Only offer rotation if there's no accelerometer */
|
||
return !self->has_accelerometer;
|
||
}
|
||
|
||
static const gchar *
|
||
string_for_rotation (CcDisplayRotation rotation)
|
||
{
|
||
switch (rotation)
|
||
{
|
||
case CC_DISPLAY_ROTATION_NONE:
|
||
case CC_DISPLAY_ROTATION_180_FLIPPED:
|
||
return C_("Display rotation", "Landscape");
|
||
case CC_DISPLAY_ROTATION_90:
|
||
case CC_DISPLAY_ROTATION_270_FLIPPED:
|
||
return C_("Display rotation", "Portrait Right");
|
||
case CC_DISPLAY_ROTATION_270:
|
||
case CC_DISPLAY_ROTATION_90_FLIPPED:
|
||
return C_("Display rotation", "Portrait Left");
|
||
case CC_DISPLAY_ROTATION_180:
|
||
case CC_DISPLAY_ROTATION_FLIPPED:
|
||
return C_("Display rotation", "Landscape (flipped)");
|
||
}
|
||
return "";
|
||
}
|
||
|
||
static const gchar *
|
||
make_aspect_string (gint width,
|
||
gint height)
|
||
{
|
||
int ratio;
|
||
const gchar *aspect = NULL;
|
||
|
||
/* We use a number of Unicode characters below:
|
||
* ∶ is U+2236 RATIO
|
||
* is U+2009 THIN SPACE,
|
||
* × is U+00D7 MULTIPLICATION SIGN
|
||
*/
|
||
if (width && height) {
|
||
if (width > height)
|
||
ratio = width * 10 / height;
|
||
else
|
||
ratio = height * 10 / width;
|
||
|
||
switch (ratio) {
|
||
case 13:
|
||
aspect = "4∶3";
|
||
break;
|
||
case 16:
|
||
aspect = "16∶10";
|
||
break;
|
||
case 17:
|
||
aspect = "16∶9";
|
||
break;
|
||
case 23:
|
||
aspect = "21∶9";
|
||
break;
|
||
case 12:
|
||
aspect = "5∶4";
|
||
break;
|
||
/* This catches 1.5625 as well (1600x1024) when maybe it shouldn't. */
|
||
case 15:
|
||
aspect = "3∶2";
|
||
break;
|
||
case 18:
|
||
aspect = "9∶5";
|
||
break;
|
||
case 10:
|
||
aspect = "1∶1";
|
||
break;
|
||
}
|
||
}
|
||
|
||
return aspect;
|
||
}
|
||
|
||
static gchar *
|
||
make_refresh_rate_string (CcDisplayMode *mode)
|
||
{
|
||
return g_strdup_printf (_("%.2lf Hz"), cc_display_mode_get_freq_f (mode));
|
||
}
|
||
|
||
static gchar *
|
||
make_resolution_string (CcDisplayMode *mode)
|
||
{
|
||
const char *interlaced;
|
||
const char *aspect;
|
||
int width, height;
|
||
|
||
cc_display_mode_get_resolution (mode, &width, &height);
|
||
aspect = make_aspect_string (width, height);
|
||
interlaced = cc_display_mode_is_interlaced (mode) ? "i" : "";
|
||
|
||
if (aspect != NULL)
|
||
return g_strdup_printf ("%d × %d%s (%s)", width, height, interlaced, aspect);
|
||
else
|
||
return g_strdup_printf ("%d × %d%s", width, height, interlaced);
|
||
}
|
||
|
||
static double
|
||
round_scale_for_ui (double scale)
|
||
{
|
||
/* Keep in sync with mutter */
|
||
return round (scale*4)/4;
|
||
}
|
||
|
||
static gchar *
|
||
make_scale_string (gdouble scale)
|
||
{
|
||
return g_strdup_printf ("%d %%", (int) (round_scale_for_ui (scale)*100));
|
||
}
|
||
|
||
static gint
|
||
sort_modes_by_area_desc (CcDisplayMode *a, CcDisplayMode *b)
|
||
{
|
||
gint wa, ha, wb, hb;
|
||
gint res;
|
||
|
||
cc_display_mode_get_resolution (a, &wa, &ha);
|
||
cc_display_mode_get_resolution (b, &wb, &hb);
|
||
|
||
/* Sort first by width, then height.
|
||
* We used to sort by area, but that can be confusing. */
|
||
res = wb - wa;
|
||
if (res)
|
||
return res;
|
||
return hb - ha;
|
||
}
|
||
|
||
static gint
|
||
sort_modes_by_freq_desc (CcDisplayMode *a, CcDisplayMode *b)
|
||
{
|
||
double delta = (cc_display_mode_get_freq_f (b) - cc_display_mode_get_freq_f (a))*1000.;
|
||
return delta;
|
||
}
|
||
|
||
static gboolean
|
||
cc_display_settings_rebuild_ui (CcDisplaySettings *self)
|
||
{
|
||
GtkWidget *child;
|
||
GList *modes;
|
||
GList *item;
|
||
gint width, height;
|
||
CcDisplayMode *current_mode;
|
||
GtkToggleButton *group = NULL;
|
||
g_autoptr(GArray) scales = NULL;
|
||
gint i;
|
||
|
||
self->idle_udpate_id = 0;
|
||
|
||
if (!self->config || !self->selected_output)
|
||
{
|
||
gtk_widget_set_visible (self->orientation_row, FALSE);
|
||
gtk_widget_set_visible (self->refresh_rate_row, FALSE);
|
||
gtk_widget_set_visible (self->resolution_row, FALSE);
|
||
gtk_widget_set_visible (self->scale_combo_row, FALSE);
|
||
gtk_widget_set_visible (self->scale_buttons_row, FALSE);
|
||
gtk_widget_set_visible (self->underscanning_row, FALSE);
|
||
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
g_object_freeze_notify ((GObject*) self->orientation_row);
|
||
g_object_freeze_notify ((GObject*) self->refresh_rate_row);
|
||
g_object_freeze_notify ((GObject*) self->resolution_row);
|
||
g_object_freeze_notify ((GObject*) self->scale_combo_row);
|
||
g_object_freeze_notify ((GObject*) self->underscanning_switch);
|
||
|
||
cc_display_monitor_get_geometry (self->selected_output, NULL, NULL, &width, &height);
|
||
|
||
/* Selecte the first mode we can find if the monitor is disabled. */
|
||
current_mode = cc_display_monitor_get_mode (self->selected_output);
|
||
if (current_mode == NULL)
|
||
current_mode = cc_display_monitor_get_preferred_mode (self->selected_output);
|
||
if (current_mode == NULL) {
|
||
modes = cc_display_monitor_get_modes (self->selected_output);
|
||
/* Lets assume that a monitor always has at least one mode. */
|
||
g_assert (modes);
|
||
current_mode = CC_DISPLAY_MODE (modes->data);
|
||
}
|
||
|
||
if (should_show_rotation (self))
|
||
{
|
||
guint i;
|
||
CcDisplayRotation rotations[] = { CC_DISPLAY_ROTATION_NONE,
|
||
CC_DISPLAY_ROTATION_90,
|
||
CC_DISPLAY_ROTATION_270,
|
||
CC_DISPLAY_ROTATION_180 };
|
||
|
||
gtk_widget_set_visible (self->orientation_row, TRUE);
|
||
|
||
gtk_string_list_splice (GTK_STRING_LIST (self->orientation_list),
|
||
0,
|
||
g_list_model_get_n_items (self->orientation_list),
|
||
NULL);
|
||
for (i = 0; i < G_N_ELEMENTS (rotations); i++)
|
||
{
|
||
g_autoptr(GObject) obj = NULL;
|
||
|
||
if (!cc_display_monitor_supports_rotation (self->selected_output, rotations[i]))
|
||
continue;
|
||
|
||
gtk_string_list_append (GTK_STRING_LIST (self->orientation_list),
|
||
string_for_rotation (rotations[i]));
|
||
obj = g_list_model_get_item (self->orientation_list, i);
|
||
g_object_set_data (G_OBJECT (obj), "rotation-value", GINT_TO_POINTER (rotations[i]));
|
||
|
||
if (cc_display_monitor_get_rotation (self->selected_output) == rotations[i])
|
||
adw_combo_row_set_selected (ADW_COMBO_ROW (self->orientation_row),
|
||
g_list_model_get_n_items (G_LIST_MODEL (self->orientation_list)) - 1);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gtk_widget_set_visible (self->orientation_row, FALSE);
|
||
}
|
||
|
||
/* Only show refresh rate if we are not in cloning mode. */
|
||
if (!cc_display_config_is_cloning (self->config))
|
||
{
|
||
GList *item;
|
||
gdouble freq;
|
||
|
||
freq = cc_display_mode_get_freq_f (current_mode);
|
||
|
||
modes = cc_display_monitor_get_modes (self->selected_output);
|
||
|
||
g_list_store_remove_all (self->refresh_rate_list);
|
||
|
||
for (item = modes; item != NULL; item = item->next)
|
||
{
|
||
gint w, h;
|
||
guint new;
|
||
CcDisplayMode *mode = CC_DISPLAY_MODE (item->data);
|
||
|
||
cc_display_mode_get_resolution (mode, &w, &h);
|
||
if (w != width || h != height)
|
||
continue;
|
||
|
||
/* At some point we used to filter very close resolutions,
|
||
* but we don't anymore these days.
|
||
*/
|
||
new = g_list_store_insert_sorted (self->refresh_rate_list,
|
||
mode,
|
||
(GCompareDataFunc) sort_modes_by_freq_desc,
|
||
NULL);
|
||
if (freq == cc_display_mode_get_freq_f (mode))
|
||
adw_combo_row_set_selected (ADW_COMBO_ROW (self->refresh_rate_row), new);
|
||
}
|
||
|
||
/* Show if we have more than one frequency to choose from. */
|
||
gtk_widget_set_visible (self->refresh_rate_row,
|
||
g_list_model_get_n_items (G_LIST_MODEL (self->refresh_rate_list)) > 1);
|
||
}
|
||
else
|
||
{
|
||
gtk_widget_set_visible (self->refresh_rate_row, FALSE);
|
||
}
|
||
|
||
|
||
/* Resolutions are always shown. */
|
||
gtk_widget_set_visible (self->resolution_row, TRUE);
|
||
if (cc_display_config_is_cloning (self->config))
|
||
modes = cc_display_config_get_cloning_modes (self->config);
|
||
else
|
||
modes = cc_display_monitor_get_modes (self->selected_output);
|
||
|
||
g_list_store_remove_all (self->resolution_list);
|
||
g_list_store_append (self->resolution_list, current_mode);
|
||
adw_combo_row_set_selected (ADW_COMBO_ROW (self->resolution_row), 0);
|
||
for (item = modes; item != NULL; item = item->next)
|
||
{
|
||
gint ins;
|
||
gint w, h;
|
||
CcDisplayMode *mode = CC_DISPLAY_MODE (item->data);
|
||
|
||
cc_display_mode_get_resolution (mode, &w, &h);
|
||
|
||
/* Find the appropriate insertion point. */
|
||
for (ins = 0; ins < g_list_model_get_n_items (G_LIST_MODEL (self->resolution_list)); ins++)
|
||
{
|
||
g_autoptr(CcDisplayMode) m = NULL;
|
||
gint cmp;
|
||
|
||
m = g_list_model_get_item (G_LIST_MODEL (self->resolution_list), ins);
|
||
|
||
cmp = sort_modes_by_area_desc (mode, m);
|
||
/* Next item is smaller, insert at this point. */
|
||
if (cmp < 0)
|
||
break;
|
||
|
||
/* Don't insert if it is already in the list */
|
||
if (cmp == 0)
|
||
{
|
||
ins = -1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (ins >= 0)
|
||
g_list_store_insert (self->resolution_list, ins, mode);
|
||
}
|
||
|
||
|
||
/* Scale row is usually shown. */
|
||
while ((child = gtk_widget_get_first_child (self->scale_bbox)) != NULL)
|
||
gtk_box_remove (GTK_BOX (self->scale_bbox), child);
|
||
|
||
gtk_string_list_splice (GTK_STRING_LIST (self->scale_list),
|
||
0,
|
||
g_list_model_get_n_items (self->scale_list),
|
||
NULL);
|
||
scales = cc_display_mode_get_supported_scales (current_mode);
|
||
self->num_scales = scales->len;
|
||
for (i = 0; i < scales->len; i++)
|
||
{
|
||
g_autofree gchar *scale_str = NULL;
|
||
g_autoptr(GObject) value_object = NULL;
|
||
double scale = g_array_index (scales, double, i);
|
||
GtkWidget *scale_btn;
|
||
gboolean is_selected;
|
||
|
||
/* ComboRow */
|
||
scale_str = make_scale_string (scale);
|
||
is_selected = G_APPROX_VALUE (cc_display_monitor_get_scale (self->selected_output),
|
||
scale, DBL_EPSILON);
|
||
|
||
gtk_string_list_append (GTK_STRING_LIST (self->scale_list), scale_str);
|
||
value_object = g_list_model_get_item (self->scale_list, i);
|
||
g_object_set_data_full (G_OBJECT (value_object), "scale",
|
||
g_memdup2 (&scale, sizeof (double)), g_free);
|
||
if (is_selected)
|
||
adw_combo_row_set_selected (ADW_COMBO_ROW (self->scale_combo_row),
|
||
g_list_model_get_n_items (G_LIST_MODEL (self->scale_list)) - 1);
|
||
|
||
/* ButtonBox */
|
||
scale_btn = gtk_toggle_button_new_with_label (scale_str);
|
||
gtk_toggle_button_set_group (GTK_TOGGLE_BUTTON (scale_btn), group);
|
||
g_object_set_data_full (G_OBJECT (scale_btn), "scale",
|
||
g_memdup2 (&scale, sizeof (double)), g_free);
|
||
|
||
if (!group)
|
||
group = GTK_TOGGLE_BUTTON (scale_btn);
|
||
gtk_box_append (GTK_BOX (self->scale_bbox), scale_btn);
|
||
/* Set active before connecting the signal */
|
||
if (is_selected)
|
||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scale_btn), TRUE);
|
||
|
||
g_signal_connect_object (scale_btn,
|
||
"notify::active",
|
||
G_CALLBACK (on_scale_btn_active_changed_cb),
|
||
self, 0);
|
||
}
|
||
cc_display_settings_refresh_layout (self, self->folded);
|
||
|
||
gtk_widget_set_visible (self->underscanning_row,
|
||
cc_display_monitor_supports_underscanning (self->selected_output) &&
|
||
!cc_display_config_is_cloning (self->config));
|
||
gtk_switch_set_active (GTK_SWITCH (self->underscanning_switch),
|
||
cc_display_monitor_get_underscanning (self->selected_output));
|
||
|
||
self->updating = TRUE;
|
||
g_object_thaw_notify ((GObject*) self->orientation_row);
|
||
g_object_thaw_notify ((GObject*) self->refresh_rate_row);
|
||
g_object_thaw_notify ((GObject*) self->resolution_row);
|
||
g_object_thaw_notify ((GObject*) self->scale_combo_row);
|
||
g_object_thaw_notify ((GObject*) self->underscanning_switch);
|
||
self->updating = FALSE;
|
||
|
||
return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
static void
|
||
on_output_changed_cb (CcDisplaySettings *self,
|
||
GParamSpec *pspec,
|
||
CcDisplayMonitor *output)
|
||
{
|
||
/* Do this frmo an idle handler, because otherwise we may create an
|
||
* infinite loop triggering the notify::selected-index from the
|
||
* combo rows. */
|
||
if (self->idle_udpate_id)
|
||
return;
|
||
|
||
self->idle_udpate_id = g_idle_add ((GSourceFunc) cc_display_settings_rebuild_ui, self);
|
||
}
|
||
|
||
static void
|
||
on_orientation_selection_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self)
|
||
{
|
||
gint idx;
|
||
g_autoptr(GObject) obj = NULL;
|
||
|
||
if (self->updating)
|
||
return;
|
||
|
||
idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->orientation_row));
|
||
obj = g_list_model_get_item (G_LIST_MODEL (self->orientation_list), idx);
|
||
|
||
if (!obj)
|
||
return;
|
||
|
||
cc_display_monitor_set_rotation (self->selected_output,
|
||
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (obj), "rotation-value")));
|
||
|
||
g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output);
|
||
}
|
||
|
||
static void
|
||
on_refresh_rate_selection_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self)
|
||
{
|
||
gint idx;
|
||
g_autoptr(CcDisplayMode) mode = NULL;
|
||
|
||
if (self->updating)
|
||
return;
|
||
|
||
idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->refresh_rate_row));
|
||
mode = g_list_model_get_item (G_LIST_MODEL (self->refresh_rate_list), idx);
|
||
|
||
if (!mode)
|
||
return;
|
||
|
||
cc_display_monitor_set_mode (self->selected_output, mode);
|
||
|
||
g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output);
|
||
}
|
||
|
||
static void
|
||
on_resolution_selection_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self)
|
||
{
|
||
gint idx;
|
||
g_autoptr(CcDisplayMode) mode = NULL;
|
||
|
||
if (self->updating)
|
||
return;
|
||
|
||
idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->resolution_row));
|
||
mode = g_list_model_get_item (G_LIST_MODEL (self->resolution_list), idx);
|
||
|
||
if (!mode)
|
||
return;
|
||
|
||
/* This is the only row that can be changed when in cloning mode. */
|
||
if (!cc_display_config_is_cloning (self->config))
|
||
cc_display_monitor_set_mode (self->selected_output, mode);
|
||
else
|
||
cc_display_config_set_mode_on_all_outputs (self->config, mode);
|
||
|
||
g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output);
|
||
}
|
||
|
||
static void
|
||
on_scale_btn_active_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self)
|
||
{
|
||
gdouble scale;
|
||
if (self->updating)
|
||
return;
|
||
|
||
if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
|
||
return;
|
||
|
||
scale = *(gdouble*) g_object_get_data (G_OBJECT (widget), "scale");
|
||
cc_display_monitor_set_scale (self->selected_output,
|
||
scale);
|
||
|
||
g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output);
|
||
}
|
||
|
||
static void
|
||
on_scale_selection_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self)
|
||
{
|
||
int idx;
|
||
double scale;
|
||
g_autoptr(GObject) obj = NULL;
|
||
|
||
if (self->updating)
|
||
return;
|
||
|
||
idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->scale_combo_row));
|
||
obj = g_list_model_get_item (G_LIST_MODEL (self->scale_list), idx);
|
||
if (!obj)
|
||
return;
|
||
scale = *(gdouble*) g_object_get_data (G_OBJECT (obj), "scale");
|
||
|
||
cc_display_monitor_set_scale (self->selected_output, scale);
|
||
|
||
g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output);
|
||
}
|
||
|
||
static void
|
||
on_underscanning_switch_active_changed_cb (GtkWidget *widget,
|
||
GParamSpec *pspec,
|
||
CcDisplaySettings *self)
|
||
{
|
||
if (self->updating)
|
||
return;
|
||
|
||
cc_display_monitor_set_underscanning (self->selected_output,
|
||
gtk_switch_get_active (GTK_SWITCH (self->underscanning_switch)));
|
||
|
||
g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output);
|
||
}
|
||
|
||
static void
|
||
cc_display_settings_get_property (GObject *object,
|
||
guint prop_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
CcDisplaySettings *self = CC_DISPLAY_SETTINGS (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case PROP_HAS_ACCELEROMETER:
|
||
g_value_set_boolean (value, cc_display_settings_get_has_accelerometer (self));
|
||
break;
|
||
|
||
case PROP_CONFIG:
|
||
g_value_set_object (value, self->config);
|
||
break;
|
||
|
||
case PROP_SELECTED_OUTPUT:
|
||
g_value_set_object (value, self->selected_output);
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
}
|
||
}
|
||
|
||
static void
|
||
cc_display_settings_set_property (GObject *object,
|
||
guint prop_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
CcDisplaySettings *self = CC_DISPLAY_SETTINGS (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case PROP_HAS_ACCELEROMETER:
|
||
cc_display_settings_set_has_accelerometer (self, g_value_get_boolean (value));
|
||
break;
|
||
|
||
case PROP_CONFIG:
|
||
cc_display_settings_set_config (self, g_value_get_object (value));
|
||
break;
|
||
|
||
case PROP_SELECTED_OUTPUT:
|
||
cc_display_settings_set_selected_output (self, g_value_get_object (value));
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
}
|
||
}
|
||
|
||
static void
|
||
cc_display_settings_finalize (GObject *object)
|
||
{
|
||
CcDisplaySettings *self = CC_DISPLAY_SETTINGS (object);
|
||
|
||
g_clear_object (&self->config);
|
||
|
||
g_clear_object (&self->orientation_list);
|
||
g_clear_object (&self->refresh_rate_list);
|
||
g_clear_object (&self->resolution_list);
|
||
g_clear_object (&self->scale_list);
|
||
|
||
if (self->idle_udpate_id)
|
||
g_source_remove (self->idle_udpate_id);
|
||
self->idle_udpate_id = 0;
|
||
|
||
G_OBJECT_CLASS (cc_display_settings_parent_class)->finalize (object);
|
||
}
|
||
|
||
static void
|
||
cc_display_settings_class_init (CcDisplaySettingsClass *klass)
|
||
{
|
||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||
|
||
gobject_class->finalize = cc_display_settings_finalize;
|
||
gobject_class->get_property = cc_display_settings_get_property;
|
||
gobject_class->set_property = cc_display_settings_set_property;
|
||
|
||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/display/cc-display-settings.ui");
|
||
|
||
props[PROP_HAS_ACCELEROMETER] =
|
||
g_param_spec_boolean ("has-accelerometer", "Has Accelerometer",
|
||
"If an accelerometre is available for the builtin display",
|
||
FALSE,
|
||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||
|
||
props[PROP_CONFIG] =
|
||
g_param_spec_object ("config", "Display Config",
|
||
"The display configuration to work with",
|
||
CC_TYPE_DISPLAY_CONFIG,
|
||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||
|
||
props[PROP_SELECTED_OUTPUT] =
|
||
g_param_spec_object ("selected-output", "Selected Output",
|
||
"The output that is currently selected on the configuration",
|
||
CC_TYPE_DISPLAY_MONITOR,
|
||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||
|
||
g_object_class_install_properties (gobject_class,
|
||
PROP_LAST,
|
||
props);
|
||
|
||
g_signal_new ("updated",
|
||
CC_TYPE_DISPLAY_SETTINGS,
|
||
G_SIGNAL_RUN_LAST,
|
||
0, NULL, NULL, NULL,
|
||
G_TYPE_NONE, 1, CC_TYPE_DISPLAY_MONITOR);
|
||
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, orientation_row);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, refresh_rate_row);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, resolution_row);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_bbox);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_buttons_row);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_combo_row);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, underscanning_row);
|
||
gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, underscanning_switch);
|
||
|
||
gtk_widget_class_bind_template_callback (widget_class, on_orientation_selection_changed_cb);
|
||
gtk_widget_class_bind_template_callback (widget_class, on_refresh_rate_selection_changed_cb);
|
||
gtk_widget_class_bind_template_callback (widget_class, on_resolution_selection_changed_cb);
|
||
gtk_widget_class_bind_template_callback (widget_class, on_scale_selection_changed_cb);
|
||
gtk_widget_class_bind_template_callback (widget_class, on_underscanning_switch_active_changed_cb);
|
||
}
|
||
|
||
static void
|
||
cc_display_settings_init (CcDisplaySettings *self)
|
||
{
|
||
GtkExpression *expression;
|
||
|
||
gtk_widget_init_template (GTK_WIDGET (self));
|
||
|
||
self->orientation_list = G_LIST_MODEL (gtk_string_list_new (NULL));
|
||
self->refresh_rate_list = g_list_store_new (CC_TYPE_DISPLAY_MODE);
|
||
self->resolution_list = g_list_store_new (CC_TYPE_DISPLAY_MODE);
|
||
self->scale_list = G_LIST_MODEL (gtk_string_list_new (NULL));
|
||
|
||
self->updating = TRUE;
|
||
|
||
adw_combo_row_set_model (ADW_COMBO_ROW (self->orientation_row),
|
||
G_LIST_MODEL (self->orientation_list));
|
||
adw_combo_row_set_model (ADW_COMBO_ROW (self->scale_combo_row),
|
||
G_LIST_MODEL (self->scale_list));
|
||
|
||
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
|
||
NULL, 0, NULL,
|
||
G_CALLBACK (make_refresh_rate_string),
|
||
self, NULL);
|
||
adw_combo_row_set_expression (ADW_COMBO_ROW (self->refresh_rate_row), expression);
|
||
adw_combo_row_set_model (ADW_COMBO_ROW (self->refresh_rate_row),
|
||
G_LIST_MODEL (self->refresh_rate_list));
|
||
|
||
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
|
||
NULL, 0, NULL,
|
||
G_CALLBACK (make_resolution_string),
|
||
self, NULL);
|
||
adw_combo_row_set_expression (ADW_COMBO_ROW (self->resolution_row), expression);
|
||
adw_combo_row_set_model (ADW_COMBO_ROW (self->resolution_row),
|
||
G_LIST_MODEL (self->resolution_list));
|
||
|
||
self->updating = FALSE;
|
||
}
|
||
|
||
CcDisplaySettings*
|
||
cc_display_settings_new (void)
|
||
{
|
||
return g_object_new (CC_TYPE_DISPLAY_SETTINGS,
|
||
NULL);
|
||
}
|
||
|
||
gboolean
|
||
cc_display_settings_get_has_accelerometer (CcDisplaySettings *settings)
|
||
{
|
||
return settings->has_accelerometer;
|
||
}
|
||
|
||
void
|
||
cc_display_settings_set_has_accelerometer (CcDisplaySettings *self,
|
||
gboolean has_accelerometer)
|
||
{
|
||
self->has_accelerometer = has_accelerometer;
|
||
|
||
cc_display_settings_rebuild_ui (self);
|
||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIG]);
|
||
}
|
||
|
||
CcDisplayConfig*
|
||
cc_display_settings_get_config (CcDisplaySettings *self)
|
||
{
|
||
return self->config;
|
||
}
|
||
|
||
void
|
||
cc_display_settings_set_config (CcDisplaySettings *self,
|
||
CcDisplayConfig *config)
|
||
{
|
||
const gchar *signals[] = { "rotation", "mode", "scale", "is-usable", "active" };
|
||
GList *outputs, *l;
|
||
guint i;
|
||
|
||
if (self->config)
|
||
{
|
||
outputs = cc_display_config_get_monitors (self->config);
|
||
for (l = outputs; l; l = l->next)
|
||
{
|
||
CcDisplayMonitor *output = l->data;
|
||
|
||
g_signal_handlers_disconnect_by_data (output, self);
|
||
}
|
||
}
|
||
g_clear_object (&self->config);
|
||
|
||
self->config = g_object_ref (config);
|
||
|
||
/* Listen to all the signals */
|
||
if (self->config)
|
||
{
|
||
outputs = cc_display_config_get_monitors (self->config);
|
||
for (l = outputs; l; l = l->next)
|
||
{
|
||
CcDisplayMonitor *output = l->data;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (signals); ++i)
|
||
g_signal_connect_object (output, signals[i], G_CALLBACK (on_output_changed_cb), self, G_CONNECT_SWAPPED);
|
||
}
|
||
}
|
||
|
||
cc_display_settings_set_selected_output (self, NULL);
|
||
|
||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIG]);
|
||
}
|
||
|
||
CcDisplayMonitor*
|
||
cc_display_settings_get_selected_output (CcDisplaySettings *self)
|
||
{
|
||
return self->selected_output;
|
||
}
|
||
|
||
void
|
||
cc_display_settings_set_selected_output (CcDisplaySettings *self,
|
||
CcDisplayMonitor *output)
|
||
{
|
||
self->selected_output = output;
|
||
|
||
cc_display_settings_rebuild_ui (self);
|
||
|
||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTED_OUTPUT]);
|
||
}
|
||
|
||
void
|
||
cc_display_settings_refresh_layout (CcDisplaySettings *self,
|
||
gboolean folded)
|
||
{
|
||
gboolean use_combo;
|
||
|
||
self->folded = folded;
|
||
use_combo = self->num_scales > MAX_SCALE_BUTTONS || (self->num_scales > 2 && folded);
|
||
|
||
gtk_widget_set_visible (self->scale_combo_row, use_combo);
|
||
gtk_widget_set_visible (self->scale_buttons_row, self->num_scales > 1 && !use_combo);
|
||
}
|