Skip to content

Commit

Permalink
Adding Display Page (without Night Mode), WIP on multiple screen conf…
Browse files Browse the repository at this point in the history
…iguration
  • Loading branch information
atsen-dev committed Mar 7, 2022
1 parent 4b214ae commit 804ee3d
Show file tree
Hide file tree
Showing 19 changed files with 1,196 additions and 29 deletions.
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ analyzer:
- "**/*.g.dart"
- "**/*.mocks.dart"
- "**/l10/*.dart"
- "**/generated/**"

linter:
# The lint rules applied to this project can be customized in the
Expand Down
116 changes: 116 additions & 0 deletions lib/api/display/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
## GetCurrentState:
@serial: configuration serial
@monitors: available monitors
@logical_monitors: current logical monitor configuration
@properties: display configuration properties
@monitors represent connected physical monitors
* s connector: connector name (e.g. HDMI-1, DP-1, etc)
* s vendor: vendor name
* s product: product name
* s serial: product serial
* a(siiddada{sv}) modes: available modes
* s id: mode ID
* i width: width in physical pixels
* i height: height in physical pixels
* d refresh rate: refresh rate
* d preferred scale: scale preferred as per calculations
* ad supported scales: scales supported by this mode
* a{sv} properties: optional properties, including:
- "is-current" (b): the mode is currently active mode
- "is-preferred" (b): the mode is the preferred mode
* a{sv} properties: optional properties, including:
- "width-mm" (i): physical width of monitor in millimeters
- "height-mm" (i): physical height of monitor in millimeters
- "is-underscanning" (b): whether underscanning is enabled
(absence of this means underscanning
not being supported)
- "max-screen-size" (ii): the maximum size a screen may have
(absence of this means unlimited screen
size)
- "is-builtin" (b): whether the monitor is built in, e.g. a
laptop panel (absence of this means it is
not built in)
- "display-name" (s): a human readable display name of the monitor
Possible mode flags:
1 : preferred mode
2 : current mode
@logical_monitors represent current logical monitor configuration
* i x: x position
* i y: y position
* d scale: scale
* u transform: transform (see below)
* b primary: true if this is the primary logical monitor
* a(sss) monitors: monitors displaying this logical monitor
* connector: name of the connector (e.g. DP-1, eDP-1 etc)
* vendor: vendor name
* product: product name
* serial: product serial
* a{sv} properties: possibly other properties
Posisble transform values:
0: normal
1: 90°
2: 180°
3: 270°
4: flipped
5: 90° flipped
6: 180° flipped
7: 270° flipped
@layout_mode current layout mode represents the way logical monitors
are layed out on the screen. Possible modes include:
1 : physical
2 : logical
With physical layout mode, each logical monitor has the same dimensions
an the monitor modes of the associated monitors assigned to it, no
matter what scale is in use.
With logical mode, the dimension of a logical monitor is the dimension
of the monitor mode, divided by the logical monitor scale.
Possible @properties are:
* "supports-mirroring" (b): FALSE if mirroring not supported; TRUE or not
present if mirroring is supported.
* "layout-mode" (u): Represents in what way logical monitors are laid
out on the screen. The layout mode can be either
of the ones listed below. Absence of this property
means the layout mode cannot be changed, and that
"logical" mode is assumed to be used.
* 1 : logical - the dimension of a logical monitor is derived from
the monitor modes associated with it, then scaled
using the logical monitor scale.
* 2 : physical - the dimension of a logical monitor is derived from
the monitor modes associated with it.
* "supports-changing-layout-mode" (b): True if the layout mode can be
changed. Absence of this means the
layout mode cannot be changed.
* "global-scale-required" (b): True if all the logical monitors must
always use the same scale. Absence of
this means logical monitor scales can
differ.

## ApplyMonitorsConfig:
@serial: configuration serial
@method: configuration method
@logical_monitors: monitors configuration
@properties: properties
@method represents the way the configuration should be handled.
Possible methods:
0: verify
1: temporary
2: persistent
@logical_monitors consists of a list of logical monitor configurations.
Each logical monitor configuration consists of:
* i: layout x position
* i: layout y position
* d: scale
* u: transform (see GetCurrentState)
* b primary: true if this is the primary logical monitor
* a(ssa{sv}): a list of monitors, each consisting of:
* s: connector
* s: monitor mode ID
* a{sv}: monitor properties, including:
- "enable_underscanning" (b): enable monitor underscanning;
may only be set when underscanning
is supported (see GetCurrentState).
@properties may effect the global monitor configuration state. Possible
properties are:
* "layout-mode" (u): layout mode the passed configuration is in; may
only be set when changing the layout mode is
supported (see GetCurrentState).
85 changes: 85 additions & 0 deletions lib/api/display/display_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import 'package:dbus/dbus.dart';
import 'package:settings/api/display/objects/dbus_displays_config.dart';
import 'package:settings/generated/dbus/display-config-remote-object.dart';
import 'package:settings/services/display_service.dart';

const displaysInterface = 'org.gnome.Mutter.DisplayConfig';

const displayPath = '/org/gnome/Mutter/DisplayConfig';

///
/// Methods:
///
/// ApplyConfiguration (UInt32 serial, Boolean persistent, Array of [Struct of (UInt32, Int32, Int32, Int32, UInt32, Array of [UInt32], Dict of {String, Variant})] crtcs, Array of [Struct of (UInt32, Dict of {String, Variant})] outputs) ↦ ()
/// ApplyMonitorsConfig (UInt32 serial, UInt32 method, Array of [Struct of (Int32, Int32, Double, UInt32, Boolean, Array of [Struct of (String, String, Dict of {String, Variant})])] logical_monitors, Dict of {String, Variant} properties) ↦ ()
/// ChangeBacklight (UInt32 serial, UInt32 output, Int32 value) ↦ (Int32 new_value)
/// GetCrtcGamma (UInt32 serial, UInt32 crtc) ↦ (Array of [UInt16] red, Array of [UInt16] green, Array of [UInt16] blue)
/// GetCurrentState () ↦ (UInt32 serial, Array of [Struct of (Struct of (String, String, String, String), Array of [Struct of (String, Int32, Int32, Double, Double, Array of [Double], Dict of {String, Variant})], Dict of {String, Variant})] monitors, Array of [Struct of (Int32, Int32, Double, UInt32, Boolean, Array of [Struct of (String, String, String, String)], Dict of {String, Variant})] logical_monitors, Dict of {String, Variant} properties)
/// GetResources () ↦ (UInt32 serial, Array of [Struct of (UInt32, Int64, Int32, Int32, Int32, Int32, Int32, UInt32, Array of [UInt32], Dict of {String, Variant})] crtcs, Array of [Struct of (UInt32, Int64, Int32, Array of [UInt32], String, Array of [UInt32], Array of [UInt32], Dict of {String, Variant})] outputs, Array of [Struct of (UInt32, Int64, UInt32, UInt32, Double, UInt32)] modes, Int32 max_screen_width, Int32 max_screen_height)
/// SetCrtcGamma (UInt32 serial, UInt32 crtc, Array of [UInt16] red, Array of [UInt16] green, Array of [UInt16] blue) ↦ ()
/// SetOutputCTM (UInt32 serial, UInt32 output, Struct of (UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64) ctm) ↦ ()
///
/// Signals:
///
/// MonitorsChanged
class DisplayApi {
DisplayApi() : _object = _createObject() {
/// Listen to signal stream, when a change occur, we update our data
_object.monitorsChanged
.listen((OrgGnomeMutterDisplayConfigMonitorsChanged signal) {
if (signal.name == 'MonitorsChanged') {
getCurrent();
}
});
}

final OrgGnomeMutterDisplayConfig _object;

Stream<DBusDisplaysConfig> get streamChanges =>
_object.monitorsChanged
.asyncMap((event) async => getCurrent());

static OrgGnomeMutterDisplayConfig _createObject() =>
OrgGnomeMutterDisplayConfig(
DBusClient.session(),
displaysInterface,
DBusObjectPath(displayPath),
);

Future<DBusDisplaysConfig> getCurrent() async {
List<DBusValue>? state = await _object.callGetCurrentState();

List<dynamic> list = state.map((e) => _toNative(e)).toList();

return DBusDisplaysConfig(list);
}

Future<void> apply(int serial, ConfigurationMethod configurationMethod,
List<DBusStruct> logicalParameterValues) =>
_object.callApplyMonitorsConfig(
serial,
configurationMethod.index,
logicalParameterValues,
{},
);

dynamic _toNative(dynamic value) {
dynamic output;

if (value is Map) {
output =
value.map((key, value) => MapEntry(_toNative(key), _toNative(value)));
} else if (value is Iterable) {
output = value.map((e) => _toNative(e)).toList();
} else if (value is DBusArray) {
output = value.toNative().map((e) => _toNative(e)).toList();
} else if (value is DBusValue) {
output = value.toNative();
} else {
return value;
}

return output;
}

}
166 changes: 166 additions & 0 deletions lib/api/display/objects/dbus_displays_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
class DBusDisplaysConfig {
DBusDisplaysConfig(List<dynamic> list)
: serial = list[0],
_monitors = list[1],
_logicalMonitors = list[2];

//_properties = list[3];

/// @serial: configuration serial
final int serial;

/// @monitors: available monitors
final List<dynamic> _monitors;

/// @logical_monitors: current logical monitor configuration
final List<dynamic> _logicalMonitors;

/// @properties: display configuration properties
//final Map<dynamic, dynamic> _properties;

/// * s connector: connector name (e.g. HDMI-1, DP-1, etc)
/// * s vendor: vendor name
/// * s product: product name
/// * s serial: product serial
_Identity identity(index) => _Identity(_monitors[index][0]);

/// * a(siiddada{sv}) modes: available modes
List<_Option> availableOptions(index) =>
(_monitors[index][1] as List<dynamic>).map((e) => _Option(e)).toList();

_Option currentOption(index) =>
availableOptions(index).where((element) => element.isCurrent).first;

///@logical_monitors represent current logical monitor configuration
/// * i x: x position
/// * i y: y position
/// * d scale: scale
/// * u transform: transform (see below)
/// * b primary: true if this is the primary logical monitor
/// * a(sss) monitors: monitors displaying this logical monitor
/// * connector: name of the connector (e.g. DP-1, eDP-1 etc)
/// * vendor: vendor name
/// * product: product name
/// * serial: product serial
/// * a{sv} properties: possibly other properties
/// @layout_mode current layout mode represents the way logical monitors
/// are layed out on the screen. Possible modes include:
/// 1 : physical
/// 2 : logical
/// With physical layout mode, each logical monitor has the same dimensions
/// an the monitor modes of the associated monitors assigned to it, no
/// matter what scale is in use.
/// With logical mode, the dimension of a logical monitor is the dimension
/// of the monitor mode, divided by the logical monitor scale.
/// Possible @properties are:
/// * "supports-mirroring" (b): FALSE if mirroring not supported; TRUE or not
/// present if mirroring is supported.
/// * "layout-mode" (u): Represents in what way logical monitors are laid
/// out on the screen. The layout mode can be either
/// of the ones listed below. Absence of this property
/// means the layout mode cannot be changed, and that
/// "logical" mode is assumed to be used.
/// * 1 : logical - the dimension of a logical monitor is derived from
/// the monitor modes associated with it, then scaled
/// using the logical monitor scale.
/// * 2 : physical - the dimension of a logical monitor is derived from
/// the monitor modes associated with it.
/// * "supports-changing-layout-mode" (b): True if the layout mode can be
/// changed. Absence of this means the
/// layout mode cannot be changed.
/// * "global-scale-required" (b): True if all the logical monitors must
/// always use the same scale. Absence of
/// this means logical monitor scales can
/// differ.
///
_LogicalConfiguration currentLogicalConfiguration(index) =>
_LogicalConfiguration(_logicalMonitors[index]);

/// * a{sv} properties: optional properties, including:
/// - "width-mm" (i): physical width of monitor in millimeters
/// - "height-mm" (i): physical height of monitor in millimeters
/// - "is-underscanning" (b): whether underscanning is enabled
/// (absence of this means underscanning
/// not being supported)
/// - "max-screen-size" (ii): the maximum size a screen may have
/// (absence of this means unlimited screen
/// size)
/// - "is-builtin" (b): whether the monitor is built in, e.g. a
/// laptop panel (absence of this means it is
/// not built in)
/// - "display-name" (s): a human readable display name of the monitor
/// Possible mode flags:
/// 1 : preferred mode
/// 2 : current mode
String displayName(index) => _monitors[index][2]['display-name'] ?? '';

bool isBuiltin(index) => _monitors[index][2]['is-builtin'] ?? false;

int get monitorsLength => _monitors.length;
}

class _Identity {
_Identity(List<dynamic> monitors)
: connector = monitors[0],
vendor = monitors[1],
product = monitors[2],
serial = monitors[3];

final String connector;
final String vendor;
final String product;
final String serial;
}

/// * a(siiddada{sv}) modes: available modes
/// * s id: mode ID
/// * i width: width in physical pixels
/// * i height: height in physical pixels
/// * d refresh rate: refresh rate
/// * d preferred scale: scale preferred as per calculations
/// * ad supported scales: scales supported by this mode
/// * a{sv} properties: optional properties, including:
/// - "is-current" (b): the mode is currently active mode
/// - "is-preferred" (b): the mode is the preferred mode
/// * a{sv} properties: optional properties, including:
/// - "width-mm" (i): physical width of monitor in millimeters
/// - "height-mm" (i): physical height of monitor in millimeters
/// - "is-underscanning" (b): whether underscanning is enabled
class _Option {
_Option(List<dynamic> monitor)
: modeId = monitor[0],
x = monitor[1],
y = monitor[2],
refreshRate = monitor[3],
scale = monitor[4],
availableScales = (monitor[5] as List<dynamic>)
.map((e) => double.parse(e.toString()))
.toList(),
isCurrent = monitor[6]['is-current'] == true,
isPreferred = monitor[6]['is-preferred'] == true;

final String modeId;
final int x;
final int y;
final double refreshRate;
final double scale;
final List<double> availableScales;
final bool isCurrent;
final bool isPreferred;
}

class _LogicalConfiguration {
_LogicalConfiguration(List<dynamic> logicalMonitor)
: offsetX = logicalMonitor[0],
offsetY = logicalMonitor[1],
scale = logicalMonitor[2],
orientation = logicalMonitor[3],
primary = logicalMonitor[4];

final int offsetX;
final int offsetY;
final double scale;
final int orientation;
final bool primary;

}
Loading

0 comments on commit 804ee3d

Please sign in to comment.