diff --git a/build.rs b/build.rs
index f78f26f..2837704 100644
--- a/build.rs
+++ b/build.rs
@@ -11,4 +11,9 @@ fn main() {
out_dir.join("river-status-unstable-v1.rs"),
Side::Client,
);
+ generate_code(
+ "./protocol/wlr-output-management-unstable-v1.xml",
+ out_dir.join("wlr-output-management-unstable-v1.rs"),
+ Side::Client
+ );
}
diff --git a/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml
new file mode 100644
index 0000000..411e2f0
--- /dev/null
+++ b/protocol/wlr-output-management-unstable-v1.xml
@@ -0,0 +1,601 @@
+
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ Create a new output configuration object. This allows to update head
+ properties.
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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).
+
+
+
+
+
+
+
+ This event introduces a mode for this head. It is sent once per
+ supported mode.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ This event describes the mode currently in use for this head. It is only
+ sent if the output is enabled.
+
+
+
+
+
+
+ This events describes the position of the head in the global compositor
+ space. It is only sent if the output is enabled.
+
+
+
+
+
+
+
+ This event describes the transformation currently applied to the head.
+ It is only sent if the output is enabled.
+
+
+
+
+
+
+ This events describes the scale of the head in the global compositor
+ space. It is only sent if the output is enabled.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+ This request indicates that the client will no longer use this head
+ object.
+
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ This event describes the mode's fixed vertical refresh rate. It is only
+ sent if the mode has a fixed refresh rate.
+
+
+
+
+
+
+ This event advertises this mode as preferred.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ This request indicates that the client will no longer use this mode
+ object.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+ Enable a head. This request creates a head configuration object that can
+ be used to change the head's properties.
+
+
+
+
+
+
+
+ Disable a head.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This request sets the head's 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.
+
+
+
+
+
+
+
+
+ This request sets the head's position in the global compositor space.
+
+
+
+
+
+
+
+ This request sets the head's transform.
+
+
+
+
+
+
+ This request sets the head's scale.
+
+
+
+
+
+
+
+
+ This request enables/disables adaptive sync. Adaptive sync is also
+ known as Variable Refresh Rate or VRR.
+
+
+
+
+
diff --git a/src/main.rs b/src/main.rs
index 8fbbabf..7351048 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,9 +1,12 @@
mod river_protocols;
-use river_protocols::zriver_seat_status_v1::ZriverSeatStatusV1;
use river_protocols::{
zriver_output_status_v1, zriver_seat_status_v1,
zriver_status_manager_v1::ZriverStatusManagerV1,
+ zriver_seat_status_v1::ZriverSeatStatusV1,
+ zwlr_output_manager_v1, zwlr_output_head_v1,
+ zwlr_output_head_v1::ZwlrOutputHeadV1,
+ zwlr_output_manager_v1::ZwlrOutputManagerV1,
};
use serde::ser::{SerializeSeq, SerializeStruct, Serializer};
use serde::Serialize;
@@ -43,7 +46,7 @@ impl Serialize for Tags {
struct OutputGeometry {
width: i32,
height: i32,
- scale: i32,
+ scale: f64,
refresh: f32,
}
@@ -62,6 +65,12 @@ struct Output {
gdk_id: u32,
}
+#[derive(Clone, Default)]
+struct Head {
+ name: Option,
+ scale: f64,
+}
+
#[derive(Default)]
struct ReadStatus {
wl_output: bool,
@@ -80,11 +89,12 @@ struct Env {
mode: Option,
focused_output: Option,
outputs: BTreeMap,
+ heads: BTreeMap,
}
impl Env {
- fn new() -> Env {
- Env {
+ fn new() -> Self {
+ Self {
status_manager: None,
flags: configuration(),
read_status: Default::default(),
@@ -93,6 +103,7 @@ impl Env {
mode: None,
focused_output: None,
outputs: Default::default(),
+ heads: Default::default(),
}
}
@@ -135,6 +146,17 @@ impl Serialize for Env {
false
};
new.gdk_id = gdk_id;
+ // find the scale by finding a head with the same name
+ new.geometry.scale = 1.0;
+ for (_, head) in &self.heads {
+ match &head.name {
+ Some(head_name) if head_name == &name => {
+ new.geometry.scale = head.scale;
+ break;
+ },
+ _ => {}
+ }
+ }
outputs.insert(name, new);
}
gdk_id += 1;
@@ -277,14 +299,47 @@ fn handle_output_event(
output.name = Some(name);
}
},
- wl_output::Event::Scale {
- factor
+ _ => {},
+ }
+ }
+}
- } => {
- if let Some(output) = env.outputs.get_mut(&output_id) {
- output.geometry.scale = factor;
+fn handle_output_manager_event(
+ _manager: Main, event: zwlr_output_manager_v1::Event,
+ mut env: DispatchData
+) {
+ if let Some(env) = env.get::() {
+ match event {
+ zwlr_output_manager_v1::Event::Head { head } => {
+ let head_id = head.as_ref().id();
+ head.quick_assign(handle_head_event);
+ env.heads.insert(head_id, Default::default());
+ },
+ _ => {},
+ }
+ }
+}
+
+fn handle_head_event(
+ head: Main, event: zwlr_output_head_v1::Event,
+ mut env: DispatchData
+) {
+ let head_id = head.as_ref().id();
+ if let Some(env) = env.get::() {
+ match event {
+ zwlr_output_head_v1::Event::Scale { scale } => {
+ if let Some(head) = env.heads.get_mut(&head_id) {
+ head.scale = scale;
}
},
+ zwlr_output_head_v1::Event::Name { name } => {
+ if let Some(head) = env.heads.get_mut(&head_id) {
+ head.name = Some(name);
+ }
+ },
+ zwlr_output_head_v1::Event::Finished => {
+ env.heads.remove(&head_id);
+ },
_ => {},
}
}
@@ -315,6 +370,11 @@ fn global_manager_callback(
env.status_manager = Some(status_manager);
}
},
+ "zwlr_output_manager_v1" if version >= 4 => {
+ let output_manager: Main =
+ registry.bind(version, id);
+ output_manager.quick_assign(handle_output_manager_event);
+ },
_ => {}
}
}
@@ -365,15 +425,7 @@ fn main() {
);
loop {
- event_queue
- .dispatch(&mut env, |event, object, _| {
- panic!(
- "[callop] Encountered an orphan event: {}@{}: {}",
- event.interface,
- object.as_ref().id(),
- event.name
- );
- })
+ event_queue.dispatch(&mut env, |_, _, _| {/* ignore */})
.unwrap();
if !env.is_empty() && env.is_first_read_done() {
env.fmt();
diff --git a/src/river_protocols.rs b/src/river_protocols.rs
index 9a0b822..221a53b 100644
--- a/src/river_protocols.rs
+++ b/src/river_protocols.rs
@@ -1,4 +1,5 @@
-pub use generated::client::*;
+pub use generated::river_status::*;
+pub use generated::wlr_output_management::*;
pub mod generated {
// The generated code tends to trigger a lot of warnings
@@ -6,22 +7,26 @@ pub mod generated {
#![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]
#![allow(non_upper_case_globals, non_snake_case, unused_imports)]
#![allow(static_mut_refs, clippy::all)]
+ // Tese imports are used by the generated code
+ use wayland_client::protocol::wl_output;
+ use wayland_client::{protocol, sys};
+ use wayland_client::{
+ AnonymousObject, Attached, Display, GlobalManager, Main, Proxy, ProxyMap,
+ };
+ use wayland_commons::map::{Object, ObjectMetadata};
+ use wayland_commons::smallvec;
+ use wayland_commons::wire::{Argument, ArgumentType, Message, MessageDesc};
+ use wayland_commons::{Interface, MessageGroup};
+ // If you protocol interacts with objects from other protocols, you'll need to import
+ // their modules, like so:
+ use wayland_client::protocol::{wl_region, wl_seat, wl_surface};
- pub mod client {
- // These imports are used by the generated code
- use wayland_client::protocol::wl_output;
- use wayland_client::{protocol, sys};
- use wayland_client::{
- AnonymousObject, Attached, Display, GlobalManager, Main, Proxy, ProxyMap,
- };
- use wayland_commons::map::{Object, ObjectMetadata};
- use wayland_commons::smallvec;
- use wayland_commons::wire::{Argument, ArgumentType, Message, MessageDesc};
- use wayland_commons::{Interface, MessageGroup};
- // pub(crate) use wayland_protocols::unstable::xdg_output::v1::client::zxdg_output_v1::Event;
- // If you protocol interacts with objects from other protocols, you'll need to import
- // their modules, like so:
- use wayland_client::protocol::{wl_region, wl_seat, wl_surface};
+ pub mod river_status {
+ use super::*;
include!(concat!(env!("OUT_DIR"), "/river-status-unstable-v1.rs"));
}
+ pub mod wlr_output_management {
+ use super::*;
+ include!(concat!(env!("OUT_DIR"), "/wlr-output-management-unstable-v1.rs"));
+ }
}