Initial commit

parents
build/
builddir/
*.so
*.o
*.mo
# Tuner Displays
Плагин Tuner для настройки мониторов.
## Поддержка
- Hyprland: чтение через `hyprctl -j monitors all`, сохранение в `~/.config/hypr/monitors.conf`.
- GNOME: чтение и применение настроек через Mutter DisplayConfig.
Backend выбирается по XDG-переменным:
- `XDG_CURRENT_DESKTOP`;
- `XDG_SESSION_DESKTOP`, если `XDG_CURRENT_DESKTOP` пустой.
## Hyprland
Плагин пишет `monitorv2` в hyprlang-формате. Для совместимости с `nwg-displays` используется файл:
```text
~/.config/hypr/monitors.conf
```
Пример:
```text
monitorv2 {
output = DP-1
mode = 1920x1080@60.00
position = 0x0
scale = 1.00
}
```
Для монитора можно включить сохранение по `desc:...`, если имя порта нестабильно.
## GNOME
GNOME-бэкенд поддерживает обычный и зеркальный режимы, выбор основного дисплея, разрешение, масштаб, поворот, частоту обновления, VRR, underscan и доступные цветовые режимы. В зеркальном режиме отдельные настройки мониторов скрываются, а общие параметры переносятся на главную страницу.
## Сборка
```sh
meson setup build
meson compile -C build
```
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/ru/ximperlinux/tuner/Displays">
<file alias="displays-view.ui" preprocess="xml-stripblanks">ui/displays-view.ui</file>
<file alias="monitor-settings-content.ui" preprocess="xml-stripblanks">ui/monitor-settings-content.ui</file>
</gresource>
</gresources>
<?xml version="1.0" encoding="UTF-8"?>
<component type="addon">
<id>ru.ximperlinux.tuner.Displays</id>
<extends>org.altlinux.Tuner</extends>
<name>Displays</name>
<summary>Monitor settings page for Tuner</summary>
<metadata_license>MIT</metadata_license>
<project_license>GPL-3.0-or-later</project_license>
</component>
subdir('ui')
resources = gnome.compile_resources(
'resources',
'gresource.xml',
source_dir: meson.current_build_dir(),
dependencies: custom_target(
'blueprints',
input: blueprints,
output: '.',
command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
)
)
i18n.merge_file(
input: 'id.metainfo.xml.in',
output: id + '.metainfo.xml',
po_dir: meson.project_source_root() / 'po',
install: true,
install_dir: get_option('datadir') / 'metainfo'
)
configure_file(
input: 'plugin.in',
output: output + '.plugin',
configuration: {
'NAME': name,
'MODULE': output,
'VERSION': version,
},
install: true,
install_dir: get_option('libdir') / 'tuner' / 'plugins'
)
[Plugin]
Name=@NAME@
Module=@MODULE@
Version=@VERSION@
Authors=Fiersik Kouji
Copyright=Copyright © 2026 Etersoft
Website=https://gitlab.eterfund.ru/ximperlinux/tuner-displays
X-Priority=-5
using Gtk 4.0;
using Adw 1;
translation-domain "tuner-displays";
template $TunerDisplaysDisplaysView : Adw.PreferencesPage {
title: _("Displays");
icon-name: "video-display-symbolic";
Adw.PreferencesGroup status_group {}
Adw.PreferencesGroup layout_group {}
Adw.PreferencesGroup monitors_group {
title: _("Details");
}
}
blueprints = files(
'displays-view.blp',
'monitor-settings-content.blp',
)
using Gtk 4.0;
using Adw 1;
translation-domain "tuner-displays";
template $TunerDisplaysMonitorSettingsContent : Adw.PreferencesPage {
Adw.PreferencesGroup basic_group {
title: _("Display");
Adw.SwitchRow enabled_row {
title: _("Enabled");
}
}
Adw.PreferencesGroup hyprland_group {
title: _("Hyprland");
}
Adw.PreferencesGroup hdr_group {
title: _("HDR / EDID overrides");
}
}
project(
'Displays',
['c', 'vala'],
version: '0.1.0',
meson_version: '>= 1.0',
)
id = 'ru.ximperlinux.tuner.Displays'
name = meson.project_name()
version = meson.project_version()
output = 'displays'
po_output = 'tuner-displays'
add_project_arguments(
'-w', '-DGETTEXT_PACKAGE="@0@"'.format(po_output),
language: 'c'
)
gnome = import('gnome')
i18n = import('i18n')
config = configuration_data()
config.set('GETTEXT_PACKAGE', po_output)
config.set('LOCALEDIR', get_option('prefix') / get_option('localedir'))
subdir('data')
subdir('src')
subdir('po')
data/ui/displays-view.blp
data/ui/monitor-settings-content.blp
src/plugin.vala
src/core/display-model.vala
src/ui/displays-view.vala
src/ui/monitor-settings-content.vala
src/ui/ui-helpers.vala
i18n.gettext(po_output, preset: 'glib')
msgid ""
msgstr ""
"Project-Id-Version: tuner-displays\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-29 23:02+0300\n"
"PO-Revision-Date: 2026-05-28 00:00+0000\n"
"Last-Translator: Automatically generated\n"
"Language-Team: Russian\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: data/ui/displays-view.blp:7 src/plugin.vala:15
msgid "Displays"
msgstr "Мониторы"
#: data/ui/displays-view.blp:15
msgid "Details"
msgstr "Параметры"
#: data/ui/monitor-settings-content.blp:8
msgid "Display"
msgstr "Дисплей"
#: data/ui/monitor-settings-content.blp:11
msgid "Enabled"
msgstr "Включён"
#: data/ui/monitor-settings-content.blp:16
msgid "Hyprland"
msgstr "Hyprland"
#: data/ui/monitor-settings-content.blp:20
msgid "HDR / EDID overrides"
msgstr "Переопределения HDR / EDID"
#: src/plugin.vala:24
msgid "Refresh"
msgstr "Обновить"
#: src/plugin.vala:29 src/plugin.vala:30 src/plugin.vala:53 src/plugin.vala:54
msgid "Apply"
msgstr "Применить"
#: src/plugin.vala:41 src/plugin.vala:47
msgid "Monitor"
msgstr "Монитор"
#: src/core/display-model.vala:62
msgid "Built-in Display"
msgstr "Встроенный дисплей"
#: src/ui/displays-view.vala:67
msgid "Failed to load monitors"
msgstr "Не удалось загрузить мониторы"
#: src/ui/displays-view.vala:76
msgid "Monitor settings applied"
msgstr "Настройки мониторов применены"
#: src/ui/displays-view.vala:159
msgid "Read-only backend"
msgstr "Режим только для чтения"
#: src/ui/displays-view.vala:160
msgid "Applying monitor layouts is not supported by this backend."
msgstr "Применение раскладок мониторов не поддерживается этим бэкендом."
#: src/ui/displays-view.vala:170
msgid "Mirror Displays"
msgstr "Зеркалировать мониторы"
#: src/ui/displays-view.vala:230 src/ui/monitor-settings-content.vala:86
#: src/ui/monitor-settings-content.vala:125
msgid "Resolution"
msgstr "Разрешение"
#: src/ui/displays-view.vala:276 src/ui/monitor-settings-content.vala:263
#: src/ui/monitor-settings-content.vala:289
msgid "Scale"
msgstr "Масштаб"
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "Normal"
msgstr "Обычный"
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "90 degrees"
msgstr "90 градусов"
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "180 degrees"
msgstr "180 градусов"
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "270 degrees"
msgstr "270 градусов"
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped"
msgstr "Отражённый"
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped 90 degrees"
msgstr "Отражённый 90 градусов"
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped 180 degrees"
msgstr "Отражённый 180 градусов"
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped 270 degrees"
msgstr "Отражённый 270 градусов"
#: src/ui/displays-view.vala:301 src/ui/monitor-settings-content.vala:313
msgid "Rotation"
msgstr "Поворот"
#: src/ui/displays-view.vala:334
msgid "Primary Display"
msgstr "Основной дисплей"
#: src/ui/monitor-settings-content.vala:154
#: src/ui/monitor-settings-content.vala:162
msgid "Refresh Rate"
msgstr "Частота обновления"
#: src/ui/monitor-settings-content.vala:184
msgid "Variable Refresh Rate"
msgstr "Переменная частота обновления"
#: src/ui/monitor-settings-content.vala:333
msgid "None"
msgstr "Нет"
#: src/ui/monitor-settings-content.vala:348
msgid "Mirror"
msgstr "Зеркалирование"
#: src/ui/monitor-settings-content.vala:364
msgid "Use description"
msgstr "Использовать описание"
#: src/ui/monitor-settings-content.vala:373
msgid "Bit depth"
msgstr "Глубина цвета"
#: src/ui/monitor-settings-content.vala:374
msgid "VRR"
msgstr "VRR"
#: src/ui/monitor-settings-content.vala:374
#: src/ui/monitor-settings-content.vala:380
#: src/ui/monitor-settings-content.vala:381
msgid "Off"
msgstr "Отключено"
#: src/ui/monitor-settings-content.vala:374
#: src/ui/monitor-settings-content.vala:380
#: src/ui/monitor-settings-content.vala:381
msgid "On"
msgstr "Включено"
#: src/ui/monitor-settings-content.vala:374
msgid "Fullscreen"
msgstr "Полный экран"
#: src/ui/monitor-settings-content.vala:374
msgid "Fullscreen video/game"
msgstr "Полноэкранное видео/игра"
#: src/ui/monitor-settings-content.vala:375
msgid "Color management"
msgstr "Управление цветом"
#: src/ui/monitor-settings-content.vala:376
msgid "SDR EOTF"
msgstr "SDR EOTF"
#: src/ui/monitor-settings-content.vala:377
msgid "SDR brightness"
msgstr "Яркость SDR"
#: src/ui/monitor-settings-content.vala:378
msgid "SDR saturation"
msgstr "Насыщенность SDR"
#: src/ui/monitor-settings-content.vala:380
msgid "Force wide color"
msgstr "Принудительно широкий цветовой охват"
#: src/ui/monitor-settings-content.vala:380
#: src/ui/monitor-settings-content.vala:381
msgid "Auto"
msgstr "Авто"
#: src/ui/monitor-settings-content.vala:381
msgid "Force HDR"
msgstr "Принудительно HDR"
#: src/ui/monitor-settings-content.vala:382
msgid "SDR min luminance"
msgstr "Мин. яркость SDR"
#: src/ui/monitor-settings-content.vala:383
msgid "SDR max luminance"
msgstr "Макс. яркость SDR"
#: src/ui/monitor-settings-content.vala:384
msgid "Min luminance"
msgstr "Мин. яркость"
#: src/ui/monitor-settings-content.vala:385
msgid "Max luminance"
msgstr "Макс. яркость"
#: src/ui/monitor-settings-content.vala:386
msgid "Max average luminance"
msgstr "Макс. средняя яркость"
#: src/ui/monitor-settings-content.vala:389
msgid "ICC profile"
msgstr "Профиль ICC"
#: src/ui/monitor-settings-content.vala:401
msgid "Underscanning"
msgstr "Underscan"
#: src/ui/monitor-settings-content.vala:412
msgid "HDR"
msgstr "HDR"
#: src/ui/monitor-settings-content.vala:571
#, c-format
msgid "Variable (up to %.2f Hz)"
msgstr "Переменная (до %.2f Гц)"
#: src/ui/monitor-settings-content.vala:572
msgid "Variable"
msgstr "Переменная"
#: src/ui/monitor-settings-content.vala:575 src/ui/ui-helpers.vala:50
#, c-format
msgid "%.2f Hz"
msgstr "%.2f Гц"
#~ msgid "Color Mode"
#~ msgstr "Цветовой режим"
#~ msgid "Default"
#~ msgstr "По умолчанию"
#, c-format
#~ msgid "Mode %d"
#~ msgstr "Режим %d"
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the tuner-displays package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: tuner-displays\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-05-29 23:02+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: data/ui/displays-view.blp:7 src/plugin.vala:15
msgid "Displays"
msgstr ""
#: data/ui/displays-view.blp:15
msgid "Details"
msgstr ""
#: data/ui/monitor-settings-content.blp:8
msgid "Display"
msgstr ""
#: data/ui/monitor-settings-content.blp:11
msgid "Enabled"
msgstr ""
#: data/ui/monitor-settings-content.blp:16
msgid "Hyprland"
msgstr ""
#: data/ui/monitor-settings-content.blp:20
msgid "HDR / EDID overrides"
msgstr ""
#: src/plugin.vala:24
msgid "Refresh"
msgstr ""
#: src/plugin.vala:29 src/plugin.vala:30 src/plugin.vala:53 src/plugin.vala:54
msgid "Apply"
msgstr ""
#: src/plugin.vala:41 src/plugin.vala:47
msgid "Monitor"
msgstr ""
#: src/core/display-model.vala:62
msgid "Built-in Display"
msgstr ""
#: src/ui/displays-view.vala:67
msgid "Failed to load monitors"
msgstr ""
#: src/ui/displays-view.vala:76
msgid "Monitor settings applied"
msgstr ""
#: src/ui/displays-view.vala:159
msgid "Read-only backend"
msgstr ""
#: src/ui/displays-view.vala:160
msgid "Applying monitor layouts is not supported by this backend."
msgstr ""
#: src/ui/displays-view.vala:170
msgid "Mirror Displays"
msgstr ""
#: src/ui/displays-view.vala:230 src/ui/monitor-settings-content.vala:86
#: src/ui/monitor-settings-content.vala:125
msgid "Resolution"
msgstr ""
#: src/ui/displays-view.vala:276 src/ui/monitor-settings-content.vala:263
#: src/ui/monitor-settings-content.vala:289
msgid "Scale"
msgstr ""
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "Normal"
msgstr ""
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "90 degrees"
msgstr ""
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "180 degrees"
msgstr ""
#: src/ui/displays-view.vala:294 src/ui/monitor-settings-content.vala:306
msgid "270 degrees"
msgstr ""
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped"
msgstr ""
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped 90 degrees"
msgstr ""
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped 180 degrees"
msgstr ""
#: src/ui/displays-view.vala:295 src/ui/monitor-settings-content.vala:307
msgid "Flipped 270 degrees"
msgstr ""
#: src/ui/displays-view.vala:301 src/ui/monitor-settings-content.vala:313
msgid "Rotation"
msgstr ""
#: src/ui/displays-view.vala:334
msgid "Primary Display"
msgstr ""
#: src/ui/monitor-settings-content.vala:154
#: src/ui/monitor-settings-content.vala:162
msgid "Refresh Rate"
msgstr ""
#: src/ui/monitor-settings-content.vala:184
msgid "Variable Refresh Rate"
msgstr ""
#: src/ui/monitor-settings-content.vala:333
msgid "None"
msgstr ""
#: src/ui/monitor-settings-content.vala:348
msgid "Mirror"
msgstr ""
#: src/ui/monitor-settings-content.vala:364
msgid "Use description"
msgstr ""
#: src/ui/monitor-settings-content.vala:373
msgid "Bit depth"
msgstr ""
#: src/ui/monitor-settings-content.vala:374
msgid "VRR"
msgstr ""
#: src/ui/monitor-settings-content.vala:374
#: src/ui/monitor-settings-content.vala:380
#: src/ui/monitor-settings-content.vala:381
msgid "Off"
msgstr ""
#: src/ui/monitor-settings-content.vala:374
#: src/ui/monitor-settings-content.vala:380
#: src/ui/monitor-settings-content.vala:381
msgid "On"
msgstr ""
#: src/ui/monitor-settings-content.vala:374
msgid "Fullscreen"
msgstr ""
#: src/ui/monitor-settings-content.vala:374
msgid "Fullscreen video/game"
msgstr ""
#: src/ui/monitor-settings-content.vala:375
msgid "Color management"
msgstr ""
#: src/ui/monitor-settings-content.vala:376
msgid "SDR EOTF"
msgstr ""
#: src/ui/monitor-settings-content.vala:377
msgid "SDR brightness"
msgstr ""
#: src/ui/monitor-settings-content.vala:378
msgid "SDR saturation"
msgstr ""
#: src/ui/monitor-settings-content.vala:380
msgid "Force wide color"
msgstr ""
#: src/ui/monitor-settings-content.vala:380
#: src/ui/monitor-settings-content.vala:381
msgid "Auto"
msgstr ""
#: src/ui/monitor-settings-content.vala:381
msgid "Force HDR"
msgstr ""
#: src/ui/monitor-settings-content.vala:382
msgid "SDR min luminance"
msgstr ""
#: src/ui/monitor-settings-content.vala:383
msgid "SDR max luminance"
msgstr ""
#: src/ui/monitor-settings-content.vala:384
msgid "Min luminance"
msgstr ""
#: src/ui/monitor-settings-content.vala:385
msgid "Max luminance"
msgstr ""
#: src/ui/monitor-settings-content.vala:386
msgid "Max average luminance"
msgstr ""
#: src/ui/monitor-settings-content.vala:389
msgid "ICC profile"
msgstr ""
#: src/ui/monitor-settings-content.vala:401
msgid "Underscanning"
msgstr ""
#: src/ui/monitor-settings-content.vala:412
msgid "HDR"
msgstr ""
#: src/ui/monitor-settings-content.vala:571
#, c-format
msgid "Variable (up to %.2f Hz)"
msgstr ""
#: src/ui/monitor-settings-content.vala:572
msgid "Variable"
msgstr ""
#: src/ui/monitor-settings-content.vala:575 src/ui/ui-helpers.vala:50
#, c-format
msgid "%.2f Hz"
msgstr ""
namespace TunerDisplays {
public errordomain BackendError {
UNSUPPORTED,
COMMAND_FAILED,
PARSE_FAILED,
APPLY_FAILED
}
public abstract class DisplayBackend : Object {
public abstract string id { get; }
public abstract string title { owned get; }
public abstract bool can_apply { get; }
public abstract Gee.ArrayList<MonitorConfig> load() throws Error;
public abstract void apply(Gee.ArrayList<MonitorConfig> monitors) throws Error;
public static DisplayBackend create_for_session() {
var desktop = (Environment.get_variable("XDG_CURRENT_DESKTOP") ?? "").down();
var session = (Environment.get_variable("XDG_SESSION_DESKTOP") ?? "").down();
var detected = desktop != "" ? desktop : session;
if ("hyprland" in detected) {
return new HyprlandBackend();
}
if ("gnome" in detected) {
return new GnomeBackend();
}
return new GnomeBackend();
}
}
}
namespace TunerDisplays {
private const string GETTEXT_PACKAGE = "@GETTEXT_PACKAGE@";
private const string LOCALEDIR = "@LOCALEDIR@";
}
namespace TunerDisplays {
public class DisplayMode : Object {
public string id { get; set; default = ""; }
public int width { get; set; }
public int height { get; set; }
public double refresh { get; set; }
public double preferred_scale { get; set; default = 1.0; }
public bool variable_refresh_rate { get; set; }
public Gee.ArrayList<double?> supported_scales { get; private set; default = new Gee.ArrayList<double?>(); }
public string label {
owned get {
return "%dx%d@%.2f".printf(width, height, refresh);
}
}
}
public class MonitorConfig : Object {
public string name { get; set; default = ""; }
public string description { get; set; default = ""; }
public string vendor { get; set; default = ""; }
public string product { get; set; default = ""; }
public string serial { get; set; default = ""; }
public bool enabled { get; set; default = true; }
public bool primary { get; set; }
public int x { get; set; }
public int y { get; set; }
public int width { get; set; }
public int height { get; set; }
public double refresh { get; set; }
public double scale { get; set; default = 1.0; }
public string transform { get; set; default = "normal"; }
public string mirror { get; set; default = ""; }
public bool mirrored { get; set; }
public bool use_description { get; set; }
public int bitdepth { get; set; default = 8; }
public int vrr { get; set; }
public string color_management_preset { get; set; default = "srgb"; }
public string sdr_eotf { get; set; default = "default"; }
public double sdr_brightness { get; set; default = 1.0; }
public double sdr_saturation { get; set; default = 1.0; }
public int supports_wide_color { get; set; }
public int supports_hdr { get; set; }
public double sdr_min_luminance { get; set; default = 0.2; }
public int sdr_max_luminance { get; set; default = 80; }
public double min_luminance { get; set; default = -1; }
public int max_luminance { get; set; default = -1; }
public int max_avg_luminance { get; set; default = -1; }
public string icc { get; set; default = ""; }
public bool underscanning { get; set; }
public int color_mode { get; set; }
public bool supports_variable_refresh_rate { get; set; }
public bool variable_refresh_rate { get; set; }
public Gee.ArrayList<int> supported_color_modes { get; private set; default = new Gee.ArrayList<int>(); }
public bool dpms { get; set; default = true; }
public Gee.ArrayList<DisplayMode> modes { get; private set; default = new Gee.ArrayList<DisplayMode>(); }
public string display_name {
owned get {
if (is_builtin())
return _("Built-in Display");
return description != "" ? description : name;
}
}
public string preview_name {
owned get {
return name;
}
}
public string title {
owned get {
return display_name;
}
}
private bool is_builtin() {
return name.has_prefix("eDP-") || name.has_prefix("LVDS-") || name.has_prefix("DSI-");
}
}
}
namespace TunerDisplays {
public class ShellCommand : Object {
public static string run(string command) throws Error {
string stdout;
string stderr;
int status;
Process.spawn_command_line_sync(command, out stdout, out stderr, out status);
if (status != 0) {
throw new BackendError.COMMAND_FAILED(
"%s failed with status %d: %s".printf(command, status, stderr.strip())
);
}
return stdout;
}
public static string quote(string value) {
return Shell.quote(value);
}
}
}
deps = [
dependency('gee-0.8'),
dependency('json-glib-1.0'),
dependency('tuner-1'),
]
sources = files(
'plugin.vala',
'backends/display-backend.vala',
'backends/gnome-backend.vala',
'backends/hyprland-backend.vala',
'core/display-model.vala',
'core/shell-command.vala',
'ui/displays-view.vala',
'ui/monitor-layout.vala',
'ui/monitor-row.vala',
'ui/monitor-settings-content.vala',
'ui/ui-helpers.vala',
) + configure_file(
input: 'build.vala.in',
output: 'build.vala',
configuration: config,
)
shared_module(
output,
resources,
sources,
dependencies: deps,
install: true,
install_dir: get_option('libdir') / 'tuner' / 'plugins'
)
namespace TunerDisplays {
public class Addin : Tuner.Addin {
private DisplaysView view;
private Tuner.Page page;
private Tuner.Page monitor_page;
private Gtk.Box monitor_page_content;
private Gtk.Label monitor_page_title;
construct {
Intl.bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
view = new DisplaysView();
page = new Tuner.Page() {
title = _("Displays"),
id = "displays",
category = "system",
icon_name = "video-display-symbolic",
priority = 900,
content = view
};
var refresh = new Gtk.Button.from_icon_name("view-refresh-symbolic") {
tooltip_text = _("Refresh")
};
refresh.clicked.connect(view.reload);
page.pack_end(refresh);
var apply = new Gtk.Button.with_label(_("Apply")) {
tooltip_text = _("Apply"),
sensitive = view.can_apply,
css_classes = { "suggested-action" }
};
apply.clicked.connect(view.apply_changes);
page.pack_end(apply);
monitor_page_content = new Gtk.Box(Gtk.Orientation.VERTICAL, 0) {
hexpand = true,
vexpand = true
};
monitor_page_title = new Gtk.Label(_("Monitor")) {
single_line_mode = true,
ellipsize = Pango.EllipsizeMode.END
};
monitor_page = new Tuner.Page() {
title = _("Monitor"),
id = monitor_settings_page_id(),
content = monitor_page_content,
title_widget = monitor_page_title
};
var monitor_apply = new Gtk.Button.with_label(_("Apply")) {
tooltip_text = _("Apply"),
sensitive = view.can_apply,
css_classes = { "suggested-action" }
};
monitor_apply.clicked.connect(view.apply_changes);
monitor_page.pack_end(monitor_apply);
page.add_page(monitor_page);
view.monitor_settings_requested.connect(show_monitor_settings);
add_page(page);
}
private void show_monitor_settings(MonitorConfig monitor) {
monitor_page.title = monitor.title;
monitor_page_title.label = monitor.title;
var child = monitor_page_content.get_first_child();
while (child != null) {
var next = child.get_next_sibling();
monitor_page_content.remove(child);
child = next;
}
monitor_page_content.append(create_monitor_page_content(monitor));
}
private MonitorSettingsContent create_monitor_page_content(MonitorConfig monitor) {
var content = new MonitorSettingsContent(monitor, view.backend_id, view.monitor_configs);
content.monitor_changed.connect(view.refresh_from_monitors);
return content;
}
}
}
public void peas_register_types(TypeModule module) {
var obj = (Peas.ObjectModule) module;
obj.register_extension_type(typeof(Tuner.Addin), typeof(TunerDisplays.Addin));
}
namespace TunerDisplays {
public class MonitorRow : Adw.ActionRow {
private MonitorConfig monitor;
private Gee.ArrayList<MonitorConfig> all_monitors;
private string page_id;
private Gtk.Switch enabled_switch;
public signal void monitor_changed();
public signal void monitor_selected(MonitorConfig monitor);
public MonitorRow(MonitorConfig monitor, string page_id, Gee.ArrayList<MonitorConfig> all_monitors) {
Object(
title: monitor.title,
subtitle: "%dx%d@%.2f scale %.2f %dx%d".printf(
monitor.width, monitor.height, monitor.refresh, monitor.scale, monitor.x, monitor.y
)
);
this.monitor = monitor;
this.all_monitors = all_monitors;
this.page_id = page_id;
build();
}
private void build() {
activatable = true;
selectable = false;
activated.connect(() => {
monitor_selected(monitor);
activate_action("navigation.push", "s", page_id);
});
enabled_switch = new Gtk.Switch() {
valign = Gtk.Align.CENTER,
active = monitor.enabled,
can_focus = true
};
enabled_switch.notify["active"].connect(() => {
if (enabled_switch.active != monitor.enabled)
place_monitor_after_active(monitor, all_monitors);
monitor.enabled = enabled_switch.active;
monitor_changed();
});
monitor.notify["enabled"].connect(() => sync_from_monitor());
add_suffix(enabled_switch);
var arrow = new Gtk.Image.from_icon_name("go-next-symbolic") {
valign = Gtk.Align.CENTER,
pixel_size = 16
};
add_suffix(arrow);
}
public void sync_from_monitor() {
if (enabled_switch.active != monitor.enabled)
enabled_switch.active = monitor.enabled;
subtitle = "%dx%d@%.2f scale %.2f %dx%d".printf(
monitor.width, monitor.height, monitor.refresh, monitor.scale, monitor.x, monitor.y
);
}
}
}
namespace TunerDisplays {
public static string monitor_settings_page_id() {
return "display-monitor-settings";
}
private static void place_monitor_after_active(MonitorConfig monitor, Gee.ArrayList<MonitorConfig> monitors) {
bool found_active = false;
int max_x = 0;
foreach (var other in monitors) {
if (other == monitor || !other.enabled)
continue;
var right = other.x + (int) Math.round(logical_width_for_monitor(other));
if (!found_active || right > max_x)
max_x = right;
found_active = true;
}
monitor.x = found_active ? max_x : 0;
monitor.y = 0;
}
private static double logical_width_for_monitor(MonitorConfig monitor) {
if (monitor.transform.index_of("90") >= 0 || monitor.transform.index_of("270") >= 0)
return monitor.height / monitor.scale;
return monitor.width / monitor.scale;
}
private static bool mode_supports_scale(DisplayMode mode, double scale) {
foreach (var supported in mode.supported_scales) {
if (Math.fabs(supported - scale) < 0.01)
return true;
}
return false;
}
private static bool has_resolution(Gee.ArrayList<DisplayMode> modes, int width, int height) {
foreach (var mode in modes) {
if (mode.width == width && mode.height == height)
return true;
}
return false;
}
private static string refresh_rate_label(DisplayMode mode) {
return _("%.2f Hz").printf(mode.refresh);
}
private static uint transform_to_index(string transform) {
switch (transform) {
case "90": return 1;
case "180": return 2;
case "270": return 3;
case "flipped": return 4;
case "flipped-90": return 5;
case "flipped-180": return 6;
case "flipped-270": return 7;
default: return 0;
}
}
private static string transform_from_index(int index) {
switch (index) {
case 1: return "90";
case 2: return "180";
case 3: return "270";
case 4: return "flipped";
case 5: return "flipped-90";
case 6: return "flipped-180";
case 7: return "flipped-270";
default: return "normal";
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment