wayland下使用命令设置屏幕分辨率和旋转

问题

众所周知,Ubuntu22.04之后,默认的桌面是Wayland,并且Wayland是新一代的显示架构,这是以后的趋势。然而,在Wayland下并不能使用xrandr命令去设置屏幕的分辨率、旋转关系。

解决方案

在此前提是,诞生了一个python脚本gnome-randr.py,这个脚本的使用方法和xrandr一样。

这个项目的来源是:Oschowa / gnome-randr · GitLab

也有人用rust实现了一套跟python一样的应用。链接:maxwellainatchi/gnome-randr-rust: `xrandr` for Gnome/wayland, on distros that don't support `wlr-randr` (github.com)

使用演示

# 加执行权限
chmod a+x gnome-randr.py

# 运行命令
./gnome-randr.py

效果如下:

实现原理

gnome-randr.py 最终通过调用 dc_iface.ApplyMonitorsConfig() 函数来实现屏幕旋转。

让我们逐步分析:

  1. 获取当前显示配置: 脚本通过 D-Bus 连接到 Mutter 显示服务器,并使用 dc_iface.GetCurrentState() 获取当前的显示配置信息,包括屏幕数量、分辨率、旋转方向等。
  2. 更新配置: 根据用户提供的命令行参数(例如 --output <output_name> --rotate left),脚本修改获取到的配置信息。 其中 config_info.update_output_config(requested_actions) 函数负责根据命令行参数更新配置。 rot_to_trans 函数将 left, right, normal, inverted 等字符串转换成 Mutter 理解的数字表示。
  3. 生成新的逻辑显示器配置: monmap_to_lm 函数根据更新后的配置信息生成新的逻辑显示器配置 (new_lm)。 这是实际发送给 Mutter 的数据结构,包含了屏幕的位置、旋转角度、缩放比例等信息。
  4. 应用配置: dc_iface.ApplyMonitorsConfig(config_info.serial, requested_actions.config_method, new_lm, {}) 这个函数调用是关键。它通过 D-Bus 将新的逻辑显示器配置 (new_lm) 发送给 Mutter,最终实现屏幕旋转。 config_info.serial 参数用于确保配置的原子性,requested_actions.config_method 参数决定配置是临时的还是永久的。

所以,虽然脚本中没有直接的“旋转屏幕”命令,但 dc_iface.ApplyMonitorsConfig() 函数是最终实现旋转的核心,它将新的配置应用到 Mutter 显示服务器。

脚本内容

#!/bin/env python3

import sys, os, dbus
from collections import defaultdict

# from stackoverflow.com/questions/5369723
nested_dict = lambda: defaultdict(nested_dict)

def fatal(str):
    print(str)
    quit(1)

def warn(str):
    print('\n! {} !\n'.format(str))

def usage():
    print('usage: {} [options]\n'
          '\twhere options are:\n'
          '\t--current\n'
          '\t--dry-run\n'
          '\t--persistent\n'
          '\t--global-scale <global-scale>\n'
          '\t--output <output>\n'
          '\t\t--auto\n'
          '\t\t--mode <mode>\n'
          '\t\t--rate <rate>\n'
          '\t\t--scale <scale>\n'
          '\t\t--off\n'
          '\t\t--right-of <output>\n'
          '\t\t--left-of <output>\n'
          '\t\t--above <output>\n'
          '\t\t--below <output>\n'
          '\t\t--same-as <output>\n'
          '\t\t--rotate normal,inverted,left,right\n'
          '\t\t--primary\n'.format(os.path.basename(sys.argv[0])))
    quit()

def get_mode_by_res(res, monitor):
    for md in monitor[1]:
        res_str = '{}x{}'.format(md[1], md[2])
        if res_str == res:
            return md

def get_mode_by_id(mode_id, monitor):
    for md in monitor[1]:
        if md[0] == mode_id:
            return md

def mode_has_rate(res, rate, monitor):
    for md in monitor[1]:
        res_str = '{}x{}'.format(md[1], md[2])
        if res_str == res and round(md[3]) == round(rate):
            return md

def get_pref_mode(monitor):
    for md in monitor[1]:
        if 'is-preferred' in md[6]:
            return md

def get_current_mode(monitor):
    for md in monitor[1]:
        if 'is-current' in md[6]:
            return md

def has_scale(scale, mode):
    for s in mode[5]:
        if s == scale:
            return s

def mode_props_to_str(props):
    str = ''
    if 'is-current' in props:
        str += '*'
    if 'is-preferred' in props:
        str += '+'
    if 'is-interlaced' in props:
        str += 'i'
    return str

def modes_to_str_pretty(modes):
    mode_strings = dict()

    len_max = 1
    for md in modes:
        res_str = '{:>13}'.format('{}x{}'.format(md[1], md[2]))
        rate_str = '{:>11}'.format('{:>8.2f}{:<3}'
                                   .format(md[3], mode_props_to_str(md[6])))
        scale_str = scales_to_str(md[4], md[5])

        if not res_str in mode_strings:
            mode_strings[res_str] = dict()
            mode_strings[res_str]['rate-str'] = rate_str
            mode_strings[res_str]['scale-str'] = scale_str
        else:
            mode_strings[res_str]['rate-str'] += rate_str

        len_pre = len(res_str) + len(mode_strings[res_str]['rate-str'])
        if len_pre > len_max:
            len_max = len_pre
        mode_strings[res_str]['len-pre'] = len_pre

    str = ''
    for res_str, v in mode_strings.items():
        ind = len_max - v['len-pre'] + 4
        str += res_str + v['rate-str'] + ' ' * ind + v['scale-str'] + '\n'
    return str

def scales_to_str(pref_scale, scales):
    str = '['
    for n in range(len(scales)):
        str += 'x{0:.1f}'.format(scales[n])
        if scales[n] == pref_scale:
            str += '+'
        if n + 1 >= len(scales):
            str += ']&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值