/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat Inc. * Copyright (C) 2018 DisplayLink (UK) Ltd. * * 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, see . * * Author: Giovanni Campagna */ /** * MetaMonitorManagerNative: * * A subclass of #MetaMonitorManager using Linux DRM * * #MetaMonitorManagerNative is a subclass of #MetaMonitorManager which * implements its functionality "natively": it uses the appropriate * functions of the Linux DRM kernel module and using a udev client. * * See also #MetaMonitorManagerXrandr for an implementation using XRandR. */ #include "config.h" #include "backends/native/meta-monitor-manager-native.h" #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor.h" #include "backends/meta-output.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-launcher.h" #include "backends/native/meta-output-kms.h" #include "backends/native/meta-renderer-native.h" #include "backends/native/meta-virtual-monitor-native.h" #include "clutter/clutter.h" #include "meta/main.h" #include "meta-dbus-display-config.h" enum { PROP_0, PROP_NEED_OUTPUTS, N_PROPS }; static GParamSpec *obj_props[N_PROPS]; typedef struct _MetaMonitorManagerNativePrivate { MetaMonitorManager parent_instance; gulong kms_resources_changed_handler_id; GHashTable *crtc_gamma_cache; gboolean needs_outputs; guint rebuild_virtual_idle_id; } MetaMonitorManagerNativePrivate; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaMonitorManagerNative, meta_monitor_manager_native, META_TYPE_MONITOR_MANAGER, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init) G_ADD_PRIVATE (MetaMonitorManagerNative)) static GBytes * meta_monitor_manager_native_read_edid (MetaMonitorManager *manager, MetaOutput *output) { return meta_output_native_read_edid (META_OUTPUT_NATIVE (output)); } static void meta_monitor_manager_native_read_current_state (MetaMonitorManager *manager) { MetaMonitorManagerClass *parent_class = META_MONITOR_MANAGER_CLASS (meta_monitor_manager_native_parent_class); MetaPowerSave power_save_mode; power_save_mode = meta_monitor_manager_get_power_save_mode (manager); if (power_save_mode != META_POWER_SAVE_ON) { MetaPowerSaveChangeReason reason; reason = META_POWER_SAVE_CHANGE_REASON_HOTPLUG; meta_monitor_manager_power_save_mode_changed (manager, META_POWER_SAVE_ON, reason); } parent_class->read_current_state (manager); } static void meta_monitor_manager_native_set_power_save_mode (MetaMonitorManager *manager, MetaPowerSave mode) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); GList *l; for (l = meta_backend_get_gpus (backend); l; l = l->next) { MetaGpuKms *gpu_kms = l->data; switch (mode) { case META_POWER_SAVE_ON: case META_POWER_SAVE_UNSUPPORTED: break; case META_POWER_SAVE_STANDBY: case META_POWER_SAVE_SUSPEND: case META_POWER_SAVE_OFF: { meta_kms_device_disable (meta_gpu_kms_get_kms_device (gpu_kms)); break; } } } } static void meta_monitor_manager_native_ensure_initial_config (MetaMonitorManager *manager) { MetaMonitorsConfig *config; config = meta_monitor_manager_ensure_configured (manager); meta_monitor_manager_update_logical_state (manager, config); } static void apply_crtc_assignments (MetaMonitorManager *manager, MetaCrtcAssignment **crtc_assignments, unsigned int n_crtc_assignments, MetaOutputAssignment **output_assignments, unsigned int n_output_assignments) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); g_autoptr (GList) to_configure_outputs = NULL; g_autoptr (GList) to_configure_crtcs = NULL; unsigned i; GList *gpus; GList *l; gpus = meta_backend_get_gpus (backend); for (l = gpus; l; l = l->next) { MetaGpu *gpu = l->data; GList *crtcs; GList *outputs; outputs = g_list_copy (meta_gpu_get_outputs (gpu)); to_configure_outputs = g_list_concat (to_configure_outputs, outputs); crtcs = g_list_copy (meta_gpu_get_crtcs (gpu)); to_configure_crtcs = g_list_concat (to_configure_crtcs, crtcs); } for (l = meta_monitor_manager_get_virtual_monitors (manager); l; l = l->next) { MetaVirtualMonitor *virtual_monitor = l->data; MetaOutput *output = meta_virtual_monitor_get_output (virtual_monitor); MetaCrtc *crtc = meta_virtual_monitor_get_crtc (virtual_monitor); to_configure_outputs = g_list_append (to_configure_outputs, output); to_configure_crtcs = g_list_append (to_configure_crtcs, crtc); } for (i = 0; i < n_crtc_assignments; i++) { MetaCrtcAssignment *crtc_assignment = crtc_assignments[i]; MetaCrtc *crtc = crtc_assignment->crtc; to_configure_crtcs = g_list_remove (to_configure_crtcs, crtc); if (crtc_assignment->mode == NULL) { meta_crtc_unset_config (crtc); } else { MetaCrtcConfig *crtc_config; unsigned int j; crtc_config = meta_crtc_config_new (&crtc_assignment->layout, crtc_assignment->mode, crtc_assignment->transform); meta_crtc_set_config (crtc, crtc_config, crtc_assignment->backend_private); for (j = 0; j < crtc_assignment->outputs->len; j++) { MetaOutput *output = g_ptr_array_index (crtc_assignment->outputs, j); MetaOutputAssignment *output_assignment; to_configure_outputs = g_list_remove (to_configure_outputs, output); output_assignment = meta_find_output_assignment (output_assignments, n_output_assignments, output); meta_output_assign_crtc (output, crtc, output_assignment); } } } g_list_foreach (to_configure_crtcs, (GFunc) meta_crtc_unset_config, NULL); g_list_foreach (to_configure_outputs, (GFunc) meta_output_unassign_crtc, NULL); } static void update_screen_size (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *l; int screen_width = 0; int screen_height = 0; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; int right_edge; int bottom_edge; right_edge = (logical_monitor_config->layout.width + logical_monitor_config->layout.x); if (right_edge > screen_width) screen_width = right_edge; bottom_edge = (logical_monitor_config->layout.height + logical_monitor_config->layout.y); if (bottom_edge > screen_height) screen_height = bottom_edge; } manager->screen_width = screen_width; manager->screen_height = screen_height; } static gboolean meta_monitor_manager_native_apply_monitors_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitorsConfigMethod method, GError **error) { GPtrArray *crtc_assignments; GPtrArray *output_assignments; if (!config) { apply_crtc_assignments (manager, NULL, 0, NULL, 0); if (!manager->in_init) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaRenderer *renderer = meta_backend_get_renderer (backend); meta_renderer_native_unset_modes (META_RENDERER_NATIVE (renderer)); } manager->screen_width = META_MONITOR_MANAGER_MIN_SCREEN_WIDTH; manager->screen_height = META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT; meta_monitor_manager_rebuild (manager, NULL); return TRUE; } if (!meta_monitor_config_manager_assign (manager, config, &crtc_assignments, &output_assignments, error)) return FALSE; if (method == META_MONITORS_CONFIG_METHOD_VERIFY) { g_ptr_array_free (crtc_assignments, TRUE); g_ptr_array_free (output_assignments, TRUE); return TRUE; } apply_crtc_assignments (manager, (MetaCrtcAssignment **) crtc_assignments->pdata, crtc_assignments->len, (MetaOutputAssignment **) output_assignments->pdata, output_assignments->len); g_ptr_array_free (crtc_assignments, TRUE); g_ptr_array_free (output_assignments, TRUE); update_screen_size (manager, config); meta_monitor_manager_rebuild (manager, config); return TRUE; } MetaGammaLut * meta_monitor_manager_native_get_cached_crtc_gamma (MetaMonitorManagerNative *manager_native, MetaCrtcKms *crtc_kms) { MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); uint64_t crtc_id; crtc_id = meta_crtc_get_id (META_CRTC (crtc_kms)); return g_hash_table_lookup (priv->crtc_gamma_cache, GUINT_TO_POINTER (crtc_id)); } void meta_monitor_manager_native_update_cached_crtc_gamma (MetaMonitorManagerNative *manager_native, MetaCrtcKms *crtc_kms, MetaGammaLut *gamma) { MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); MetaCrtc *crtc = META_CRTC (crtc_kms); g_hash_table_replace (priv->crtc_gamma_cache, GUINT_TO_POINTER (meta_crtc_get_id (crtc)), gamma); } static void handle_hotplug_event (MetaMonitorManager *manager) { meta_monitor_manager_reload (manager); } static void on_kms_resources_changed (MetaKms *kms, MetaKmsResourceChanges changes, MetaMonitorManager *manager) { gboolean needs_emit_privacy_screen_change = FALSE; g_assert (changes != META_KMS_RESOURCE_CHANGE_NONE); if (changes == META_KMS_RESOURCE_CHANGE_GAMMA) { meta_dbus_display_config_emit_monitors_changed (manager->display_config); return; } if (changes & META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN) { if (manager->privacy_screen_change_state == META_PRIVACY_SCREEN_CHANGE_STATE_NONE) { /* Privacy screen has been changed by "something", the best guess * we can do is that has been triggered by an hotkey. */ manager->privacy_screen_change_state = META_PRIVACY_SCREEN_CHANGE_STATE_PENDING_HOTKEY; } needs_emit_privacy_screen_change = TRUE; if (changes == META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN) goto out; } handle_hotplug_event (manager); out: if (needs_emit_privacy_screen_change) meta_monitor_manager_maybe_emit_privacy_screen_change (manager); } static void meta_monitor_manager_native_connect_hotplug_handler (MetaMonitorManagerNative *manager_native) { MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_native); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); priv->kms_resources_changed_handler_id = g_signal_connect (kms, "resources-changed", G_CALLBACK (on_kms_resources_changed), manager); } static void meta_monitor_manager_native_disconnect_hotplug_handler (MetaMonitorManagerNative *manager_native) { MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_native); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); g_clear_signal_handler (&priv->kms_resources_changed_handler_id, kms); } void meta_monitor_manager_native_pause (MetaMonitorManagerNative *manager_native) { meta_monitor_manager_native_disconnect_hotplug_handler (manager_native); } void meta_monitor_manager_native_resume (MetaMonitorManagerNative *manager_native) { meta_monitor_manager_native_connect_hotplug_handler (manager_native); } static MetaMonitorScalesConstraint get_monitor_scale_constraints_from_layout_mode (MetaLogicalMonitorLayoutMode layout_mode) { MetaMonitorScalesConstraint constraints = META_MONITOR_SCALES_CONSTRAINT_NONE; switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; break; } return constraints; } static float meta_monitor_manager_native_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { MetaMonitorScalesConstraint constraints = get_monitor_scale_constraints_from_layout_mode (layout_mode); return meta_monitor_calculate_mode_scale (monitor, monitor_mode, constraints); } static float * meta_monitor_manager_native_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { MetaMonitorScalesConstraint constraints = get_monitor_scale_constraints_from_layout_mode (layout_mode); return meta_monitor_calculate_supported_scales (monitor, monitor_mode, constraints, n_supported_scales); } static MetaMonitorManagerCapability meta_monitor_manager_native_get_capabilities (MetaMonitorManager *manager) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); MetaMonitorManagerCapability capabilities = META_MONITOR_MANAGER_CAPABILITY_NONE; if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; return capabilities; } static gboolean meta_monitor_manager_native_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { return FALSE; } static MetaLogicalMonitorLayoutMode meta_monitor_manager_native_get_default_layout_mode (MetaMonitorManager *manager) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) return META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; else return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } static MetaVirtualMonitorNative * find_virtual_monitor (MetaMonitorManagerNative *manager_native, uint64_t id) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_native); GList *l; for (l = meta_monitor_manager_get_virtual_monitors (manager); l; l = l->next) { MetaVirtualMonitorNative *virtual_monitor_native = l->data; if (meta_virtual_monitor_native_get_id (virtual_monitor_native) == id) return virtual_monitor_native; } return NULL; } static uint64_t allocate_virtual_monitor_id (MetaMonitorManagerNative *manager_native) { uint64_t id; id = 0; while (TRUE) { if (!find_virtual_monitor (manager_native, id)) return id; id++; } } static gboolean rebuild_virtual_idle_cb (gpointer user_data) { MetaMonitorManager *manager = user_data; MetaMonitorManagerNative *manager_native = META_MONITOR_MANAGER_NATIVE (manager); MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); priv->rebuild_virtual_idle_id = 0; meta_monitor_manager_reconfigure (manager); return G_SOURCE_REMOVE; } static void on_virtual_monitor_mode_changed (MetaVirtualMonitor *virtual_monitor, GParamSpec *pspec, MetaMonitorManager *manager) { MetaMonitorManagerNative *manager_native = META_MONITOR_MANAGER_NATIVE (manager); MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); if (priv->rebuild_virtual_idle_id) return; priv->rebuild_virtual_idle_id = g_idle_add (rebuild_virtual_idle_cb, manager); } static MetaVirtualMonitor * meta_monitor_manager_native_create_virtual_monitor (MetaMonitorManager *manager, const MetaVirtualMonitorInfo *info, GError **error) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaMonitorManagerNative *manager_native = META_MONITOR_MANAGER_NATIVE (manager); MetaVirtualMonitorNative *virtual_monitor_native; uint64_t id; id = allocate_virtual_monitor_id (manager_native); virtual_monitor_native = meta_virtual_monitor_native_new (backend, id, info); g_signal_connect (virtual_monitor_native, "notify::crtc-mode", G_CALLBACK (on_virtual_monitor_mode_changed), manager); return META_VIRTUAL_MONITOR (virtual_monitor_native); } static void meta_monitor_manager_native_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaMonitorManagerNative *manager_native = META_MONITOR_MANAGER_NATIVE (object); MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); switch (prop_id) { case PROP_NEED_OUTPUTS: priv->needs_outputs = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_monitor_manager_native_dispose (GObject *object) { MetaMonitorManagerNative *manager_native = META_MONITOR_MANAGER_NATIVE (object); MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); g_clear_handle_id (&priv->rebuild_virtual_idle_id, g_source_remove); g_clear_pointer (&priv->crtc_gamma_cache, g_hash_table_unref); G_OBJECT_CLASS (meta_monitor_manager_native_parent_class)->dispose (object); } static gboolean meta_monitor_manager_native_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaMonitorManagerNative *manager_native = META_MONITOR_MANAGER_NATIVE (initable); MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_native); MetaBackend *backend = meta_monitor_manager_get_backend (manager); gboolean can_have_outputs; GList *l; meta_monitor_manager_native_connect_hotplug_handler (manager_native); can_have_outputs = FALSE; for (l = meta_backend_get_gpus (backend); l; l = l->next) { MetaGpuKms *gpu_kms = l->data; if (meta_gpu_kms_can_have_outputs (gpu_kms)) { can_have_outputs = TRUE; break; } } if (priv->needs_outputs && !can_have_outputs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No GPUs with outputs found"); return FALSE; } priv->crtc_gamma_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_gamma_lut_free); return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = meta_monitor_manager_native_initable_init; } static void meta_monitor_manager_native_init (MetaMonitorManagerNative *manager_native) { MetaMonitorManagerNativePrivate *priv = meta_monitor_manager_native_get_instance_private (manager_native); priv->needs_outputs = TRUE; } static void meta_monitor_manager_native_class_init (MetaMonitorManagerNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); object_class->set_property = meta_monitor_manager_native_set_property; object_class->dispose = meta_monitor_manager_native_dispose; manager_class->read_edid = meta_monitor_manager_native_read_edid; manager_class->read_current_state = meta_monitor_manager_native_read_current_state; manager_class->ensure_initial_config = meta_monitor_manager_native_ensure_initial_config; manager_class->apply_monitors_config = meta_monitor_manager_native_apply_monitors_config; manager_class->set_power_save_mode = meta_monitor_manager_native_set_power_save_mode; manager_class->calculate_monitor_mode_scale = meta_monitor_manager_native_calculate_monitor_mode_scale; manager_class->calculate_supported_scales = meta_monitor_manager_native_calculate_supported_scales; manager_class->get_capabilities = meta_monitor_manager_native_get_capabilities; manager_class->get_max_screen_size = meta_monitor_manager_native_get_max_screen_size; manager_class->get_default_layout_mode = meta_monitor_manager_native_get_default_layout_mode; manager_class->create_virtual_monitor = meta_monitor_manager_native_create_virtual_monitor; obj_props[PROP_NEED_OUTPUTS] = g_param_spec_boolean ("needs-outputs", NULL, NULL, TRUE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); }