listen to output events from the wlr protocol

This commit is contained in:
2025-01-04 15:44:06 +00:00
parent 5718d8d772
commit 0f72c41b42
5 changed files with 553 additions and 13 deletions

263
Cargo.lock generated
View File

@@ -8,12 +8,52 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cc"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "dlib"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "errno"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "itoa"
version = "1.0.14"
@@ -32,6 +72,34 @@ dependencies = [
"winnow",
]
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
version = "2.7.4"
@@ -134,6 +202,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "pkg-config"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "proc-macro2"
version = "1.0.92"
@@ -143,6 +217,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.37"
@@ -152,12 +235,31 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "serde"
version = "1.0.216"
@@ -190,6 +292,18 @@ dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "swayout"
version = "1.2.2"
@@ -197,6 +311,9 @@ dependencies = [
"kdl",
"serde",
"serde_json",
"wayland-client",
"wayland-protocols",
"wayland-protocols-wlr",
"xdg",
]
@@ -243,6 +360,152 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "wayland-backend"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6"
dependencies = [
"cc",
"downcast-rs",
"rustix",
"scoped-tls",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280"
dependencies = [
"bitflags",
"rustix",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e"
dependencies = [
"bitflags",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022"
dependencies = [
"bitflags",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09"
dependencies = [
"dlib",
"log",
"pkg-config",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.20"

View File

@@ -16,3 +16,7 @@ serde_json = "1.0.133"
kdl = "6.1.0"
xdg = "2.5.2"
wayland-client = "0.31.7"
wayland-protocols = { version = "0.32.5" , features = ["client", "unstable"] }
wayland-protocols-wlr = { version = "0.3.5", features = ["client"] }

View File

@@ -9,6 +9,8 @@ use crate::lid::is_lid_closed;
mod sway;
use crate::sway::{apply_outputs, get_outputs, Output};
mod listen;
/// Determine the available layout names.
/// Return one layout for each layout defined in the configuration file
/// for which all outputs are available (and lids are not closed),
@@ -240,3 +242,7 @@ pub fn apply_automatic() -> Result<String,String> {
Err("No automatic layouts available.".to_string())
}
}
pub fn listen() {
listen::listen()
}

261
src/listen.rs Normal file
View File

@@ -0,0 +1,261 @@
use wayland_client::{event_created_child, protocol::wl_registry, Connection, Dispatch, QueueHandle};
use wayland_client::protocol::wl_output::Transform;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_head_v1::{AdaptiveSyncState, ZwlrOutputHeadV1};
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_manager_v1;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_manager_v1::ZwlrOutputManagerV1;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_head_v1::Event as HeadEvent;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_mode_v1::Event as ModeEvent;
use wayland_protocols_wlr::output_management::v1::client::zwlr_output_mode_v1::ZwlrOutputModeV1;
// The state of this app.
struct AppState {
output_names: Vec<String>,
}
impl AppState {
fn reset(&mut self) {
self.output_names.clear()
}
fn add_output(&mut self, output_name:String) {
self.output_names.push(output_name)
}
}
// When we get the global registry event, use the registry to bind the ZwlrOutputManagerV1 to get output events
impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
fn event(
_state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_data: &(),
_conn: &Connection,
qh: &QueueHandle<AppState>,
) {
// When receiving events from the wl_registry, we are only interested in the
// `global` event, which signals a new available global.
// When receiving this event, we just print its characteristics in this example.
if let wl_registry::Event::Global { name, interface, version } = event {
//println!("[{}] {} (v{})", name, interface, version);
match interface.as_str() {
"zwlr_output_manager_v1" => {
println!("found output manager");
registry.bind::<ZwlrOutputManagerV1, _, _>(name, version, qh, ());
}
_ => {}
}
}
}
}
// Handle ZwlrOutputManagerV1 events
impl Dispatch<ZwlrOutputManagerV1, ()> for AppState {
event_created_child!(AppState, ZwlrOutputManagerV1, [
0 => (ZwlrOutputHeadV1, ()) // How did I know that 0 was for ZwlrOutputHeadV1?
]);
fn event(
state: &mut Self,
_: &ZwlrOutputManagerV1,
event: zwlr_output_manager_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<AppState>,
) {
match event {
zwlr_output_manager_v1::Event::Head { head } => {
println!("-=-= Head")
}
zwlr_output_manager_v1::Event::Finished => {
println!("-=-= Finished")
}
zwlr_output_manager_v1::Event::Done { serial } => {
print!("-=-= Done. Output names({}): ", &state.output_names.len());
for output_name in &state.output_names {
print!("{},", output_name);
}
println!(".");
}
_ => {}
}
}
}
impl Dispatch<ZwlrOutputHeadV1, ()> for AppState {
event_created_child!(AppState, ZwlrOutputManagerV1, [
3 => (ZwlrOutputModeV1, ()) // How did I know that 3 is ZwlrOutputModeV1?
]);
fn event(
state: &mut Self,
_: &ZwlrOutputHeadV1,
event: HeadEvent,
_: &(),
_: &Connection,
_: &QueueHandle<AppState>,
) {
//println!("headv1 op:{}", event.opcode());
match event {
HeadEvent::Name { name } => {
println!("name: {}", &name);
state.add_output(name);
}
HeadEvent::Description { description} => {
// HeadEvent.Description is "<make> <model> <serial> (<name>)"
println!("description: {}", description)
}
HeadEvent::PhysicalSize { width, height } => {
println!("size: {}x{}", width, height)
}
HeadEvent::Mode { mode } => {
//println!("mode")
}
HeadEvent::Enabled { enabled} => {
println!("enabled: {}", enabled)
}
HeadEvent::CurrentMode { mode } => {
println!("mode...");
}
HeadEvent::Position { x, y } => {
println!("position: {},{}", x, y)
}
HeadEvent::Transform { transform } => {
let mut s = String::new();
match transform.into_result() {
Ok(t) => {
s.push_str(
match t {
Transform::Normal => { "normal" }
Transform::_90 => { "90" }
Transform::_180 => { "180" }
Transform::_270 => { "270" }
Transform::Flipped => { "flipped" }
Transform::Flipped90 => { "flipped90" }
Transform::Flipped180 => { "flipped180" }
Transform::Flipped270 => { "flipped270" }
_ => { "wat?" }
}
)
}
Err(e) => {
s.push_str(e.to_string().as_str())
}
};
println!("transform: {}", s)
}
HeadEvent::Scale { scale } => {
println!("scale: {}", scale)
}
HeadEvent::Finished => {
// This happens when something is disconnected
println!("finished")
}
// I never get Make
HeadEvent::Make { make } => {
println!("make: {}", make)
}
// I never get Model
HeadEvent::Model { model } => {
println!("model: {}", model)
}
// I never get SerialNumber
HeadEvent::SerialNumber { serial_number } => {
println!("serial: {}", serial_number)
}
HeadEvent::AdaptiveSync { state } => {
let mut s = String::new();
match state.into_result() {
Ok(st) => {
s.push_str(
match st {
AdaptiveSyncState::Disabled => { "disabled" }
AdaptiveSyncState::Enabled => { "enabled" }
_ => { "wat?" }
}
)
}
Err(e) => {
s.push_str(e.to_string().as_str())
}
};
println!("adaptive sync: {}", s)
}
_ => {
println!("head unhandled {}", event.opcode())
}
}
}
}
impl Dispatch<ZwlrOutputModeV1, ()> for AppState {
fn event(
_state: &mut Self,
_: &ZwlrOutputModeV1,
event: ModeEvent,
_: &(),
_: &Connection,
_: &QueueHandle<AppState>,
) {
match event {
ModeEvent::Size { width, height } => {
//println!("mode size: {}x{}", width, height)
}
ModeEvent::Refresh { refresh } => {
//println!("mode refresh: {}", refresh)
}
ModeEvent::Preferred => {
//println!("mode preferred")
}
ModeEvent::Finished => {
//println!("mode finished")
}
_ => {
println!("mode unhandled {}", event.opcode())
}
}
}
}
// The main function of our program
pub fn listen() {
// Create a Wayland connection by connecting to the server through the
// environment-provided configuration.
let conn = Connection::connect_to_env().unwrap();
// Retrieve the WlDisplay Wayland object from the connection. This object is
// the starting point of any Wayland program, from which all other objects will
// be created.
let display = conn.display();
// Create an event queue for our event processing
let mut event_queue = conn.new_event_queue();
// And get its handle to associate new objects to it
let qh = event_queue.handle();
// Create a wl_registry object by sending the wl_display.get_registry request.
// This method takes two arguments: a handle to the queue that the newly created
// wl_registry will be assigned to, and the user-data that should be associated
// with this registry (here it is () as we don't need user-data).
let _registry = display.get_registry(&qh, ());
// At this point everything is ready, and we just need to wait to receive the events
// from the wl_registry. Our callback will print the advertised globals.
println!("Advertised globals:");
// To actually receive the events, we invoke the `roundtrip` method. This method
// is special and you will generally only invoke it during the setup of your program:
// it will block until the server has received and processed all the messages you've
// sent up to now.
//
// In our case, that means it'll block until the server has received our
// wl_display.get_registry request, and as a reaction has sent us a batch of
// wl_registry.global events.
//
// `roundtrip` will then empty the internal buffer of the queue it has been invoked
// on, and thus invoke our `Dispatch` implementation that prints the list of advertised
// globals.
let mut state = AppState { output_names : Vec::new() };
event_queue.roundtrip(&mut state).unwrap();
loop {
event_queue.blocking_dispatch(&mut state).unwrap();
}
}

View File

@@ -6,21 +6,27 @@ fn main() {
// first arg is executable
swayout::print_layout_names();
} else if args.len() == 2 {
let arg = &args[1];
if arg == "--automatic" {
match swayout::apply_automatic() {
Ok(layout_name) => {
println!("{}", layout_name);
}
Err(e) => {
eprintln!("{}", e);
process::exit(2)
let arg = args[1].as_str();
match arg {
"--automatic" => {
match swayout::apply_automatic() {
Ok(layout_name) => {
println!("{}", layout_name);
}
Err(e) => {
eprintln!("{}", e);
process::exit(2)
}
}
},
"--listen" => {
swayout::listen()
}
} else {
if let Err(e) = swayout::apply_layout(&args[1]) {
eprintln!("{}", e);
process::exit(3)
_ => {
if let Err(e) = swayout::apply_layout(&args[1]) {
eprintln!("{}", e);
process::exit(3)
}
}
}
} else {