Skip to content

Commit 3851d20

Browse files
committed
examples/gui_vm: Add ability to specify display params
Add ability to specify display refresh rate and physical size, by extending the --display argument value. Signed-off-by: Matej Hrica <[email protected]>
1 parent eb91652 commit 3851d20

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

examples/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/gui_vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ clap = "4.5.39"
1111
clap_derive = "4.5.32"
1212
log = "0.4.27"
1313
env_logger = "0.9.3"
14+
regex = "1.11.1"

examples/gui_vm/src/main.rs

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,81 @@ use gtk_display::DisplayBackendHandle;
44
use krun_sys::{
55
VIRGLRENDERER_RENDER_SERVER, VIRGLRENDERER_THREAD_SYNC, VIRGLRENDERER_USE_ASYNC_FENCE_CB,
66
VIRGLRENDERER_USE_EGL, VIRGLRENDERER_VENUS, krun_add_display, krun_create_ctx,
7+
krun_display_set_dpi, krun_display_set_physical_size, krun_display_set_refresh_rate,
78
krun_set_display_backend, krun_set_exec, krun_set_gpu_options, krun_set_log_level,
89
krun_set_root, krun_start_enter,
910
};
1011
use log::LevelFilter;
12+
use regex::{Captures, Regex};
1113
use std::ffi::{CString, c_void};
14+
use std::fmt::Display;
1215
use std::process::exit;
1316
use std::ptr::null;
17+
use std::str::FromStr;
18+
use std::sync::LazyLock;
1419
use std::thread;
1520

1621
mod krun_utils;
1722

23+
#[derive(Debug, Copy, Clone)]
24+
pub enum PhysicalSize {
25+
Dpi(u32),
26+
DimensionsMillimeters(u16, u16),
27+
}
28+
1829
#[derive(Debug, Clone, Copy)]
1930
struct DisplayArg {
2031
width: u32,
2132
height: u32,
33+
refresh_rate: Option<u32>,
34+
physical_size: Option<PhysicalSize>,
2235
}
2336

24-
fn parse_display(s: &str) -> Result<DisplayArg, String> {
25-
let parts: Vec<&str> = s.split('x').collect();
26-
if parts.len() != 2 {
27-
return Err("Expected format: [width]x[height]".to_string());
37+
/// Parses a display settings string.
38+
/// The expected format is "WIDTHxHEIGHT[@FPS][:DPIdpi|:PHYSICAL_WIDTHxPHYSICAL_HEIGHTmm]".
39+
fn parse_display(display_string: &str) -> Result<DisplayArg, String> {
40+
static RE: LazyLock<Regex> = LazyLock::new(|| {
41+
Regex::new(
42+
r"^(?P<width>\d+)x(?P<height>\d+)(?:@(?P<refresh_rate>\d+))?(?::(?P<dpi>\d+)dpi|:(?P<width_mm>\d+)x(?P<height_mm>\d+)mm)?$",
43+
).unwrap()
44+
});
45+
46+
let captures = RE.captures(display_string).ok_or_else(|| {
47+
format!("Invalid display string '{s}' format. Examples of valid values:\n '1920x1080', '1920x1080@60', '1920x1080:162x91mm', '1920x1080:300dpi', '1920x1080@90:300dpi'")
48+
})?;
49+
50+
fn parse_group<T: FromStr>(captures: &Captures, name: &str) -> Result<Option<T>, String>
51+
where
52+
T::Err: Display,
53+
{
54+
captures
55+
.name(name)
56+
.map(|match_| {
57+
match_
58+
.as_str()
59+
.parse::<T>()
60+
.map_err(|e| format!("Failed to parse {name}: {e}"))
61+
})
62+
.transpose()
2863
}
29-
let width = parts[0].parse().map_err(|_| "Invalid width")?;
30-
let height = parts[1].parse().map_err(|_| "Invalid height")?;
31-
Ok(DisplayArg { width, height })
64+
65+
Ok(DisplayArg {
66+
width: parse_group(&captures, "width")?.expect("regex bug"),
67+
height: parse_group(&captures, "height")?.expect("regex bug"),
68+
refresh_rate: parse_group(&captures, "refresh_rate")?,
69+
physical_size: match (
70+
parse_group(&captures, "dpi")?,
71+
parse_group(&captures, "width_mm")?,
72+
parse_group(&captures, "height_mm")?,
73+
) {
74+
(Some(dpi), None, None) => Some(PhysicalSize::Dpi(dpi)),
75+
(None, Some(width_mm), Some(height_mm)) => {
76+
Some(PhysicalSize::DimensionsMillimeters(width_mm, height_mm))
77+
}
78+
(None, None, None) => None,
79+
_ => unreachable!("regex bug"),
80+
},
81+
})
3282
}
3383

3484
#[derive(Parser, Debug)]
@@ -38,6 +88,7 @@ struct Args {
3888

3989
executable: Option<CString>,
4090
argv: Vec<CString>,
91+
// Display specifications in the format WIDTHxHEIGHT[@FPS][:DPIdpi|:PHYSICAL_WIDTHxPHYSICAL_HEIGHTmm]
4192
#[clap(long, value_parser = parse_display)]
4293
display: Vec<DisplayArg>,
4394
}
@@ -71,7 +122,21 @@ fn krun_thread(args: &Args, display_backend_handle: DisplayBackendHandle) -> any
71122
}
72123

73124
for display in &args.display {
74-
krun_call!(krun_add_display(ctx, display.width, display.height))?;
125+
let display_id = krun_call_u32!(krun_add_display(ctx, display.width, display.height))?;
126+
if let Some(refresh_rate) = display.refresh_rate {
127+
krun_call!(krun_display_set_refresh_rate(ctx, display_id, refresh_rate))?;
128+
}
129+
match display.physical_size {
130+
None => (),
131+
Some(PhysicalSize::Dpi(dpi)) => {
132+
krun_call!(krun_display_set_dpi(ctx, display_id, dpi))?;
133+
}
134+
Some(PhysicalSize::DimensionsMillimeters(width_mm, height_mm)) => {
135+
krun_call!(krun_display_set_physical_size(
136+
ctx, display_id, width_mm, height_mm
137+
))?;
138+
}
139+
};
75140
}
76141
let display_backend = display_backend_handle.get();
77142
krun_call!(krun_set_display_backend(

0 commit comments

Comments
 (0)