mirror of
https://github.com/hyprwm/Hyprland
synced 2024-12-22 14:29:48 +01:00
output-management: move to new impl
This commit is contained in:
parent
d5bf15387a
commit
8a2269272b
17 changed files with 1386 additions and 139 deletions
|
@ -109,7 +109,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET
|
|||
wayland-server wayland-client wayland-cursor wayland-protocols
|
||||
cairo pango pangocairo pixman-1
|
||||
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm
|
||||
hyprwayland-scanner>=0.3.3 hyprlang>=0.3.2 hyprcursor>=0.1.7
|
||||
hyprwayland-scanner>=0.3.4 hyprlang>=0.3.2 hyprcursor>=0.1.7
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
|
@ -264,6 +264,7 @@ protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-
|
|||
protocolNew("protocols/virtual-keyboard-unstable-v1.xml" "virtual-keyboard-unstable-v1" true)
|
||||
protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer-unstable-v1" true)
|
||||
protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true)
|
||||
protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true)
|
||||
protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
|
||||
protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
|
||||
protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
|
||||
|
|
|
@ -82,11 +82,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1714589749,
|
||||
"narHash": "sha256-zzkb5vc4n/YI5KHrMie7oMZlgCKxs7zm/ybVXNL02Z0=",
|
||||
"lastModified": 1714755542,
|
||||
"narHash": "sha256-D0pg+ZRwrt4lavZ97Ca8clsgbPA3duLj8iEM7riaIFY=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "c8c2151c607a036ddfc790f5f70237ab984266aa",
|
||||
"rev": "1270ebaa539e56d61b708c24b072b09cbbd3a828",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -41,6 +41,7 @@ new_protocols = [
|
|||
['input-method-unstable-v2.xml'],
|
||||
['virtual-keyboard-unstable-v1.xml'],
|
||||
['wlr-virtual-pointer-unstable-v1.xml'],
|
||||
['wlr-output-management-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
|
||||
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
|
|
601
protocols/wlr-output-management-unstable-v1.xml
Normal file
601
protocols/wlr-output-management-unstable-v1.xml
Normal file
|
@ -0,0 +1,601 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_output_management_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2019 Purism SPC
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<description summary="protocol to configure output devices">
|
||||
This protocol exposes interfaces to obtain and modify output device
|
||||
configuration.
|
||||
|
||||
Warning! The protocol described in this file is experimental and
|
||||
backward incompatible changes may be made. Backward compatible changes
|
||||
may be added together with the corresponding interface version bump.
|
||||
Backward incompatible changes are done by bumping the version number in
|
||||
the protocol and interface names and resetting the interface version.
|
||||
Once the protocol is to be declared stable, the 'z' prefix and the
|
||||
version number in the protocol and interface names are removed and the
|
||||
interface version number is reset.
|
||||
</description>
|
||||
|
||||
<interface name="zwlr_output_manager_v1" version="4">
|
||||
<description summary="output device configuration manager">
|
||||
This interface is a manager that allows reading and writing the current
|
||||
output device configuration.
|
||||
|
||||
Output devices that display pixels (e.g. a physical monitor or a virtual
|
||||
output in a window) are represented as heads. Heads cannot be created nor
|
||||
destroyed by the client, but they can be enabled or disabled and their
|
||||
properties can be changed. Each head may have one or more available modes.
|
||||
|
||||
Whenever a head appears (e.g. a monitor is plugged in), it will be
|
||||
advertised via the head event. Immediately after the output manager is
|
||||
bound, all current heads are advertised.
|
||||
|
||||
Whenever a head's properties change, the relevant wlr_output_head events
|
||||
will be sent. Not all head properties will be sent: only properties that
|
||||
have changed need to.
|
||||
|
||||
Whenever a head disappears (e.g. a monitor is unplugged), a
|
||||
wlr_output_head.finished event will be sent.
|
||||
|
||||
After one or more heads appear, change or disappear, the done event will
|
||||
be sent. It carries a serial which can be used in a create_configuration
|
||||
request to update heads properties.
|
||||
|
||||
The information obtained from this protocol should only be used for output
|
||||
configuration purposes. This protocol is not designed to be a generic
|
||||
output property advertisement protocol for regular clients. Instead,
|
||||
protocols such as xdg-output should be used.
|
||||
</description>
|
||||
|
||||
<event name="head">
|
||||
<description summary="introduce a new head">
|
||||
This event introduces a new head. This happens whenever a new head
|
||||
appears (e.g. a monitor is plugged in) or after the output manager is
|
||||
bound.
|
||||
</description>
|
||||
<arg name="head" type="new_id" interface="zwlr_output_head_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="sent all information about current configuration">
|
||||
This event is sent after all information has been sent after binding to
|
||||
the output manager object and after any subsequent changes. This applies
|
||||
to child head and mode objects as well. In other words, this event is
|
||||
sent whenever a head or mode is created or destroyed and whenever one of
|
||||
their properties has been changed. Not all state is re-sent each time
|
||||
the current configuration changes: only the actual changes are sent.
|
||||
|
||||
This allows changes to the output configuration to be seen as atomic,
|
||||
even if they happen via multiple events.
|
||||
|
||||
A serial is sent to be used in a future create_configuration request.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="current configuration serial"/>
|
||||
</event>
|
||||
|
||||
<request name="create_configuration">
|
||||
<description summary="create a new output configuration object">
|
||||
Create a new output configuration object. This allows to update head
|
||||
properties.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_output_configuration_v1"/>
|
||||
<arg name="serial" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
Indicates the client no longer wishes to receive events for output
|
||||
configuration changes. However the compositor may emit further events,
|
||||
until the finished event is emitted.
|
||||
|
||||
The client must not send any more requests after this one.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="finished" type="destructor">
|
||||
<description summary="the compositor has finished with the manager">
|
||||
This event indicates that the compositor is done sending manager events.
|
||||
The compositor will destroy the object immediately after sending this
|
||||
event, so it will become invalid and the client should release any
|
||||
resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_head_v1" version="4">
|
||||
<description summary="output device">
|
||||
A head is an output device. The difference between a wl_output object and
|
||||
a head is that heads are advertised even if they are turned off. A head
|
||||
object only advertises properties and cannot be used directly to change
|
||||
them.
|
||||
|
||||
A head has some read-only properties: modes, name, description and
|
||||
physical_size. These cannot be changed by clients.
|
||||
|
||||
Other properties can be updated via a wlr_output_configuration object.
|
||||
|
||||
Properties sent via this interface are applied atomically via the
|
||||
wlr_output_manager.done event. No guarantees are made regarding the order
|
||||
in which properties are sent.
|
||||
</description>
|
||||
|
||||
<event name="name">
|
||||
<description summary="head name">
|
||||
This event describes the head name.
|
||||
|
||||
The naming convention is compositor defined, but limited to alphanumeric
|
||||
characters and dashes (-). Each name is unique among all wlr_output_head
|
||||
objects, but if a wlr_output_head object is destroyed the same name may
|
||||
be reused later. The names will also remain consistent across sessions
|
||||
with the same hardware and software configuration.
|
||||
|
||||
Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
|
||||
not assume that the name is a reflection of an underlying DRM
|
||||
connector, X11 connection, etc.
|
||||
|
||||
If the compositor implements the xdg-output protocol and this head is
|
||||
enabled, the xdg_output.name event must report the same name.
|
||||
|
||||
The name event is sent after a wlr_output_head object is created. This
|
||||
event is only sent once per object, and the name does not change over
|
||||
the lifetime of the wlr_output_head object.
|
||||
</description>
|
||||
<arg name="name" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="description">
|
||||
<description summary="head description">
|
||||
This event describes a human-readable description of the head.
|
||||
|
||||
The description is a UTF-8 string with no convention defined for its
|
||||
contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11
|
||||
output via :1'. However, do not assume that the name is a reflection of
|
||||
the make, model, serial of the underlying DRM connector or the display
|
||||
name of the underlying X11 connection, etc.
|
||||
|
||||
If the compositor implements xdg-output and this head is enabled,
|
||||
the xdg_output.description must report the same description.
|
||||
|
||||
The description event is sent after a wlr_output_head object is created.
|
||||
This event is only sent once per object, and the description does not
|
||||
change over the lifetime of the wlr_output_head object.
|
||||
</description>
|
||||
<arg name="description" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="physical_size">
|
||||
<description summary="head physical size">
|
||||
This event describes the physical size of the head. This event is only
|
||||
sent if the head has a physical size (e.g. is not a projector or a
|
||||
virtual device).
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width in millimeters of the output"/>
|
||||
<arg name="height" type="int" summary="height in millimeters of the output"/>
|
||||
</event>
|
||||
|
||||
<event name="mode">
|
||||
<description summary="introduce a mode">
|
||||
This event introduces a mode for this head. It is sent once per
|
||||
supported mode.
|
||||
</description>
|
||||
<arg name="mode" type="new_id" interface="zwlr_output_mode_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="enabled">
|
||||
<description summary="head is enabled or disabled">
|
||||
This event describes whether the head is enabled. A disabled head is not
|
||||
mapped to a region of the global compositor space.
|
||||
|
||||
When a head is disabled, some properties (current_mode, position,
|
||||
transform and scale) are irrelevant.
|
||||
</description>
|
||||
<arg name="enabled" type="int" summary="zero if disabled, non-zero if enabled"/>
|
||||
</event>
|
||||
|
||||
<event name="current_mode">
|
||||
<description summary="current mode">
|
||||
This event describes the mode currently in use for this head. It is only
|
||||
sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="position">
|
||||
<description summary="current position">
|
||||
This events describes the position of the head in the global compositor
|
||||
space. It is only sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="x" type="int"
|
||||
summary="x position within the global compositor space"/>
|
||||
<arg name="y" type="int"
|
||||
summary="y position within the global compositor space"/>
|
||||
</event>
|
||||
|
||||
<event name="transform">
|
||||
<description summary="current transformation">
|
||||
This event describes the transformation currently applied to the head.
|
||||
It is only sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="transform" type="int" enum="wl_output.transform"/>
|
||||
</event>
|
||||
|
||||
<event name="scale">
|
||||
<description summary="current scale">
|
||||
This events describes the scale of the head in the global compositor
|
||||
space. It is only sent if the output is enabled.
|
||||
</description>
|
||||
<arg name="scale" type="fixed"/>
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the head has disappeared">
|
||||
This event indicates that the head is no longer available. The head
|
||||
object becomes inert. Clients should send a destroy request and release
|
||||
any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<event name="make" since="2">
|
||||
<description summary="head manufacturer">
|
||||
This event describes the manufacturer of the head.
|
||||
|
||||
This must report the same make as the wl_output interface does in its
|
||||
geometry event.
|
||||
|
||||
Together with the model and serial_number events the purpose is to
|
||||
allow clients to recognize heads from previous sessions and for example
|
||||
load head-specific configurations back.
|
||||
|
||||
It is not guaranteed this event will be ever sent. A reason for that
|
||||
can be that the compositor does not have information about the make of
|
||||
the head or the definition of a make is not sensible in the current
|
||||
setup, for example in a virtual session. Clients can still try to
|
||||
identify the head by available information from other events but should
|
||||
be aware that there is an increased risk of false positives.
|
||||
|
||||
It is not recommended to display the make string in UI to users. For
|
||||
that the string provided by the description event should be preferred.
|
||||
</description>
|
||||
<arg name="make" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="model" since="2">
|
||||
<description summary="head model">
|
||||
This event describes the model of the head.
|
||||
|
||||
This must report the same model as the wl_output interface does in its
|
||||
geometry event.
|
||||
|
||||
Together with the make and serial_number events the purpose is to
|
||||
allow clients to recognize heads from previous sessions and for example
|
||||
load head-specific configurations back.
|
||||
|
||||
It is not guaranteed this event will be ever sent. A reason for that
|
||||
can be that the compositor does not have information about the model of
|
||||
the head or the definition of a model is not sensible in the current
|
||||
setup, for example in a virtual session. Clients can still try to
|
||||
identify the head by available information from other events but should
|
||||
be aware that there is an increased risk of false positives.
|
||||
|
||||
It is not recommended to display the model string in UI to users. For
|
||||
that the string provided by the description event should be preferred.
|
||||
</description>
|
||||
<arg name="model" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="serial_number" since="2">
|
||||
<description summary="head serial number">
|
||||
This event describes the serial number of the head.
|
||||
|
||||
Together with the make and model events the purpose is to allow clients
|
||||
to recognize heads from previous sessions and for example load head-
|
||||
specific configurations back.
|
||||
|
||||
It is not guaranteed this event will be ever sent. A reason for that
|
||||
can be that the compositor does not have information about the serial
|
||||
number of the head or the definition of a serial number is not sensible
|
||||
in the current setup. Clients can still try to identify the head by
|
||||
available information from other events but should be aware that there
|
||||
is an increased risk of false positives.
|
||||
|
||||
It is not recommended to display the serial_number string in UI to
|
||||
users. For that the string provided by the description event should be
|
||||
preferred.
|
||||
</description>
|
||||
<arg name="serial_number" type="string"/>
|
||||
</event>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="release" type="destructor" since="3">
|
||||
<description summary="destroy the head object">
|
||||
This request indicates that the client will no longer use this head
|
||||
object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 4 additions -->
|
||||
|
||||
<enum name="adaptive_sync_state" since="4">
|
||||
<entry name="disabled" value="0" summary="adaptive sync is disabled"/>
|
||||
<entry name="enabled" value="1" summary="adaptive sync is enabled"/>
|
||||
</enum>
|
||||
|
||||
<event name="adaptive_sync" since="4">
|
||||
<description summary="current adaptive sync state">
|
||||
This event describes whether adaptive sync is currently enabled for
|
||||
the head or not. Adaptive sync is also known as Variable Refresh
|
||||
Rate or VRR.
|
||||
</description>
|
||||
<arg name="state" type="uint" enum="adaptive_sync_state"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_mode_v1" version="3">
|
||||
<description summary="output mode">
|
||||
This object describes an output mode.
|
||||
|
||||
Some heads don't support output modes, in which case modes won't be
|
||||
advertised.
|
||||
|
||||
Properties sent via this interface are applied atomically via the
|
||||
wlr_output_manager.done event. No guarantees are made regarding the order
|
||||
in which properties are sent.
|
||||
</description>
|
||||
|
||||
<event name="size">
|
||||
<description summary="mode size">
|
||||
This event describes the mode size. The size is given in physical
|
||||
hardware units of the output device. This is not necessarily the same as
|
||||
the output size in the global compositor space. For instance, the output
|
||||
may be scaled or transformed.
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width of the mode in hardware units"/>
|
||||
<arg name="height" type="int" summary="height of the mode in hardware units"/>
|
||||
</event>
|
||||
|
||||
<event name="refresh">
|
||||
<description summary="mode refresh rate">
|
||||
This event describes the mode's fixed vertical refresh rate. It is only
|
||||
sent if the mode has a fixed refresh rate.
|
||||
</description>
|
||||
<arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
|
||||
</event>
|
||||
|
||||
<event name="preferred">
|
||||
<description summary="mode is preferred">
|
||||
This event advertises this mode as preferred.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the mode has disappeared">
|
||||
This event indicates that the mode is no longer available. The mode
|
||||
object becomes inert. Clients should send a destroy request and release
|
||||
any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="release" type="destructor" since="3">
|
||||
<description summary="destroy the mode object">
|
||||
This request indicates that the client will no longer use this mode
|
||||
object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_configuration_v1" version="4">
|
||||
<description summary="output configuration">
|
||||
This object is used by the client to describe a full output configuration.
|
||||
|
||||
First, the client needs to setup the output configuration. Each head can
|
||||
be either enabled (and configured) or disabled. It is a protocol error to
|
||||
send two enable_head or disable_head requests with the same head. It is a
|
||||
protocol error to omit a head in a configuration.
|
||||
|
||||
Then, the client can apply or test the configuration. The compositor will
|
||||
then reply with a succeeded, failed or cancelled event. Finally the client
|
||||
should destroy the configuration object.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="already_configured_head" value="1"
|
||||
summary="head has been configured twice"/>
|
||||
<entry name="unconfigured_head" value="2"
|
||||
summary="head has not been configured"/>
|
||||
<entry name="already_used" value="3"
|
||||
summary="request sent after configuration has been applied or tested"/>
|
||||
</enum>
|
||||
|
||||
<request name="enable_head">
|
||||
<description summary="enable and configure a head">
|
||||
Enable a head. This request creates a head configuration object that can
|
||||
be used to change the head's properties.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_output_configuration_head_v1"
|
||||
summary="a new object to configure the head"/>
|
||||
<arg name="head" type="object" interface="zwlr_output_head_v1"
|
||||
summary="the head to be enabled"/>
|
||||
</request>
|
||||
|
||||
<request name="disable_head">
|
||||
<description summary="disable a head">
|
||||
Disable a head.
|
||||
</description>
|
||||
<arg name="head" type="object" interface="zwlr_output_head_v1"
|
||||
summary="the head to be disabled"/>
|
||||
</request>
|
||||
|
||||
<request name="apply">
|
||||
<description summary="apply the configuration">
|
||||
Apply the new output configuration.
|
||||
|
||||
In case the configuration is successfully applied, there is no guarantee
|
||||
that the new output state matches completely the requested
|
||||
configuration. For instance, a compositor might round the scale if it
|
||||
doesn't support fractional scaling.
|
||||
|
||||
After this request has been sent, the compositor must respond with an
|
||||
succeeded, failed or cancelled event. Sending a request that isn't the
|
||||
destructor is a protocol error.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="test">
|
||||
<description summary="test the configuration">
|
||||
Test the new output configuration. The configuration won't be applied,
|
||||
but will only be validated.
|
||||
|
||||
Even if the compositor succeeds to test a configuration, applying it may
|
||||
fail.
|
||||
|
||||
After this request has been sent, the compositor must respond with an
|
||||
succeeded, failed or cancelled event. Sending a request that isn't the
|
||||
destructor is a protocol error.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="succeeded">
|
||||
<description summary="configuration changes succeeded">
|
||||
Sent after the compositor has successfully applied the changes or
|
||||
tested them.
|
||||
|
||||
Upon receiving this event, the client should destroy this object.
|
||||
|
||||
If the current configuration has changed, events to describe the changes
|
||||
will be sent followed by a wlr_output_manager.done event.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="failed">
|
||||
<description summary="configuration changes failed">
|
||||
Sent if the compositor rejects the changes or failed to apply them. The
|
||||
compositor should revert any changes made by the apply request that
|
||||
triggered this event.
|
||||
|
||||
Upon receiving this event, the client should destroy this object.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="cancelled">
|
||||
<description summary="configuration has been cancelled">
|
||||
Sent if the compositor cancels the configuration because the state of an
|
||||
output changed and the client has outdated information (e.g. after an
|
||||
output has been hotplugged).
|
||||
|
||||
The client can create a new configuration with a newer serial and try
|
||||
again.
|
||||
|
||||
Upon receiving this event, the client should destroy this object.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the output configuration">
|
||||
Using this request a client can tell the compositor that it is not going
|
||||
to use the configuration object anymore. Any changes to the outputs
|
||||
that have not been applied will be discarded.
|
||||
|
||||
This request also destroys wlr_output_configuration_head objects created
|
||||
via this object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_output_configuration_head_v1" version="4">
|
||||
<description summary="head configuration">
|
||||
This object is used by the client to update a single head's configuration.
|
||||
|
||||
It is a protocol error to set the same property twice.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="already_set" value="1" summary="property has already been set"/>
|
||||
<entry name="invalid_mode" value="2" summary="mode doesn't belong to head"/>
|
||||
<entry name="invalid_custom_mode" value="3" summary="mode is invalid"/>
|
||||
<entry name="invalid_transform" value="4" summary="transform value outside enum"/>
|
||||
<entry name="invalid_scale" value="5" summary="scale negative or zero"/>
|
||||
<entry name="invalid_adaptive_sync_state" value="6" since="4"
|
||||
summary="invalid enum value used in the set_adaptive_sync request"/>
|
||||
</enum>
|
||||
|
||||
<request name="set_mode">
|
||||
<description summary="set the mode">
|
||||
This request sets the head's mode.
|
||||
</description>
|
||||
<arg name="mode" type="object" interface="zwlr_output_mode_v1"/>
|
||||
</request>
|
||||
|
||||
<request name="set_custom_mode">
|
||||
<description summary="set a custom mode">
|
||||
This request assigns a custom mode to the head. The size is given in
|
||||
physical hardware units of the output device. If set to zero, the
|
||||
refresh rate is unspecified.
|
||||
|
||||
It is a protocol error to set both a mode and a custom mode.
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width of the mode in hardware units"/>
|
||||
<arg name="height" type="int" summary="height of the mode in hardware units"/>
|
||||
<arg name="refresh" type="int" summary="vertical refresh rate in mHz or zero"/>
|
||||
</request>
|
||||
|
||||
<request name="set_position">
|
||||
<description summary="set the position">
|
||||
This request sets the head's position in the global compositor space.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="x position in the global compositor space"/>
|
||||
<arg name="y" type="int" summary="y position in the global compositor space"/>
|
||||
</request>
|
||||
|
||||
<request name="set_transform">
|
||||
<description summary="set the transform">
|
||||
This request sets the head's transform.
|
||||
</description>
|
||||
<arg name="transform" type="int" enum="wl_output.transform"/>
|
||||
</request>
|
||||
|
||||
<request name="set_scale">
|
||||
<description summary="set the scale">
|
||||
This request sets the head's scale.
|
||||
</description>
|
||||
<arg name="scale" type="fixed"/>
|
||||
</request>
|
||||
|
||||
<!-- Version 4 additions -->
|
||||
|
||||
<request name="set_adaptive_sync" since="4">
|
||||
<description summary="enable/disable adaptive sync">
|
||||
This request enables/disables adaptive sync. Adaptive sync is also
|
||||
known as Variable Refresh Rate or VRR.
|
||||
</description>
|
||||
<arg name="state" type="uint" enum="zwlr_output_head_v1.adaptive_sync_state"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
|
@ -243,8 +243,6 @@ void CCompositor::initServer() {
|
|||
m_sWLRServerDecoMgr = wlr_server_decoration_manager_create(m_sWLDisplay);
|
||||
wlr_server_decoration_manager_set_default_mode(m_sWLRServerDecoMgr, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
|
||||
|
||||
m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
|
||||
|
||||
m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend);
|
||||
if (!m_sWRLDRMLeaseMgr) {
|
||||
Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager");
|
||||
|
@ -300,9 +298,6 @@ void CCompositor::initAllSignals() {
|
|||
addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat");
|
||||
addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat");
|
||||
addWLSignal(&m_sWLRLayerShell->events.new_surface, &Events::listen_newLayerSurface, m_sWLRLayerShell, "LayerShell");
|
||||
addWLSignal(&m_sWLROutputLayout->events.change, &Events::listen_change, m_sWLROutputLayout, "OutputLayout");
|
||||
addWLSignal(&m_sWLROutputMgr->events.apply, &Events::listen_outputMgrApply, m_sWLROutputMgr, "OutputMgr");
|
||||
addWLSignal(&m_sWLROutputMgr->events.test, &Events::listen_outputMgrTest, m_sWLROutputMgr, "OutputMgr");
|
||||
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
|
||||
|
||||
if (m_sWRLDRMLeaseMgr)
|
||||
|
@ -340,9 +335,6 @@ void CCompositor::removeAllSignals() {
|
|||
removeWLSignal(&Events::listen_requestSetSel);
|
||||
removeWLSignal(&Events::listen_requestSetPrimarySel);
|
||||
removeWLSignal(&Events::listen_newLayerSurface);
|
||||
removeWLSignal(&Events::listen_change);
|
||||
removeWLSignal(&Events::listen_outputMgrApply);
|
||||
removeWLSignal(&Events::listen_outputMgrTest);
|
||||
removeWLSignal(&Events::listen_RendererDestroy);
|
||||
|
||||
if (m_sWRLDRMLeaseMgr)
|
||||
|
|
|
@ -55,7 +55,6 @@ class CCompositor {
|
|||
wlr_layer_shell_v1* m_sWLRLayerShell;
|
||||
wlr_xdg_shell* m_sWLRXDGShell;
|
||||
wlr_cursor* m_sWLRCursor;
|
||||
wlr_output_manager_v1* m_sWLROutputMgr;
|
||||
wlr_presentation* m_sWLRPresentation;
|
||||
wlr_egl* m_sWLREGL;
|
||||
int m_iDRMFD;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <ranges>
|
||||
|
||||
extern "C" char** environ;
|
||||
|
||||
|
@ -954,7 +955,7 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s
|
|||
}
|
||||
|
||||
SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) {
|
||||
for (auto& r : m_dMonitorRules) {
|
||||
for (auto& r : m_dMonitorRules | std::views::reverse) {
|
||||
if (PMONITOR.matchesStaticSelector(r.name)) {
|
||||
return r;
|
||||
}
|
||||
|
@ -1236,6 +1237,10 @@ void CConfigManager::dispatchExecOnce() {
|
|||
g_pCompositor->performUserChecks();
|
||||
}
|
||||
|
||||
void CConfigManager::appendMonitorRule(const SMonitorRule& r) {
|
||||
m_dMonitorRules.emplace_back(r);
|
||||
}
|
||||
|
||||
void CConfigManager::performMonitorReload() {
|
||||
|
||||
bool overAgain = false;
|
||||
|
|
|
@ -127,6 +127,7 @@ class CConfigManager {
|
|||
void dispatchExecOnce();
|
||||
|
||||
void performMonitorReload();
|
||||
void appendMonitorRule(const SMonitorRule&);
|
||||
bool m_bWantsMonitorReload = false;
|
||||
bool m_bForceReload = false;
|
||||
bool m_bNoMonitorReload = false;
|
||||
|
|
|
@ -65,10 +65,6 @@ namespace Events {
|
|||
LISTENER(requestSetSel);
|
||||
LISTENER(requestSetPrimarySel);
|
||||
|
||||
// outputMgr
|
||||
LISTENER(outputMgrApply);
|
||||
LISTENER(outputMgrTest);
|
||||
|
||||
// Monitor part 2 the sequel
|
||||
DYNLISTENFUNC(monitorFrame);
|
||||
DYNLISTENFUNC(monitorDestroy);
|
||||
|
|
|
@ -16,16 +16,6 @@
|
|||
// //
|
||||
// ------------------------------ //
|
||||
|
||||
void Events::listener_outputMgrApply(wl_listener* listener, void* data) {
|
||||
const auto CONFIG = (wlr_output_configuration_v1*)data;
|
||||
g_pHyprRenderer->outputMgrApplyTest(CONFIG, false);
|
||||
}
|
||||
|
||||
void Events::listener_outputMgrTest(wl_listener* listener, void* data) {
|
||||
const auto CONFIG = (wlr_output_configuration_v1*)data;
|
||||
g_pHyprRenderer->outputMgrApplyTest(CONFIG, true);
|
||||
}
|
||||
|
||||
void Events::listener_leaseRequest(wl_listener* listener, void* data) {
|
||||
const auto REQUEST = (wlr_drm_lease_request_v1*)data;
|
||||
struct wlr_drm_lease_v1* lease = wlr_drm_lease_request_v1_grant(REQUEST);
|
||||
|
|
|
@ -16,50 +16,6 @@
|
|||
// //
|
||||
// --------------------------------------------------------- //
|
||||
|
||||
void Events::listener_change(wl_listener* listener, void* data) {
|
||||
// layout got changed, let's update monitors.
|
||||
const auto CONFIG = wlr_output_configuration_v1_create();
|
||||
|
||||
if (!CONFIG)
|
||||
return;
|
||||
|
||||
for (auto& m : g_pCompositor->m_vRealMonitors) {
|
||||
if (!m->output)
|
||||
continue;
|
||||
|
||||
if (g_pCompositor->m_pUnsafeOutput == m.get())
|
||||
continue;
|
||||
|
||||
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
|
||||
|
||||
CBox BOX;
|
||||
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, BOX.pWlr());
|
||||
BOX.applyFromWlr();
|
||||
|
||||
//m->vecSize.x = BOX.width;
|
||||
// m->vecSize.y = BOX.height;
|
||||
m->vecPosition.x = BOX.x;
|
||||
m->vecPosition.y = BOX.y;
|
||||
|
||||
CONFIGHEAD->state.enabled = m->output->enabled;
|
||||
CONFIGHEAD->state.mode = m->output->current_mode;
|
||||
if (!m->output->current_mode) {
|
||||
CONFIGHEAD->state.custom_mode = {
|
||||
m->output->width,
|
||||
m->output->height,
|
||||
m->output->refresh,
|
||||
};
|
||||
}
|
||||
CONFIGHEAD->state.x = m->vecPosition.x;
|
||||
CONFIGHEAD->state.y = m->vecPosition.y;
|
||||
CONFIGHEAD->state.transform = m->transform;
|
||||
CONFIGHEAD->state.scale = m->scale;
|
||||
CONFIGHEAD->state.adaptive_sync_enabled = m->vrrActive;
|
||||
}
|
||||
|
||||
wlr_output_manager_v1_set_configuration(g_pCompositor->m_sWLROutputMgr, CONFIG);
|
||||
}
|
||||
|
||||
static void checkDefaultCursorWarp(std::shared_ptr<CMonitor>* PNEWMONITORWRAP, std::string monitorName) {
|
||||
const auto PNEWMONITOR = PNEWMONITORWRAP->get();
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ class CMonitor {
|
|||
float xwaylandScale = 1.f;
|
||||
std::array<float, 9> projMatrix = {0};
|
||||
std::optional<Vector2D> forceSize;
|
||||
wlr_output_mode* currentMode = nullptr;
|
||||
|
||||
bool dpmsStatus = true;
|
||||
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "../protocols/InputMethodV2.hpp"
|
||||
#include "../protocols/VirtualKeyboard.hpp"
|
||||
#include "../protocols/VirtualPointer.hpp"
|
||||
#include "../protocols/OutputManagement.hpp"
|
||||
|
||||
CProtocolManager::CProtocolManager() {
|
||||
|
||||
|
@ -47,6 +48,7 @@ CProtocolManager::CProtocolManager() {
|
|||
PROTO::ime = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
|
||||
PROTO::virtualKeyboard = std::make_unique<CVirtualKeyboardProtocol>(&zwp_virtual_keyboard_manager_v1_interface, 1, "VirtualKeyboard");
|
||||
PROTO::virtualPointer = std::make_unique<CVirtualPointerProtocol>(&zwlr_virtual_pointer_manager_v1_interface, 2, "VirtualPointer");
|
||||
PROTO::outputManagement = std::make_unique<COutputManagementProtocol>(&zwlr_output_manager_v1_interface, 4, "OutputManagement");
|
||||
|
||||
// Old protocol implementations.
|
||||
// TODO: rewrite them to use hyprwayland-scanner.
|
||||
|
|
597
src/protocols/OutputManagement.cpp
Normal file
597
src/protocols/OutputManagement.cpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
#include "OutputManagement.hpp"
|
||||
#include <algorithm>
|
||||
#include "../Compositor.hpp"
|
||||
|
||||
#define LOGM PROTO::outputManagement->protoLog
|
||||
|
||||
COutputManager::COutputManager(SP<CZwlrOutputManagerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
LOGM(LOG, "New OutputManager registered");
|
||||
|
||||
resource->setOnDestroy([this](CZwlrOutputManagerV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
|
||||
resource->setStop([this](CZwlrOutputManagerV1* r) { stopped = true; });
|
||||
|
||||
resource->setCreateConfiguration([this](CZwlrOutputManagerV1* r, uint32_t id, uint32_t serial) {
|
||||
LOGM(LOG, "Creating new configuration");
|
||||
|
||||
const auto RESOURCE = PROTO::outputManagement->m_vConfigurations.emplace_back(
|
||||
std::make_shared<COutputConfiguration>(std::make_shared<CZwlrOutputConfigurationV1>(resource->client(), resource->version(), id), self.lock()));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
resource->noMemory();
|
||||
PROTO::outputManagement->m_vConfigurations.pop_back();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// send all heads at start
|
||||
for (auto& m : g_pCompositor->m_vRealMonitors) {
|
||||
if (m.get() == g_pCompositor->m_pUnsafeOutput)
|
||||
continue;
|
||||
|
||||
LOGM(LOG, " | sending output head for {}", m->szName);
|
||||
|
||||
makeAndSendNewHead(m.get());
|
||||
}
|
||||
|
||||
sendDone();
|
||||
}
|
||||
|
||||
bool COutputManager::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
void COutputManager::makeAndSendNewHead(CMonitor* pMonitor) {
|
||||
if (stopped)
|
||||
return;
|
||||
|
||||
const auto RESOURCE =
|
||||
PROTO::outputManagement->m_vHeads.emplace_back(std::make_shared<COutputHead>(std::make_shared<CZwlrOutputHeadV1>(resource->client(), resource->version(), 0), pMonitor));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
resource->noMemory();
|
||||
PROTO::outputManagement->m_vHeads.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
heads.push_back(RESOURCE);
|
||||
|
||||
resource->sendHead(RESOURCE->resource.get());
|
||||
RESOURCE->sendAllData();
|
||||
}
|
||||
|
||||
void COutputManager::ensureMonitorSent(CMonitor* pMonitor) {
|
||||
if (pMonitor == g_pCompositor->m_pUnsafeOutput)
|
||||
return;
|
||||
|
||||
for (auto& hw : heads) {
|
||||
auto h = hw.lock();
|
||||
|
||||
if (!h)
|
||||
continue;
|
||||
|
||||
if (h->pMonitor == pMonitor)
|
||||
return;
|
||||
}
|
||||
|
||||
makeAndSendNewHead(pMonitor);
|
||||
|
||||
sendDone();
|
||||
}
|
||||
|
||||
void COutputManager::sendDone() {
|
||||
resource->sendDone(wl_display_next_serial(g_pCompositor->m_sWLDisplay));
|
||||
}
|
||||
|
||||
COutputHead::COutputHead(SP<CZwlrOutputHeadV1> resource_, CMonitor* pMonitor_) : resource(resource_), pMonitor(pMonitor_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setRelease([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
|
||||
listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any d) {
|
||||
resource->sendFinished();
|
||||
|
||||
for (auto& mw : modes) {
|
||||
auto m = mw.lock();
|
||||
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
m->resource->sendFinished();
|
||||
}
|
||||
|
||||
pMonitor = nullptr;
|
||||
});
|
||||
|
||||
listeners.monitorModeChange = pMonitor->events.modeChanged.registerListener([this](std::any d) { updateMode(); });
|
||||
}
|
||||
|
||||
bool COutputHead::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
void COutputHead::sendAllData() {
|
||||
const auto VERSION = resource->version();
|
||||
|
||||
resource->sendName(pMonitor->szName.c_str());
|
||||
resource->sendDescription(pMonitor->szDescription.c_str());
|
||||
if (pMonitor->output->phys_width > 0 && pMonitor->output->phys_height > 0)
|
||||
resource->sendPhysicalSize(pMonitor->output->phys_width, pMonitor->output->phys_height);
|
||||
resource->sendEnabled(pMonitor->m_bEnabled);
|
||||
|
||||
if (pMonitor->m_bEnabled) {
|
||||
resource->sendPosition(pMonitor->vecPosition.x, pMonitor->vecPosition.y);
|
||||
resource->sendTransform(pMonitor->transform);
|
||||
resource->sendScale(wl_fixed_from_double(pMonitor->scale));
|
||||
}
|
||||
|
||||
if (pMonitor->output->make && VERSION >= 2)
|
||||
resource->sendMake(pMonitor->output->make);
|
||||
if (pMonitor->output->model && VERSION >= 2)
|
||||
resource->sendModel(pMonitor->output->model);
|
||||
if (pMonitor->output->serial && VERSION >= 2)
|
||||
resource->sendSerialNumber(pMonitor->output->serial);
|
||||
|
||||
if (VERSION >= 4)
|
||||
resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
|
||||
|
||||
// send all available modes
|
||||
|
||||
if (modes.empty()) {
|
||||
if (!wl_list_empty(&pMonitor->output->modes)) {
|
||||
wlr_output_mode* mode;
|
||||
|
||||
wl_list_for_each(mode, &pMonitor->output->modes, link) {
|
||||
makeAndSendNewMode(mode);
|
||||
}
|
||||
} else
|
||||
makeAndSendNewMode(nullptr);
|
||||
}
|
||||
|
||||
// send current mode
|
||||
if (pMonitor->m_bEnabled) {
|
||||
for (auto& mw : modes) {
|
||||
auto m = mw.lock();
|
||||
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
if (m->mode == pMonitor->currentMode) {
|
||||
if (m->mode)
|
||||
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh);
|
||||
else
|
||||
LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName);
|
||||
resource->sendCurrentMode(m->resource->resource());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void COutputHead::updateMode() {
|
||||
resource->sendEnabled(pMonitor->m_bEnabled);
|
||||
|
||||
if (pMonitor->m_bEnabled) {
|
||||
resource->sendPosition(pMonitor->vecPosition.x, pMonitor->vecPosition.y);
|
||||
resource->sendTransform(pMonitor->transform);
|
||||
resource->sendScale(wl_fixed_from_double(pMonitor->scale));
|
||||
}
|
||||
|
||||
if (resource->version() >= 4)
|
||||
resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
|
||||
|
||||
if (pMonitor->m_bEnabled) {
|
||||
for (auto& mw : modes) {
|
||||
auto m = mw.lock();
|
||||
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
if (m->mode == pMonitor->currentMode) {
|
||||
if (m->mode)
|
||||
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh);
|
||||
else
|
||||
LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName);
|
||||
resource->sendCurrentMode(m->resource->resource());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void COutputHead::makeAndSendNewMode(wlr_output_mode* mode) {
|
||||
const auto RESOURCE =
|
||||
PROTO::outputManagement->m_vModes.emplace_back(std::make_shared<COutputMode>(std::make_shared<CZwlrOutputModeV1>(resource->client(), resource->version(), 0), mode));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
resource->noMemory();
|
||||
PROTO::outputManagement->m_vModes.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
modes.push_back(RESOURCE);
|
||||
resource->sendMode(RESOURCE->resource.get());
|
||||
RESOURCE->sendAllData();
|
||||
}
|
||||
|
||||
CMonitor* COutputHead::monitor() {
|
||||
return pMonitor;
|
||||
}
|
||||
|
||||
COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_) : resource(resource_), mode(mode_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setRelease([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
}
|
||||
|
||||
void COutputMode::sendAllData() {
|
||||
if (!mode)
|
||||
return;
|
||||
|
||||
LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->width, mode->height, mode->refresh, mode->preferred);
|
||||
|
||||
resource->sendSize(mode->width, mode->height);
|
||||
if (mode->refresh > 0)
|
||||
resource->sendRefresh(mode->refresh);
|
||||
if (mode->preferred)
|
||||
resource->sendPreferred();
|
||||
}
|
||||
|
||||
bool COutputMode::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
wlr_output_mode* COutputMode::getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
COutputConfiguration::COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_) : resource(resource_), owner(owner_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
|
||||
resource->setEnableHead([this](CZwlrOutputConfigurationV1* r, uint32_t id, wl_resource* outputHead) {
|
||||
const auto HEAD = PROTO::outputManagement->headFromResource(outputHead);
|
||||
|
||||
if (!HEAD) {
|
||||
LOGM(ERR, "No head in setEnableHead??");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PMONITOR = HEAD->monitor();
|
||||
|
||||
if (!PMONITOR) {
|
||||
LOGM(ERR, "No monitor in setEnableHead??");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto RESOURCE = PROTO::outputManagement->m_vConfigurationHeads.emplace_back(
|
||||
std::make_shared<COutputConfigurationHead>(std::make_shared<CZwlrOutputConfigurationHeadV1>(resource->client(), resource->version(), id), PMONITOR));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
resource->noMemory();
|
||||
PROTO::outputManagement->m_vConfigurationHeads.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
heads.push_back(RESOURCE);
|
||||
|
||||
LOGM(LOG, "enableHead on {}. For now, doing nothing. Waiting for apply().", PMONITOR->szName);
|
||||
});
|
||||
|
||||
resource->setDisableHead([this](CZwlrOutputConfigurationV1* r, wl_resource* outputHead) {
|
||||
const auto HEAD = PROTO::outputManagement->headFromResource(outputHead);
|
||||
|
||||
if (!HEAD) {
|
||||
LOGM(ERR, "No head in setDisableHead??");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PMONITOR = HEAD->monitor();
|
||||
|
||||
if (!PMONITOR) {
|
||||
LOGM(ERR, "No monitor in setDisableHead??");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGM(LOG, "disableHead on {}", PMONITOR->szName);
|
||||
|
||||
PMONITOR->activeMonitorRule.disabled = true;
|
||||
g_pHyprRenderer->applyMonitorRule(PMONITOR, &PMONITOR->activeMonitorRule, false);
|
||||
});
|
||||
|
||||
resource->setTest([this](CZwlrOutputConfigurationV1* r) {
|
||||
const auto SUCCESS = applyTestConfiguration(true);
|
||||
|
||||
if (SUCCESS)
|
||||
resource->sendSucceeded();
|
||||
else
|
||||
resource->sendFailed();
|
||||
});
|
||||
|
||||
resource->setApply([this](CZwlrOutputConfigurationV1* r) {
|
||||
const auto SUCCESS = applyTestConfiguration(false);
|
||||
|
||||
if (SUCCESS)
|
||||
resource->sendSucceeded();
|
||||
else
|
||||
resource->sendFailed();
|
||||
|
||||
owner.lock()->sendDone();
|
||||
});
|
||||
}
|
||||
|
||||
bool COutputConfiguration::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
bool COutputConfiguration::applyTestConfiguration(bool test) {
|
||||
if (test) {
|
||||
LOGM(WARN, "TODO: STUB: applyTestConfiguration for test not implemented, returning true.");
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGM(LOG, "Applying configuration");
|
||||
|
||||
for (auto& headw : heads) {
|
||||
auto head = headw.lock();
|
||||
|
||||
if (!head)
|
||||
continue;
|
||||
|
||||
const auto PMONITOR = head->pMonitor;
|
||||
|
||||
if (!PMONITOR)
|
||||
continue;
|
||||
|
||||
LOGM(LOG, "Applying config for monitor {}", PMONITOR->szName);
|
||||
|
||||
SMonitorRule newRule = PMONITOR->activeMonitorRule;
|
||||
newRule.name = PMONITOR->szName;
|
||||
|
||||
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) {
|
||||
newRule.resolution = {head->state.mode.lock()->getMode()->width, head->state.mode.lock()->getMode()->height};
|
||||
newRule.refreshRate = head->state.mode.lock()->getMode()->refresh / 1000.F;
|
||||
} else if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) {
|
||||
newRule.resolution = head->state.customMode.size;
|
||||
newRule.refreshRate = head->state.customMode.refresh / 1000.F;
|
||||
}
|
||||
|
||||
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION)
|
||||
newRule.offset = head->state.position;
|
||||
|
||||
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC)
|
||||
newRule.vrr = head->state.adaptiveSync;
|
||||
|
||||
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE)
|
||||
newRule.scale = head->state.scale;
|
||||
|
||||
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM)
|
||||
newRule.transform = head->state.transform;
|
||||
|
||||
// reset properties for next set.
|
||||
head->committedProperties = 0;
|
||||
|
||||
g_pConfigManager->appendMonitorRule(newRule);
|
||||
g_pConfigManager->m_bWantsMonitorReload = true;
|
||||
}
|
||||
|
||||
LOGM(LOG, "Applied configuration");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
COutputConfigurationHead::COutputConfigurationHead(SP<CZwlrOutputConfigurationHeadV1> resource_, CMonitor* pMonitor_) : resource(resource_), pMonitor(pMonitor_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CZwlrOutputConfigurationHeadV1* r) { PROTO::outputManagement->destroyResource(this); });
|
||||
|
||||
listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any d) { pMonitor = nullptr; });
|
||||
|
||||
resource->setSetMode([this](CZwlrOutputConfigurationHeadV1* r, wl_resource* outputMode) {
|
||||
const auto MODE = PROTO::outputManagement->modeFromResource(outputMode);
|
||||
|
||||
if (!MODE || !MODE->getMode()) {
|
||||
LOGM(ERR, "No mode in setMode??");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "setMode on inert resource");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committedProperties & OUTPUT_HEAD_COMMITTED_MODE) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
|
||||
return;
|
||||
}
|
||||
|
||||
committedProperties |= OUTPUT_HEAD_COMMITTED_MODE;
|
||||
state.mode = MODE;
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->width, MODE->getMode()->height, MODE->getMode()->refresh);
|
||||
});
|
||||
|
||||
resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) {
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "setCustomMode on inert resource");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (w <= 0 || h <= 0 || refresh <= 100) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE, "Invalid mode");
|
||||
return;
|
||||
}
|
||||
|
||||
committedProperties |= OUTPUT_HEAD_COMMITTED_CUSTOM_MODE;
|
||||
state.customMode = {{w, h}, (uint32_t)refresh};
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set custom mode to {}x{}@{}", pMonitor->szName, w, h, refresh);
|
||||
});
|
||||
|
||||
resource->setSetPosition([this](CZwlrOutputConfigurationHeadV1* r, int32_t x, int32_t y) {
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "setMode on inert resource");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
|
||||
return;
|
||||
}
|
||||
|
||||
committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION;
|
||||
state.position = {x, y};
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set pos to {}, {}", pMonitor->szName, x, y);
|
||||
});
|
||||
|
||||
resource->setSetTransform([this](CZwlrOutputConfigurationHeadV1* r, int32_t transform) {
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "setMode on inert resource");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (transform < 0 || transform > 7) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "Invalid transform");
|
||||
return;
|
||||
}
|
||||
|
||||
committedProperties |= OUTPUT_HEAD_COMMITTED_TRANSFORM;
|
||||
state.transform = (wl_output_transform)transform;
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set transform to {}", pMonitor->szName, transform);
|
||||
});
|
||||
|
||||
resource->setSetScale([this](CZwlrOutputConfigurationHeadV1* r, wl_fixed_t scale_) {
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "setMode on inert resource");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
|
||||
return;
|
||||
}
|
||||
|
||||
double scale = wl_fixed_to_double(scale_);
|
||||
|
||||
if (scale < 0.1 || scale > 10.0) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE, "Invalid scale");
|
||||
return;
|
||||
}
|
||||
|
||||
committedProperties |= OUTPUT_HEAD_COMMITTED_SCALE;
|
||||
state.scale = scale;
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set scale to {:.2f}", pMonitor->szName, scale);
|
||||
});
|
||||
|
||||
resource->setSetAdaptiveSync([this](CZwlrOutputConfigurationHeadV1* r, uint32_t as) {
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "setMode on inert resource");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (as > 1) {
|
||||
resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, "Invalid adaptive sync state");
|
||||
return;
|
||||
}
|
||||
|
||||
committedProperties |= OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC;
|
||||
state.adaptiveSync = as;
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set adaptiveSync to {}", pMonitor->szName, as);
|
||||
});
|
||||
}
|
||||
|
||||
bool COutputConfigurationHead::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
COutputManagementProtocol::COutputManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); });
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(std::make_shared<COutputManager>(std::make_shared<CZwlrOutputManagerV1>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::destroyResource(COutputManager* resource) {
|
||||
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::destroyResource(COutputHead* resource) {
|
||||
std::erase_if(m_vHeads, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::destroyResource(COutputMode* resource) {
|
||||
std::erase_if(m_vModes, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::destroyResource(COutputConfiguration* resource) {
|
||||
std::erase_if(m_vConfigurations, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::destroyResource(COutputConfigurationHead* resource) {
|
||||
std::erase_if(m_vConfigurationHeads, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void COutputManagementProtocol::updateAllOutputs() {
|
||||
for (auto& m : g_pCompositor->m_vRealMonitors) {
|
||||
for (auto& mgr : m_vManagers) {
|
||||
mgr->ensureMonitorSent(m.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SP<COutputHead> COutputManagementProtocol::headFromResource(wl_resource* r) {
|
||||
for (auto& h : m_vHeads) {
|
||||
if (h->resource->resource() == r)
|
||||
return h;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SP<COutputMode> COutputManagementProtocol::modeFromResource(wl_resource* r) {
|
||||
for (auto& h : m_vModes) {
|
||||
if (h->resource->resource() == r)
|
||||
return h;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
166
src/protocols/OutputManagement.hpp
Normal file
166
src/protocols/OutputManagement.hpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "wlr-output-management-unstable-v1.hpp"
|
||||
#include "../helpers/signal/Listener.hpp"
|
||||
|
||||
class CMonitor;
|
||||
|
||||
class COutputHead;
|
||||
class COutputMode;
|
||||
|
||||
class COutputManager {
|
||||
public:
|
||||
COutputManager(SP<CZwlrOutputManagerV1> resource_);
|
||||
|
||||
bool good();
|
||||
void ensureMonitorSent(CMonitor* pMonitor);
|
||||
void sendDone();
|
||||
|
||||
private:
|
||||
SP<CZwlrOutputManagerV1> resource;
|
||||
bool stopped = false;
|
||||
|
||||
WP<COutputManager> self;
|
||||
|
||||
std::vector<WP<COutputHead>> heads;
|
||||
|
||||
void makeAndSendNewHead(CMonitor* pMonitor);
|
||||
friend class COutputManagementProtocol;
|
||||
};
|
||||
|
||||
class COutputMode {
|
||||
public:
|
||||
COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_);
|
||||
|
||||
bool good();
|
||||
wlr_output_mode* getMode();
|
||||
void sendAllData();
|
||||
|
||||
private:
|
||||
SP<CZwlrOutputModeV1> resource;
|
||||
wlr_output_mode* mode = nullptr;
|
||||
|
||||
friend class COutputHead;
|
||||
friend class COutputManagementProtocol;
|
||||
};
|
||||
|
||||
class COutputHead {
|
||||
public:
|
||||
COutputHead(SP<CZwlrOutputHeadV1> resource_, CMonitor* pMonitor_);
|
||||
|
||||
bool good();
|
||||
void sendAllData(); // this has to be separate as we need to send the head first, then set the data
|
||||
void updateMode();
|
||||
CMonitor* monitor();
|
||||
|
||||
private:
|
||||
SP<CZwlrOutputHeadV1> resource;
|
||||
CMonitor* pMonitor = nullptr;
|
||||
|
||||
void makeAndSendNewMode(wlr_output_mode* mode);
|
||||
void sendCurrentMode();
|
||||
|
||||
std::vector<WP<COutputMode>> modes;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener monitorDestroy;
|
||||
CHyprSignalListener monitorModeChange;
|
||||
} listeners;
|
||||
|
||||
friend class COutputManager;
|
||||
friend class COutputManagementProtocol;
|
||||
};
|
||||
|
||||
class COutputConfigurationHead {
|
||||
public:
|
||||
COutputConfigurationHead(SP<CZwlrOutputConfigurationHeadV1> resource_, CMonitor* pMonitor_);
|
||||
|
||||
bool good();
|
||||
|
||||
enum eCommittedProperties : uint32_t {
|
||||
OUTPUT_HEAD_COMMITTED_MODE = (1 << 0),
|
||||
OUTPUT_HEAD_COMMITTED_CUSTOM_MODE = (1 << 1),
|
||||
OUTPUT_HEAD_COMMITTED_POSITION = (1 << 2),
|
||||
OUTPUT_HEAD_COMMITTED_TRANSFORM = (1 << 3),
|
||||
OUTPUT_HEAD_COMMITTED_SCALE = (1 << 4),
|
||||
OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC = (1 << 5),
|
||||
};
|
||||
|
||||
uint32_t committedProperties = 0;
|
||||
|
||||
struct {
|
||||
WP<COutputMode> mode;
|
||||
struct {
|
||||
Vector2D size;
|
||||
uint32_t refresh = 0;
|
||||
} customMode;
|
||||
Vector2D position;
|
||||
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
float scale = 1.F;
|
||||
bool adaptiveSync = false;
|
||||
} state;
|
||||
|
||||
private:
|
||||
SP<CZwlrOutputConfigurationHeadV1> resource;
|
||||
CMonitor* pMonitor = nullptr;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener monitorDestroy;
|
||||
} listeners;
|
||||
|
||||
friend class COutputConfiguration;
|
||||
};
|
||||
|
||||
class COutputConfiguration {
|
||||
public:
|
||||
COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CZwlrOutputConfigurationV1> resource;
|
||||
std::vector<WP<COutputConfigurationHead>> heads;
|
||||
WP<COutputManager> owner;
|
||||
|
||||
bool applyTestConfiguration(bool test);
|
||||
};
|
||||
|
||||
class COutputManagementProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
COutputManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
private:
|
||||
void destroyResource(COutputManager* resource);
|
||||
void destroyResource(COutputHead* resource);
|
||||
void destroyResource(COutputMode* resource);
|
||||
void destroyResource(COutputConfiguration* resource);
|
||||
void destroyResource(COutputConfigurationHead* resource);
|
||||
|
||||
void updateAllOutputs();
|
||||
|
||||
//
|
||||
std::vector<SP<COutputManager>> m_vManagers;
|
||||
std::vector<SP<COutputHead>> m_vHeads;
|
||||
std::vector<SP<COutputMode>> m_vModes;
|
||||
std::vector<SP<COutputConfiguration>> m_vConfigurations;
|
||||
std::vector<SP<COutputConfigurationHead>> m_vConfigurationHeads;
|
||||
|
||||
SP<COutputHead> headFromResource(wl_resource* r);
|
||||
SP<COutputMode> modeFromResource(wl_resource* r);
|
||||
|
||||
friend class COutputManager;
|
||||
friend class COutputHead;
|
||||
friend class COutputMode;
|
||||
friend class COutputConfiguration;
|
||||
friend class COutputConfigurationHead;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<COutputManagementProtocol> outputManagement;
|
||||
};
|
|
@ -1474,70 +1474,6 @@ void CHyprRenderer::setWindowScanoutMode(PHLWINDOW pWindow) {
|
|||
Debug::log(LOG, "Scanout mode ON set for {}", pWindow);
|
||||
}
|
||||
|
||||
void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool test) {
|
||||
wlr_output_configuration_head_v1* head;
|
||||
bool ok = true;
|
||||
|
||||
wl_list_for_each(head, &config->heads, link) {
|
||||
|
||||
std::string commandForCfg = "";
|
||||
const auto OUTPUT = head->state.output;
|
||||
|
||||
commandForCfg += std::string(OUTPUT->name) + ",";
|
||||
|
||||
if (!head->state.enabled) {
|
||||
commandForCfg += "disabled";
|
||||
if (!test)
|
||||
g_pConfigManager->parseKeyword("monitor", commandForCfg);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getRealMonitorFromOutput(OUTPUT);
|
||||
RASSERT(PMONITOR, "nullptr monitor in outputMgrApplyTest");
|
||||
wlr_output_state_set_enabled(PMONITOR->state.wlr(), head->state.enabled);
|
||||
|
||||
if (head->state.mode)
|
||||
commandForCfg +=
|
||||
std::to_string(head->state.mode->width) + "x" + std::to_string(head->state.mode->height) + "@" + std::to_string(head->state.mode->refresh / 1000.f) + ",";
|
||||
else
|
||||
commandForCfg += std::to_string(head->state.custom_mode.width) + "x" + std::to_string(head->state.custom_mode.height) + "@" +
|
||||
std::to_string(head->state.custom_mode.refresh / 1000.f) + ",";
|
||||
|
||||
commandForCfg += std::to_string(head->state.x) + "x" + std::to_string(head->state.y) + "," + std::to_string(head->state.scale) + ",transform," +
|
||||
std::to_string((int)head->state.transform);
|
||||
|
||||
if (!test) {
|
||||
g_pConfigManager->parseKeyword("monitor", commandForCfg);
|
||||
wlr_output_state_set_adaptive_sync_enabled(PMONITOR->state.wlr(), head->state.adaptive_sync_enabled);
|
||||
}
|
||||
|
||||
ok = wlr_output_test_state(OUTPUT, PMONITOR->state.wlr());
|
||||
|
||||
if (!ok)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test) {
|
||||
g_pConfigManager->m_bWantsMonitorReload = true; // for monitor keywords
|
||||
// if everything is disabled, performMonitorReload won't be called from renderMonitor
|
||||
bool allDisabled = std::all_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(),
|
||||
[](const auto m) { return !m->m_bEnabled || g_pCompositor->m_pUnsafeOutput == m.get(); });
|
||||
if (allDisabled) {
|
||||
Debug::log(LOG, "OutputMgr apply: All monitors disabled; performing monitor reload.");
|
||||
g_pConfigManager->performMonitorReload();
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
wlr_output_configuration_v1_send_succeeded(config);
|
||||
else
|
||||
wlr_output_configuration_v1_send_failed(config);
|
||||
|
||||
wlr_output_configuration_v1_destroy(config);
|
||||
|
||||
Debug::log(LOG, "OutputMgr Applied/Tested.");
|
||||
}
|
||||
|
||||
// taken from Sway.
|
||||
// this is just too much of a spaghetti for me to understand
|
||||
static void applyExclusive(wlr_box& usableArea, uint32_t anchor, int32_t exclusive, int32_t marginTop, int32_t marginRight, int32_t marginBottom, int32_t marginLeft) {
|
||||
|
@ -1939,6 +1875,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
// Needed in case we are switching from a custom modeline to a standard mode
|
||||
pMonitor->customDrmMode = {};
|
||||
pMonitor->currentMode = nullptr;
|
||||
bool autoScale = false;
|
||||
|
||||
if (RULE->scale > 0.1) {
|
||||
|
@ -1981,6 +1918,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
pMonitor->refreshRate = mode->refresh / 1000.f;
|
||||
pMonitor->vecSize = Vector2D(mode->width, mode->height);
|
||||
pMonitor->currentMode = mode;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -2010,6 +1948,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
|
||||
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
|
||||
pMonitor->currentMode = PREFERREDMODE;
|
||||
} else {
|
||||
Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", RULE->resolution, (float)RULE->refreshRate);
|
||||
}
|
||||
|
@ -2118,6 +2057,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
|
||||
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
|
||||
pMonitor->currentMode = PREFERREDMODE;
|
||||
} else {
|
||||
|
||||
Debug::log(LOG, "Monitor {}: Applying highest mode {}x{}@{:2f}.", pMonitor->output->name, (int)currentWidth, (int)currentHeight, (int)currentRefresh / 1000.f);
|
||||
|
@ -2148,6 +2088,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
pMonitor->refreshRate = mode->refresh / 1000.f;
|
||||
pMonitor->vecSize = Vector2D(mode->width, mode->height);
|
||||
pMonitor->currentMode = mode;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -2158,6 +2099,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height);
|
||||
pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f;
|
||||
pMonitor->currentMode = PREFERREDMODE;
|
||||
|
||||
Debug::log(LOG, "Setting preferred mode for {}", pMonitor->output->name);
|
||||
}
|
||||
|
@ -2307,8 +2249,6 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
|||
|
||||
pMonitor->events.modeChanged.emit();
|
||||
|
||||
Events::listener_change(nullptr, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ class CHyprRenderer {
|
|||
CHyprRenderer();
|
||||
|
||||
void renderMonitor(CMonitor* pMonitor);
|
||||
void outputMgrApplyTest(wlr_output_configuration_v1*, bool);
|
||||
void arrangeLayersForMonitor(const int&);
|
||||
void damageSurface(wlr_surface*, double, double, double scale = 1.0);
|
||||
void damageWindow(PHLWINDOW, bool forceFull = false);
|
||||
|
|
Loading…
Reference in a new issue