forked from rhinstaller/anaconda
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Anaconda uses xrandr to set the screen resolution when the boot option "inst.resolution" [1] is used. In order to be able to drop the X.Org server, use Mutter's API instead of xrandr. The kickstart equivalent option has been removed, so we don't need to care about it [2]. [1] https://anaconda-installer.readthedocs.io/en/latest/boot-options.html#inst-resolution [2] https://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#xconfig
- Loading branch information
Showing
5 changed files
with
197 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
# | ||
# Copyright (C) 2024 Red Hat, Inc. | ||
# | ||
# This copyrighted material is made available to anyone wishing to use, | ||
# modify, copy, or redistribute it subject to the terms and conditions of | ||
# the GNU General Public License v.2, or (at your option) any later version. | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY expressed or implied, including the implied warranties 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. Any Red Hat trademarks that are incorporated in the | ||
# source code or documentation are not subject to the GNU General Public | ||
# License and may only be used or replicated with the express permission of | ||
# Red Hat, Inc. | ||
# | ||
|
||
from dasbus.client.observer import DBusObserver | ||
from pyanaconda.core.dbus import SessionBus | ||
from pyanaconda.modules.common.constants.services import MUTTER_DISPLAY_CONFIG | ||
from pyanaconda.core.regexes import SCREEN_RESOLUTION_CONFIG | ||
|
||
|
||
__all__ = ['MutterDisplay', 'MutterConfigError'] | ||
|
||
|
||
class MutterConfigError(Exception): | ||
"""Exception class for mutter configuration related problems""" | ||
pass | ||
|
||
|
||
class MonitorId(object): | ||
"""Collection of properties that identify a unique monitor.""" | ||
|
||
def __init__(self, props): | ||
self.connector = props[0] | ||
self.vendor = props[1] | ||
self.product = props[2] | ||
self.serial = props[3] | ||
|
||
def __eq__(self, other): | ||
return self.connector == other.connector and \ | ||
self.vendor == other.vendor and \ | ||
self.product == other.product and \ | ||
self.serial == other.serial | ||
|
||
|
||
class MonitorMode(object): | ||
"""Available modes for a monitor.""" | ||
|
||
def __init__(self, props): | ||
self.id = props[0] | ||
self.width = props[1] | ||
self.height = props[2] | ||
self.refresh_rate = props[3] | ||
self.preferred_scale = props[4] | ||
self.supported_scales = props[5] | ||
self.properties = props[6] | ||
|
||
|
||
class Monitor(object): | ||
"""Represent a connected physical monitor.""" | ||
|
||
def __init__(self, props): | ||
self.id = MonitorId(props[0]) | ||
self.modes = list(map(MonitorMode, props[1])) | ||
self.properties = props[2] | ||
|
||
|
||
class LogicalMonitor(object): | ||
"""Represent the current logical monitor configuration""" | ||
|
||
def __init__(self, props): | ||
self.x = props[0] | ||
self.y = props[1] | ||
self.scale = props[2] | ||
self.transform = props[3] | ||
self.primary = props[4] | ||
self.monitor_ids = list(map(MonitorId, props[5])) | ||
self.properties = props[6] | ||
|
||
|
||
class LogicalMonitorConfig(object): | ||
"""Logical monitor configuration object""" | ||
|
||
def __init__(self, logical_monitor, monitors, x, y, width, height): | ||
"""Creates a LogicalMonitorConfig setting the given resolution if available.""" | ||
self._logical_monitor = logical_monitor | ||
self._monitors = monitors | ||
|
||
self.x = x | ||
self.y = y | ||
self.scale = logical_monitor.scale | ||
self.transform = logical_monitor.transform | ||
self.primary = logical_monitor.primary | ||
|
||
self.monitors = list() | ||
for monitor_id in logical_monitor.monitor_ids: | ||
connector = monitor_id.connector | ||
mode_id = self._get_matching_monitor_mode_id(monitors, monitor_id, width, height) | ||
self.monitors.append((connector, mode_id, {})) | ||
|
||
def _get_matching_monitor_mode_id(self, monitors, monitor_id, width, height): | ||
monitor = next(filter(lambda m: m.id == monitor_id, monitors)) | ||
for mode in monitor.modes: | ||
if mode.width == width and mode.height == height: | ||
return mode.id | ||
|
||
raise MutterConfigError('Monitor mode with selected resolution not found') | ||
|
||
def to_dbus(self): | ||
return ( | ||
self.x, | ||
self.y, | ||
self.scale, | ||
self.transform, | ||
self.primary, | ||
self.monitors, | ||
) | ||
|
||
|
||
class MutterDisplay(object): | ||
"""Class wrapping Mutter's display configuration API.""" | ||
|
||
def __init__(self): | ||
self._proxy = MUTTER_DISPLAY_CONFIG.get_proxy() | ||
|
||
def on_service_ready(self, callback): | ||
observer = DBusObserver(SessionBus, 'org.gnome.Kiosk') | ||
observer.service_available.connect(callback) | ||
observer.connect_once_available() | ||
|
||
def set_resolution(self, res_str): | ||
"""Changes the screen resolution. | ||
:param res_str: Screen resolution configuration with format "800x600". | ||
:raises MutterConfigError on failure. | ||
""" | ||
if not self._proxy.ApplyMonitorsConfigAllowed: | ||
raise MutterConfigError('Monitor configuration is not allowed') | ||
|
||
(width, height) = self._parse_resolution_str(res_str) | ||
(serial, monitor_props, logical_monitor_props, _) = self._proxy.GetCurrentState() | ||
|
||
# Configuration method as described in org.gnome.Mutter.DisplayConfig.xml: | ||
# 0: verify | ||
# 1: temporary | ||
# 2: persistent | ||
persistent_config = 2 | ||
|
||
monitors = list(map(Monitor, monitor_props)) | ||
logical_monitors = list(map(LogicalMonitor, logical_monitor_props)) | ||
|
||
# Align the monitors in a row starting at X coordinate 0 | ||
x = 0 | ||
|
||
configs = list() | ||
for logical_monitor in logical_monitors: | ||
config = LogicalMonitorConfig(logical_monitor, monitors, x, 0, width, height) | ||
x += width | ||
configs.append(config.to_dbus()) | ||
|
||
self._proxy.ApplyMonitorsConfig(serial, persistent_config, configs, {}) | ||
|
||
def _parse_resolution_str(self, res_str): | ||
if not SCREEN_RESOLUTION_CONFIG.match(res_str): | ||
raise MutterConfigError('Invalid configuration resolution') | ||
|
||
[width, height] = res_str.split('x') | ||
return (int(width, 10), int(height, 10)) |