/* * gdkmonitor.c * * Copyright 2016 Red Hat, Inc. * * Matthias Clasen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include "gdkmonitorprivate.h" #include "gdkdisplay.h" #include "gdkenumtypes.h" #include "gdkrectangle.h" #include /** * GdkMonitor: * * `GdkMonitor` objects represent the individual outputs that are * associated with a `GdkDisplay`. * * `GdkDisplay` keeps a `GListModel` to enumerate and monitor * monitors with [method@Gdk.Display.get_monitors]. You can use * [method@Gdk.Display.get_monitor_at_surface] to find a particular * monitor. */ enum { PROP_0, PROP_DESCRIPTION, PROP_DISPLAY, PROP_MANUFACTURER, PROP_MODEL, PROP_CONNECTOR, PROP_SCALE_FACTOR, PROP_SCALE, PROP_GEOMETRY, PROP_WIDTH_MM, PROP_HEIGHT_MM, PROP_REFRESH_RATE, PROP_SUBPIXEL_LAYOUT, PROP_VALID, LAST_PROP }; static GParamSpec *props[LAST_PROP] = { NULL, }; enum { INVALIDATE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (GdkMonitor, gdk_monitor, G_TYPE_OBJECT) static void gdk_monitor_init (GdkMonitor *monitor) { monitor->scale_factor = 1; monitor->scale = 1.0; monitor->scale_set = FALSE; monitor->valid = TRUE; } static void gdk_monitor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GdkMonitor *monitor = GDK_MONITOR (object); switch (prop_id) { case PROP_DESCRIPTION: g_value_set_string (value, monitor->description); break; case PROP_DISPLAY: g_value_set_object (value, monitor->display); break; case PROP_MANUFACTURER: g_value_set_string (value, monitor->manufacturer); break; case PROP_MODEL: g_value_set_string (value, monitor->model); break; case PROP_CONNECTOR: g_value_set_string (value, monitor->connector); break; case PROP_SCALE_FACTOR: g_value_set_int (value, monitor->scale_factor); break; case PROP_SCALE: g_value_set_double (value, monitor->scale); break; case PROP_GEOMETRY: g_value_set_boxed (value, &monitor->geometry); break; case PROP_WIDTH_MM: g_value_set_int (value, monitor->width_mm); break; case PROP_HEIGHT_MM: g_value_set_int (value, monitor->height_mm); break; case PROP_REFRESH_RATE: g_value_set_int (value, monitor->refresh_rate); break; case PROP_SUBPIXEL_LAYOUT: g_value_set_enum (value, monitor->subpixel_layout); break; case PROP_VALID: g_value_set_boolean (value, monitor->valid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gdk_monitor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GdkMonitor *monitor = GDK_MONITOR (object); switch (prop_id) { case PROP_DISPLAY: monitor->display = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gdk_monitor_finalize (GObject *object) { GdkMonitor *monitor = GDK_MONITOR (object); g_free (monitor->description); g_free (monitor->connector); g_free (monitor->manufacturer); g_free (monitor->model); G_OBJECT_CLASS (gdk_monitor_parent_class)->finalize (object); } static void gdk_monitor_class_init (GdkMonitorClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->finalize = gdk_monitor_finalize; object_class->get_property = gdk_monitor_get_property; object_class->set_property = gdk_monitor_set_property; /** * GdkMonitor:description: * * A short description of the monitor, meant for display to the user. * * Since: 4.10 */ props[PROP_DESCRIPTION] = g_param_spec_string ("description", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:display: * * The `GdkDisplay` of the monitor. */ props[PROP_DISPLAY] = g_param_spec_object ("display", NULL, NULL, GDK_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:manufacturer: * * The manufacturer name. */ props[PROP_MANUFACTURER] = g_param_spec_string ("manufacturer", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:model: * * The model name. */ props[PROP_MODEL] = g_param_spec_string ("model", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:connector: * * The connector name. */ props[PROP_CONNECTOR] = g_param_spec_string ("connector", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:scale-factor: * * The scale factor. * * The scale factor is the next larger integer, * compared to [property@Gdk.Surface:scale]. */ props[PROP_SCALE_FACTOR] = g_param_spec_int ("scale-factor", NULL, NULL, 1, G_MAXINT, 1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:scale: * * The scale of the monitor. * * Since: 4.14 */ props[PROP_SCALE] = g_param_spec_double ("scale", NULL, NULL, 1., G_MAXDOUBLE, 1., G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:geometry: * * The geometry of the monitor. */ props[PROP_GEOMETRY] = g_param_spec_boxed ("geometry", NULL, NULL, GDK_TYPE_RECTANGLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:width-mm: * * The width of the monitor, in millimeters. */ props[PROP_WIDTH_MM] = g_param_spec_int ("width-mm", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:height-mm: * * The height of the monitor, in millimeters. */ props[PROP_HEIGHT_MM] = g_param_spec_int ("height-mm", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:refresh-rate: * * The refresh rate, in milli-Hertz. */ props[PROP_REFRESH_RATE] = g_param_spec_int ("refresh-rate", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:subpixel-layout: * * The subpixel layout. */ props[PROP_SUBPIXEL_LAYOUT] = g_param_spec_enum ("subpixel-layout", NULL, NULL, GDK_TYPE_SUBPIXEL_LAYOUT, GDK_SUBPIXEL_LAYOUT_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GdkMonitor:valid: (getter is_valid) * * Whether the object is still valid. */ props[PROP_VALID] = g_param_spec_boolean ("valid", NULL, NULL, TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROP, props); /** * GdkMonitor::invalidate: * @monitor: the object on which this signal was emitted * * Emitted when the output represented by @monitor gets disconnected. */ signals[INVALIDATE] = g_signal_new (g_intern_static_string ("invalidate"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /** * gdk_monitor_get_display: * @monitor: a `GdkMonitor` * * Gets the display that this monitor belongs to. * * Returns: (transfer none): the display */ GdkDisplay * gdk_monitor_get_display (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL); return monitor->display; } /** * gdk_monitor_get_geometry: * @monitor: a `GdkMonitor` * @geometry: (out): a `GdkRectangle` to be filled with the monitor geometry * * Retrieves the size and position of the monitor within the * display coordinate space. * * The returned geometry is in ”application pixels”, not in * ”device pixels” (see [method@Gdk.Monitor.get_scale]). */ void gdk_monitor_get_geometry (GdkMonitor *monitor, GdkRectangle *geometry) { g_return_if_fail (GDK_IS_MONITOR (monitor)); g_return_if_fail (geometry != NULL); *geometry = monitor->geometry; } /** * gdk_monitor_get_width_mm: * @monitor: a `GdkMonitor` * * Gets the width in millimeters of the monitor. * * Returns: the physical width of the monitor */ int gdk_monitor_get_width_mm (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0); return monitor->width_mm; } /** * gdk_monitor_get_height_mm: * @monitor: a `GdkMonitor` * * Gets the height in millimeters of the monitor. * * Returns: the physical height of the monitor */ int gdk_monitor_get_height_mm (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0); return monitor->height_mm; } /** * gdk_monitor_get_connector: * @monitor: a `GdkMonitor` * * Gets the name of the monitor's connector, if available. * * These are strings such as "eDP-1", or "HDMI-2". They depend * on software and hardware configuration, and should not be * relied on as stable identifiers of a specific monitor. * * Returns: (transfer none) (nullable): the name of the connector */ const char * gdk_monitor_get_connector (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL); return monitor->connector; } /** * gdk_monitor_get_manufacturer: * @monitor: a `GdkMonitor` * * Gets the name or PNP ID of the monitor's manufacturer. * * Note that this value might also vary depending on actual * display backend. * * The PNP ID registry is located at * [https://uefi.org/pnp_id_list](https://uefi.org/pnp_id_list). * * Returns: (transfer none) (nullable): the name of the manufacturer */ const char * gdk_monitor_get_manufacturer (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL); return monitor->manufacturer; } /** * gdk_monitor_get_model: * @monitor: a `GdkMonitor` * * Gets the string identifying the monitor model, if available. * * Returns: (transfer none) (nullable): the monitor model */ const char * gdk_monitor_get_model (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL); return monitor->model; } /** * gdk_monitor_get_scale_factor: * @monitor: a `GdkMonitor` * * Gets the internal scale factor that maps from monitor coordinates * to device pixels. * * On traditional systems this is 1, but on very high density outputs * it can be a higher value (often 2). * * This can be used if you want to create pixel based data for a * particular monitor, but most of the time you’re drawing to a surface * where it is better to use [method@Gdk.Surface.get_scale_factor] instead. * * Returns: the scale factor */ int gdk_monitor_get_scale_factor (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), 1); return monitor->scale_factor; } /** * gdk_monitor_get_scale: * @monitor: a `GdkMonitor` * * Gets the internal scale factor that maps from monitor coordinates * to device pixels. * * This can be used if you want to create pixel based data for a * particular monitor, but most of the time you’re drawing to a surface * where it is better to use [method@Gdk.Surface.get_scale] instead. * * Returns: the scale * * Since: 4.14 */ double gdk_monitor_get_scale (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), 1.); return monitor->scale; } /** * gdk_monitor_get_refresh_rate: * @monitor: a `GdkMonitor` * * Gets the refresh rate of the monitor, if available. * * The value is in milli-Hertz, so a refresh rate of 60Hz * is returned as 60000. * * Returns: the refresh rate in milli-Hertz, or 0 */ int gdk_monitor_get_refresh_rate (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0); return monitor->refresh_rate; } /** * gdk_monitor_get_subpixel_layout: * @monitor: a `GdkMonitor` * * Gets information about the layout of red, green and blue * primaries for pixels. * * Returns: the subpixel layout */ GdkSubpixelLayout gdk_monitor_get_subpixel_layout (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), GDK_SUBPIXEL_LAYOUT_UNKNOWN); return monitor->subpixel_layout; } GdkMonitor * gdk_monitor_new (GdkDisplay *display) { return GDK_MONITOR (g_object_new (GDK_TYPE_MONITOR, "display", display, NULL)); } void gdk_monitor_set_manufacturer (GdkMonitor *monitor, const char *manufacturer) { g_free (monitor->manufacturer); monitor->manufacturer = g_strdup (manufacturer); g_object_notify (G_OBJECT (monitor), "manufacturer"); } void gdk_monitor_set_model (GdkMonitor *monitor, const char *model) { g_free (monitor->model); monitor->model = g_strdup (model); g_object_notify (G_OBJECT (monitor), "model"); } void gdk_monitor_set_connector (GdkMonitor *monitor, const char *connector) { g_free (monitor->connector); monitor->connector = g_strdup (connector); g_object_notify (G_OBJECT (monitor), "connector"); } void gdk_monitor_set_geometry (GdkMonitor *monitor, const GdkRectangle *geometry) { if (gdk_rectangle_equal (&monitor->geometry, geometry)) return; monitor->geometry = *geometry; g_object_notify (G_OBJECT (monitor), "geometry"); } void gdk_monitor_set_physical_size (GdkMonitor *monitor, int width_mm, int height_mm) { g_object_freeze_notify (G_OBJECT (monitor)); if (monitor->width_mm != width_mm) { monitor->width_mm = width_mm; g_object_notify (G_OBJECT (monitor), "width-mm"); } if (monitor->height_mm != height_mm) { monitor->height_mm = height_mm; g_object_notify (G_OBJECT (monitor), "height-mm"); } g_object_thaw_notify (G_OBJECT (monitor)); } void gdk_monitor_set_scale_factor (GdkMonitor *monitor, int scale_factor) { g_return_if_fail (scale_factor >= 1); if (monitor->scale_set) return; if (monitor->scale_factor == scale_factor) return; monitor->scale_factor = scale_factor; monitor->scale = scale_factor; g_object_notify (G_OBJECT (monitor), "scale-factor"); g_object_notify (G_OBJECT (monitor), "scale"); } void gdk_monitor_set_scale (GdkMonitor *monitor, double scale) { g_return_if_fail (scale > 0.); monitor->scale_set = TRUE; if (monitor->scale == scale) return; monitor->scale = scale; monitor->scale_factor = (int) ceil (scale); g_object_notify (G_OBJECT (monitor), "scale"); g_object_notify (G_OBJECT (monitor), "scale-factor"); } void gdk_monitor_set_refresh_rate (GdkMonitor *monitor, int refresh_rate) { if (monitor->refresh_rate == refresh_rate) return; monitor->refresh_rate = refresh_rate; g_object_notify (G_OBJECT (monitor), "refresh-rate"); } void gdk_monitor_set_subpixel_layout (GdkMonitor *monitor, GdkSubpixelLayout subpixel_layout) { if (monitor->subpixel_layout == subpixel_layout) return; monitor->subpixel_layout = subpixel_layout; g_object_notify (G_OBJECT (monitor), "subpixel-layout"); } void gdk_monitor_invalidate (GdkMonitor *monitor) { monitor->valid = FALSE; g_object_notify (G_OBJECT (monitor), "valid"); g_signal_emit (monitor, signals[INVALIDATE], 0); } /** * gdk_monitor_is_valid: (get-property valid) * @monitor: a `GdkMonitor` * * Returns %TRUE if the @monitor object corresponds to a * physical monitor. * * The @monitor becomes invalid when the physical monitor * is unplugged or removed. * * Returns: %TRUE if the object corresponds to a physical monitor */ gboolean gdk_monitor_is_valid (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), FALSE); return monitor->valid; } /** * gdk_monitor_get_description: * @monitor: a `GdkMonitor` * * Gets a string describing the monitor, if available. * * This can be used to identify a monitor in the UI. * * Returns: (transfer none) (nullable): the monitor description * * Since: 4.10 */ const char * gdk_monitor_get_description (GdkMonitor *monitor) { g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL); return monitor->description; } void gdk_monitor_set_description (GdkMonitor *monitor, const char *description) { g_free (monitor->description); monitor->description = g_strdup (description); g_object_notify_by_pspec (G_OBJECT (monitor), props[PROP_DESCRIPTION]); } #define MM_PER_INCH 25.4 double gdk_monitor_get_dpi (GdkMonitor *monitor) { return MAX ((monitor->geometry.width * monitor->scale) / (monitor->width_mm / MM_PER_INCH), (monitor->geometry.height * monitor->scale) / (monitor->height_mm / MM_PER_INCH)); }