ltk/platform/wayland/
output_probe.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! The screen's logical size before any surface exists, read on an event queue of its own.

use smithay_client_toolkit::reexports::client::{ Connection, Dispatch, Proxy, QueueHandle, WEnum };
use smithay_client_toolkit::reexports::client::globals::GlobalList;
use smithay_client_toolkit::reexports::client::protocol::wl_output::{ self, WlOutput };
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_manager_v1::ZxdgOutputManagerV1;
use wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_v1::{ self, ZxdgOutputV1 };

struct Output
{
	mode:    Option<( i32, i32 )>,
	scale:   i32,
	logical: Option<( i32, i32 )>,
}

struct Probe
{
	outputs: Vec<Output>,
}

/// Logical size of the first output: `xdg_output`'s when the compositor offers it, else the current mode over the scale.
pub( crate ) fn output_logical_size( conn: &Connection, globals: &GlobalList ) -> Option<( u32, u32 )>
{
	let mut queue = conn.new_event_queue::<Probe>();
	let qh = queue.handle();
	let registry = globals.registry();
	let ( outputs, manager ) = globals.contents().with_list( |list|
	{
		let outputs: Vec<WlOutput> = list.iter()
			.filter( |g| g.interface == WlOutput::interface().name )
			.enumerate()
			.map( |( i, g )| registry.bind::<WlOutput, _, _>( g.name, g.version.min( 4 ), &qh, i ) )
			.collect();
		let manager = list.iter()
			.find( |g| g.interface == ZxdgOutputManagerV1::interface().name )
			.map( |g| registry.bind::<ZxdgOutputManagerV1, _, _>( g.name, g.version.min( 3 ), &qh, () ) );
		( outputs, manager )
	} );
	if outputs.is_empty()
	{
		return None;
	}

	let mut xdg_outputs = Vec::new();
	if let Some( manager ) = &manager
	{
		for ( i, output ) in outputs.iter().enumerate()
		{
			xdg_outputs.push( manager.get_xdg_output( output, &qh, i ) );
		}
	}

	let mut probe = Probe { outputs: outputs.iter().map( |_| Output { mode: None, scale: 1, logical: None } ).collect() };
	let answered = queue.roundtrip( &mut probe ).is_ok();

	for xdg_output in xdg_outputs
	{
		xdg_output.destroy();
	}
	if let Some( manager ) = manager
	{
		manager.destroy();
	}
	for output in outputs
	{
		if output.version() >= 3
		{
			output.release();
		}
	}
	if !answered
	{
		return None;
	}

	probe.outputs.iter().find_map( |o|
	{
		let scale = o.scale.max( 1 );
		let ( w, h ) = o.logical.or_else( || o.mode.map( |( w, h )| ( w / scale, h / scale ) ) )?;
		( w > 0 && h > 0 ).then_some( ( w as u32, h as u32 ) )
	} )
}

impl Dispatch<WlOutput, usize> for Probe
{
	fn event( state: &mut Self, _: &WlOutput, event: wl_output::Event, index: &usize, _: &Connection, _: &QueueHandle<Self> )
	{
		let Some( output ) = state.outputs.get_mut( *index ) else { return };
		match event
		{
			wl_output::Event::Mode { flags: WEnum::Value( flags ), width, height, .. } if flags.contains( wl_output::Mode::Current ) =>
			{
				output.mode = Some( ( width, height ) );
			}
			wl_output::Event::Scale { factor } => output.scale = factor,
			_ => {}
		}
	}
}

impl Dispatch<ZxdgOutputManagerV1, ()> for Probe
{
	fn event( _: &mut Self, _: &ZxdgOutputManagerV1, _: <ZxdgOutputManagerV1 as Proxy>::Event, _: &(), _: &Connection, _: &QueueHandle<Self> ) {}
}

impl Dispatch<ZxdgOutputV1, usize> for Probe
{
	fn event( state: &mut Self, _: &ZxdgOutputV1, event: zxdg_output_v1::Event, index: &usize, _: &Connection, _: &QueueHandle<Self> )
	{
		if let ( Some( output ), zxdg_output_v1::Event::LogicalSize { width, height } ) = ( state.outputs.get_mut( *index ), event )
		{
			output.logical = Some( ( width, height ) );
		}
	}
}