Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
tuner-displays
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ximper Linux
tuner-displays
Commits
ffcc0360
Verified
Commit
ffcc0360
authored
Jun 15, 2026
by
Kirill Unitsaev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ui: reorganize display settings sources
parent
355bd1d9
Hide whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
1971 additions
and
1346 deletions
+1971
-1346
gresource.xml
data/gresource.xml
+5
-2
meson.build
data/ui/meson.build
+5
-2
monitor-settings-content.blp
data/ui/monitor-settings-content.blp
+0
-22
displays-page.blp
data/ui/pages/displays-page.blp
+114
-0
monitor-settings-content.blp
data/ui/pages/monitor-settings-content.blp
+311
-0
backdrop-color-widget.blp
data/ui/widgets/backdrop-color-widget.blp
+22
-0
config-include-widget.blp
data/ui/widgets/config-include-widget.blp
+8
-23
monitor-layout.blp
data/ui/widgets/monitor-layout.blp
+9
-0
POTFILES
po/POTFILES
+14
-6
meson.build
src/meson.build
+18
-6
plugin.vala
src/plugin.vala
+43
-46
displays-controller.vala
src/ui/common/displays-controller.vala
+271
-0
displays-visibility.vala
src/ui/common/displays-visibility.vala
+94
-0
ui-utils.vala
src/ui/common/ui-utils.vala
+21
-9
displays-view.vala
src/ui/displays-view.vala
+0
-532
monitor-settings-content.vala
src/ui/monitor-settings-content.vala
+0
-686
monitor-settings-content.vala
src/ui/pages/monitor-settings-content.vala
+23
-0
monitor-choice-loader.vala
src/ui/settings/monitor-choice-loader.vala
+131
-0
monitor-setting-binding.vala
src/ui/settings/monitor-setting-binding.vala
+353
-0
monitor-setting-validator.vala
src/ui/settings/monitor-setting-validator.vala
+87
-0
monitor-settings-context.vala
src/ui/settings/monitor-settings-context.vala
+23
-0
backdrop-color-widget.vala
src/ui/widgets/backdrop-color-widget.vala
+85
-0
config-include-widget.vala
src/ui/widgets/config-include-widget.vala
+39
-12
mirror-settings-widget.vala
src/ui/widgets/mirror-settings-widget.vala
+106
-0
monitor-layout-widget.vala
src/ui/widgets/monitor-layout-widget.vala
+29
-0
monitor-layout.vala
src/ui/widgets/monitor-layout.vala
+9
-0
monitor-list-widget.vala
src/ui/widgets/monitor-list-widget.vala
+30
-0
monitor-row.vala
src/ui/widgets/monitor-row.vala
+0
-0
primary-display-widget.vala
src/ui/widgets/primary-display-widget.vala
+50
-0
single-monitor-widget.vala
src/ui/widgets/single-monitor-widget.vala
+37
-0
status-widget.vala
src/ui/widgets/status-widget.vala
+34
-0
No files found.
data/gresource.xml
View file @
ffcc0360
<?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>
<file
alias=
"pages/displays-page.ui"
preprocess=
"xml-stripblanks"
>
ui/pages/displays-page.ui
</file>
<file
alias=
"pages/monitor-settings-content.ui"
preprocess=
"xml-stripblanks"
>
ui/pages/monitor-settings-content.ui
</file>
<file
alias=
"widgets/backdrop-color-widget.ui"
preprocess=
"xml-stripblanks"
>
ui/widgets/backdrop-color-widget.ui
</file>
<file
alias=
"widgets/config-include-widget.ui"
preprocess=
"xml-stripblanks"
>
ui/widgets/config-include-widget.ui
</file>
<file
alias=
"widgets/monitor-layout.ui"
preprocess=
"xml-stripblanks"
>
ui/widgets/monitor-layout.ui
</file>
</gresource>
</gresources>
data/ui/meson.build
View file @
ffcc0360
blueprints = files(
'displays-view.blp',
'monitor-settings-content.blp',
'pages/displays-page.blp',
'pages/monitor-settings-content.blp',
'widgets/backdrop-color-widget.blp',
'widgets/config-include-widget.blp',
'widgets/monitor-layout.blp',
)
data/ui/monitor-settings-content.blp
deleted
100644 → 0
View file @
355bd1d9
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");
}
}
data/ui/pages/displays-page.blp
0 → 100644
View file @
ffcc0360
using Gtk 4.0;
using Tuner 1;
translation-domain "tuner-displays";
Tuner.Page displays_page {
title: _("Displays");
id: "displays";
category: "system";
icon-name: "video-display-symbolic";
priority: 900;
Tuner.Group {
id: "status";
$TunerDisplaysConfigIncludeWidget config_include_widget {
binding: $TunerDisplaysConfigIncludeBinding {
validator: $TunerDisplaysConfigIncludeValidator {};
};
}
$TunerDisplaysStatusWidget status_widget {
binding: $TunerDisplaysDisplaysVisibilityBinding {
mode: "status-group";
validator: $TunerDisplaysDisplaysVisibilityValidator {
group-mode: "status-group";
};
};
}
Tuner.Switch {
title: _("Mirror Displays");
binding: $TunerDisplaysMirrorModeBinding {
validator: $TunerDisplaysDisplaysVisibilityValidator {
mode: "mirror-switch";
group-mode: "status-group";
};
};
}
}
Tuner.Group {
id: "layout";
$TunerDisplaysMonitorLayoutWidget layout_widget {
binding: $TunerDisplaysDisplaysVisibilityBinding {
mode: "layout";
validator: $TunerDisplaysDisplaysVisibilityValidator {
group-mode: "layout-group";
};
};
}
$TunerDisplaysMirrorSettingsWidget mirror_settings_widget {}
}
Tuner.Group {
title: _("Details");
id: "monitors";
$TunerDisplaysPrimaryDisplayWidget primary_display_widget {}
$TunerDisplaysMonitorListWidget monitor_list_widget {
binding: $TunerDisplaysDisplaysVisibilityBinding {
mode: "details";
validator: $TunerDisplaysDisplaysVisibilityValidator {};
};
}
}
Tuner.Group {
id: "single-monitor";
$TunerDisplaysSingleMonitorWidget single_monitor_widget {
binding: $TunerDisplaysDisplaysVisibilityBinding {
mode: "single-monitor";
validator: $TunerDisplaysDisplaysVisibilityValidator {};
};
}
}
}
Button refresh_button {
icon-name: "view-refresh-symbolic";
tooltip-text: _("Refresh");
}
Button apply_button {
label: _("Apply");
tooltip-text: _("Apply");
styles ["suggested-action"]
}
Tuner.Page monitor_settings_page {
title: _("Monitor");
id: "display-monitor-settings";
content: Box monitor_page_content {
orientation: vertical;
hexpand: true;
vexpand: true;
};
title-widget: Label monitor_page_title {
label: _("Monitor");
};
}
Button monitor_apply_button {
label: _("Apply");
tooltip-text: _("Apply");
styles ["suggested-action"]
}
data/ui/pages/monitor-settings-content.blp
0 → 100644
View file @
ffcc0360
using Gtk 4.0;
using Tuner 1;
translation-domain "tuner-displays";
Tuner.Page monitor_settings_content_page {
title: _("Monitor");
id: "display-monitor-settings-content";
Tuner.Group {
title: _("Display");
id: "display";
Tuner.Switch {
title: _("Enabled");
binding: $TunerDisplaysMonitorSettingBinding {
field: "enabled";
validator: $TunerDisplaysMonitorSettingValidator { feature: "enabled"; };
};
}
Tuner.Combo {
title: _("Resolution");
binding: $TunerDisplaysMonitorSettingBinding {
field: "mode";
validator: $TunerDisplaysMonitorSettingValidator { feature: "combined-mode"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "mode"; }
}
Tuner.Combo {
title: _("Resolution");
binding: $TunerDisplaysMonitorSettingBinding {
field: "resolution";
validator: $TunerDisplaysMonitorSettingValidator { feature: "separate-refresh"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "resolution"; }
}
Tuner.Expander {
title: _("Refresh Rate");
binding: $TunerDisplaysMonitorSettingBinding {
field: "refresh-visible";
validator: $TunerDisplaysMonitorSettingValidator { feature: "separate-refresh"; };
};
Tuner.Combo {
title: _("Refresh Rate");
binding: $TunerDisplaysMonitorSettingBinding {
field: "refresh";
validator: $TunerDisplaysMonitorSettingValidator { feature: "fixed-refresh"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "refresh"; }
}
Tuner.Switch {
title: _("Variable Refresh Rate");
binding: $TunerDisplaysMonitorSettingBinding {
field: "variable-refresh";
validator: $TunerDisplaysMonitorSettingValidator { feature: "variable-refresh"; };
};
}
}
Tuner.Combo {
title: _("Scale");
binding: $TunerDisplaysMonitorSettingBinding {
field: "scale-choice";
validator: $TunerDisplaysMonitorSettingValidator { feature: "scale-choice"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "scale"; }
}
Tuner.Spin {
title: _("Scale");
digits: 2;
binding: $TunerDisplaysMonitorSettingBinding {
field: "scale-spin";
lower: 0.25;
upper: 8.0;
step: 0.05;
validator: $TunerDisplaysMonitorSettingValidator { feature: "scale-spin"; };
};
}
Tuner.Combo {
title: _("Rotation");
binding: $TunerDisplaysMonitorSettingBinding { field: "transform"; };
$TunerDisplaysMonitorChoiceLoader { field: "transform"; }
}
Tuner.Combo {
title: _("Mirror");
binding: $TunerDisplaysMonitorSettingBinding {
field: "mirror";
validator: $TunerDisplaysMonitorSettingValidator { feature: "mirror"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "mirror"; }
}
Tuner.Combo {
title: _("Variable Refresh Rate");
binding: $TunerDisplaysMonitorSettingBinding {
field: "niri-vrr";
validator: $TunerDisplaysMonitorSettingValidator { feature: "adaptive-sync"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "niri-vrr"; }
}
Tuner.Switch {
title: _("Focus at startup");
binding: $TunerDisplaysMonitorSettingBinding {
field: "niri-focus";
validator: $TunerDisplaysMonitorSettingValidator { feature: "focus-at-startup"; };
};
}
$TunerDisplaysBackdropColorWidget {
binding: $TunerDisplaysMonitorSettingBinding {
field: "backdrop-color";
validator: $TunerDisplaysMonitorSettingValidator { feature: "backdrop-color"; };
};
}
Tuner.Combo {
title: _("Hot corners");
binding: $TunerDisplaysMonitorSettingBinding {
field: "hot-corners";
validator: $TunerDisplaysMonitorSettingValidator { feature: "hot-corners"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "hot-corners"; }
}
Tuner.Switch {
title: _("Adjust for TV");
binding: $TunerDisplaysMonitorSettingBinding {
field: "underscanning";
validator: $TunerDisplaysMonitorSettingValidator { feature: "underscanning"; };
};
}
Tuner.Switch {
title: _("HDR");
binding: $TunerDisplaysMonitorSettingBinding {
field: "hdr-toggle";
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-toggle"; };
};
}
}
Tuner.Group {
title: _("Hyprland");
id: "hyprland";
Tuner.Switch {
title: _("Use description");
binding: $TunerDisplaysMonitorSettingBinding {
field: "use-description";
validator: $TunerDisplaysMonitorSettingValidator { feature: "description-identifier"; group-feature: "hyprland"; };
};
}
Tuner.Combo {
title: _("Bit depth");
binding: $TunerDisplaysMonitorSettingBinding {
field: "bitdepth";
validator: $TunerDisplaysMonitorSettingValidator { feature: "bit-depth"; group-feature: "hyprland"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "bitdepth"; }
}
Tuner.Combo {
title: _("VRR");
binding: $TunerDisplaysMonitorSettingBinding {
field: "hypr-vrr";
validator: $TunerDisplaysMonitorSettingValidator { feature: "vrr-modes"; group-feature: "hyprland"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "hypr-vrr"; }
}
Tuner.Combo {
title: _("Color management");
binding: $TunerDisplaysMonitorSettingBinding {
field: "color-management";
validator: $TunerDisplaysMonitorSettingValidator { feature: "color-management"; group-feature: "hyprland"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "color-management"; }
}
Tuner.Combo {
title: _("SDR EOTF");
binding: $TunerDisplaysMonitorSettingBinding {
field: "sdr-eotf";
validator: $TunerDisplaysMonitorSettingValidator { feature: "color-management"; group-feature: "hyprland"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "sdr-eotf"; }
}
Tuner.Spin {
title: _("SDR brightness");
digits: 2;
binding: $TunerDisplaysMonitorSettingBinding {
field: "sdr-brightness";
lower: 0.1;
upper: 4.0;
step: 0.05;
validator: $TunerDisplaysMonitorSettingValidator { feature: "color-management"; group-feature: "hyprland"; };
};
}
Tuner.Spin {
title: _("SDR saturation");
digits: 2;
binding: $TunerDisplaysMonitorSettingBinding {
field: "sdr-saturation";
lower: 0.1;
upper: 4.0;
step: 0.05;
validator: $TunerDisplaysMonitorSettingValidator { feature: "color-management"; group-feature: "hyprland"; };
};
}
}
Tuner.Group {
title: _("HDR / EDID overrides");
id: "hdr";
Tuner.Combo {
title: _("Force wide color");
binding: $TunerDisplaysMonitorSettingBinding {
field: "force-wide-color";
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "force-toggle"; }
}
Tuner.Combo {
title: _("Force HDR");
binding: $TunerDisplaysMonitorSettingBinding {
field: "force-hdr";
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
$TunerDisplaysMonitorChoiceLoader { field: "force-toggle"; }
}
Tuner.Spin {
title: _("SDR min luminance");
digits: 2;
binding: $TunerDisplaysMonitorSettingBinding {
field: "sdr-min-luminance";
lower: 0.0;
upper: 10.0;
step: 0.01;
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
}
Tuner.Spin {
title: _("SDR max luminance");
binding: $TunerDisplaysMonitorSettingBinding {
field: "sdr-max-luminance";
lower: 1;
upper: 1000;
step: 1;
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
}
Tuner.Spin {
title: _("Min luminance");
digits: 2;
binding: $TunerDisplaysMonitorSettingBinding {
field: "min-luminance";
lower: -1;
upper: 10.0;
step: 0.01;
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
}
Tuner.Spin {
title: _("Max luminance");
binding: $TunerDisplaysMonitorSettingBinding {
field: "max-luminance";
lower: -1;
upper: 10000;
step: 1;
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
}
Tuner.Spin {
title: _("Max average luminance");
binding: $TunerDisplaysMonitorSettingBinding {
field: "max-avg-luminance";
lower: -1;
upper: 10000;
step: 1;
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
}
Tuner.Entry {
title: _("ICC profile");
binding: $TunerDisplaysMonitorSettingBinding {
field: "icc";
validator: $TunerDisplaysMonitorSettingValidator { feature: "hdr-metadata"; group-feature: "hdr"; };
};
}
}
}
data/ui/widgets/backdrop-color-widget.blp
0 → 100644
View file @
ffcc0360
using Gtk 4.0;
using Adw 1;
translation-domain "tuner-displays";
template $TunerDisplaysBackdropColorContent : Adw.ActionRow {
title: _("Backdrop color");
[suffix]
ColorDialogButton color_button {
valign: center;
dialog: ColorDialog {
title: _("Backdrop color");
with-alpha: true;
};
}
[suffix]
Switch enabled_switch {
valign: center;
}
}
data/ui/
displays-view
.blp
→
data/ui/
widgets/config-include-widget
.blp
View file @
ffcc0360
...
...
@@ -3,28 +3,13 @@ using Adw 1;
translation-domain "tuner-displays";
template $TunerDisplaysDisplaysView : Adw.PreferencesPage {
title: _("Displays");
icon-name: "video-display-symbolic";
Adw.PreferencesGroup config_include_group {
Adw.ActionRow config_include_row {
visible: false;
title: _("Monitor configuration is not connected");
[suffix]
Button config_include_button {
valign: center;
label: _("Connect");
}
}
}
Adw.PreferencesGroup status_group {}
Adw.PreferencesGroup layout_group {}
Adw.PreferencesGroup monitors_group {
title: _("Details");
template $TunerDisplaysConfigIncludeContent : Adw.ActionRow {
visible: false;
title: _("Monitor configuration is not connected");
[suffix]
Button connect_button {
valign: center;
label: _("Connect");
}
}
data/ui/widgets/monitor-layout.blp
0 → 100644
View file @
ffcc0360
using Gtk 4.0;
template $TunerDisplaysMonitorLayout : DrawingArea {
height-request: 320;
hexpand: true;
vexpand: true;
can-focus: false;
focusable: false;
}
po/POTFILES
View file @
ffcc0360
data/ui/displays-view.blp
data/ui/monitor-settings-content.blp
data/ui/pages/displays-page.blp
data/ui/pages/monitor-settings-content.blp
data/ui/widgets/backdrop-color-widget.blp
data/ui/widgets/config-include-widget.blp
src/plugin.vala
src/backends/gnome-backend.vala
src/backends/hyprland-backend.vala
src/backends/niri-backend.vala
src/core/display-model.vala
src/ui/displays-view.vala
src/ui/monitor-row.vala
src/ui/monitor-settings-content.vala
src/ui/ui-helpers.vala
src/ui/common/displays-controller.vala
src/ui/common/ui-utils.vala
src/ui/pages/monitor-settings-content.vala
src/ui/settings/monitor-choice-loader.vala
src/ui/settings/monitor-setting-binding.vala
src/ui/widgets/config-include-widget.vala
src/ui/widgets/mirror-settings-widget.vala
src/ui/widgets/monitor-row.vala
src/ui/widgets/primary-display-widget.vala
src/ui/widgets/status-widget.vala
src/meson.build
View file @
ffcc0360
...
...
@@ -12,12 +12,24 @@ sources = files(
'backends/niri-backend.vala',
'core/display-model.vala',
'core/shell-command.vala',
'ui/config-include-validator.vala',
'ui/displays-view.vala',
'ui/monitor-layout.vala',
'ui/monitor-row.vala',
'ui/monitor-settings-content.vala',
'ui/ui-helpers.vala',
'ui/common/displays-controller.vala',
'ui/common/displays-visibility.vala',
'ui/common/ui-utils.vala',
'ui/widgets/backdrop-color-widget.vala',
'ui/widgets/config-include-widget.vala',
'ui/widgets/mirror-settings-widget.vala',
'ui/widgets/monitor-list-widget.vala',
'ui/widgets/monitor-layout.vala',
'ui/widgets/monitor-layout-widget.vala',
'ui/widgets/monitor-row.vala',
'ui/widgets/primary-display-widget.vala',
'ui/widgets/single-monitor-widget.vala',
'ui/widgets/status-widget.vala',
'ui/pages/monitor-settings-content.vala',
'ui/settings/monitor-choice-loader.vala',
'ui/settings/monitor-setting-binding.vala',
'ui/settings/monitor-setting-validator.vala',
'ui/settings/monitor-settings-context.vala',
) + configure_file(
input: 'build.vala.in',
output: 'build.vala',
...
...
src/plugin.vala
View file @
ffcc0360
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
;
...
...
@@ -10,58 +9,56 @@ namespace TunerDisplays {
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
);
DisplaysContext
.
controller
=
new
DisplaysController
();
typeof
(
ConfigIncludeBinding
).
ensure
();
typeof
(
ConfigIncludeValidator
).
ensure
();
typeof
(
ConfigIncludeContent
).
ensure
();
typeof
(
ConfigIncludeWidget
).
ensure
();
typeof
(
StatusWidget
).
ensure
();
typeof
(
MirrorModeBinding
).
ensure
();
typeof
(
DisplaysVisibilityBinding
).
ensure
();
typeof
(
DisplaysVisibilityValidator
).
ensure
();
typeof
(
MonitorLayout
).
ensure
();
typeof
(
MonitorLayoutWidget
).
ensure
();
typeof
(
MirrorSettingsWidget
).
ensure
();
typeof
(
PrimaryDisplayWidget
).
ensure
();
typeof
(
MonitorListWidget
).
ensure
();
typeof
(
SingleMonitorWidget
).
ensure
();
typeof
(
MonitorSettingBinding
).
ensure
();
typeof
(
MonitorSettingValidator
).
ensure
();
typeof
(
MonitorChoiceLoader
).
ensure
();
typeof
(
BackdropColorContent
).
ensure
();
typeof
(
BackdropColorWidget
).
ensure
();
var
builder
=
new
Gtk
.
Builder
.
from_resource
(
"/ru/ximperlinux/tuner/Displays/pages/displays-page.ui"
);
page
=
builder
.
get_object
(
"displays_page"
)
as
Tuner
.
Page
;
var
refresh
=
builder
.
get_object
(
"refresh_button"
)
as
Gtk
.
Button
;
refresh
.
clicked
.
connect
(
DisplaysContext
.
controller
.
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
);
var
apply
=
builder
.
get_object
(
"apply_button"
)
as
Gtk
.
Button
;
apply
.
sensitive
=
DisplaysContext
.
controller
.
can_apply
;
apply
.
clicked
.
connect
(
DisplaysContext
.
controller
.
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
=
builder
.
get_object
(
"monitor_settings_page"
)
as
Tuner
.
Page
;
monitor_page_content
=
builder
.
get_object
(
"monitor_page_content"
)
as
Gtk
.
Box
;
monitor_page_title
=
builder
.
get_object
(
"monitor_page_title"
)
as
Gtk
.
Label
;
monitor_page_title
.
single_line_mode
=
true
;
monitor_page_title
.
ellipsize
=
Pango
.
EllipsizeMode
.
END
;
var
monitor_apply
=
builder
.
get_object
(
"monitor_apply_button"
)
as
Gtk
.
Button
;
monitor_apply
.
sensitive
=
DisplaysContext
.
controller
.
can_apply
;
monitor_apply
.
clicked
.
connect
(
DisplaysContext
.
controller
.
apply_changes
);
monitor_page
.
pack_end
(
monitor_apply
);
page
.
add_page
(
monitor_page
);
view
.
monitor_settings_requested
.
connect
(
show_monitor_settings
);
DisplaysContext
.
controller
.
monitor_settings_requested
.
connect
(
show_monitor_settings
);
add_page
(
page
);
DisplaysContext
.
controller
.
reload
();
}
private
void
show_monitor_settings
(
MonitorConfig
monitor
)
{
...
...
@@ -79,8 +76,8 @@ namespace TunerDisplays {
}
private
MonitorSettingsContent
create_monitor_page_content
(
MonitorConfig
monitor
)
{
var
content
=
new
MonitorSettingsContent
(
monitor
,
view
.
display_backend
,
view
.
monitor_config
s
);
content
.
monitor_changed
.
connect
(
view
.
refresh_from_monitors
);
var
content
=
new
MonitorSettingsContent
(
monitor
,
DisplaysContext
.
controller
.
backend
,
DisplaysContext
.
controller
.
monitor
s
);
content
.
monitor_changed
.
connect
(
DisplaysContext
.
controller
.
refresh_from_monitors
);
return
content
;
}
}
...
...
src/ui/common/displays-controller.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
DisplaysContext
:
Object
{
public
static
DisplaysController
controller
;
}
public
class
DisplaysController
:
Object
{
public
DisplayBackend
backend
{
get
;
private
set
;
}
public
Gee
.
ArrayList
<
MonitorConfig
>
monitors
{
get
;
private
set
;
default
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
}
public
string
?
status_title
{
get
;
private
set
;
}
public
string
?
status_subtitle
{
get
;
private
set
;
}
private
DBusConnection
?
session_bus
;
private
uint
monitors_changed_id
;
public
bool
can_apply
{
get
{
return
backend
.
can_apply
;
}
}
public
bool
has_status
{
get
{
return
status_title
!=
null
;
}
}
public
signal
void
changed
();
public
signal
void
monitor_settings_requested
(
MonitorConfig
monitor
);
public
DisplaysController
()
{
backend
=
DisplayBackend
.
create_for_session
();
subscribe_monitor_changes
();
}
~
DisplaysController
()
{
if
(
session_bus
!=
null
&&
monitors_changed_id
!=
0
)
session_bus
.
signal_unsubscribe
(
monitors_changed_id
);
}
public
void
reload
()
{
status_title
=
null
;
status_subtitle
=
null
;
try
{
monitors
=
merge_loaded_monitors
(
monitors
,
backend
.
load
());
}
catch
(
Error
err
)
{
monitors
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
status_title
=
_
(
"Failed to load monitors"
);
status_subtitle
=
err
.
message
;
}
changed
();
}
public
void
apply_changes
()
{
try
{
backend
.
apply
(
monitors
);
Tuner
.
toast
(
_
(
"Monitor settings applied"
));
}
catch
(
Error
err
)
{
Tuner
.
toast
(
err
.
message
);
}
}
public
void
refresh_from_monitors
()
{
changed
();
}
public
void
open_monitor_settings
(
MonitorConfig
monitor
)
{
monitor_settings_requested
(
monitor
);
}
public
bool
mirror_enabled
()
{
return
backend
.
supports_global_mirroring
&&
monitors
.
size
>
0
&&
monitors
[
0
].
mirrored
;
}
public
bool
single_monitor_mode
()
{
return
monitors
.
size
==
1
;
}
public
bool
layout_visible
()
{
return
!
mirror_enabled
()
&&
!
single_monitor_mode
();
}
public
bool
layout_group_visible
()
{
return
layout_visible
()
||
mirror_enabled
();
}
public
bool
mirror_mode_visible
()
{
return
backend
.
supports_global_mirroring
&&
monitors
.
size
>
1
;
}
public
void
set_mirror_enabled
(
bool
active
)
{
foreach
(
var
monitor
in
monitors
)
{
monitor
.
mirrored
=
active
;
if
(
active
)
{
monitor
.
enabled
=
true
;
monitor
.
x
=
0
;
monitor
.
y
=
0
;
}
else
{
place_monitor_after_active
(
monitor
,
monitors
);
}
}
changed
();
}
public
void
set_primary
(
MonitorConfig
selected
)
{
foreach
(
var
monitor
in
monitors
)
monitor
.
primary
=
monitor
==
selected
;
changed
();
}
public
Gee
.
ArrayList
<
MonitorConfig
>
enabled_monitors
()
{
var
enabled
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
foreach
(
var
monitor
in
monitors
)
{
if
(
monitor
.
enabled
)
enabled
.
add
(
monitor
);
}
return
enabled
;
}
public
Gee
.
ArrayList
<
DisplayMode
>
common_mirror_resolutions
()
{
var
resolutions
=
new
Gee
.
ArrayList
<
DisplayMode
>();
foreach
(
var
mode
in
common_mirror_modes
())
{
bool
exists
=
false
;
foreach
(
var
existing
in
resolutions
)
{
if
(
existing
.
width
==
mode
.
width
&&
existing
.
height
==
mode
.
height
)
{
exists
=
true
;
break
;
}
}
if
(!
exists
)
resolutions
.
add
(
mode
);
}
return
resolutions
;
}
public
DisplayMode
?
selected_common_mirror_mode
()
{
if
(
monitors
.
size
==
0
)
return
null
;
foreach
(
var
mode
in
common_mirror_resolutions
())
{
if
(
mode
.
width
==
monitors
[
0
].
width
&&
mode
.
height
==
monitors
[
0
].
height
)
return
mode
;
}
var
modes
=
common_mirror_resolutions
();
return
modes
.
size
>
0
?
modes
[
0
]
:
null
;
}
public
Gee
.
ArrayList
<
double
?>
common_mirror_scales
(
DisplayMode
mode
)
{
var
scales
=
new
Gee
.
ArrayList
<
double
?>();
foreach
(
var
scale
in
mode
.
supported_scales
)
{
bool
supported
=
true
;
foreach
(
var
monitor
in
monitors
)
{
var
compatible
=
find_compatible_mirror_mode
(
monitor
,
mode
);
if
(
compatible
==
null
||
!
compatible
.
supports_scale
(
scale
))
{
supported
=
false
;
break
;
}
}
if
(
supported
)
scales
.
add
(
scale
);
}
return
scales
;
}
public
void
apply_mirror_mode
(
DisplayMode
selected_mode
)
{
foreach
(
var
monitor
in
monitors
)
{
var
mode
=
find_compatible_mirror_mode
(
monitor
,
selected_mode
);
if
(
mode
==
null
)
continue
;
monitor
.
width
=
mode
.
width
;
monitor
.
height
=
mode
.
height
;
monitor
.
refresh
=
mode
.
refresh
;
monitor
.
variable_refresh_rate
=
mode
.
variable_refresh_rate
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
}
changed
();
}
public
void
apply_mirror_scale
(
double
scale
)
{
foreach
(
var
monitor
in
monitors
)
monitor
.
scale
=
scale
;
changed
();
}
public
void
apply_mirror_transform
(
string
transform
)
{
foreach
(
var
monitor
in
monitors
)
monitor
.
transform
=
transform
;
changed
();
}
private
Gee
.
ArrayList
<
DisplayMode
>
common_mirror_modes
()
{
var
modes
=
new
Gee
.
ArrayList
<
DisplayMode
>();
if
(
monitors
.
size
==
0
)
return
modes
;
foreach
(
var
mode
in
monitors
[
0
].
modes
)
{
if
(
all_monitors_support_mode
(
mode
))
modes
.
add
(
mode
);
}
return
modes
;
}
private
bool
all_monitors_support_mode
(
DisplayMode
mode
)
{
foreach
(
var
monitor
in
monitors
)
{
if
(
find_compatible_mirror_mode
(
monitor
,
mode
)
==
null
)
return
false
;
}
return
true
;
}
private
void
subscribe_monitor_changes
()
{
if
(!
backend
.
supports_monitor_change_events
)
return
;
try
{
session_bus
=
Bus
.
get_sync
(
BusType
.
SESSION
);
monitors_changed_id
=
session_bus
.
signal_subscribe
(
"org.gnome.Mutter.DisplayConfig"
,
"org.gnome.Mutter.DisplayConfig"
,
"MonitorsChanged"
,
"/org/gnome/Mutter/DisplayConfig"
,
null
,
DBusSignalFlags
.
NONE
,
()
=>
{
Idle
.
add
(()
=>
{
reload
();
return
false
;
});
}
);
}
catch
(
Error
err
)
{
warning
(
"Failed to subscribe to GNOME monitor changes: %s"
,
err
.
message
);
}
}
private
static
DisplayMode
?
find_compatible_mirror_mode
(
MonitorConfig
monitor
,
DisplayMode
mode
)
{
foreach
(
var
candidate
in
monitor
.
modes
)
{
if
(
candidate
.
width
==
mode
.
width
&&
candidate
.
height
==
mode
.
height
&&
Math
.
fabs
(
candidate
.
refresh
-
mode
.
refresh
)
<
0.02
)
{
return
candidate
;
}
}
foreach
(
var
candidate
in
monitor
.
modes
)
{
if
(
candidate
.
width
==
mode
.
width
&&
candidate
.
height
==
mode
.
height
)
return
candidate
;
}
return
null
;
}
private
static
Gee
.
ArrayList
<
MonitorConfig
>
merge_loaded_monitors
(
Gee
.
ArrayList
<
MonitorConfig
>
current
,
Gee
.
ArrayList
<
MonitorConfig
>
loaded
)
{
var
merged
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
foreach
(
var
loaded_monitor
in
loaded
)
{
var
monitor
=
find_monitor_by_name
(
current
,
loaded_monitor
.
name
);
if
(
monitor
==
null
)
monitor
=
loaded_monitor
;
else
monitor
.
copy_from
(
loaded_monitor
);
merged
.
add
(
monitor
);
}
return
merged
;
}
private
static
MonitorConfig
?
find_monitor_by_name
(
Gee
.
ArrayList
<
MonitorConfig
>
monitors
,
string
name
)
{
foreach
(
var
monitor
in
monitors
)
{
if
(
monitor
.
name
==
name
)
return
monitor
;
}
return
null
;
}
}
}
src/ui/common/displays-visibility.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MirrorModeBinding
:
Tuner
.
Binding
{
construct
{
DisplaysContext
.
controller
.
changed
.
connect
(
emit_changed
);
}
public
override
Type
expected_type
{
get
{
return
typeof
(
bool
);
}
}
public
override
bool
get_value
(
ref
Value
value
)
{
value
=
DisplaysContext
.
controller
.
mirror_enabled
();
return
true
;
}
public
override
void
set_value
(
Value
value
)
{
DisplaysContext
.
controller
.
set_mirror_enabled
(
value
.
get_boolean
());
}
}
public
class
DisplaysVisibilityBinding
:
Tuner
.
Binding
{
public
string
mode
{
get
;
set
;
default
=
"always"
;
}
construct
{
DisplaysContext
.
controller
.
changed
.
connect
(
emit_changed
);
}
public
override
Type
expected_type
{
get
{
return
typeof
(
bool
);
}
}
public
override
bool
get_value
(
ref
Value
value
)
{
value
=
displays_visibility
(
mode
);
return
true
;
}
public
override
void
set_value
(
Value
value
)
{
}
}
public
class
DisplaysVisibilityValidator
:
Tuner
.
Validator
{
public
string
mode
{
get
;
set
;
default
=
""
;
}
public
string
group_mode
{
get
;
set
;
default
=
""
;
}
public
override
void
apply
(
Tuner
.
Binding
binding
,
Gtk
.
Widget
native_widget
)
{
update
(
binding
,
native_widget
);
DisplaysContext
.
controller
.
changed
.
connect
(()
=>
update
(
binding
,
native_widget
));
}
private
void
update
(
Tuner
.
Binding
binding
,
Gtk
.
Widget
widget
)
{
widget
.
visible
=
current_visibility
(
binding
,
mode
);
var
group
=
widget
.
get_ancestor
(
typeof
(
Adw
.
PreferencesGroup
));
if
(
group
!=
null
)
group
.
visible
=
group_mode
==
""
?
widget
.
visible
:
displays_visibility
(
group_mode
);
}
private
static
bool
current_visibility
(
Tuner
.
Binding
binding
,
string
mode
)
{
if
(
mode
!=
""
)
return
displays_visibility
(
mode
);
Value
value
=
Value
(
typeof
(
bool
));
return
binding
.
get_value
(
ref
value
)
&&
value
.
get_boolean
();
}
}
internal
static
bool
displays_visibility
(
string
mode
)
{
var
controller
=
DisplaysContext
.
controller
;
switch
(
mode
)
{
case
"always"
:
return
true
;
case
"mirror-switch"
:
return
controller
.
mirror_mode_visible
();
case
"layout"
:
return
controller
.
layout_visible
();
case
"layout-group"
:
return
controller
.
layout_group_visible
();
case
"status-group"
:
return
config_include_visible
()
||
controller
.
has_status
||
!
controller
.
can_apply
||
controller
.
mirror_mode_visible
();
case
"details"
:
return
!
controller
.
mirror_enabled
()
&&
!
controller
.
single_monitor_mode
();
case
"single-monitor"
:
return
controller
.
single_monitor_mode
();
default
:
return
true
;
}
}
internal
static
bool
config_include_visible
()
{
var
info
=
DisplaysContext
.
controller
.
backend
.
config_include_info
();
return
info
.
state
==
ConfigIncludeState
.
NOT_INCLUDED
||
info
.
state
==
ConfigIncludeState
.
MISSING_MAIN_CONFIG
;
}
}
src/ui/
ui-helper
s.vala
→
src/ui/
common/ui-util
s.vala
View file @
ffcc0360
namespace
TunerDisplays
{
internal
static
Gtk
.
ListBox
create_boxed_list
()
{
var
list
=
new
Gtk
.
ListBox
()
{
selection_mode
=
Gtk
.
SelectionMode
.
NONE
};
list
.
add_css_class
(
"boxed-list"
);
return
list
;
}
internal
static
void
clear_list
(
Gtk
.
ListBox
list
)
{
var
child
=
list
.
get_first_child
();
while
(
child
!=
null
)
{
var
next
=
child
.
get_next_sibling
();
list
.
remove
(
child
);
child
=
next
;
}
}
internal
static
bool
list_has_children
(
Gtk
.
ListBox
list
)
{
return
list
.
get_first_child
()
!=
null
;
}
public
static
string
monitor_settings_page_id
()
{
return
"display-monitor-settings"
;
}
...
...
@@ -22,15 +43,6 @@ namespace TunerDisplays {
monitor
.
y
=
0
;
}
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
);
}
...
...
src/ui/displays-view.vala
deleted
100644 → 0
View file @
355bd1d9
namespace
TunerDisplays
{
[
GtkTemplate
(
ui
=
"/ru/ximperlinux/tuner/Displays/displays-view.ui"
)]
public
class
DisplaysView
:
Adw
.
PreferencesPage
{
private
DisplayBackend
backend
;
private
Gee
.
ArrayList
<
MonitorConfig
>
monitors
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
private
Gee
.
ArrayList
<
Gtk
.
Widget
>
monitor_rows
=
new
Gee
.
ArrayList
<
Gtk
.
Widget
>();
private
Gee
.
ArrayList
<
Gtk
.
Widget
>
status_rows
=
new
Gee
.
ArrayList
<
Gtk
.
Widget
>();
private
Gee
.
ArrayList
<
Gtk
.
Widget
>
mirror_settings_rows
=
new
Gee
.
ArrayList
<
Gtk
.
Widget
>();
private
Gee
.
ArrayList
<
Adw
.
PreferencesGroup
>
single_monitor_groups
=
new
Gee
.
ArrayList
<
Adw
.
PreferencesGroup
>();
private
MonitorSettingsContent
?
single_monitor_content
;
private
MonitorLayout
layout
;
private
Adw
.
PreferencesRow
layout_row
;
private
ConfigIncludeBinding
config_include_binding
;
private
DBusConnection
?
session_bus
;
private
uint
monitors_changed_id
;
[
GtkChild
]
private
unowned
Adw
.
ActionRow
config_include_row
;
[
GtkChild
]
private
unowned
Gtk
.
Button
config_include_button
;
[
GtkChild
]
private
unowned
Adw
.
PreferencesGroup
monitors_group
;
[
GtkChild
]
private
unowned
Adw
.
PreferencesGroup
layout_group
;
[
GtkChild
]
private
unowned
Adw
.
PreferencesGroup
status_group
;
public
bool
can_apply
{
get
{
return
backend
.
can_apply
;
}
}
public
Gee
.
ArrayList
<
MonitorConfig
>
monitor_configs
{
get
{
return
monitors
;
}
}
public
DisplayBackend
display_backend
{
get
{
return
backend
;
}
}
public
signal
void
monitor_settings_requested
(
MonitorConfig
monitor
);
construct
{
backend
=
DisplayBackend
.
create_for_session
();
config_include_binding
=
new
ConfigIncludeBinding
(
backend
);
layout_row
=
new
Adw
.
PreferencesRow
()
{
activatable
=
false
,
selectable
=
false
,
can_focus
=
false
,
focusable
=
false
};
layout
=
new
MonitorLayout
()
{
height_request
=
320
,
hexpand
=
true
,
require_connected
=
backend
.
requires_connected_layout
,
can_focus
=
false
,
focusable
=
false
};
layout
.
layout_changed
.
connect
(
sync_rows
);
layout_row
.
child
=
layout
;
layout_group
.
add
(
layout_row
);
config_include_button
.
clicked
.
connect
(
connect_monitor_config
);
new
ConfigIncludeValidator
().
apply
(
config_include_binding
,
config_include_row
);
config_include_binding
.
changed
.
connect
(
sync_config_include_button
);
subscribe_monitor_changes
();
reload
();
}
~
DisplaysView
()
{
if
(
session_bus
!=
null
&&
monitors_changed_id
!=
0
)
session_bus
.
signal_unsubscribe
(
monitors_changed_id
);
}
public
void
reload
()
{
clear_monitor_rows
();
clear_status_rows
();
clear_mirror_settings_rows
();
clear_single_monitor_settings
();
try
{
monitors
=
merge_loaded_monitors
(
monitors
,
backend
.
load
());
layout
.
set_monitors
(
monitors
);
rebuild_rows
();
}
catch
(
Error
err
)
{
layout
.
set_monitors
(
new
Gee
.
ArrayList
<
MonitorConfig
>());
sync_layout_visibility
();
sync_group_visibility
();
add_status
(
new
Adw
.
ActionRow
()
{
title
=
_
(
"Failed to load monitors"
),
subtitle
=
err
.
message
});
}
}
public
void
apply_changes
()
{
try
{
backend
.
apply
(
monitors
);
Tuner
.
toast
(
_
(
"Monitor settings applied"
));
}
catch
(
Error
err
)
{
Tuner
.
toast
(
err
.
message
);
}
}
public
void
refresh_from_monitors
()
{
layout
.
recenter
();
sync_rows
();
}
private
void
sync_rows
()
{
foreach
(
var
widget
in
monitor_rows
)
{
var
row
=
widget
as
MonitorRow
;
if
(
row
!=
null
)
row
.
sync_from_monitor
();
}
}
private
void
add_status
(
Gtk
.
Widget
row
)
{
status_group
.
add
(
row
);
status_rows
.
add
(
row
);
}
private
void
add_mirror_setting
(
Gtk
.
Widget
row
)
{
layout_group
.
add
(
row
);
mirror_settings_rows
.
add
(
row
);
}
private
void
clear_monitor_rows
()
{
foreach
(
var
row
in
monitor_rows
)
monitors_group
.
remove
(
row
);
monitor_rows
.
clear
();
}
private
void
clear_status_rows
()
{
foreach
(
var
row
in
status_rows
)
status_group
.
remove
(
row
);
status_rows
.
clear
();
}
private
void
clear_mirror_settings_rows
()
{
foreach
(
var
row
in
mirror_settings_rows
)
layout_group
.
remove
(
row
);
mirror_settings_rows
.
clear
();
}
private
void
clear_single_monitor_settings
()
{
foreach
(
var
group
in
single_monitor_groups
)
remove
(
group
);
single_monitor_groups
.
clear
();
single_monitor_content
=
null
;
}
private
void
rebuild_rows
()
{
clear_monitor_rows
();
clear_status_rows
();
clear_mirror_settings_rows
();
clear_single_monitor_settings
();
config_include_binding
.
emit_changed
();
sync_layout_visibility
();
sync_group_visibility
();
add_gnome_mirror_row
();
if
(
gnome_mirror_enabled
())
{
add_gnome_mirror_settings_rows
();
}
else
if
(
single_monitor_mode
())
{
add_single_monitor_settings
();
}
else
{
add_gnome_primary_row
();
foreach
(
var
monitor
in
monitors
)
{
var
row
=
new
MonitorRow
(
monitor
,
monitor_settings_page_id
(),
monitors
,
backend
);
row
.
monitor_selected
.
connect
(
monitor
=>
monitor_settings_requested
(
monitor
));
row
.
monitor_changed
.
connect
(()
=>
layout
.
recenter
());
monitors_group
.
add
(
row
);
monitor_rows
.
add
(
row
);
}
}
sync_layout_visibility
();
sync_group_visibility
();
if
(!
backend
.
can_apply
)
{
add_status
(
new
Adw
.
ActionRow
()
{
title
=
_
(
"Read-only backend"
),
subtitle
=
_
(
"Applying monitor layouts is not supported by this backend."
)
});
}
}
private
void
sync_config_include_button
()
{
config_include_button
.
visible
=
config_include_binding
.
info
().
state
==
ConfigIncludeState
.
NOT_INCLUDED
;
}
private
void
connect_monitor_config
()
{
try
{
config_include_binding
.
connect_config
();
Tuner
.
toast
(
_
(
"Monitor configuration connected"
));
reload
();
}
catch
(
Error
err
)
{
Tuner
.
toast
(
err
.
message
);
}
}
private
void
add_gnome_mirror_row
()
{
if
(!
backend
.
supports_global_mirroring
||
monitors
.
size
<=
1
)
return
;
var
row
=
new
Adw
.
SwitchRow
()
{
title
=
_
(
"Mirror Displays"
),
active
=
monitors
.
size
>
0
&&
monitors
[
0
].
mirrored
};
row
.
notify
[
"active"
].
connect
(()
=>
{
foreach
(
var
monitor
in
monitors
)
{
monitor
.
mirrored
=
row
.
active
;
if
(
row
.
active
)
{
monitor
.
enabled
=
true
;
monitor
.
x
=
0
;
monitor
.
y
=
0
;
}
else
{
place_monitor_after_active
(
monitor
,
monitors
);
}
}
layout
.
recenter
();
rebuild_rows
();
});
add_status
(
row
);
}
private
void
add_single_monitor_settings
()
{
if
(
monitors
.
size
!=
1
)
return
;
single_monitor_content
=
new
MonitorSettingsContent
(
monitors
[
0
],
backend
,
monitors
,
false
);
single_monitor_content
.
monitor_changed
.
connect
(
refresh_from_monitors
);
while
(
single_monitor_content
.
get_group
(
0
)
!=
null
)
{
var
group
=
single_monitor_content
.
get_group
(
0
);
single_monitor_content
.
remove
(
group
);
add
(
group
);
single_monitor_groups
.
add
(
group
);
}
}
private
void
add_gnome_mirror_settings_rows
()
{
add_gnome_mirror_mode_row
();
add_gnome_mirror_scale_row
();
add_gnome_mirror_transform_row
();
}
private
void
add_gnome_mirror_mode_row
()
{
var
modes
=
common_mirror_resolutions
();
if
(
modes
.
size
==
0
)
return
;
var
model
=
new
Gtk
.
StringList
(
null
);
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
modes
.
size
;
i
++)
{
var
mode
=
modes
[
i
];
model
.
append
(
"%dx%d"
.
printf
(
mode
.
width
,
mode
.
height
));
if
(
monitors
.
size
>
0
&&
mode
.
width
==
monitors
[
0
].
width
&&
mode
.
height
==
monitors
[
0
].
height
)
{
selected
=
i
;
}
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Resolution"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
<
0
||
index
>=
modes
.
size
)
return
;
var
selected_mode
=
modes
[
index
];
foreach
(
var
monitor
in
monitors
)
{
var
mode
=
find_compatible_mirror_mode
(
monitor
,
selected_mode
);
if
(
mode
==
null
)
continue
;
monitor
.
width
=
mode
.
width
;
monitor
.
height
=
mode
.
height
;
monitor
.
refresh
=
mode
.
refresh
;
monitor
.
variable_refresh_rate
=
mode
.
variable_refresh_rate
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
}
rebuild_rows
();
});
add_mirror_setting
(
row
);
}
private
void
add_gnome_mirror_scale_row
()
{
var
mode
=
selected_common_mirror_mode
();
if
(
mode
==
null
)
return
;
var
scales
=
common_mirror_scales
(
mode
);
if
(
scales
.
size
==
0
)
return
;
var
model
=
new
Gtk
.
StringList
(
null
);
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
scales
.
size
;
i
++)
{
var
scale
=
scales
[
i
];
model
.
append
(
"%.0f%%"
.
printf
(
scale
*
100
));
if
(
monitors
.
size
>
0
&&
Math
.
fabs
(
scale
-
monitors
[
0
].
scale
)
<
0.01
)
selected
=
i
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Scale"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
<
0
||
index
>=
scales
.
size
)
return
;
foreach
(
var
monitor
in
monitors
)
monitor
.
scale
=
scales
[
index
];
});
add_mirror_setting
(
row
);
}
private
void
add_gnome_mirror_transform_row
()
{
var
model
=
new
Gtk
.
StringList
(
null
);
string
[]
titles
=
{
_
(
"Normal"
),
_
(
"90 degrees"
),
_
(
"180 degrees"
),
_
(
"270 degrees"
),
_
(
"Flipped"
),
_
(
"Flipped 90 degrees"
),
_
(
"Flipped 180 degrees"
),
_
(
"Flipped 270 degrees"
)
};
foreach
(
var
title
in
titles
)
model
.
append
(
title
);
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Rotation"
),
model
=
model
,
selected
=
monitors
.
size
>
0
?
transform_to_index
(
monitors
[
0
].
transform
)
:
0
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
transform
=
transform_from_index
((
int
)
row
.
selected
);
foreach
(
var
monitor
in
monitors
)
monitor
.
transform
=
transform
;
});
add_mirror_setting
(
row
);
}
private
void
add_gnome_primary_row
()
{
if
(!
backend
.
supports_primary_display
||
monitors
.
size
<=
1
)
return
;
var
model
=
new
Gtk
.
StringList
(
null
);
var
values
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
uint
selected
=
0
;
foreach
(
var
monitor
in
monitors
)
{
if
(!
monitor
.
enabled
)
continue
;
model
.
append
(
monitor
.
title
);
values
.
add
(
monitor
);
if
(
monitor
.
primary
)
selected
=
(
uint
)
values
.
size
-
1
;
}
if
(
values
.
size
<=
1
)
return
;
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Primary Display"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
<
0
||
index
>=
values
.
size
)
return
;
foreach
(
var
monitor
in
monitors
)
monitor
.
primary
=
monitor
==
values
[
index
];
});
monitors_group
.
add
(
row
);
monitor_rows
.
add
(
row
);
}
private
void
sync_layout_visibility
()
{
layout_row
.
visible
=
!
gnome_mirror_enabled
()
&&
!
single_monitor_mode
();
layout_group
.
visible
=
(!
gnome_mirror_enabled
()
&&
!
single_monitor_mode
())
||
mirror_settings_rows
.
size
>
0
;
}
private
void
sync_group_visibility
()
{
monitors_group
.
visible
=
monitor_rows
.
size
>
0
;
}
private
bool
gnome_mirror_enabled
()
{
return
backend
.
supports_global_mirroring
&&
monitors
.
size
>
0
&&
monitors
[
0
].
mirrored
;
}
private
bool
single_monitor_mode
()
{
return
monitors
.
size
==
1
;
}
private
void
subscribe_monitor_changes
()
{
if
(!
backend
.
supports_monitor_change_events
)
return
;
try
{
session_bus
=
Bus
.
get_sync
(
BusType
.
SESSION
);
monitors_changed_id
=
session_bus
.
signal_subscribe
(
"org.gnome.Mutter.DisplayConfig"
,
"org.gnome.Mutter.DisplayConfig"
,
"MonitorsChanged"
,
"/org/gnome/Mutter/DisplayConfig"
,
null
,
DBusSignalFlags
.
NONE
,
()
=>
{
Idle
.
add
(()
=>
{
reload
();
return
false
;
});
}
);
}
catch
(
Error
err
)
{
warning
(
"Failed to subscribe to GNOME monitor changes: %s"
,
err
.
message
);
}
}
private
Gee
.
ArrayList
<
DisplayMode
>
common_mirror_modes
()
{
var
modes
=
new
Gee
.
ArrayList
<
DisplayMode
>();
if
(
monitors
.
size
==
0
)
return
modes
;
foreach
(
var
mode
in
monitors
[
0
].
modes
)
{
if
(
all_monitors_support_mode
(
mode
))
modes
.
add
(
mode
);
}
return
modes
;
}
private
Gee
.
ArrayList
<
DisplayMode
>
common_mirror_resolutions
()
{
var
resolutions
=
new
Gee
.
ArrayList
<
DisplayMode
>();
foreach
(
var
mode
in
common_mirror_modes
())
{
bool
exists
=
false
;
foreach
(
var
existing
in
resolutions
)
{
if
(
existing
.
width
==
mode
.
width
&&
existing
.
height
==
mode
.
height
)
{
exists
=
true
;
break
;
}
}
if
(!
exists
)
resolutions
.
add
(
mode
);
}
return
resolutions
;
}
private
DisplayMode
?
selected_common_mirror_mode
()
{
if
(
monitors
.
size
==
0
)
return
null
;
foreach
(
var
mode
in
common_mirror_resolutions
())
{
if
(
mode
.
width
==
monitors
[
0
].
width
&&
mode
.
height
==
monitors
[
0
].
height
)
{
return
mode
;
}
}
var
modes
=
common_mirror_resolutions
();
return
modes
.
size
>
0
?
modes
[
0
]
:
null
;
}
private
Gee
.
ArrayList
<
double
?>
common_mirror_scales
(
DisplayMode
mode
)
{
var
scales
=
new
Gee
.
ArrayList
<
double
?>();
foreach
(
var
scale
in
mode
.
supported_scales
)
{
bool
supported
=
true
;
foreach
(
var
monitor
in
monitors
)
{
var
compatible
=
find_compatible_mirror_mode
(
monitor
,
mode
);
if
(
compatible
==
null
||
!
compatible
.
supports_scale
(
scale
))
{
supported
=
false
;
break
;
}
}
if
(
supported
)
scales
.
add
(
scale
);
}
return
scales
;
}
private
bool
all_monitors_support_mode
(
DisplayMode
mode
)
{
foreach
(
var
monitor
in
monitors
)
{
if
(
find_compatible_mirror_mode
(
monitor
,
mode
)
==
null
)
return
false
;
}
return
true
;
}
private
static
DisplayMode
?
find_compatible_mirror_mode
(
MonitorConfig
monitor
,
DisplayMode
mode
)
{
foreach
(
var
candidate
in
monitor
.
modes
)
{
if
(
candidate
.
width
==
mode
.
width
&&
candidate
.
height
==
mode
.
height
&&
Math
.
fabs
(
candidate
.
refresh
-
mode
.
refresh
)
<
0.02
)
{
return
candidate
;
}
}
foreach
(
var
candidate
in
monitor
.
modes
)
{
if
(
candidate
.
width
==
mode
.
width
&&
candidate
.
height
==
mode
.
height
)
return
candidate
;
}
return
null
;
}
private
static
Gee
.
ArrayList
<
MonitorConfig
>
merge_loaded_monitors
(
Gee
.
ArrayList
<
MonitorConfig
>
current
,
Gee
.
ArrayList
<
MonitorConfig
>
loaded
)
{
var
merged
=
new
Gee
.
ArrayList
<
MonitorConfig
>();
foreach
(
var
loaded_monitor
in
loaded
)
{
var
monitor
=
find_monitor_by_name
(
current
,
loaded_monitor
.
name
);
if
(
monitor
==
null
)
monitor
=
loaded_monitor
;
else
monitor
.
copy_from
(
loaded_monitor
);
merged
.
add
(
monitor
);
}
return
merged
;
}
private
static
MonitorConfig
?
find_monitor_by_name
(
Gee
.
ArrayList
<
MonitorConfig
>
monitors
,
string
name
)
{
foreach
(
var
monitor
in
monitors
)
{
if
(
monitor
.
name
==
name
)
return
monitor
;
}
return
null
;
}
}
}
src/ui/monitor-settings-content.vala
deleted
100644 → 0
View file @
355bd1d9
namespace
TunerDisplays
{
[
GtkTemplate
(
ui
=
"/ru/ximperlinux/tuner/Displays/monitor-settings-content.ui"
)]
public
class
MonitorSettingsContent
:
Adw
.
PreferencesPage
{
private
MonitorConfig
monitor
;
private
Gee
.
ArrayList
<
MonitorConfig
>
all_monitors
;
private
DisplayBackend
backend
;
private
bool
show_enabled
;
[
GtkChild
]
private
unowned
Adw
.
SwitchRow
enabled_row
;
private
Adw
.
ExpanderRow
?
gnome_refresh_expander_row
;
private
Adw
.
ComboRow
?
gnome_refresh_rate_row
;
private
Adw
.
SwitchRow
?
gnome_variable_refresh_row
;
private
Gtk
.
Label
?
gnome_refresh_value_label
;
private
Gee
.
ArrayList
<
DisplayMode
>
gnome_refresh_values
=
new
Gee
.
ArrayList
<
DisplayMode
>();
private
bool
updating_gnome_refresh_row
;
[
GtkChild
]
private
unowned
Adw
.
PreferencesGroup
basic_group
;
[
GtkChild
]
private
unowned
Adw
.
PreferencesGroup
hyprland_group
;
[
GtkChild
]
private
unowned
Adw
.
PreferencesGroup
hdr_group
;
public
signal
void
monitor_changed
();
public
MonitorSettingsContent
(
MonitorConfig
monitor
,
DisplayBackend
backend
,
Gee
.
ArrayList
<
MonitorConfig
>
all_monitors
,
bool
show_enabled
=
true
)
{
Object
(
title
:
monitor
.
title
);
this
.
monitor
=
monitor
;
this
.
backend
=
backend
;
this
.
all_monitors
=
all_monitors
;
this
.
show_enabled
=
show_enabled
;
build_preferences
();
}
private
void
build_preferences
()
{
enabled_row
.
visible
=
show_enabled
;
enabled_row
.
active
=
monitor
.
enabled
;
enabled_row
.
notify
[
"active"
].
connect
(()
=>
{
if
(
enabled_row
.
active
!=
monitor
.
enabled
&&
!
backend
.
preserves_disabled_monitor_position
)
place_monitor_after_active
(
monitor
,
all_monitors
);
monitor
.
enabled
=
enabled_row
.
active
;
emit_changed
();
});
monitor
.
notify
[
"enabled"
].
connect
(
sync_from_monitor
);
if
(
backend
.
uses_separate_refresh_rate_controls
)
{
add_gnome_resolution_row
(
basic_group
);
add_gnome_refresh_rate_row
(
basic_group
);
add_gnome_scale_row
(
basic_group
);
}
else
{
add_mode_row
(
basic_group
);
add_scale_row
(
basic_group
);
}
add_transform_row
(
basic_group
);
add_mirror_row
(
basic_group
);
if
(
backend
.
supports_description_identifier
||
backend
.
supports_bit_depth
||
backend
.
supports_vrr_modes
||
backend
.
supports_color_management
||
backend
.
supports_hdr_metadata
)
{
add_hyprland_rows
();
}
add_niri_rows
(
basic_group
);
add_gnome_rows
(
basic_group
);
hyprland_group
.
visible
=
hyprland_group
.
get_first_child
()
!=
null
;
hdr_group
.
visible
=
hdr_group
.
get_first_child
()
!=
null
;
}
private
void
add_mode_row
(
Adw
.
PreferencesGroup
group
)
{
var
model
=
new
Gtk
.
StringList
(
null
);
int
selected
=
-
1
;
for
(
int
i
=
0
;
i
<
monitor
.
modes
.
size
;
i
++)
{
var
mode
=
monitor
.
modes
[
i
];
model
.
append
(
mode
.
label
);
if
(
mode
.
width
==
monitor
.
width
&&
mode
.
height
==
monitor
.
height
&&
Math
.
fabs
(
mode
.
refresh
-
monitor
.
refresh
)
<
0.02
&&
selected
<
0
)
{
selected
=
i
;
}
}
if
(
selected
<
0
)
{
model
.
append
(
"%dx%d@%.2f"
.
printf
(
monitor
.
width
,
monitor
.
height
,
monitor
.
refresh
));
selected
=
(
int
)
model
.
get_n_items
()
-
1
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Resolution"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
monitor
.
modes
.
size
)
{
var
mode
=
monitor
.
modes
[
index
];
monitor
.
width
=
mode
.
width
;
monitor
.
height
=
mode
.
height
;
monitor
.
refresh
=
mode
.
refresh
;
if
(
backend
.
uses_separate_refresh_rate_controls
&&
!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
emit_changed
();
}
});
group
.
add
(
row
);
}
private
void
add_gnome_resolution_row
(
Adw
.
PreferencesGroup
group
)
{
var
model
=
new
Gtk
.
StringList
(
null
);
var
values
=
new
Gee
.
ArrayList
<
DisplayMode
>();
uint
selected
=
0
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
has_resolution
(
values
,
mode
.
width
,
mode
.
height
))
continue
;
model
.
append
(
"%dx%d"
.
printf
(
mode
.
width
,
mode
.
height
));
values
.
add
(
mode
);
if
(
mode
.
width
==
monitor
.
width
&&
mode
.
height
==
monitor
.
height
)
selected
=
(
uint
)
values
.
size
-
1
;
}
if
(
values
.
size
==
0
)
{
model
.
append
(
"%dx%d"
.
printf
(
monitor
.
width
,
monitor
.
height
));
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Resolution"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
<
0
||
index
>=
values
.
size
)
return
;
var
mode
=
find_best_mode_for_resolution
(
values
[
index
].
width
,
values
[
index
].
height
);
if
(
mode
==
null
)
return
;
monitor
.
width
=
mode
.
width
;
monitor
.
height
=
mode
.
height
;
monitor
.
refresh
=
mode
.
refresh
;
monitor
.
variable_refresh_rate
=
mode
.
variable_refresh_rate
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
if
(
gnome_variable_refresh_row
!=
null
)
gnome_variable_refresh_row
.
visible
=
has_variable_mode_for_resolution
();
update_gnome_refresh_row
();
emit_changed
();
});
group
.
add
(
row
);
}
private
void
add_gnome_refresh_rate_row
(
Adw
.
PreferencesGroup
group
)
{
gnome_refresh_expander_row
=
new
Adw
.
ExpanderRow
()
{
title
=
_
(
"Refresh Rate"
)
};
gnome_refresh_value_label
=
new
Gtk
.
Label
(
null
)
{
valign
=
Gtk
.
Align
.
CENTER
};
gnome_refresh_expander_row
.
add_suffix
(
gnome_refresh_value_label
);
gnome_refresh_rate_row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Refresh Rate"
)
};
gnome_refresh_rate_row
.
notify
[
"selected"
].
connect
(()
=>
{
if
(
updating_gnome_refresh_row
)
return
;
var
index
=
(
int
)
gnome_refresh_rate_row
.
selected
;
if
(
index
<
0
||
index
>=
gnome_refresh_values
.
size
)
return
;
var
mode
=
gnome_refresh_values
[
index
];
monitor
.
refresh
=
mode
.
refresh
;
monitor
.
variable_refresh_rate
=
false
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
sync_gnome_refresh_state
();
emit_changed
();
});
gnome_refresh_expander_row
.
add_row
(
gnome_refresh_rate_row
);
if
(
monitor
.
supports_variable_refresh_rate
&&
has_variable_mode_for_resolution
())
{
gnome_variable_refresh_row
=
new
Adw
.
SwitchRow
()
{
title
=
_
(
"Variable Refresh Rate"
),
active
=
monitor
.
variable_refresh_rate
};
gnome_variable_refresh_row
.
notify
[
"active"
].
connect
(()
=>
{
if
(
updating_gnome_refresh_row
)
return
;
monitor
.
variable_refresh_rate
=
gnome_variable_refresh_row
.
active
;
if
(
monitor
.
variable_refresh_rate
)
{
var
mode
=
find_variable_mode_for_resolution
();
if
(
mode
!=
null
)
{
monitor
.
refresh
=
mode
.
refresh
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
}
}
else
{
var
mode
=
find_best_fixed_mode_for_resolution
(
monitor
.
width
,
monitor
.
height
);
if
(
mode
!=
null
)
{
monitor
.
refresh
=
mode
.
refresh
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
}
}
sync_gnome_refresh_state
();
emit_changed
();
});
gnome_refresh_expander_row
.
add_row
(
gnome_variable_refresh_row
);
}
update_gnome_refresh_row
();
group
.
add
(
gnome_refresh_expander_row
);
}
private
void
update_gnome_refresh_row
()
{
if
(
gnome_refresh_expander_row
==
null
||
gnome_refresh_rate_row
==
null
)
return
;
updating_gnome_refresh_row
=
true
;
gnome_refresh_values
.
clear
();
var
model
=
new
Gtk
.
StringList
(
null
);
uint
selected
=
0
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
!=
monitor
.
width
||
mode
.
height
!=
monitor
.
height
)
continue
;
if
(
mode
.
variable_refresh_rate
)
continue
;
model
.
append
(
refresh_rate_label
(
mode
));
gnome_refresh_values
.
add
(
mode
);
if
(
Math
.
fabs
(
mode
.
refresh
-
monitor
.
refresh
)
<
0.02
&&
!
monitor
.
variable_refresh_rate
)
{
selected
=
(
uint
)
gnome_refresh_values
.
size
-
1
;
}
}
gnome_refresh_rate_row
.
model
=
model
;
gnome_refresh_rate_row
.
selected
=
selected
;
gnome_refresh_rate_row
.
sensitive
=
!
monitor
.
variable_refresh_rate
&&
gnome_refresh_values
.
size
>
0
;
if
(
gnome_variable_refresh_row
!=
null
)
gnome_variable_refresh_row
.
active
=
monitor
.
variable_refresh_rate
;
if
(
gnome_refresh_value_label
!=
null
)
gnome_refresh_value_label
.
label
=
current_refresh_rate_label
();
updating_gnome_refresh_row
=
false
;
}
private
void
sync_gnome_refresh_state
()
{
updating_gnome_refresh_row
=
true
;
if
(
gnome_refresh_rate_row
!=
null
)
gnome_refresh_rate_row
.
sensitive
=
!
monitor
.
variable_refresh_rate
&&
gnome_refresh_values
.
size
>
0
;
if
(
gnome_variable_refresh_row
!=
null
&&
gnome_variable_refresh_row
.
active
!=
monitor
.
variable_refresh_rate
)
gnome_variable_refresh_row
.
active
=
monitor
.
variable_refresh_rate
;
if
(
gnome_refresh_value_label
!=
null
)
gnome_refresh_value_label
.
label
=
current_refresh_rate_label
();
updating_gnome_refresh_row
=
false
;
}
private
void
add_scale_row
(
Adw
.
PreferencesGroup
group
)
{
group
.
add
(
create_spin_row
(
_
(
"Scale"
),
monitor
.
scale
,
0.25
,
8.0
,
0.05
,
2
,
value
=>
monitor
.
scale
=
value
));
}
private
void
add_gnome_scale_row
(
Adw
.
PreferencesGroup
group
)
{
var
mode
=
selected_mode
();
if
(
mode
==
null
||
mode
.
supported_scales
.
size
==
0
)
{
add_scale_row
(
group
);
return
;
}
var
labels
=
new
Gee
.
ArrayList
<
string
>();
var
values
=
new
Gee
.
ArrayList
<
double
?>();
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
mode
.
supported_scales
.
size
;
i
++)
{
var
scale
=
mode
.
supported_scales
[
i
];
labels
.
add
(
"%.0f%%"
.
printf
(
scale
*
100
));
values
.
add
(
scale
);
if
(
Math
.
fabs
(
scale
-
monitor
.
scale
)
<
0.01
)
selected
=
i
;
}
var
model
=
new
Gtk
.
StringList
(
null
);
foreach
(
var
label
in
labels
)
model
.
append
(
label
);
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Scale"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
values
.
size
)
{
monitor
.
scale
=
values
[
index
];
emit_changed
();
}
});
group
.
add
(
row
);
}
private
void
add_transform_row
(
Adw
.
PreferencesGroup
group
)
{
var
model
=
new
Gtk
.
StringList
(
null
);
string
[]
titles
=
{
_
(
"Normal"
),
_
(
"90 degrees"
),
_
(
"180 degrees"
),
_
(
"270 degrees"
),
_
(
"Flipped"
),
_
(
"Flipped 90 degrees"
),
_
(
"Flipped 180 degrees"
),
_
(
"Flipped 270 degrees"
)
};
foreach
(
var
title
in
titles
)
model
.
append
(
title
);
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Rotation"
),
model
=
model
,
selected
=
transform_to_index
(
monitor
.
transform
)
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
monitor
.
transform
=
transform_from_index
((
int
)
row
.
selected
);
emit_changed
();
});
group
.
add
(
row
);
}
private
void
add_mirror_row
(
Adw
.
PreferencesGroup
group
)
{
if
(!
backend
.
supports_output_mirroring
)
return
;
if
(
all_monitors
.
size
<=
1
)
return
;
var
model
=
new
Gtk
.
StringList
(
null
);
var
values
=
new
Gee
.
ArrayList
<
string
>();
model
.
append
(
_
(
"None"
));
values
.
add
(
""
);
uint
selected
=
0
;
foreach
(
var
other
in
all_monitors
)
{
if
(
other
==
monitor
)
continue
;
model
.
append
(
other
.
title
);
values
.
add
(
other
.
name
);
if
(
other
.
name
==
monitor
.
mirror
)
selected
=
(
uint
)
values
.
size
-
1
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Mirror"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
values
.
size
)
{
monitor
.
mirror
=
values
[
index
];
emit_changed
();
}
});
group
.
add
(
row
);
}
private
void
add_hyprland_rows
()
{
if
(
backend
.
supports_description_identifier
)
{
var
use_description
=
new
Adw
.
SwitchRow
()
{
title
=
_
(
"Use description"
),
active
=
monitor
.
use_description
};
use_description
.
notify
[
"active"
].
connect
(()
=>
{
monitor
.
use_description
=
use_description
.
active
;
emit_changed
();
});
hyprland_group
.
add
(
use_description
);
}
if
(
backend
.
supports_bit_depth
)
add_int_combo
(
hyprland_group
,
_
(
"Bit depth"
),
new
string
[]
{
"8"
,
"10"
},
new
int
[]
{
8
,
10
},
monitor
.
bitdepth
,
value
=>
monitor
.
bitdepth
=
value
);
if
(
backend
.
supports_vrr_modes
)
add_int_combo
(
hyprland_group
,
_
(
"VRR"
),
new
string
[]
{
_
(
"Off"
),
_
(
"On"
),
_
(
"Fullscreen"
),
_
(
"Fullscreen video/game"
)
},
new
int
[]
{
0
,
1
,
2
,
3
},
monitor
.
vrr
,
value
=>
monitor
.
vrr
=
value
);
if
(
backend
.
supports_color_management
)
{
add_string_combo
(
hyprland_group
,
_
(
"Color management"
),
new
string
[]
{
"auto"
,
"srgb"
,
"dcip3"
,
"dp3"
,
"adobe"
,
"wide"
,
"edid"
,
"hdr"
,
"hdredid"
},
monitor
.
color_management_preset
,
value
=>
monitor
.
color_management_preset
=
value
);
add_string_combo
(
hyprland_group
,
_
(
"SDR EOTF"
),
new
string
[]
{
"default"
,
"gamma22"
,
"srgb"
},
monitor
.
sdr_eotf
,
value
=>
monitor
.
sdr_eotf
=
value
);
hyprland_group
.
add
(
create_spin_row
(
_
(
"SDR brightness"
),
monitor
.
sdr_brightness
,
0.1
,
4.0
,
0.05
,
2
,
value
=>
monitor
.
sdr_brightness
=
value
));
hyprland_group
.
add
(
create_spin_row
(
_
(
"SDR saturation"
),
monitor
.
sdr_saturation
,
0.1
,
4.0
,
0.05
,
2
,
value
=>
monitor
.
sdr_saturation
=
value
));
}
if
(
backend
.
supports_hdr_metadata
)
{
add_int_combo
(
hdr_group
,
_
(
"Force wide color"
),
new
string
[]
{
_
(
"Auto"
),
_
(
"Off"
),
_
(
"On"
)
},
new
int
[]
{
0
,
-
1
,
1
},
monitor
.
supports_wide_color
,
value
=>
monitor
.
supports_wide_color
=
value
);
add_int_combo
(
hdr_group
,
_
(
"Force HDR"
),
new
string
[]
{
_
(
"Auto"
),
_
(
"Off"
),
_
(
"On"
)
},
new
int
[]
{
0
,
-
1
,
1
},
monitor
.
supports_hdr
,
value
=>
monitor
.
supports_hdr
=
value
);
hdr_group
.
add
(
create_spin_row
(
_
(
"SDR min luminance"
),
monitor
.
sdr_min_luminance
,
0.0
,
10.0
,
0.01
,
2
,
value
=>
monitor
.
sdr_min_luminance
=
value
));
hdr_group
.
add
(
create_spin_row
(
_
(
"SDR max luminance"
),
monitor
.
sdr_max_luminance
,
1
,
1000
,
1
,
0
,
value
=>
monitor
.
sdr_max_luminance
=
(
int
)
value
));
hdr_group
.
add
(
create_spin_row
(
_
(
"Min luminance"
),
monitor
.
min_luminance
,
-
1
,
10.0
,
0.01
,
2
,
value
=>
monitor
.
min_luminance
=
value
));
hdr_group
.
add
(
create_spin_row
(
_
(
"Max luminance"
),
monitor
.
max_luminance
,
-
1
,
10000
,
1
,
0
,
value
=>
monitor
.
max_luminance
=
(
int
)
value
));
hdr_group
.
add
(
create_spin_row
(
_
(
"Max average luminance"
),
monitor
.
max_avg_luminance
,
-
1
,
10000
,
1
,
0
,
value
=>
monitor
.
max_avg_luminance
=
(
int
)
value
));
var
icc
=
new
Adw
.
EntryRow
()
{
title
=
_
(
"ICC profile"
)
};
icc
.
text
=
monitor
.
icc
;
icc
.
notify
[
"text"
].
connect
(()
=>
{
monitor
.
icc
=
icc
.
text
;
emit_changed
();
});
hdr_group
.
add
(
icc
);
}
}
private
void
add_niri_rows
(
Adw
.
PreferencesGroup
group
)
{
if
(
backend
.
supports_adaptive_sync
&&
monitor
.
supports_variable_refresh_rate
)
{
add_int_combo
(
group
,
_
(
"Variable Refresh Rate"
),
new
string
[]
{
_
(
"Off"
),
_
(
"On"
),
_
(
"On demand"
)
},
new
int
[]
{
0
,
1
,
2
},
monitor
.
vrr
,
value
=>
monitor
.
vrr
=
value
);
}
if
(
backend
.
supports_focus_at_startup
)
{
var
focus
=
new
Adw
.
SwitchRow
()
{
title
=
_
(
"Focus at startup"
),
active
=
monitor
.
niri_focus_at_startup
};
focus
.
notify
[
"active"
].
connect
(()
=>
{
monitor
.
niri_focus_at_startup
=
focus
.
active
;
emit_changed
();
});
group
.
add
(
focus
);
}
if
(
backend
.
supports_backdrop_color
)
add_niri_backdrop_color_row
(
group
);
if
(
backend
.
supports_hot_corners
)
{
add_string_combo_with_labels
(
group
,
_
(
"Hot corners"
),
new
string
[]
{
_
(
"Default"
),
_
(
"Off"
),
_
(
"All"
),
_
(
"Top left"
),
_
(
"Top right"
),
_
(
"Bottom left"
),
_
(
"Bottom right"
)
},
new
string
[]
{
""
,
"off"
,
"all"
,
"top-left"
,
"top-right"
,
"bottom-left"
,
"bottom-right"
},
monitor
.
niri_hot_corners
,
value
=>
monitor
.
niri_hot_corners
=
value
);
}
}
private
void
add_niri_backdrop_color_row
(
Adw
.
PreferencesGroup
group
)
{
var
row
=
new
Adw
.
ActionRow
()
{
title
=
_
(
"Backdrop color"
)
};
var
enabled
=
new
Gtk
.
Switch
()
{
valign
=
Gtk
.
Align
.
CENTER
,
active
=
monitor
.
niri_backdrop_color
!=
""
};
Gdk
.
RGBA
color
=
{
0
,
0
,
0
,
1
};
if
(
monitor
.
niri_backdrop_color
!=
""
)
color
.
parse
(
monitor
.
niri_backdrop_color
);
var
button
=
new
Gtk
.
ColorButton
.
with_rgba
(
color
)
{
valign
=
Gtk
.
Align
.
CENTER
,
sensitive
=
enabled
.
active
,
title
=
_
(
"Backdrop color"
),
use_alpha
=
true
};
enabled
.
notify
[
"active"
].
connect
(()
=>
{
button
.
sensitive
=
enabled
.
active
;
var
selected
=
button
.
rgba
;
monitor
.
niri_backdrop_color
=
enabled
.
active
?
rgba_to_css
(
selected
)
:
""
;
emit_changed
();
});
button
.
notify
[
"rgba"
].
connect
(()
=>
{
if
(!
enabled
.
active
)
return
;
var
selected
=
button
.
rgba
;
monitor
.
niri_backdrop_color
=
rgba_to_css
(
selected
);
emit_changed
();
});
row
.
add_suffix
(
button
);
row
.
add_suffix
(
enabled
);
group
.
add
(
row
);
}
private
void
add_gnome_rows
(
Adw
.
PreferencesGroup
group
)
{
if
(!
backend
.
supports_underscanning
&&
!
backend
.
supports_hdr_toggle
)
return
;
if
(
backend
.
supports_underscanning
)
{
var
underscanning
=
new
Adw
.
SwitchRow
()
{
title
=
_
(
"Adjust for TV"
),
active
=
monitor
.
underscanning
};
underscanning
.
notify
[
"active"
].
connect
(()
=>
{
monitor
.
underscanning
=
underscanning
.
active
;
emit_changed
();
});
group
.
add
(
underscanning
);
}
if
(
backend
.
supports_hdr_toggle
&&
monitor
.
supported_color_modes
.
contains
(
1
))
{
var
hdr
=
new
Adw
.
SwitchRow
()
{
title
=
_
(
"HDR"
),
active
=
monitor
.
color_mode
==
1
};
hdr
.
notify
[
"active"
].
connect
(()
=>
{
monitor
.
color_mode
=
hdr
.
active
?
1
:
0
;
emit_changed
();
});
group
.
add
(
hdr
);
}
}
private
delegate
void
NumberChanged
(
double
value
);
private
delegate
void
IntChanged
(
int
value
);
private
delegate
void
StringChanged
(
string
value
);
private
Adw
.
ActionRow
create_spin_row
(
string
title
,
double
value
,
double
min
,
double
max
,
double
step
,
uint
digits
,
NumberChanged
changed
)
{
var
row
=
new
Adw
.
ActionRow
()
{
title
=
title
};
var
spin
=
new
Gtk
.
SpinButton
.
with_range
(
min
,
max
,
step
)
{
valign
=
Gtk
.
Align
.
CENTER
,
value
=
value
,
digits
=
digits
};
spin
.
value_changed
.
connect
(()
=>
{
changed
(
spin
.
value
);
emit_changed
();
});
row
.
add_suffix
(
spin
);
return
row
;
}
private
void
add_int_combo
(
Adw
.
PreferencesGroup
group
,
string
title
,
string
[]
labels
,
int
[]
values
,
int
current
,
IntChanged
changed
)
{
var
model
=
new
Gtk
.
StringList
(
null
);
var
stored_values
=
new
Gee
.
ArrayList
<
int
>();
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
labels
.
length
;
i
++)
{
model
.
append
(
labels
[
i
]);
stored_values
.
add
(
values
[
i
]);
if
(
values
[
i
]
==
current
)
selected
=
i
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
title
,
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
stored_values
.
size
)
{
changed
(
stored_values
[
index
]);
emit_changed
();
}
});
group
.
add
(
row
);
}
private
void
add_string_combo
(
Adw
.
PreferencesGroup
group
,
string
title
,
string
[]
values
,
string
current
,
StringChanged
changed
)
{
add_string_combo_with_labels
(
group
,
title
,
values
,
values
,
current
,
changed
);
}
private
void
add_string_combo_with_labels
(
Adw
.
PreferencesGroup
group
,
string
title
,
string
[]
labels
,
string
[]
values
,
string
current
,
StringChanged
changed
)
{
var
model
=
new
Gtk
.
StringList
(
null
);
var
stored_values
=
new
Gee
.
ArrayList
<
string
>();
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
values
.
length
;
i
++)
{
model
.
append
(
labels
[
i
]);
stored_values
.
add
(
values
[
i
]);
if
(
values
[
i
]
==
current
)
selected
=
i
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
title
,
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
stored_values
.
size
)
{
changed
(
stored_values
[
index
]);
emit_changed
();
}
});
group
.
add
(
row
);
}
private
static
string
rgba_to_css
(
Gdk
.
RGBA
rgba
)
{
if
(
rgba
.
alpha
>=
0.999
)
{
return
"#%02x%02x%02x"
.
printf
(
(
int
)
Math
.
round
(
rgba
.
red
*
255
),
(
int
)
Math
.
round
(
rgba
.
green
*
255
),
(
int
)
Math
.
round
(
rgba
.
blue
*
255
)
);
}
var
alpha
=
"%.3f"
.
printf
(
rgba
.
alpha
).
replace
(
","
,
"."
);
return
"rgba(%d, %d, %d, %s)"
.
printf
(
(
int
)
Math
.
round
(
rgba
.
red
*
255
),
(
int
)
Math
.
round
(
rgba
.
green
*
255
),
(
int
)
Math
.
round
(
rgba
.
blue
*
255
),
alpha
);
}
private
void
emit_changed
()
{
monitor_changed
();
}
private
void
sync_from_monitor
()
{
if
(
enabled_row
.
active
!=
monitor
.
enabled
)
enabled_row
.
active
=
monitor
.
enabled
;
}
private
DisplayMode
?
selected_mode
()
{
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
==
monitor
.
width
&&
mode
.
height
==
monitor
.
height
&&
Math
.
fabs
(
mode
.
refresh
-
monitor
.
refresh
)
<
0.02
&&
mode
.
variable_refresh_rate
==
monitor
.
variable_refresh_rate
)
{
return
mode
;
}
}
return
monitor
.
modes
.
size
>
0
?
monitor
.
modes
[
0
]
:
null
;
}
private
DisplayMode
?
find_best_mode_for_resolution
(
int
width
,
int
height
)
{
DisplayMode
?
fallback
=
null
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
!=
width
||
mode
.
height
!=
height
)
continue
;
if
(
Math
.
fabs
(
mode
.
refresh
-
monitor
.
refresh
)
<
0.02
&&
mode
.
variable_refresh_rate
==
monitor
.
variable_refresh_rate
)
{
return
mode
;
}
if
(
fallback
==
null
)
fallback
=
mode
;
}
return
fallback
;
}
private
DisplayMode
?
find_best_fixed_mode_for_resolution
(
int
width
,
int
height
)
{
DisplayMode
?
fallback
=
null
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
!=
width
||
mode
.
height
!=
height
||
mode
.
variable_refresh_rate
)
continue
;
if
(
Math
.
fabs
(
mode
.
refresh
-
monitor
.
refresh
)
<
0.02
)
return
mode
;
if
(
fallback
==
null
)
fallback
=
mode
;
}
return
fallback
;
}
private
DisplayMode
?
find_variable_mode_for_resolution
()
{
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
==
monitor
.
width
&&
mode
.
height
==
monitor
.
height
&&
mode
.
variable_refresh_rate
)
return
mode
;
}
return
null
;
}
private
bool
has_variable_mode_for_resolution
()
{
return
find_variable_mode_for_resolution
()
!=
null
;
}
private
string
current_refresh_rate_label
()
{
if
(
monitor
.
variable_refresh_rate
)
{
var
mode
=
find_variable_mode_for_resolution
();
if
(
mode
!=
null
)
return
_
(
"Variable (up to %.2f Hz)"
).
printf
(
mode
.
refresh
);
return
_
(
"Variable"
);
}
return
_
(
"%.2f Hz"
).
printf
(
monitor
.
refresh
);
}
}
}
src/ui/pages/monitor-settings-content.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorSettingsContent
:
Tuner
.
PanelContent
{
private
MonitorSettingsContext
context
;
private
Tuner
.
Page
settings_page
;
public
signal
void
monitor_changed
();
public
MonitorSettingsContent
(
MonitorConfig
monitor
,
DisplayBackend
backend
,
Gee
.
ArrayList
<
MonitorConfig
>
all_monitors
,
bool
show_enabled
=
true
)
{
Object
();
context
=
new
MonitorSettingsContext
(
monitor
,
backend
,
all_monitors
,
show_enabled
);
MonitorSettingsState
.
current
=
context
;
var
builder
=
new
Gtk
.
Builder
.
from_resource
(
"/ru/ximperlinux/tuner/Displays/pages/monitor-settings-content.ui"
);
settings_page
=
builder
.
get_object
(
"monitor_settings_content_page"
)
as
Tuner
.
Page
;
page
=
settings_page
;
context
.
changed
.
connect
(()
=>
monitor_changed
());
build
();
}
}
}
src/ui/settings/monitor-choice-loader.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorChoiceLoader
:
Tuner
.
ChoiceLoader
{
public
string
field
{
get
;
set
;
default
=
""
;
}
public
override
void
load
(
ListStore
model
)
{
switch
(
field
)
{
case
"mode"
:
load_modes
(
model
);
break
;
case
"resolution"
:
load_resolutions
(
model
);
break
;
case
"refresh"
:
load_refresh_rates
(
model
);
break
;
case
"scale"
:
load_scales
(
model
);
break
;
case
"transform"
:
add_choice
(
model
,
_
(
"Normal"
),
"normal"
);
add_choice
(
model
,
_
(
"90 degrees"
),
"90"
);
add_choice
(
model
,
_
(
"180 degrees"
),
"180"
);
add_choice
(
model
,
_
(
"270 degrees"
),
"270"
);
add_choice
(
model
,
_
(
"Flipped"
),
"flipped"
);
add_choice
(
model
,
_
(
"Flipped 90 degrees"
),
"flipped-90"
);
add_choice
(
model
,
_
(
"Flipped 180 degrees"
),
"flipped-180"
);
add_choice
(
model
,
_
(
"Flipped 270 degrees"
),
"flipped-270"
);
break
;
case
"mirror"
:
load_mirrors
(
model
);
break
;
case
"bitdepth"
:
add_choice
(
model
,
"8"
,
"8"
);
add_choice
(
model
,
"10"
,
"10"
);
break
;
case
"hypr-vrr"
:
add_choice
(
model
,
_
(
"Off"
),
"0"
);
add_choice
(
model
,
_
(
"On"
),
"1"
);
add_choice
(
model
,
_
(
"Fullscreen"
),
"2"
);
add_choice
(
model
,
_
(
"Fullscreen video/game"
),
"3"
);
break
;
case
"niri-vrr"
:
add_choice
(
model
,
_
(
"Off"
),
"0"
);
add_choice
(
model
,
_
(
"On"
),
"1"
);
add_choice
(
model
,
_
(
"On demand"
),
"2"
);
break
;
case
"color-management"
:
foreach
(
var
value
in
new
string
[]
{
"auto"
,
"srgb"
,
"dcip3"
,
"dp3"
,
"adobe"
,
"wide"
,
"edid"
,
"hdr"
,
"hdredid"
})
add_choice
(
model
,
value
,
value
);
break
;
case
"sdr-eotf"
:
foreach
(
var
value
in
new
string
[]
{
"default"
,
"gamma22"
,
"srgb"
})
add_choice
(
model
,
value
,
value
);
break
;
case
"force-toggle"
:
add_choice
(
model
,
_
(
"Auto"
),
"0"
);
add_choice
(
model
,
_
(
"Off"
),
"-1"
);
add_choice
(
model
,
_
(
"On"
),
"1"
);
break
;
case
"hot-corners"
:
add_choice
(
model
,
_
(
"Default"
),
""
);
add_choice
(
model
,
_
(
"Off"
),
"off"
);
add_choice
(
model
,
_
(
"All"
),
"all"
);
add_choice
(
model
,
_
(
"Top left"
),
"top-left"
);
add_choice
(
model
,
_
(
"Top right"
),
"top-right"
);
add_choice
(
model
,
_
(
"Bottom left"
),
"bottom-left"
);
add_choice
(
model
,
_
(
"Bottom right"
),
"bottom-right"
);
break
;
}
}
private
static
void
load_modes
(
ListStore
model
)
{
foreach
(
var
mode
in
MonitorSettingsState
.
current
.
monitor
.
modes
)
{
add_choice
(
model
,
mode
.
label
,
MonitorSettingBinding
.
mode_key
(
mode
.
width
,
mode
.
height
,
mode
.
refresh
,
mode
.
variable_refresh_rate
)
);
}
}
private
static
void
load_resolutions
(
ListStore
model
)
{
var
values
=
new
Gee
.
ArrayList
<
string
>();
foreach
(
var
mode
in
MonitorSettingsState
.
current
.
monitor
.
modes
)
{
var
key
=
MonitorSettingBinding
.
resolution_key
(
mode
.
width
,
mode
.
height
);
if
(
values
.
contains
(
key
))
continue
;
values
.
add
(
key
);
add_choice
(
model
,
key
,
key
);
}
}
private
static
void
load_refresh_rates
(
ListStore
model
)
{
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
!=
monitor
.
width
||
mode
.
height
!=
monitor
.
height
||
mode
.
variable_refresh_rate
)
continue
;
add_choice
(
model
,
refresh_rate_label
(
mode
),
MonitorSettingBinding
.
refresh_key
(
mode
.
refresh
));
}
}
private
static
void
load_scales
(
ListStore
model
)
{
var
mode
=
MonitorSettingBinding
.
find_best_mode_for_resolution
(
MonitorSettingsState
.
current
.
monitor
.
width
,
MonitorSettingsState
.
current
.
monitor
.
height
);
if
(
mode
==
null
)
return
;
foreach
(
var
scale
in
mode
.
supported_scales
)
add_choice
(
model
,
"%.0f%%"
.
printf
(
scale
*
100
),
"%.3f"
.
printf
(
scale
).
replace
(
","
,
"."
));
}
private
static
void
load_mirrors
(
ListStore
model
)
{
var
context
=
MonitorSettingsState
.
current
;
add_choice
(
model
,
_
(
"None"
),
""
);
foreach
(
var
monitor
in
context
.
all_monitors
)
{
if
(
monitor
!=
context
.
monitor
)
add_choice
(
model
,
monitor
.
title
,
monitor
.
name
);
}
}
private
static
void
add_choice
(
ListStore
model
,
string
title
,
string
value
)
{
model
.
append
(
new
Tuner
.
Choice
()
{
title
=
title
,
value
=
new
Variant
.
string
(
value
)
});
}
}
}
src/ui/settings/monitor-setting-binding.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorSettingBinding
:
Tuner
.
Binding
{
public
string
field
{
get
;
set
;
default
=
""
;
}
public
double
lower
{
get
;
set
;
default
=
0
;
}
public
double
upper
{
get
;
set
;
default
=
100
;
}
public
double
step
{
get
;
set
;
default
=
1
;
}
public
override
Type
expected_type
{
get
{
switch
(
field
)
{
case
"enabled"
:
case
"variable-refresh"
:
case
"niri-focus"
:
case
"underscanning"
:
case
"hdr-toggle"
:
case
"use-description"
:
case
"refresh-visible"
:
return
Type
.
BOOLEAN
;
case
"scale-spin"
:
case
"sdr-brightness"
:
case
"sdr-saturation"
:
case
"sdr-min-luminance"
:
case
"sdr-max-luminance"
:
case
"min-luminance"
:
case
"max-luminance"
:
case
"max-avg-luminance"
:
return
Type
.
DOUBLE
;
default
:
return
Type
.
STRING
;
}
}
}
public
override
Gtk
.
Adjustment
?
create_adjustment
()
{
if
(
expected_type
!=
Type
.
DOUBLE
)
return
null
;
return
new
Gtk
.
Adjustment
(
0
,
lower
,
upper
,
step
,
step
*
10
,
0
);
}
public
override
bool
get_value
(
ref
Value
value
)
{
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
switch
(
field
)
{
case
"enabled"
:
value
.
set_boolean
(
monitor
.
enabled
);
break
;
case
"variable-refresh"
:
value
.
set_boolean
(
monitor
.
variable_refresh_rate
);
break
;
case
"niri-focus"
:
value
.
set_boolean
(
monitor
.
niri_focus_at_startup
);
break
;
case
"underscanning"
:
value
.
set_boolean
(
monitor
.
underscanning
);
break
;
case
"hdr-toggle"
:
value
.
set_boolean
(
monitor
.
color_mode
==
1
);
break
;
case
"use-description"
:
value
.
set_boolean
(
monitor
.
use_description
);
break
;
case
"refresh-visible"
:
value
.
set_boolean
(
true
);
break
;
case
"scale-spin"
:
value
.
set_double
(
monitor
.
scale
);
break
;
case
"sdr-brightness"
:
value
.
set_double
(
monitor
.
sdr_brightness
);
break
;
case
"sdr-saturation"
:
value
.
set_double
(
monitor
.
sdr_saturation
);
break
;
case
"sdr-min-luminance"
:
value
.
set_double
(
monitor
.
sdr_min_luminance
);
break
;
case
"sdr-max-luminance"
:
value
.
set_double
(
monitor
.
sdr_max_luminance
);
break
;
case
"min-luminance"
:
value
.
set_double
(
monitor
.
min_luminance
);
break
;
case
"max-luminance"
:
value
.
set_double
(
monitor
.
max_luminance
);
break
;
case
"max-avg-luminance"
:
value
.
set_double
(
monitor
.
max_avg_luminance
);
break
;
default
:
value
.
set_string
(
get_string_value
(
field
));
break
;
}
return
true
;
}
public
override
void
set_value
(
Value
value
)
{
var
context
=
MonitorSettingsState
.
current
;
var
monitor
=
context
.
monitor
;
switch
(
field
)
{
case
"enabled"
:
var
enabled
=
value
.
get_boolean
();
if
(
enabled
!=
monitor
.
enabled
&&
!
context
.
backend
.
preserves_disabled_monitor_position
)
place_monitor_after_active
(
monitor
,
context
.
all_monitors
);
monitor
.
enabled
=
enabled
;
break
;
case
"variable-refresh"
:
set_variable_refresh
(
value
.
get_boolean
());
break
;
case
"niri-focus"
:
monitor
.
niri_focus_at_startup
=
value
.
get_boolean
();
break
;
case
"underscanning"
:
monitor
.
underscanning
=
value
.
get_boolean
();
break
;
case
"hdr-toggle"
:
monitor
.
color_mode
=
value
.
get_boolean
()
?
1
:
0
;
break
;
case
"use-description"
:
monitor
.
use_description
=
value
.
get_boolean
();
break
;
case
"scale-spin"
:
monitor
.
scale
=
value
.
get_double
();
break
;
case
"sdr-brightness"
:
monitor
.
sdr_brightness
=
value
.
get_double
();
break
;
case
"sdr-saturation"
:
monitor
.
sdr_saturation
=
value
.
get_double
();
break
;
case
"sdr-min-luminance"
:
monitor
.
sdr_min_luminance
=
value
.
get_double
();
break
;
case
"sdr-max-luminance"
:
monitor
.
sdr_max_luminance
=
(
int
)
Math
.
round
(
value
.
get_double
());
break
;
case
"min-luminance"
:
monitor
.
min_luminance
=
value
.
get_double
();
break
;
case
"max-luminance"
:
monitor
.
max_luminance
=
(
int
)
Math
.
round
(
value
.
get_double
());
break
;
case
"max-avg-luminance"
:
monitor
.
max_avg_luminance
=
(
int
)
Math
.
round
(
value
.
get_double
());
break
;
default
:
set_string_value
(
field
,
value
.
get_string
()
??
""
);
break
;
}
context
.
emit_changed
();
}
private
static
string
get_string_value
(
string
field
)
{
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
switch
(
field
)
{
case
"mode"
:
return
mode_key
(
monitor
.
width
,
monitor
.
height
,
monitor
.
refresh
,
monitor
.
variable_refresh_rate
);
case
"resolution"
:
return
resolution_key
(
monitor
.
width
,
monitor
.
height
);
case
"refresh"
:
return
refresh_key
(
monitor
.
refresh
);
case
"scale-choice"
:
return
scale_key
(
monitor
.
scale
);
case
"transform"
:
return
monitor
.
transform
;
case
"mirror"
:
return
monitor
.
mirror
;
case
"bitdepth"
:
return
monitor
.
bitdepth
.
to_string
();
case
"hypr-vrr"
:
case
"niri-vrr"
:
return
monitor
.
vrr
.
to_string
();
case
"color-management"
:
return
monitor
.
color_management_preset
;
case
"sdr-eotf"
:
return
monitor
.
sdr_eotf
;
case
"force-wide-color"
:
return
monitor
.
supports_wide_color
.
to_string
();
case
"force-hdr"
:
return
monitor
.
supports_hdr
.
to_string
();
case
"icc"
:
return
monitor
.
icc
;
case
"hot-corners"
:
return
monitor
.
niri_hot_corners
;
case
"backdrop-color"
:
return
monitor
.
niri_backdrop_color
;
default
:
return
""
;
}
}
private
static
void
set_string_value
(
string
field
,
string
text
)
{
var
context
=
MonitorSettingsState
.
current
;
var
monitor
=
context
.
monitor
;
switch
(
field
)
{
case
"mode"
:
apply_mode_key
(
text
);
break
;
case
"resolution"
:
apply_resolution_key
(
text
);
break
;
case
"refresh"
:
apply_refresh_key
(
text
);
break
;
case
"scale-choice"
:
monitor
.
scale
=
double
.
parse
(
text
);
break
;
case
"transform"
:
monitor
.
transform
=
text
;
break
;
case
"mirror"
:
monitor
.
mirror
=
text
;
break
;
case
"bitdepth"
:
monitor
.
bitdepth
=
int
.
parse
(
text
);
break
;
case
"hypr-vrr"
:
case
"niri-vrr"
:
monitor
.
vrr
=
int
.
parse
(
text
);
break
;
case
"color-management"
:
monitor
.
color_management_preset
=
text
;
break
;
case
"sdr-eotf"
:
monitor
.
sdr_eotf
=
text
;
break
;
case
"force-wide-color"
:
monitor
.
supports_wide_color
=
int
.
parse
(
text
);
break
;
case
"force-hdr"
:
monitor
.
supports_hdr
=
int
.
parse
(
text
);
break
;
case
"icc"
:
monitor
.
icc
=
text
;
break
;
case
"hot-corners"
:
monitor
.
niri_hot_corners
=
text
;
break
;
case
"backdrop-color"
:
monitor
.
niri_backdrop_color
=
text
;
break
;
}
}
private
static
void
set_variable_refresh
(
bool
active
)
{
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
monitor
.
variable_refresh_rate
=
active
;
DisplayMode
?
mode
=
active
?
find_variable_mode_for_resolution
(
monitor
.
width
,
monitor
.
height
)
:
find_best_fixed_mode_for_resolution
(
monitor
.
width
,
monitor
.
height
);
if
(
mode
!=
null
)
apply_mode
(
mode
);
}
private
static
void
apply_mode_key
(
string
key
)
{
foreach
(
var
mode
in
MonitorSettingsState
.
current
.
monitor
.
modes
)
{
if
(
mode_key
(
mode
.
width
,
mode
.
height
,
mode
.
refresh
,
mode
.
variable_refresh_rate
)
==
key
)
{
apply_mode
(
mode
);
return
;
}
}
}
private
static
void
apply_resolution_key
(
string
key
)
{
var
parts
=
key
.
split
(
"x"
);
if
(
parts
.
length
!=
2
)
return
;
var
mode
=
find_best_mode_for_resolution
(
int
.
parse
(
parts
[
0
]),
int
.
parse
(
parts
[
1
]));
if
(
mode
!=
null
)
apply_mode
(
mode
);
}
private
static
void
apply_refresh_key
(
string
key
)
{
var
refresh
=
double
.
parse
(
key
);
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
==
monitor
.
width
&&
mode
.
height
==
monitor
.
height
&&
!
mode
.
variable_refresh_rate
&&
Math
.
fabs
(
mode
.
refresh
-
refresh
)
<
0.02
)
{
apply_mode
(
mode
);
monitor
.
variable_refresh_rate
=
false
;
return
;
}
}
}
internal
static
DisplayMode
?
find_best_mode_for_resolution
(
int
width
,
int
height
)
{
DisplayMode
?
fallback
=
null
;
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
foreach
(
var
mode
in
monitor
.
modes
)
{
if
(
mode
.
width
!=
width
||
mode
.
height
!=
height
)
continue
;
if
(
Math
.
fabs
(
mode
.
refresh
-
monitor
.
refresh
)
<
0.02
&&
mode
.
variable_refresh_rate
==
monitor
.
variable_refresh_rate
)
return
mode
;
if
(
fallback
==
null
)
fallback
=
mode
;
}
return
fallback
;
}
internal
static
DisplayMode
?
find_best_fixed_mode_for_resolution
(
int
width
,
int
height
)
{
DisplayMode
?
fallback
=
null
;
foreach
(
var
mode
in
MonitorSettingsState
.
current
.
monitor
.
modes
)
{
if
(
mode
.
width
!=
width
||
mode
.
height
!=
height
||
mode
.
variable_refresh_rate
)
continue
;
if
(
fallback
==
null
||
mode
.
refresh
>
fallback
.
refresh
)
fallback
=
mode
;
}
return
fallback
;
}
internal
static
DisplayMode
?
find_variable_mode_for_resolution
(
int
width
,
int
height
)
{
foreach
(
var
mode
in
MonitorSettingsState
.
current
.
monitor
.
modes
)
{
if
(
mode
.
width
==
width
&&
mode
.
height
==
height
&&
mode
.
variable_refresh_rate
)
return
mode
;
}
return
null
;
}
internal
static
void
apply_mode
(
DisplayMode
mode
)
{
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
monitor
.
width
=
mode
.
width
;
monitor
.
height
=
mode
.
height
;
monitor
.
refresh
=
mode
.
refresh
;
monitor
.
variable_refresh_rate
=
mode
.
variable_refresh_rate
;
if
(!
mode
.
supports_scale
(
monitor
.
scale
))
monitor
.
scale
=
mode
.
preferred_scale
;
}
internal
static
string
mode_key
(
int
width
,
int
height
,
double
refresh
,
bool
variable
)
{
return
"%dx%d@%s:%s"
.
printf
(
width
,
height
,
refresh_key
(
refresh
),
variable
.
to_string
());
}
internal
static
string
resolution_key
(
int
width
,
int
height
)
{
return
"%dx%d"
.
printf
(
width
,
height
);
}
internal
static
string
refresh_key
(
double
refresh
)
{
return
"%.3f"
.
printf
(
refresh
).
replace
(
","
,
"."
);
}
internal
static
string
scale_key
(
double
scale
)
{
return
"%.3f"
.
printf
(
scale
).
replace
(
","
,
"."
);
}
}
}
src/ui/settings/monitor-setting-validator.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorSettingValidator
:
Tuner
.
Validator
{
public
string
feature
{
get
;
set
;
default
=
""
;
}
public
string
group_feature
{
get
;
set
;
default
=
""
;
}
public
override
void
apply
(
Tuner
.
Binding
binding
,
Gtk
.
Widget
native_widget
)
{
update
(
native_widget
);
MonitorSettingsState
.
current
.
changed
.
connect
(()
=>
update
(
native_widget
));
}
private
void
update
(
Gtk
.
Widget
widget
)
{
widget
.
visible
=
monitor_feature_visible
(
feature
);
var
group
=
widget
.
get_ancestor
(
typeof
(
Adw
.
PreferencesGroup
));
if
(
group
!=
null
&&
group_feature
!=
""
)
group
.
visible
=
monitor_feature_visible
(
group_feature
);
}
}
internal
static
bool
monitor_feature_visible
(
string
feature
)
{
var
context
=
MonitorSettingsState
.
current
;
var
backend
=
context
.
backend
;
var
monitor
=
context
.
monitor
;
switch
(
feature
)
{
case
""
:
case
"always"
:
return
true
;
case
"enabled"
:
return
context
.
show_enabled
;
case
"combined-mode"
:
return
!
backend
.
uses_separate_refresh_rate_controls
;
case
"separate-refresh"
:
return
backend
.
uses_separate_refresh_rate_controls
;
case
"fixed-refresh"
:
return
backend
.
uses_separate_refresh_rate_controls
&&
!
monitor
.
variable_refresh_rate
;
case
"variable-refresh"
:
return
backend
.
uses_separate_refresh_rate_controls
&&
monitor
.
supports_variable_refresh_rate
&&
MonitorSettingBinding
.
find_variable_mode_for_resolution
(
monitor
.
width
,
monitor
.
height
)
!=
null
;
case
"scale-choice"
:
return
backend
.
uses_separate_refresh_rate_controls
&&
selected_mode_has_scales
();
case
"scale-spin"
:
return
!
backend
.
uses_separate_refresh_rate_controls
||
!
selected_mode_has_scales
();
case
"mirror"
:
return
backend
.
supports_output_mirroring
&&
context
.
all_monitors
.
size
>
1
;
case
"adaptive-sync"
:
return
backend
.
supports_adaptive_sync
&&
monitor
.
supports_variable_refresh_rate
;
case
"focus-at-startup"
:
return
backend
.
supports_focus_at_startup
;
case
"backdrop-color"
:
return
backend
.
supports_backdrop_color
;
case
"hot-corners"
:
return
backend
.
supports_hot_corners
;
case
"underscanning"
:
return
backend
.
supports_underscanning
;
case
"hdr-toggle"
:
return
backend
.
supports_hdr_toggle
&&
monitor
.
supported_color_modes
.
contains
(
1
);
case
"description-identifier"
:
return
backend
.
supports_description_identifier
;
case
"bit-depth"
:
return
backend
.
supports_bit_depth
;
case
"vrr-modes"
:
return
backend
.
supports_vrr_modes
;
case
"color-management"
:
return
backend
.
supports_color_management
;
case
"hdr-metadata"
:
return
backend
.
supports_hdr_metadata
;
case
"hyprland"
:
return
backend
.
supports_description_identifier
||
backend
.
supports_bit_depth
||
backend
.
supports_vrr_modes
||
backend
.
supports_color_management
;
case
"hdr"
:
return
backend
.
supports_hdr_metadata
;
default
:
return
true
;
}
}
private
static
bool
selected_mode_has_scales
()
{
var
monitor
=
MonitorSettingsState
.
current
.
monitor
;
var
mode
=
MonitorSettingBinding
.
find_best_mode_for_resolution
(
monitor
.
width
,
monitor
.
height
);
return
mode
!=
null
&&
mode
.
supported_scales
.
size
>
0
;
}
}
src/ui/settings/monitor-settings-context.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorSettingsContext
:
Object
{
public
MonitorConfig
monitor
{
get
;
construct
;
}
public
DisplayBackend
backend
{
get
;
construct
;
}
public
Gee
.
ArrayList
<
MonitorConfig
>
all_monitors
{
get
;
construct
;
}
public
bool
show_enabled
{
get
;
construct
;
}
public
signal
void
changed
();
public
MonitorSettingsContext
(
MonitorConfig
monitor
,
DisplayBackend
backend
,
Gee
.
ArrayList
<
MonitorConfig
>
all_monitors
,
bool
show_enabled
)
{
Object
(
monitor
:
monitor
,
backend
:
backend
,
all_monitors
:
all_monitors
,
show_enabled
:
show_enabled
);
}
public
void
emit_changed
()
{
changed
();
}
}
public
class
MonitorSettingsState
:
Object
{
public
static
MonitorSettingsContext
current
;
}
}
src/ui/widgets/backdrop-color-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
[
GtkTemplate
(
ui
=
"/ru/ximperlinux/tuner/Displays/widgets/backdrop-color-widget.ui"
)]
public
class
BackdropColorContent
:
Adw
.
ActionRow
{
[
GtkChild
]
private
unowned
Gtk
.
Switch
enabled_switch
;
[
GtkChild
]
private
unowned
Gtk
.
ColorDialogButton
color_button
;
private
MonitorSettingBinding
color_binding
;
private
bool
updating
;
public
void
setup
(
MonitorSettingBinding
binding
)
{
color_binding
=
binding
;
sync_from_binding
();
enabled_switch
.
notify
[
"active"
].
connect
(()
=>
{
if
(
updating
)
return
;
color_button
.
sensitive
=
enabled_switch
.
active
;
color_binding
.
set_value
(
color_value
());
});
color_button
.
notify
[
"rgba"
].
connect
(()
=>
{
if
(
updating
||
!
enabled_switch
.
active
)
return
;
color_binding
.
set_value
(
color_value
());
});
binding
.
changed
.
connect
(
sync_from_binding
);
}
private
void
sync_from_binding
()
{
if
(
color_binding
==
null
)
return
;
Value
value
=
Value
(
typeof
(
string
));
if
(!
color_binding
.
get_value
(
ref
value
))
return
;
updating
=
true
;
var
text
=
value
.
get_string
()
??
""
;
enabled_switch
.
active
=
text
!=
""
;
color_button
.
sensitive
=
enabled_switch
.
active
;
Gdk
.
RGBA
color
=
{
0
,
0
,
0
,
1
};
if
(
text
!=
""
)
color
.
parse
(
text
);
color_button
.
rgba
=
color
;
updating
=
false
;
}
private
Value
color_value
()
{
Value
value
=
Value
(
typeof
(
string
));
value
.
set_string
(
enabled_switch
.
active
?
rgba_to_css
(
color_button
.
get_rgba
())
:
""
);
return
value
;
}
private
static
string
rgba_to_css
(
Gdk
.
RGBA
?
rgba
)
{
if
(
rgba
==
null
)
return
""
;
if
(
rgba
.
alpha
>=
0.999
)
{
return
"#%02x%02x%02x"
.
printf
(
(
int
)
Math
.
round
(
rgba
.
red
*
255
),
(
int
)
Math
.
round
(
rgba
.
green
*
255
),
(
int
)
Math
.
round
(
rgba
.
blue
*
255
)
);
}
var
alpha
=
"%.3f"
.
printf
(
rgba
.
alpha
).
replace
(
","
,
"."
);
return
"rgba(%d, %d, %d, %s)"
.
printf
(
(
int
)
Math
.
round
(
rgba
.
red
*
255
),
(
int
)
Math
.
round
(
rgba
.
green
*
255
),
(
int
)
Math
.
round
(
rgba
.
blue
*
255
),
alpha
);
}
}
public
class
BackdropColorWidget
:
Tuner
.
Widget
{
public
override
Gtk
.
Widget
?
create
()
{
var
row
=
new
BackdropColorContent
();
var
color_binding
=
binding
as
MonitorSettingBinding
;
if
(
color_binding
!=
null
)
row
.
setup
(
color_binding
);
if
(
binding
!=
null
&&
binding
.
validator
!=
null
)
binding
.
validator
.
apply
(
binding
,
row
);
return
row
;
}
}
}
src/ui/
config-include-validator
.vala
→
src/ui/
widgets/config-include-widget
.vala
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
ConfigIncludeBinding
:
Tuner
.
Binding
{
private
DisplayBackend
backend
;
public
ConfigIncludeBinding
(
DisplayBackend
backend
)
{
this
.
backend
=
backend
;
}
public
ConfigIncludeInfo
info
()
{
return
backend
.
config_include_info
();
return
DisplaysContext
.
controller
.
backend
.
config_include_info
();
}
public
void
connect_config
()
throws
Error
{
backend
.
include_monitor_config
();
DisplaysContext
.
controller
.
backend
.
include_monitor_config
();
emit_changed
();
DisplaysContext
.
controller
.
reload
();
}
public
override
Type
expected_type
{
get
{
return
typeof
(
bool
);
}
}
public
override
bool
get_value
(
ref
Value
value
)
{
value
=
info
().
state
==
ConfigIncludeState
.
NOT_INCLUDED
;
value
=
config_include_visible
()
;
return
true
;
}
...
...
@@ -41,13 +36,12 @@ namespace TunerDisplays {
private
static
void
update
(
ConfigIncludeBinding
binding
,
Adw
.
ActionRow
row
)
{
var
info
=
binding
.
info
();
var
visible
=
info
.
state
==
ConfigIncludeState
.
NOT_INCLUDED
||
info
.
state
==
ConfigIncludeState
.
MISSING_MAIN_CONFIG
;
var
visible
=
config_include_visible
();
row
.
visible
=
visible
;
var
group
=
row
.
get_ancestor
(
typeof
(
Adw
.
PreferencesGroup
));
if
(
group
!=
null
)
group
.
visible
=
visible
;
group
.
visible
=
displays_visibility
(
"status-group"
)
;
if
(!
visible
)
return
;
...
...
@@ -56,4 +50,37 @@ namespace TunerDisplays {
row
.
subtitle
=
info
.
subtitle
;
}
}
[
GtkTemplate
(
ui
=
"/ru/ximperlinux/tuner/Displays/widgets/config-include-widget.ui"
)]
public
class
ConfigIncludeContent
:
Adw
.
ActionRow
{
[
GtkChild
]
private
unowned
Gtk
.
Button
connect_button
;
private
ConfigIncludeBinding
include_binding
;
construct
{
connect_button
.
clicked
.
connect
(()
=>
{
try
{
include_binding
.
connect_config
();
Tuner
.
toast
(
_
(
"Monitor configuration connected"
));
}
catch
(
Error
err
)
{
Tuner
.
toast
(
err
.
message
);
}
});
}
public
void
setup
(
ConfigIncludeBinding
binding
)
{
include_binding
=
binding
;
}
}
public
class
ConfigIncludeWidget
:
Tuner
.
Widget
{
public
override
Gtk
.
Widget
?
create
()
{
var
row
=
new
ConfigIncludeContent
();
var
include_binding
=
binding
as
ConfigIncludeBinding
;
if
(
include_binding
!=
null
)
row
.
setup
(
include_binding
);
if
(
binding
!=
null
&&
binding
.
validator
!=
null
)
binding
.
validator
.
apply
(
binding
,
row
);
return
row
;
}
}
}
src/ui/widgets/mirror-settings-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MirrorSettingsWidget
:
Tuner
.
Widget
{
private
Gtk
.
ListBox
list
;
public
override
Gtk
.
Widget
?
create
()
{
list
=
create_boxed_list
();
DisplaysContext
.
controller
.
changed
.
connect
(
rebuild
);
rebuild
();
return
list
;
}
private
void
rebuild
()
{
clear_list
(
list
);
var
controller
=
DisplaysContext
.
controller
;
if
(!
controller
.
mirror_enabled
())
{
list
.
visible
=
false
;
return
;
}
add_mirror_mode_row
(
controller
);
add_mirror_scale_row
(
controller
);
add_mirror_transform_row
(
controller
);
list
.
visible
=
list_has_children
(
list
);
}
private
void
add_mirror_mode_row
(
DisplaysController
controller
)
{
var
modes
=
controller
.
common_mirror_resolutions
();
if
(
modes
.
size
==
0
)
return
;
var
model
=
new
Gtk
.
StringList
(
null
);
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
modes
.
size
;
i
++)
{
var
mode
=
modes
[
i
];
model
.
append
(
"%dx%d"
.
printf
(
mode
.
width
,
mode
.
height
));
if
(
controller
.
monitors
.
size
>
0
&&
mode
.
width
==
controller
.
monitors
[
0
].
width
&&
mode
.
height
==
controller
.
monitors
[
0
].
height
)
{
selected
=
i
;
}
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Resolution"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
modes
.
size
)
controller
.
apply_mirror_mode
(
modes
[
index
]);
});
list
.
append
(
row
);
}
private
void
add_mirror_scale_row
(
DisplaysController
controller
)
{
var
mode
=
controller
.
selected_common_mirror_mode
();
if
(
mode
==
null
)
return
;
var
scales
=
controller
.
common_mirror_scales
(
mode
);
if
(
scales
.
size
==
0
)
return
;
var
model
=
new
Gtk
.
StringList
(
null
);
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
scales
.
size
;
i
++)
{
var
scale
=
scales
[
i
];
model
.
append
(
"%.0f%%"
.
printf
(
scale
*
100
));
if
(
controller
.
monitors
.
size
>
0
&&
Math
.
fabs
(
scale
-
controller
.
monitors
[
0
].
scale
)
<
0.01
)
selected
=
i
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Scale"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
scales
.
size
)
controller
.
apply_mirror_scale
(
scales
[
index
]);
});
list
.
append
(
row
);
}
private
void
add_mirror_transform_row
(
DisplaysController
controller
)
{
var
model
=
new
Gtk
.
StringList
(
null
);
string
[]
titles
=
{
_
(
"Normal"
),
_
(
"90 degrees"
),
_
(
"180 degrees"
),
_
(
"270 degrees"
),
_
(
"Flipped"
),
_
(
"Flipped 90 degrees"
),
_
(
"Flipped 180 degrees"
),
_
(
"Flipped 270 degrees"
)
};
foreach
(
var
title
in
titles
)
model
.
append
(
title
);
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Rotation"
),
model
=
model
,
selected
=
controller
.
monitors
.
size
>
0
?
transform_to_index
(
controller
.
monitors
[
0
].
transform
)
:
0
};
row
.
notify
[
"selected"
].
connect
(()
=>
controller
.
apply_mirror_transform
(
transform_from_index
((
int
)
row
.
selected
)));
list
.
append
(
row
);
}
}
}
src/ui/widgets/monitor-layout-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorLayoutWidget
:
Tuner
.
Widget
{
private
MonitorLayout
content
;
public
override
Gtk
.
Widget
?
create
()
{
var
row
=
new
Adw
.
PreferencesRow
()
{
activatable
=
false
,
selectable
=
false
,
can_focus
=
false
,
focusable
=
false
};
content
=
new
MonitorLayout
();
update_layout
();
content
.
layout_changed
.
connect
(
DisplaysContext
.
controller
.
refresh_from_monitors
);
DisplaysContext
.
controller
.
changed
.
connect
(
update_layout
);
if
(
binding
!=
null
&&
binding
.
validator
!=
null
)
binding
.
validator
.
apply
(
binding
,
row
);
row
.
child
=
content
;
return
row
;
}
private
void
update_layout
()
{
var
controller
=
DisplaysContext
.
controller
;
content
.
require_connected
=
controller
.
backend
.
requires_connected_layout
;
content
.
set_monitors
(
controller
.
monitors
);
}
}
}
src/ui/monitor-layout.vala
→
src/ui/
widgets/
monitor-layout.vala
View file @
ffcc0360
namespace
TunerDisplays
{
[
GtkTemplate
(
ui
=
"/ru/ximperlinux/tuner/Displays/widgets/monitor-layout.ui"
)]
public
class
MonitorLayout
:
Gtk
.
DrawingArea
{
private
const
double
EDGE_PADDING
=
6
;
private
const
double
MARGIN_MON
=
0.66
;
...
...
@@ -24,6 +25,7 @@ namespace TunerDisplays {
public
signal
void
layout_changed
();
public
MonitorLayout
()
{
init_template
();
set_draw_func
(
draw
);
var
drag
=
new
Gtk
.
GestureDrag
();
...
...
@@ -68,6 +70,13 @@ namespace TunerDisplays {
}
public
void
set_monitors
(
Gee
.
ArrayList
<
MonitorConfig
>
monitors
)
{
if
(
this
.
monitors
==
monitors
)
{
if
(!
drag_active
)
needs_recenter
=
true
;
queue_draw
();
return
;
}
this
.
monitors
=
monitors
;
dragged
=
-
1
;
needs_recenter
=
true
;
...
...
src/ui/widgets/monitor-list-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
MonitorListWidget
:
Tuner
.
Widget
{
private
Gtk
.
ListBox
list
;
public
override
Gtk
.
Widget
?
create
()
{
list
=
create_boxed_list
();
DisplaysContext
.
controller
.
changed
.
connect
(
rebuild
);
rebuild
();
return
list
;
}
private
void
rebuild
()
{
clear_list
(
list
);
var
controller
=
DisplaysContext
.
controller
;
if
(
controller
.
mirror_enabled
()
||
controller
.
single_monitor_mode
())
{
list
.
visible
=
false
;
return
;
}
foreach
(
var
monitor
in
controller
.
monitors
)
{
var
row
=
new
MonitorRow
(
monitor
,
monitor_settings_page_id
(),
controller
.
monitors
,
controller
.
backend
);
row
.
monitor_selected
.
connect
(
controller
.
open_monitor_settings
);
row
.
monitor_changed
.
connect
(
controller
.
refresh_from_monitors
);
list
.
append
(
row
);
}
list
.
visible
=
list_has_children
(
list
);
}
}
}
src/ui/monitor-row.vala
→
src/ui/
widgets/
monitor-row.vala
View file @
ffcc0360
File moved
src/ui/widgets/primary-display-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
PrimaryDisplayWidget
:
Tuner
.
Widget
{
private
Gtk
.
ListBox
list
;
public
override
Gtk
.
Widget
?
create
()
{
list
=
create_boxed_list
();
DisplaysContext
.
controller
.
changed
.
connect
(
rebuild
);
rebuild
();
return
list
;
}
private
void
rebuild
()
{
clear_list
(
list
);
var
controller
=
DisplaysContext
.
controller
;
if
(!
controller
.
backend
.
supports_primary_display
||
controller
.
monitors
.
size
<=
1
||
controller
.
mirror_enabled
())
{
list
.
visible
=
false
;
return
;
}
var
model
=
new
Gtk
.
StringList
(
null
);
var
values
=
controller
.
enabled_monitors
();
uint
selected
=
0
;
for
(
int
i
=
0
;
i
<
values
.
size
;
i
++)
{
var
monitor
=
values
[
i
];
model
.
append
(
monitor
.
title
);
if
(
monitor
.
primary
)
selected
=
i
;
}
if
(
values
.
size
<=
1
)
{
list
.
visible
=
false
;
return
;
}
var
row
=
new
Adw
.
ComboRow
()
{
title
=
_
(
"Primary Display"
),
model
=
model
,
selected
=
selected
};
row
.
notify
[
"selected"
].
connect
(()
=>
{
var
index
=
(
int
)
row
.
selected
;
if
(
index
>=
0
&&
index
<
values
.
size
)
controller
.
set_primary
(
values
[
index
]);
});
list
.
append
(
row
);
list
.
visible
=
true
;
}
}
}
src/ui/widgets/single-monitor-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
SingleMonitorWidget
:
Tuner
.
Widget
{
private
Gtk
.
Box
box
;
private
MonitorSettingsContent
?
content
;
public
override
Gtk
.
Widget
?
create
()
{
box
=
new
Gtk
.
Box
(
Gtk
.
Orientation
.
VERTICAL
,
0
);
DisplaysContext
.
controller
.
changed
.
connect
(
rebuild
);
rebuild
();
if
(
binding
!=
null
&&
binding
.
validator
!=
null
)
binding
.
validator
.
apply
(
binding
,
box
);
return
box
;
}
private
void
rebuild
()
{
var
child
=
box
.
get_first_child
();
while
(
child
!=
null
)
{
var
next
=
child
.
get_next_sibling
();
box
.
remove
(
child
);
child
=
next
;
}
content
=
null
;
var
controller
=
DisplaysContext
.
controller
;
if
(!
controller
.
single_monitor_mode
())
{
box
.
visible
=
false
;
return
;
}
content
=
new
MonitorSettingsContent
(
controller
.
monitors
[
0
],
controller
.
backend
,
controller
.
monitors
,
false
);
content
.
monitor_changed
.
connect
(
controller
.
refresh_from_monitors
);
box
.
append
(
content
);
box
.
visible
=
true
;
}
}
}
src/ui/widgets/status-widget.vala
0 → 100644
View file @
ffcc0360
namespace
TunerDisplays
{
public
class
StatusWidget
:
Tuner
.
Widget
{
private
Gtk
.
ListBox
list
;
public
override
Gtk
.
Widget
?
create
()
{
list
=
create_boxed_list
();
DisplaysContext
.
controller
.
changed
.
connect
(
rebuild
);
rebuild
();
if
(
binding
!=
null
&&
binding
.
validator
!=
null
)
binding
.
validator
.
apply
(
binding
,
list
);
return
list
;
}
private
void
rebuild
()
{
clear_list
(
list
);
var
controller
=
DisplaysContext
.
controller
;
if
(
controller
.
has_status
)
{
list
.
append
(
new
Adw
.
ActionRow
()
{
title
=
controller
.
status_title
,
subtitle
=
controller
.
status_subtitle
});
}
if
(!
controller
.
can_apply
)
{
list
.
append
(
new
Adw
.
ActionRow
()
{
title
=
_
(
"Read-only backend"
),
subtitle
=
_
(
"Applying monitor layouts is not supported by this backend."
)
});
}
list
.
visible
=
list_has_children
(
list
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment