Attempt to detect the correct monitor, fix extensions management of resources and effect state
This commit is contained in:
parent
01285ec525
commit
3ba8a98169
|
|
@ -0,0 +1,453 @@
|
||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
<!--
|
||||||
|
org.gnome.Mutter.DisplayConfig:
|
||||||
|
@short_description: display configuration interface
|
||||||
|
|
||||||
|
This interface is used by mutter and gnome-settings-daemon
|
||||||
|
to apply multiple monitor configuration.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<interface name="org.gnome.Mutter.DisplayConfig">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetResources:
|
||||||
|
@serial: configuration serial
|
||||||
|
@crtcs: available CRTCs
|
||||||
|
@outputs: available outputs
|
||||||
|
@modes: available modes
|
||||||
|
@max_screen_width:
|
||||||
|
@max_screen_height:
|
||||||
|
|
||||||
|
Retrieves the current layout of the hardware.
|
||||||
|
|
||||||
|
@serial is an unique identifier representing the current state
|
||||||
|
of the screen. It must be passed back to ApplyConfiguration()
|
||||||
|
and will be increased for every configuration change (so that
|
||||||
|
mutter can detect that the new configuration is based on old
|
||||||
|
state).
|
||||||
|
|
||||||
|
A CRTC (CRT controller) is a logical monitor, ie a portion
|
||||||
|
of the compositor coordinate space. It might correspond
|
||||||
|
to multiple monitors, when in clone mode, but not that
|
||||||
|
it is possible to implement clone mode also by setting different
|
||||||
|
CRTCs to the same coordinates.
|
||||||
|
|
||||||
|
The number of CRTCs represent the maximum number of monitors
|
||||||
|
that can be set to expand and it is a HW constraint; if more
|
||||||
|
monitors are connected, then necessarily some will clone. This
|
||||||
|
is complementary to the concept of the encoder (not exposed in
|
||||||
|
the API), which groups outputs that necessarily will show the
|
||||||
|
same image (again a HW constraint).
|
||||||
|
|
||||||
|
A CRTC is represented by a DBus structure with the following
|
||||||
|
layout:
|
||||||
|
* u ID: the ID in the API of this CRTC
|
||||||
|
* x winsys_id: the low-level ID of this CRTC (which might
|
||||||
|
be a XID, a KMS handle or something entirely
|
||||||
|
different)
|
||||||
|
* i x, y, width, height: the geometry of this CRTC
|
||||||
|
(might be invalid if the CRTC is not in
|
||||||
|
use)
|
||||||
|
* i current_mode: the current mode of the CRTC, or -1 if this
|
||||||
|
CRTC is not used
|
||||||
|
Note: the size of the mode will always correspond
|
||||||
|
to the width and height of the CRTC
|
||||||
|
* u current_transform: the current transform (espressed according
|
||||||
|
to the wayland protocol)
|
||||||
|
* au transforms: all possible transforms
|
||||||
|
* a{sv} properties: other high-level properties that affect this
|
||||||
|
CRTC; they are not necessarily reflected in
|
||||||
|
the hardware.
|
||||||
|
No property is specified in this version of the API.
|
||||||
|
|
||||||
|
Note: all geometry information refers to the untransformed
|
||||||
|
display.
|
||||||
|
|
||||||
|
An output represents a physical screen, connected somewhere to
|
||||||
|
the computer. Floating connectors are not exposed in the API.
|
||||||
|
An output is a DBus struct with the following fields:
|
||||||
|
* u ID: the ID in the API
|
||||||
|
* x winsys_id: the low-level ID of this output (XID or KMS handle)
|
||||||
|
* i current_crtc: the CRTC that is currently driving this output,
|
||||||
|
or -1 if the output is disabled
|
||||||
|
* au possible_crtcs: all CRTCs that can control this output
|
||||||
|
* s name: the name of the connector to which the output is attached
|
||||||
|
(like VGA1 or HDMI)
|
||||||
|
* au modes: valid modes for this output
|
||||||
|
* au clones: valid clones for this output, ie other outputs that
|
||||||
|
can be assigned the same CRTC as this one; if you
|
||||||
|
want to mirror two outputs that don't have each other
|
||||||
|
in the clone list, you must configure two different
|
||||||
|
CRTCs for the same geometry
|
||||||
|
* a{sv} properties: other high-level properties that affect this
|
||||||
|
output; they are not necessarily reflected in
|
||||||
|
the hardware.
|
||||||
|
Known properties:
|
||||||
|
- "vendor" (s): (readonly) the human readable name
|
||||||
|
of the manufacturer
|
||||||
|
- "product" (s): (readonly) the human readable name
|
||||||
|
of the display model
|
||||||
|
- "serial" (s): (readonly) the serial number of this
|
||||||
|
particular hardware part
|
||||||
|
- "display-name" (s): (readonly) a human readable name
|
||||||
|
of this output, to be shown in the UI
|
||||||
|
- "backlight" (i): (readonly, use the specific interface)
|
||||||
|
the backlight value as a percentage
|
||||||
|
(-1 if not supported)
|
||||||
|
- "primary" (b): whether this output is primary
|
||||||
|
or not
|
||||||
|
- "presentation" (b): whether this output is
|
||||||
|
for presentation only
|
||||||
|
Note: properties might be ignored if not consistenly
|
||||||
|
applied to all outputs in the same clone group. In
|
||||||
|
general, it's expected that presentation or primary
|
||||||
|
outputs will not be cloned.
|
||||||
|
|
||||||
|
A mode represents a set of parameters that are applied to
|
||||||
|
each output, such as resolution and refresh rate. It is a separate
|
||||||
|
object so that it can be referenced by CRTCs and outputs.
|
||||||
|
Multiple outputs in the same CRTCs must all have the same mode.
|
||||||
|
A mode is exposed as:
|
||||||
|
* u ID: the ID in the API
|
||||||
|
* x winsys_id: the low-level ID of this mode
|
||||||
|
* u width, height: the resolution
|
||||||
|
* d frequency: refresh rate
|
||||||
|
* u flags: mode flags as defined in xf86drmMode.h and randr.h
|
||||||
|
|
||||||
|
Output and modes are read-only objects (except for output properties),
|
||||||
|
they can change only in accordance to HW changes (such as hotplugging
|
||||||
|
a monitor), while CRTCs can be changed with ApplyConfiguration().
|
||||||
|
|
||||||
|
XXX: actually, if you insist enough, you can add new modes
|
||||||
|
through xrandr command line or the KMS API, overriding what the
|
||||||
|
kernel driver and the EDID say.
|
||||||
|
Usually, it only matters with old cards with broken drivers, or
|
||||||
|
old monitors with broken EDIDs, but it happens more often with
|
||||||
|
projectors (if for example the kernel driver doesn't add the
|
||||||
|
640x480 - 800x600 - 1024x768 default modes). Probably something
|
||||||
|
that we need to handle in mutter anyway.
|
||||||
|
-->
|
||||||
|
<method name="GetResources">
|
||||||
|
<arg name="serial" direction="out" type="u" />
|
||||||
|
<arg name="crtcs" direction="out" type="a(uxiiiiiuaua{sv})" />
|
||||||
|
<arg name="outputs" direction="out" type="a(uxiausauaua{sv})" />
|
||||||
|
<arg name="modes" direction="out" type="a(uxuudu)" />
|
||||||
|
<arg name="max_screen_width" direction="out" type="i" />
|
||||||
|
<arg name="max_screen_height" direction="out" type="i" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ApplyConfiguration:
|
||||||
|
@serial: configuration serial
|
||||||
|
@persistent: whether this configuration should be saved on disk
|
||||||
|
@crtcs: new data for CRTCs
|
||||||
|
@outputs: new data for outputs
|
||||||
|
|
||||||
|
Applies the requested configuration changes.
|
||||||
|
|
||||||
|
@serial must match the serial from the last GetResources() call,
|
||||||
|
or org.freedesktop.DBus.AccessDenied will be generated.
|
||||||
|
|
||||||
|
If @persistent is true, mutter will attempt to replicate this
|
||||||
|
configuration the next time this HW layout appears.
|
||||||
|
|
||||||
|
@crtcs represents the new logical configuration, as a list
|
||||||
|
of structures containing:
|
||||||
|
- u ID: the API ID from the corresponding GetResources() call
|
||||||
|
- i new_mode: the API ID of the new mode to configure the CRTC
|
||||||
|
with, or -1 if the CRTC should be disabled
|
||||||
|
- i x, y: the new coordinates of the top left corner
|
||||||
|
the geometry will be completed with the size information
|
||||||
|
from @new_mode
|
||||||
|
- u transform: the desired transform
|
||||||
|
- au outputs: the API ID of outputs that should be assigned to
|
||||||
|
this CRTC
|
||||||
|
- a{sv} properties: properties whose value should be changed
|
||||||
|
|
||||||
|
Note: CRTCs not referenced in the array will be disabled.
|
||||||
|
|
||||||
|
@outputs represent the output property changes as:
|
||||||
|
- u ID: the API ID of the output to change
|
||||||
|
- a{sv} properties: properties whose value should be changed
|
||||||
|
|
||||||
|
Note: both for CRTCs and outputs, properties not included in
|
||||||
|
the dictionary will not be changed.
|
||||||
|
|
||||||
|
Note: unrecognized properties will have no effect, but if the
|
||||||
|
configuration change succeeds the property will be reported
|
||||||
|
by the next GetResources() call, and if @persistent is true,
|
||||||
|
it will also be saved to disk.
|
||||||
|
|
||||||
|
If the configuration is invalid according to the previous
|
||||||
|
GetResources() call, for example because a CRTC references
|
||||||
|
an output it cannot drive, or not all outputs support the
|
||||||
|
chosen mode, the error org.freedesktop.DBus.InvalidArgs will
|
||||||
|
be generated.
|
||||||
|
|
||||||
|
If the configuration cannot be applied for any other reason
|
||||||
|
(eg. the screen size would exceed texture limits), the error
|
||||||
|
org.freedesktop.DBus.Error.LimitsExceeded will be generated.
|
||||||
|
-->
|
||||||
|
<method name="ApplyConfiguration">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="persistent" direction="in" type="b" />
|
||||||
|
<arg name="crtcs" direction="in" type="a(uiiiuaua{sv})" />
|
||||||
|
<arg name="outputs" direction="in" type="a(ua{sv})" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ChangeBacklight:
|
||||||
|
@serial: configuration serial
|
||||||
|
@output: the API id of the output
|
||||||
|
@value: the new backlight value
|
||||||
|
|
||||||
|
Changes the backlight of @output to @value, which is
|
||||||
|
expressed as a percentage and rounded to the HW limits.
|
||||||
|
|
||||||
|
Returns the new value after rounding.
|
||||||
|
-->
|
||||||
|
<method name="ChangeBacklight">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="output" direction="in" type="u" />
|
||||||
|
<arg name="value" direction="in" type="i" />
|
||||||
|
<arg name="new_value" direction="out" type="i" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
GetCrtcGamma:
|
||||||
|
@serial: configuration serial
|
||||||
|
@crtc: API id of the crtc
|
||||||
|
@red: red gamma ramp
|
||||||
|
@green: green gamma ramp
|
||||||
|
@blue: blue gamma ramp
|
||||||
|
|
||||||
|
Requests the current gamma ramps of @crtc.
|
||||||
|
-->
|
||||||
|
<method name="GetCrtcGamma">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="crtc" direction="in" type="u" />
|
||||||
|
<arg name="red" direction="out" type="aq" />
|
||||||
|
<arg name="green" direction="out" type="aq" />
|
||||||
|
<arg name="blue" direction="out" type="aq" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SetCrtcGamma:
|
||||||
|
@serial: configuration serial
|
||||||
|
@crtc: API id of the crtc
|
||||||
|
@red: red gamma ramp
|
||||||
|
@green: green gamma ramp
|
||||||
|
@blue: blue gamma ramp
|
||||||
|
|
||||||
|
Changes the gamma ramps of @crtc.
|
||||||
|
-->
|
||||||
|
<method name="SetCrtcGamma">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="crtc" direction="in" type="u" />
|
||||||
|
<arg name="red" direction="in" type="aq" />
|
||||||
|
<arg name="green" direction="in" type="aq" />
|
||||||
|
<arg name="blue" direction="in" type="aq" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
PowerSaveMode:
|
||||||
|
|
||||||
|
Contains the current power saving mode for the screen, and
|
||||||
|
allows changing it.
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
- 0: on
|
||||||
|
- 1: standby
|
||||||
|
- 2: suspend
|
||||||
|
- 3: off
|
||||||
|
- -1: unknown (unsupported)
|
||||||
|
|
||||||
|
A client should not attempt to change the powersave mode
|
||||||
|
from -1 (unknown) to any other value, and viceversa.
|
||||||
|
Note that the actual effects of the different values
|
||||||
|
depend on the hardware and the kernel driver in use, and
|
||||||
|
it's perfectly possible that all values different than on
|
||||||
|
have the same effect.
|
||||||
|
Also, setting the PowerSaveMode to 3 (off) may or may
|
||||||
|
not have the same effect as disabling all outputs by
|
||||||
|
setting no CRTC on them with ApplyConfiguration(), and
|
||||||
|
may or may not cause a configuration change.
|
||||||
|
|
||||||
|
Also note that this property might become out of date
|
||||||
|
if changed through different means (for example using the
|
||||||
|
XRandR interface directly).
|
||||||
|
-->
|
||||||
|
<property name="PowerSaveMode" type="i" access="readwrite" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
MonitorsChanged:
|
||||||
|
|
||||||
|
The signal is emitted every time the screen configuration
|
||||||
|
changes.
|
||||||
|
The client should then call GetResources() to read the new layout.
|
||||||
|
-->
|
||||||
|
<signal name="MonitorsChanged" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
- "is-interlaced" (b): the mode is an interlaced 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
|
||||||
|
as 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.
|
||||||
|
* "legacy-ui-scaling-factor" (i): The legacy scaling factor traditionally
|
||||||
|
used to scale X11 clients (commonly
|
||||||
|
communicated via the
|
||||||
|
Gdk/WindowScalingFactor XSetting entry).
|
||||||
|
-->
|
||||||
|
<method name="GetCurrentState">
|
||||||
|
<arg name="serial" direction="out" type="u" />
|
||||||
|
<arg name="monitors" direction="out" type="a((ssss)a(siiddada{sv})a{sv})" />
|
||||||
|
<arg name="logical_monitors" direction="out" type="a(iiduba(ssss)a{sv})" />
|
||||||
|
<arg name="properties" direction="out" type="a{sv}" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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).
|
||||||
|
-->
|
||||||
|
<method name="ApplyMonitorsConfig">
|
||||||
|
<arg name="serial" direction="in" type="u" />
|
||||||
|
<arg name="method" direction="in" type="u" />
|
||||||
|
<arg name="logical_monitors" direction="in" type="a(iiduba(ssa{sv}))" />
|
||||||
|
<arg name="properties" direction="in" type="a{sv}" />
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
|
|
@ -7,35 +7,72 @@ import St from 'gi://St';
|
||||||
|
|
||||||
import { CursorManager } from './cursormanager.js';
|
import { CursorManager } from './cursormanager.js';
|
||||||
import Globals from './globals.js';
|
import Globals from './globals.js';
|
||||||
|
import MonitorManager from './monitormanager.js';
|
||||||
import { IPC_FILE_PATH, XREffect } from './xrEffect.js';
|
import { IPC_FILE_PATH, XREffect } from './xrEffect.js';
|
||||||
|
|
||||||
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||||
|
|
||||||
|
const SUPPORTED_MONITOR_PRODUCTS = [
|
||||||
|
'VITURE',
|
||||||
|
'Air',
|
||||||
|
'MetaMonitor' // nested mode dummy monitor
|
||||||
|
];
|
||||||
|
|
||||||
export default class BreezyDesktopExtension extends Extension {
|
export default class BreezyDesktopExtension extends Extension {
|
||||||
constructor(metadata, uuid) {
|
constructor(metadata, uuid) {
|
||||||
super(metadata, uuid);
|
super(metadata, uuid);
|
||||||
|
|
||||||
// Set/destroyed by enable/disable
|
// Set/destroyed by enable/disable
|
||||||
this._cursorManager = null;
|
this._cursor_manager = null;
|
||||||
|
this._monitor_manager = null;
|
||||||
this._xr_effect = null;
|
this._xr_effect = null;
|
||||||
this._overlay = null;
|
this._overlay = null;
|
||||||
|
this._target_monitor = null;
|
||||||
|
this._is_effect_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
if (!this._check_driver_running()) {
|
Globals.extension_dir = this.path;
|
||||||
this._running_poller_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, (() => {
|
this._monitor_manager = new MonitorManager(this.path);
|
||||||
if (this._check_driver_running()) {
|
this._monitor_manager.setChangeHook(this._monitors_changed.bind(this));
|
||||||
this._effect_enable();
|
this._monitor_manager.enable();
|
||||||
this._running_poller_id = undefined;
|
|
||||||
return GLib.SOURCE_REMOVE;
|
this._poll_for_ready();
|
||||||
} else {
|
}
|
||||||
return GLib.SOURCE_CONTINUE;
|
|
||||||
}
|
_poll_for_ready() {
|
||||||
}).bind(this));
|
this._running_poller_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, (() => {
|
||||||
} else {
|
const is_driver_running = this._check_driver_running();
|
||||||
this._effect_enable();
|
if (is_driver_running && this._target_monitor) {
|
||||||
|
console.log('Driver is running, supported monitor connected. Enabling XR effect.');
|
||||||
|
this._effect_enable();
|
||||||
|
this._running_poller_id = undefined;
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
} else {
|
||||||
|
console.log(`Not ready: driver_running ${is_driver_running}, target_monitor ${JSON.stringify(this._target_monitor)}`);
|
||||||
|
return GLib.SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
}).bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
_find_supported_monitor() {
|
||||||
|
const target_monitor_id = this._monitor_manager.getMonitorPropertiesList()
|
||||||
|
.find(monitor => SUPPORTED_MONITOR_PRODUCTS.includes(monitor.product))?.index;
|
||||||
|
if (target_monitor_id !== undefined) {
|
||||||
|
return this._monitor_manager.getMonitors()[target_monitor_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_monitors_changed() {
|
||||||
|
if (this._is_effect_running) {
|
||||||
|
console.log('Monitors changed, disabling effect');
|
||||||
|
this._effect_disable();
|
||||||
|
}
|
||||||
|
this._target_monitor = this._find_supported_monitor();
|
||||||
|
this._poll_for_ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
_check_driver_running() {
|
_check_driver_running() {
|
||||||
|
|
@ -44,14 +81,9 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
_effect_enable() {
|
_effect_enable() {
|
||||||
if (!Globals.extension_dir) Globals.extension_dir = this.metadata.path;
|
if (!this._is_effect_running) {
|
||||||
|
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup);
|
||||||
if (!this._cursorManager) this._cursorManager = new CursorManager(Main.layoutManager.uiGroup);
|
this._cursor_manager.enable();
|
||||||
this._cursorManager.enable();
|
|
||||||
|
|
||||||
if (!this._overlay) {
|
|
||||||
const monitors = Main.layoutManager.monitors;
|
|
||||||
this._target_monitor = monitors[monitors.length-1];
|
|
||||||
|
|
||||||
this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);'});
|
this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);'});
|
||||||
this._overlay.opacity = 255;
|
this._overlay.opacity = 255;
|
||||||
|
|
@ -60,6 +92,8 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
|
|
||||||
const overlayContent = new Clutter.Actor({clip_to_allocation: true});
|
const overlayContent = new Clutter.Actor({clip_to_allocation: true});
|
||||||
const uiClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup, clip_to_allocation: true });
|
const uiClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup, clip_to_allocation: true });
|
||||||
|
uiClone.x = -this._target_monitor.x;
|
||||||
|
uiClone.y = -this._target_monitor.y;
|
||||||
overlayContent.add_actor(uiClone);
|
overlayContent.add_actor(uiClone);
|
||||||
|
|
||||||
this._overlay.set_child(overlayContent);
|
this._overlay.set_child(overlayContent);
|
||||||
|
|
@ -67,29 +101,48 @@ export default class BreezyDesktopExtension extends Extension {
|
||||||
global.stage.insert_child_above(this._overlay, null);
|
global.stage.insert_child_above(this._overlay, null);
|
||||||
Shell.util_set_hidden_from_pick(this._overlay, true);
|
Shell.util_set_hidden_from_pick(this._overlay, true);
|
||||||
|
|
||||||
uiClone.x = -this._target_monitor.x;
|
|
||||||
uiClone.y = -this._target_monitor.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._xr_effect) {
|
|
||||||
this._xr_effect = new XREffect({
|
this._xr_effect = new XREffect({
|
||||||
target_monitor: this._target_monitor,
|
target_monitor: this._target_monitor,
|
||||||
target_framerate: 60
|
target_framerate: 60
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._overlay.add_effect_with_name('xr-desktop', this._xr_effect);
|
||||||
|
Meta.disable_unredirect_for_display(global.display);
|
||||||
|
|
||||||
|
this._is_effect_running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_effect_disable() {
|
||||||
|
if (this._running_poller_id) GLib.source_remove(this._running_poller_id);
|
||||||
|
|
||||||
|
Meta.enable_unredirect_for_display(global.display);
|
||||||
|
this._overlay.remove_effect_by_name('xr-desktop');
|
||||||
|
if (this._xr_effect) {
|
||||||
|
this._xr_effect.unref();
|
||||||
|
this._xr_effect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._overlay.add_effect_with_name('xr-desktop', this._xr_effect);
|
if (this._overlay) {
|
||||||
Meta.disable_unredirect_for_display(global.display);
|
global.stage.remove_child(this._overlay);
|
||||||
|
this._overlay.destroy();
|
||||||
|
this._overlay = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._cursor_manager) {
|
||||||
|
this._cursor_manager.disable();
|
||||||
|
this._cursor_manager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._is_effect_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
disable() {
|
disable() {
|
||||||
if (this._running_poller_id) {
|
this._effect_disable();
|
||||||
GLib.source_remove(this._running_poller_id);
|
this._target_monitor = null;
|
||||||
} else {
|
if (this._monitor_manager) {
|
||||||
Meta.enable_unredirect_for_display(global.display);
|
this._monitor_manager.disable();
|
||||||
this._overlay.remove_effect_by_name('xr-desktop');
|
this._monitor_manager = null;
|
||||||
this._cursorManager.disable();
|
|
||||||
this._cursorManager = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
// Taken from https://github.com/jkitching/soft-brightness-plus
|
||||||
|
//
|
||||||
|
// Copyright (C) 2019, 2021 Philippe Troin (F-i-f on Github)
|
||||||
|
// Copyright (C) 2023 Joel Kitching (jkitching on Github)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import Gio from 'gi://Gio';
|
||||||
|
|
||||||
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||||
|
|
||||||
|
let cachedDisplayConfigProxy = null;
|
||||||
|
|
||||||
|
function getDisplayConfigProxy(extPath) {
|
||||||
|
if (cachedDisplayConfigProxy == null) {
|
||||||
|
let xml = null;
|
||||||
|
const file = Gio.File.new_for_path(extPath + '/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml');
|
||||||
|
try {
|
||||||
|
const [ok, bytes] = file.load_contents(null);
|
||||||
|
if (ok) {
|
||||||
|
xml = new TextDecoder().decode(bytes);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('failed to load DisplayConfig interface XML');
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
cachedDisplayConfigProxy = Gio.DBusProxy.makeProxyWrapper(xml);
|
||||||
|
}
|
||||||
|
return cachedDisplayConfigProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newDisplayConfig(extPath, callback) {
|
||||||
|
const DisplayConfigProxy = getDisplayConfigProxy(extPath);
|
||||||
|
new DisplayConfigProxy(
|
||||||
|
Gio.DBus.session,
|
||||||
|
'org.gnome.Mutter.DisplayConfig',
|
||||||
|
'/org/gnome/Mutter/DisplayConfig',
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMonitorConfig(displayConfigProxy, callback) {
|
||||||
|
displayConfigProxy.GetResourcesRemote((result) => {
|
||||||
|
if (result.length <= 2) {
|
||||||
|
callback(null, 'Cannot get DisplayConfig: No outputs in GetResources()');
|
||||||
|
} else {
|
||||||
|
const monitors = [];
|
||||||
|
for (let i = 0; i < result[2].length; i++) {
|
||||||
|
const output = result[2][i];
|
||||||
|
if (output.length <= 7) {
|
||||||
|
callback(null, 'Cannot get DisplayConfig: No properties on output #' + i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const props = output[7];
|
||||||
|
const displayName = props['display-name'].get_string()[0];
|
||||||
|
const connectorName = output[4];
|
||||||
|
if (!displayName || displayName == '') {
|
||||||
|
const displayName = 'Monitor on output ' + connectorName;
|
||||||
|
}
|
||||||
|
const vendor = props['vendor'].get_string()[0];
|
||||||
|
const product = props['product'].get_string()[0];
|
||||||
|
const serial = props['serial'].get_string()[0];
|
||||||
|
monitors.push([displayName, connectorName, vendor, product, serial]);
|
||||||
|
}
|
||||||
|
callback(monitors, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitor change handling
|
||||||
|
export default class MonitorManager {
|
||||||
|
constructor(extPath) {
|
||||||
|
this._extPath = extPath;
|
||||||
|
|
||||||
|
this._monitorsChangedConnection = null;
|
||||||
|
this._displayConfigProxy = null;
|
||||||
|
this._backendManager = null;
|
||||||
|
this._monitorProperties = null;
|
||||||
|
this._changeHookFn = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
enable() {
|
||||||
|
this._backendManager = global.backend.get_monitor_manager();
|
||||||
|
newDisplayConfig(this._extPath, (proxy, error) => {
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._displayConfigProxy = proxy;
|
||||||
|
this._on_monitors_change();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._monitorsChangedConnection = Main.layoutManager.connect('monitors-changed', this._on_monitors_change.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
disable() {
|
||||||
|
Main.layoutManager.disconnect(this._monitorsChangedConnection);
|
||||||
|
|
||||||
|
this._monitorsChangedConnection = null;
|
||||||
|
this._displayConfigProxy = null;
|
||||||
|
this._backendManager = null;
|
||||||
|
this._monitorProperties = null;
|
||||||
|
this._changeHookFn = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setChangeHook(fn) {
|
||||||
|
this._changeHookFn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPostCallback(callback) {
|
||||||
|
this._postCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMonitors() {
|
||||||
|
return Main.layoutManager.monitors;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMonitorPropertiesList() {
|
||||||
|
return this._monitorProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
_on_monitors_change() {
|
||||||
|
if (this._displayConfigProxy == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getMonitorConfig(this._displayConfigProxy, (result, error) => {
|
||||||
|
if (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const monitorProperties = [];
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
const [monitorName, connectorName, vendor, product, serial] = result[i];
|
||||||
|
const monitorIndex = this._backendManager.get_monitor_for_connector(connectorName);
|
||||||
|
console.log(`\n\nFound monitor ${monitorName}, vendor ${vendor}, product ${product}, serial ${serial}, connector ${connectorName}, index ${monitorIndex}\n\n`);
|
||||||
|
if (monitorIndex >= 0) {
|
||||||
|
monitorProperties[monitorIndex] = {
|
||||||
|
index: monitorIndex,
|
||||||
|
name: monitorName,
|
||||||
|
vendor: vendor,
|
||||||
|
product: product,
|
||||||
|
serial: serial,
|
||||||
|
connector: connectorName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._monitorProperties = monitorProperties;
|
||||||
|
if (this._changeHookFn !== null) {
|
||||||
|
this._changeHookFn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -204,12 +204,12 @@ export const XREffect = GObject.registerClass({
|
||||||
this.setIntermittentUniformVariables = setIntermittentUniformVariables.bind(this);
|
this.setIntermittentUniformVariables = setIntermittentUniformVariables.bind(this);
|
||||||
this.setIntermittentUniformVariables();
|
this.setIntermittentUniformVariables();
|
||||||
|
|
||||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, this._frametime, () => {
|
this._redraw_timeout_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this._frametime, () => {
|
||||||
if ((now - lastPaint) > frametime) global.stage.queue_redraw();
|
if ((now - lastPaint) > frametime) global.stage.queue_redraw();
|
||||||
return GLib.SOURCE_CONTINUE;
|
return GLib.SOURCE_CONTINUE;
|
||||||
});
|
});
|
||||||
|
|
||||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, (() => {
|
this._uniforms_timeout_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, (() => {
|
||||||
this.setIntermittentUniformVariables();
|
this.setIntermittentUniformVariables();
|
||||||
return GLib.SOURCE_CONTINUE;
|
return GLib.SOURCE_CONTINUE;
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
|
|
@ -235,4 +235,9 @@ export const XREffect = GObject.registerClass({
|
||||||
}
|
}
|
||||||
this._last_paint = now;
|
this._last_paint = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vfunc_dispose() {
|
||||||
|
if (this._redraw_timeout_id) GLib.source_remove(this._redraw_timeout_id);
|
||||||
|
if (this._uniforms_timeout_id) GLib.source_remove(this._uniforms_timeout_id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Loading…
Reference in New Issue