Skip to main content

jay_config/
video.rs

1//! Tools for configuring graphics cards and monitors.
2
3use {
4    crate::{
5        _private::WireMode,
6        Direction, PciId, Workspace,
7        video::connector_type::{
8            CON_9PIN_DIN, CON_COMPONENT, CON_COMPOSITE, CON_DISPLAY_PORT, CON_DPI, CON_DSI,
9            CON_DVIA, CON_DVID, CON_DVII, CON_EDP, CON_EMBEDDED_WINDOW, CON_HDMIA, CON_HDMIB,
10            CON_LVDS, CON_SPI, CON_SVIDEO, CON_TV, CON_UNKNOWN, CON_USB, CON_VGA, CON_VIRTUAL,
11            CON_WRITEBACK, ConnectorType,
12        },
13    },
14    serde::{Deserialize, Serialize},
15    std::{str::FromStr, time::Duration},
16};
17
18/// The mode of a connector.
19///
20/// Currently a mode consists of three properties:
21///
22/// - width in pixels
23/// - height in pixels
24/// - refresh rate in mhz.
25#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
26pub struct Mode {
27    pub(crate) width: i32,
28    pub(crate) height: i32,
29    pub(crate) refresh_millihz: u32,
30}
31
32impl Mode {
33    /// Returns the width of the mode.
34    pub fn width(&self) -> i32 {
35        self.width
36    }
37
38    /// Returns the height of the mode.
39    pub fn height(&self) -> i32 {
40        self.height
41    }
42
43    /// Returns the refresh rate of the mode in mhz.
44    ///
45    /// For a 60hz monitor, this function would return 60_000.
46    pub fn refresh_rate(&self) -> u32 {
47        self.refresh_millihz
48    }
49
50    pub(crate) fn zeroed() -> Self {
51        Self {
52            width: 0,
53            height: 0,
54            refresh_millihz: 0,
55        }
56    }
57}
58
59/// A connector that is potentially connected to an output device.
60///
61/// A connector is the part that sticks out of your graphics card. A graphics card usually
62/// has many connectors but one few of them are actually connected to a monitor.
63#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
64pub struct Connector(pub u64);
65
66impl Connector {
67    /// Returns whether this connector existed at the time `get_connector` was called.
68    ///
69    /// This only implies existence at the time `get_connector` was called. Even if this
70    /// function returns true, the connector might since have disappeared.
71    pub fn exists(self) -> bool {
72        self.0 != 0
73    }
74
75    /// Returns whether the connector is connected to an output device.
76    pub fn connected(self) -> bool {
77        if !self.exists() {
78            return false;
79        }
80        get!(false).connector_connected(self)
81    }
82
83    /// Returns whether this connector is used as an output by the compositor.
84    pub fn compositor_output(self) -> bool {
85        get!(false).connector_compositor_output(self)
86    }
87
88    /// Returns the scale of the currently connected monitor.
89    pub fn scale(self) -> f64 {
90        if !self.exists() {
91            return 1.0;
92        }
93        get!(1.0).connector_get_scale(self)
94    }
95
96    /// Sets the scale to use for the currently connected monitor.
97    pub fn set_scale(self, scale: f64) {
98        if !self.exists() {
99            return;
100        }
101        log::info!("setting scale to {}", scale);
102        get!().connector_set_scale(self, scale);
103    }
104
105    /// Returns the connector type.
106    pub fn ty(self) -> ConnectorType {
107        if !self.exists() {
108            return CON_UNKNOWN;
109        }
110        get!(CON_UNKNOWN).connector_type(self)
111    }
112
113    /// Returns the current mode of the connector.
114    pub fn mode(self) -> Mode {
115        if !self.exists() {
116            return Mode::zeroed();
117        }
118        get!(Mode::zeroed()).connector_mode(self)
119    }
120
121    /// Tries to set the mode of the connector.
122    ///
123    /// If the refresh rate is not specified, tries to use the first mode with the given
124    /// width and height.
125    ///
126    /// The default mode is the first mode advertised by the connector. This is usually
127    /// the native mode.
128    pub fn set_mode(self, width: i32, height: i32, refresh_millihz: Option<u32>) {
129        if !self.exists() {
130            log::warn!("set_mode called on a connector that does not exist");
131            return;
132        }
133        let refresh_millihz = match refresh_millihz {
134            Some(r) => r,
135            _ => match self
136                .modes()
137                .iter()
138                .find(|m| m.width == width && m.height == height)
139            {
140                Some(m) => m.refresh_millihz,
141                _ => {
142                    log::warn!("Could not find any mode with width {width} and height {height}");
143                    return;
144                }
145            },
146        };
147        get!().connector_set_mode(
148            self,
149            WireMode {
150                width,
151                height,
152                refresh_millihz,
153            },
154        )
155    }
156
157    /// Returns the available modes of the connector.
158    pub fn modes(self) -> Vec<Mode> {
159        if !self.exists() {
160            return Vec::new();
161        }
162        get!(Vec::new()).connector_modes(self)
163    }
164
165    /// Returns whether this connector supports arbitrary modes.
166    pub fn supports_arbitrary_modes(self) -> bool {
167        if !self.exists() {
168            return false;
169        }
170        get!(false).connector_supports_arbitrary_modes(self)
171    }
172
173    /// Returns the logical width of the connector.
174    ///
175    /// The returned value will be different from `mode().width()` if the scale is not 1.
176    pub fn width(self) -> i32 {
177        get!().connector_size(self).0
178    }
179
180    /// Returns the logical height of the connector.
181    ///
182    /// The returned value will be different from `mode().height()` if the scale is not 1.
183    pub fn height(self) -> i32 {
184        get!().connector_size(self).1
185    }
186
187    /// Returns the refresh rate in mhz of the current mode of the connector.
188    ///
189    /// This is a shortcut for `mode().refresh_rate()`.
190    pub fn refresh_rate(self) -> u32 {
191        self.mode().refresh_millihz
192    }
193
194    /// Retrieves the position of the output in the global compositor space.
195    pub fn position(self) -> (i32, i32) {
196        if !self.connected() {
197            return (0, 0);
198        }
199        get!().connector_get_position(self)
200    }
201
202    /// Sets the position of the connector in the global compositor space.
203    ///
204    /// `x` and `y` must be non-negative and must not exceed a currently unspecified limit.
205    /// Any reasonable values for `x` and `y` should work.
206    ///
207    /// This function allows the connector to overlap with other connectors, however, such
208    /// configurations are not supported and might result in unexpected behavior.
209    pub fn set_position(self, x: i32, y: i32) {
210        if !self.exists() {
211            log::warn!("set_position called on a connector that does not exist");
212            return;
213        }
214        get!().connector_set_position(self, x, y);
215    }
216
217    /// Enables or disables the connector.
218    ///
219    /// By default, all connectors are enabled.
220    pub fn set_enabled(self, enabled: bool) {
221        if !self.exists() {
222            log::warn!("set_enabled called on a connector that does not exist");
223            return;
224        }
225        get!().connector_set_enabled(self, enabled);
226    }
227
228    /// Sets the transformation to apply to the content of this connector.
229    pub fn set_transform(self, transform: Transform) {
230        if !self.exists() {
231            log::warn!("set_transform called on a connector that does not exist");
232            return;
233        }
234        get!().connector_set_transform(self, transform);
235    }
236
237    pub fn name(self) -> String {
238        if !self.exists() {
239            return String::new();
240        }
241        get!(String::new()).connector_get_name(self)
242    }
243
244    pub fn model(self) -> String {
245        if !self.exists() {
246            return String::new();
247        }
248        get!(String::new()).connector_get_model(self)
249    }
250
251    pub fn manufacturer(self) -> String {
252        if !self.exists() {
253            return String::new();
254        }
255        get!(String::new()).connector_get_manufacturer(self)
256    }
257
258    pub fn serial_number(self) -> String {
259        if !self.exists() {
260            return String::new();
261        }
262        get!(String::new()).connector_get_serial_number(self)
263    }
264
265    /// Sets the VRR mode.
266    pub fn set_vrr_mode(self, mode: VrrMode) {
267        get!().set_vrr_mode(Some(self), mode)
268    }
269
270    /// Sets the VRR cursor refresh rate.
271    ///
272    /// Limits the rate at which cursors are updated on screen when VRR is active.
273    ///
274    /// Setting this to infinity disables the limiter.
275    pub fn set_vrr_cursor_hz(self, hz: f64) {
276        get!().set_vrr_cursor_hz(Some(self), hz)
277    }
278
279    /// Sets the tearing mode.
280    pub fn set_tearing_mode(self, mode: TearingMode) {
281        get!().set_tearing_mode(Some(self), mode)
282    }
283
284    /// Sets the format to use for framebuffers.
285    pub fn set_format(self, format: Format) {
286        get!().connector_set_format(self, format);
287    }
288
289    /// Sets the color space and EOTF of the connector.
290    ///
291    /// By default, the default values are used which usually means sRGB color space with
292    /// gamma22 EOTF.
293    ///
294    /// If the output supports it, HDR10 can be enabled by setting the color space to
295    /// BT.2020 and the EOTF to PQ.
296    ///
297    /// Note that some displays might ignore incompatible settings.
298    pub fn set_colors(self, color_space: ColorSpace, eotf: Eotf) {
299        get!().connector_set_colors(self, color_space, eotf);
300    }
301
302    /// Sets the space in which blending is performed for this output.
303    ///
304    /// The default is [`BlendSpace::SRGB`]
305    pub fn set_blend_space(self, blend_space: BlendSpace) {
306        get!().connector_set_blend_space(self, blend_space);
307    }
308
309    /// Sets the brightness of the output.
310    ///
311    /// By default or when `brightness` is `None`, the brightness depends on the
312    /// EOTF:
313    ///
314    /// - [`Eotf::DEFAULT`]: The maximum brightness of the output.
315    /// - [`Eotf::PQ`]: 203 cd/m^2.
316    ///
317    /// This should only be used with the PQ transfer function. If the default transfer
318    /// function is used, you should instead calibrate the hardware directly.
319    ///
320    /// When used with the default transfer function, the default brightness is anchored
321    /// at 80 cd/m^2. That is, setting this to 40 cd/m^2 makes everything appear half as
322    /// bright as normal and creates 50% HDR headroom.
323    ///
324    /// This has no effect unless the vulkan renderer is used.
325    pub fn set_brightness(self, brightness: Option<f64>) {
326        get!().connector_set_brightness(self, brightness);
327    }
328
329    /// Get the currently visible/active workspace.
330    ///
331    /// If this connector is not connected, or is there no active workspace, returns a
332    /// workspace whose `exists()` returns false.
333    pub fn active_workspace(self) -> Workspace {
334        get!(Workspace(0)).get_connector_active_workspace(self)
335    }
336
337    /// Get all workspaces on this connector.
338    ///
339    /// If this connector is not connected, returns an empty list.
340    pub fn workspaces(self) -> Vec<Workspace> {
341        get!().get_connector_workspaces(self)
342    }
343
344    /// Find the closest connector in the given direction.
345    ///
346    /// Uses center-to-center distance calculation and prefers outputs better aligned
347    /// with the movement axis.
348    ///
349    /// If no connector exists in the given direction, returns a connector whose
350    /// `exists()` returns false.
351    pub fn connector_in_direction(self, direction: Direction) -> Connector {
352        get!(Connector(0)).get_connector_in_direction(self, direction)
353    }
354
355    /// Configures whether the display primaries are used.
356    ///
357    /// By default, Jay pretends that the display uses sRGB primaries. This is also how
358    /// most other systems behave. In reality, most displays use a much larger gamut. For
359    /// example, they advertise that they support 95% of the DCI-P3 gamut. If the display
360    /// is interpreting colors in their native gamut, then colors will appear more
361    /// saturated than their specification.
362    ///
363    /// If this is set to `true`, Jay assumes that the display uses the primaries
364    /// advertised in its EDID. This might produce more accurate colors while also
365    /// allowing color-managed applications to use the full gamut of the display.
366    ///
367    /// This setting has no effect when the display is explicitly operating in a wide
368    /// color space.
369    ///
370    /// The default is `false`.
371    pub fn set_use_native_gamut(self, use_native_gamut: bool) {
372        get!().connector_set_use_native_gamut(self, use_native_gamut);
373    }
374
375    /// Sets the scaling filter of the output.
376    ///
377    /// The default is [`ScalingFilter::LINEAR`]
378    pub fn set_scaling_filter(self, scaling_filter: ScalingFilter) {
379        get!().connector_set_scaling_filter(self, scaling_filter);
380    }
381}
382
383/// Returns all available DRM devices.
384pub fn drm_devices() -> Vec<DrmDevice> {
385    get!().drm_devices()
386}
387
388/// Sets the callback to be called when a new DRM device appears.
389pub fn on_new_drm_device<F: FnMut(DrmDevice) + 'static>(f: F) {
390    get!().on_new_drm_device(f)
391}
392
393/// Sets the callback to be called when a DRM device is removed.
394pub fn on_drm_device_removed<F: FnMut(DrmDevice) + 'static>(f: F) {
395    get!().on_del_drm_device(f)
396}
397
398/// Sets the callback to be called when a new connector appears.
399pub fn on_new_connector<F: FnMut(Connector) + 'static>(f: F) {
400    get!().on_new_connector(f)
401}
402
403/// Sets the callback to be called when a connector becomes connected to an output device.
404pub fn on_connector_connected<F: FnMut(Connector) + 'static>(f: F) {
405    get!().on_connector_connected(f)
406}
407
408/// Sets the callback to be called when a connector is disconnected from an output device.
409pub fn on_connector_disconnected<F: FnMut(Connector) + 'static>(f: F) {
410    get!().on_connector_disconnected(f)
411}
412
413/// Sets the callback to be called when the graphics of the compositor have been initialized.
414///
415/// This callback is only invoked once during the lifetime of the compositor. This is a good place
416/// to auto start graphical applications.
417pub fn on_graphics_initialized<F: FnOnce() + 'static>(f: F) {
418    get!().on_graphics_initialized(f)
419}
420
421pub fn connectors() -> Vec<Connector> {
422    get!().connectors(None)
423}
424
425/// Returns the connector with the given id.
426///
427/// The linux kernel identifies connectors by a (type, idx) tuple, e.g., `DP-0`.
428/// If the connector does not exist at the time this function is called, a sentinel value is
429/// returned. This can be checked by calling `exists()` on the returned connector.
430///
431/// The `id` argument can either be an explicit tuple, e.g. `(CON_DISPLAY_PORT, 0)`, or a string
432/// that can be parsed to such a tuple, e.g. `"DP-0"`.
433///
434/// The following string prefixes exist:
435///
436/// - `DP`
437/// - `eDP`
438/// - `HDMI-A`
439/// - `HDMI-B`
440/// - `EmbeddedWindow` - this is an implementation detail of the compositor and used if it
441///   runs as an embedded application.
442/// - `VGA`
443/// - `DVI-I`
444/// - `DVI-D`
445/// - `DVI-A`
446/// - `Composite`
447/// - `SVIDEO`
448/// - `LVDS`
449/// - `Component`
450/// - `DIN`
451/// - `TV`
452/// - `Virtual`
453/// - `DSI`
454/// - `DPI`
455/// - `Writeback`
456/// - `SPI`
457/// - `USB`
458pub fn get_connector(id: impl ToConnectorId) -> Connector {
459    let (ty, idx) = match id.to_connector_id() {
460        Ok(id) => id,
461        Err(e) => {
462            log::error!("{}", e);
463            return Connector(0);
464        }
465    };
466    get!(Connector(0)).get_connector(ty, idx)
467}
468
469/// Returns the connector with the given name.
470///
471/// Unlike [`get_connector`], this function can also be used for connectors whose names
472/// don't follow the `<type>-<id>` pattern.
473pub fn get_connector_by_name(name: &str) -> Connector {
474    get!(Connector(0)).get_connector_by_name(name)
475}
476
477/// A type that can be converted to a `(ConnectorType, idx)` tuple.
478pub trait ToConnectorId {
479    fn to_connector_id(&self) -> Result<(ConnectorType, u32), String>;
480}
481
482impl ToConnectorId for (ConnectorType, u32) {
483    fn to_connector_id(&self) -> Result<(ConnectorType, u32), String> {
484        Ok(*self)
485    }
486}
487
488impl ToConnectorId for &'_ str {
489    fn to_connector_id(&self) -> Result<(ConnectorType, u32), String> {
490        let pairs = [
491            ("DP-", CON_DISPLAY_PORT),
492            ("eDP-", CON_EDP),
493            ("HDMI-A-", CON_HDMIA),
494            ("HDMI-B-", CON_HDMIB),
495            ("EmbeddedWindow-", CON_EMBEDDED_WINDOW),
496            ("VGA-", CON_VGA),
497            ("DVI-I-", CON_DVII),
498            ("DVI-D-", CON_DVID),
499            ("DVI-A-", CON_DVIA),
500            ("Composite-", CON_COMPOSITE),
501            ("SVIDEO-", CON_SVIDEO),
502            ("LVDS-", CON_LVDS),
503            ("Component-", CON_COMPONENT),
504            ("DIN-", CON_9PIN_DIN),
505            ("TV-", CON_TV),
506            ("Virtual-", CON_VIRTUAL),
507            ("DSI-", CON_DSI),
508            ("DPI-", CON_DPI),
509            ("Writeback-", CON_WRITEBACK),
510            ("SPI-", CON_SPI),
511            ("USB-", CON_USB),
512        ];
513        for (prefix, ty) in pairs {
514            if let Some(suffix) = self.strip_prefix(prefix)
515                && let Ok(idx) = u32::from_str(suffix)
516            {
517                return Ok((ty, idx));
518            }
519        }
520        Err(format!("`{}` is not a valid connector identifier", self))
521    }
522}
523
524/// Module containing all known connector types.
525pub mod connector_type {
526    use serde::{Deserialize, Serialize};
527
528    /// The type of a connector.
529    #[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
530    pub struct ConnectorType(pub u32);
531
532    pub const CON_UNKNOWN: ConnectorType = ConnectorType(0);
533    pub const CON_VGA: ConnectorType = ConnectorType(1);
534    pub const CON_DVII: ConnectorType = ConnectorType(2);
535    pub const CON_DVID: ConnectorType = ConnectorType(3);
536    pub const CON_DVIA: ConnectorType = ConnectorType(4);
537    pub const CON_COMPOSITE: ConnectorType = ConnectorType(5);
538    pub const CON_SVIDEO: ConnectorType = ConnectorType(6);
539    pub const CON_LVDS: ConnectorType = ConnectorType(7);
540    pub const CON_COMPONENT: ConnectorType = ConnectorType(8);
541    pub const CON_9PIN_DIN: ConnectorType = ConnectorType(9);
542    pub const CON_DISPLAY_PORT: ConnectorType = ConnectorType(10);
543    pub const CON_HDMIA: ConnectorType = ConnectorType(11);
544    pub const CON_HDMIB: ConnectorType = ConnectorType(12);
545    pub const CON_TV: ConnectorType = ConnectorType(13);
546    pub const CON_EDP: ConnectorType = ConnectorType(14);
547    pub const CON_VIRTUAL: ConnectorType = ConnectorType(15);
548    pub const CON_DSI: ConnectorType = ConnectorType(16);
549    pub const CON_DPI: ConnectorType = ConnectorType(17);
550    pub const CON_WRITEBACK: ConnectorType = ConnectorType(18);
551    pub const CON_SPI: ConnectorType = ConnectorType(19);
552    pub const CON_USB: ConnectorType = ConnectorType(20);
553    pub const CON_EMBEDDED_WINDOW: ConnectorType = ConnectorType(u32::MAX);
554    pub const CON_VIRTUAL_OUTPUT: ConnectorType = ConnectorType(u32::MAX - 1);
555}
556
557/// A *Direct Rendering Manager* (DRM) device.
558///
559/// It's easiest to think of a DRM device as a graphics card.
560/// There are also DRM devices that are emulated in software but you are unlikely to encounter
561/// those accidentally.
562#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
563pub struct DrmDevice(pub u64);
564
565impl DrmDevice {
566    /// Returns the connectors of this device.
567    pub fn connectors(self) -> Vec<Connector> {
568        get!().connectors(Some(self))
569    }
570
571    /// Returns the devnode of this device.
572    ///
573    /// E.g. `/dev/dri/card0`.
574    pub fn devnode(self) -> String {
575        get!().drm_device_devnode(self)
576    }
577
578    /// Returns the syspath of this device.
579    ///
580    /// E.g. `/sys/devices/pci0000:00/0000:00:03.1/0000:07:00.0`.
581    pub fn syspath(self) -> String {
582        get!().drm_device_syspath(self)
583    }
584
585    /// Returns the vendor of this device.
586    ///
587    /// E.g. `Advanced Micro Devices, Inc. [AMD/ATI]`.
588    pub fn vendor(self) -> String {
589        get!().drm_device_vendor(self)
590    }
591
592    /// Returns the model of this device.
593    ///
594    /// E.g. `Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570 Armor 8G OC)`.
595    pub fn model(self) -> String {
596        get!().drm_device_model(self)
597    }
598
599    /// Returns the PIC ID of this device.
600    ///
601    /// E.g. `1002:67DF`.
602    pub fn pci_id(self) -> PciId {
603        get!().drm_device_pci_id(self)
604    }
605
606    /// Makes this device the render device.
607    pub fn make_render_device(self) {
608        get!().make_render_device(self);
609    }
610
611    /// Sets the preferred graphics API for this device.
612    ///
613    /// If the API cannot be used, the compositor will try other APIs.
614    pub fn set_gfx_api(self, gfx_api: GfxApi) {
615        get!().set_gfx_api(Some(self), gfx_api);
616    }
617
618    /// Enables or disables direct scanout of client surfaces for this device.
619    pub fn set_direct_scanout_enabled(self, enabled: bool) {
620        get!().set_direct_scanout_enabled(Some(self), enabled);
621    }
622
623    /// Sets the flip margin of this device.
624    ///
625    /// This is duration between the compositor initiating a page flip and the output's
626    /// vblank event. This determines the minimum input latency. The default is 1.5 ms.
627    ///
628    /// Note that if the margin is too small, the compositor will dynamically increase it.
629    pub fn set_flip_margin(self, margin: Duration) {
630        get!().set_flip_margin(self, margin);
631    }
632}
633
634/// A graphics API.
635#[non_exhaustive]
636#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
637pub enum GfxApi {
638    OpenGl,
639    Vulkan,
640}
641
642/// Sets the default graphics API.
643///
644/// If the API cannot be used, the compositor will try other APIs.
645///
646/// This setting can be overwritten per-device with [DrmDevice::set_gfx_api].
647///
648/// This call has no effect on devices that have already been initialized.
649pub fn set_gfx_api(gfx_api: GfxApi) {
650    get!().set_gfx_api(None, gfx_api);
651}
652
653/// Enables or disables direct scanout of client surfaces.
654///
655/// The default is `true`.
656///
657/// This setting can be overwritten per-device with [DrmDevice::set_direct_scanout_enabled].
658pub fn set_direct_scanout_enabled(enabled: bool) {
659    get!().set_direct_scanout_enabled(None, enabled);
660}
661
662/// A transformation.
663#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
664pub enum Transform {
665    /// No transformation.
666    #[default]
667    None,
668    /// Rotate 90 degrees counter-clockwise.
669    Rotate90,
670    /// Rotate 180 degrees counter-clockwise.
671    Rotate180,
672    /// Rotate 270 degrees counter-clockwise.
673    Rotate270,
674    /// Flip around the vertical axis.
675    Flip,
676    /// Flip around the vertical axis, then rotate 90 degrees counter-clockwise.
677    FlipRotate90,
678    /// Flip around the vertical axis, then rotate 180 degrees counter-clockwise.
679    FlipRotate180,
680    /// Flip around the vertical axis, then rotate 270 degrees counter-clockwise.
681    FlipRotate270,
682}
683
684/// The VRR mode of a connector.
685#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
686pub struct VrrMode(pub u32);
687
688impl VrrMode {
689    /// VRR is never enabled.
690    pub const NEVER: Self = Self(0);
691    /// VRR is always enabled.
692    pub const ALWAYS: Self = Self(1);
693    /// VRR is enabled when one or more applications are displayed fullscreen.
694    pub const VARIANT_1: Self = Self(2);
695    /// VRR is enabled when a single application is displayed fullscreen.
696    pub const VARIANT_2: Self = Self(3);
697    /// VRR is enabled when a single game or video is displayed fullscreen.
698    pub const VARIANT_3: Self = Self(4);
699}
700
701/// Sets the default VRR mode.
702///
703/// This setting can be overwritten on a per-connector basis with [Connector::set_vrr_mode].
704pub fn set_vrr_mode(mode: VrrMode) {
705    get!().set_vrr_mode(None, mode)
706}
707
708/// Sets the VRR cursor refresh rate.
709///
710/// Limits the rate at which cursors are updated on screen when VRR is active.
711///
712/// Setting this to infinity disables the limiter.
713///
714/// This setting can be overwritten on a per-connector basis with [Connector::set_vrr_cursor_hz].
715pub fn set_vrr_cursor_hz(hz: f64) {
716    get!().set_vrr_cursor_hz(None, hz)
717}
718
719/// The tearing mode of a connector.
720#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
721pub struct TearingMode(pub u32);
722
723impl TearingMode {
724    /// Tearing is never enabled.
725    pub const NEVER: Self = Self(0);
726    /// Tearing is always enabled.
727    pub const ALWAYS: Self = Self(1);
728    /// Tearing is enabled when one or more applications are displayed fullscreen.
729    pub const VARIANT_1: Self = Self(2);
730    /// Tearing is enabled when a single application is displayed fullscreen.
731    pub const VARIANT_2: Self = Self(3);
732    /// Tearing is enabled when a single application is displayed fullscreen and the
733    /// application has requested tearing.
734    ///
735    /// This is the default.
736    pub const VARIANT_3: Self = Self(4);
737}
738
739/// Sets the default tearing mode.
740///
741/// This setting can be overwritten on a per-connector basis with [Connector::set_tearing_mode].
742pub fn set_tearing_mode(mode: TearingMode) {
743    get!().set_tearing_mode(None, mode)
744}
745
746/// Creates a virtual output with the given name.
747///
748/// This is a no-op if a virtual output with that name already exists.
749///
750/// The created connector can be accessed with [`get_connector_by_name("VO-{name}")`].
751///
752/// A newly created connector is initially disabled. When a connector is destroyed and
753/// later recreated, its previous state is restored.
754pub fn create_virtual_output(name: &str) {
755    get!().create_virtual_output(name);
756}
757
758/// Removes the virtual output with the given name.
759///
760/// This is a no-op if a virtual output with that name does not exist.
761pub fn remove_virtual_output(name: &str) {
762    get!().remove_virtual_output(name);
763}
764
765/// A graphics format.
766#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
767pub struct Format(pub u32);
768
769impl Format {
770    pub const ARGB8888: Self = Self(0);
771    pub const XRGB8888: Self = Self(1);
772    pub const ABGR8888: Self = Self(2);
773    pub const XBGR8888: Self = Self(3);
774    pub const R8: Self = Self(4);
775    pub const GR88: Self = Self(5);
776    pub const RGB888: Self = Self(6);
777    pub const BGR888: Self = Self(7);
778    pub const RGBA4444: Self = Self(8);
779    pub const RGBX4444: Self = Self(9);
780    pub const BGRA4444: Self = Self(10);
781    pub const BGRX4444: Self = Self(11);
782    pub const RGB565: Self = Self(12);
783    pub const BGR565: Self = Self(13);
784    pub const RGBA5551: Self = Self(14);
785    pub const RGBX5551: Self = Self(15);
786    pub const BGRA5551: Self = Self(16);
787    pub const BGRX5551: Self = Self(17);
788    pub const ARGB1555: Self = Self(18);
789    pub const XRGB1555: Self = Self(19);
790    pub const ARGB2101010: Self = Self(20);
791    pub const XRGB2101010: Self = Self(21);
792    pub const ABGR2101010: Self = Self(22);
793    pub const XBGR2101010: Self = Self(23);
794    pub const ABGR16161616: Self = Self(24);
795    pub const XBGR16161616: Self = Self(25);
796    pub const ABGR16161616F: Self = Self(26);
797    pub const XBGR16161616F: Self = Self(27);
798    pub const BGR161616: Self = Self(28);
799    pub const R16F: Self = Self(29);
800    pub const GR1616F: Self = Self(30);
801    pub const BGR161616F: Self = Self(31);
802    pub const R32F: Self = Self(32);
803    pub const GR3232F: Self = Self(33);
804    pub const BGR323232F: Self = Self(34);
805    pub const ABGR32323232F: Self = Self(35);
806}
807
808/// A color space.
809#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
810pub struct ColorSpace(pub u32);
811
812impl ColorSpace {
813    /// The default color space (usually sRGB).
814    pub const DEFAULT: Self = Self(0);
815    /// The BT.2020 color space.
816    pub const BT2020: Self = Self(1);
817}
818
819/// An electro-optical transfer function (EOTF).
820#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
821pub struct Eotf(pub u32);
822
823#[deprecated = "use the Eotf type instead"]
824pub type TransferFunction = Eotf;
825
826impl Eotf {
827    /// The default EOTF (usually gamma22).
828    pub const DEFAULT: Self = Self(0);
829    /// The PQ EOTF.
830    pub const PQ: Self = Self(1);
831}
832
833/// A space in which color blending is performed.
834#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
835pub struct BlendSpace(pub u32);
836
837impl BlendSpace {
838    /// The sRGB blend space with sRGB primaries and gamma22 transfer function. This is
839    /// the classic desktop blend space.
840    pub const SRGB: Self = Self(0);
841    /// The linear blend space performs blending in linear space, which is more physically
842    /// correct but leads to much lighter output when blending light and dark colors.
843    pub const LINEAR: Self = Self(1);
844}
845
846/// How textures are scaled.
847#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
848pub struct ScalingFilter(pub u32);
849
850impl ScalingFilter {
851    /// Bilinear filtering.
852    pub const LINEAR: Self = Self(0);
853    /// Nearest filtering.
854    pub const NEAREST: Self = Self(1);
855}