EvanZhu0721 / 公开仓库
SBMS
SBMS complete the calculation rules for the default logical desktop topology relationship and the physical dimensions of the displays in the Windows multi-screen collaboration system.
文件
../ diagnostic_view.rs6.1 KBgeometry_view.rs15.9 KBhandlers.rs32.1 KBprojection.rs6.7 KBstate.rs13.2 KBsrc/ui/geometry_view.rs15.9 KB
1
use super::*;2
3
pub(super) fn update_geometry_preview(ui: &QuickAccess, displays: &[DisplayOption]) {4
update_resolution_suggestions(ui);5
ui.set_geometry_error("".into());6
ui.set_geometry_valid(false);7
let request = match geometry_request(ui, displays) {8
Ok(Some(request)) => request,9
Ok(None) => {10
ui.set_geometry_result("Enter display measurements".into());11
ui.set_geometry_result_detail("Enter pixels, diagonal inches, and aspect ratio".into());12
return;13
}14
Err(error) => {15
ui.set_geometry_result("Check the values".into());16
ui.set_geometry_result_detail("".into());17
ui.set_geometry_error(error.into());18
return;19
}20
};21
match request.calculate() {22
Ok(result) => set_geometry_result(ui, result, false),23
Err(error) => {24
ui.set_geometry_result("Check the values".into());25
ui.set_geometry_result_detail("".into());26
ui.set_geometry_error(error.to_string().into());27
}28
}29
}30
31
pub(super) const COMMON_WIDTHS: &[&str] = &[32
"1280", "1920", "2560", "3440", "3840", "4096", "5120", "6016", "7680",33
];34
pub(super) const COMMON_HEIGHTS: &[&str] = &[35
"720", "1080", "1440", "1600", "2160", "2880", "3384", "4320",36
];37
pub(super) const DEFAULT_STREAM_ASPECT_WIDTH: u64 = 16;38
pub(super) const DEFAULT_STREAM_ASPECT_HEIGHT: u64 = 10;39
40
pub(super) fn resolution_suggestion(41
input: &str,42
candidates: &[&'static str],43
) -> Option<&'static str> {44
let input = input.trim();45
if input.is_empty() || !input.bytes().all(|byte| byte.is_ascii_digit()) {46
return None;47
}48
49
let mut matches = candidates50
.iter()51
.copied()52
.filter(|candidate| candidate.len() > input.len() && candidate.starts_with(input));53
let suggestion = matches.next()?;54
matches.next().is_none().then_some(suggestion)55
}56
57
pub(super) fn set_resolution_suggestion(58
input: &str,59
candidates: &[&'static str],60
set_suggestion: impl FnOnce(SharedString),61
set_suffix: impl FnOnce(SharedString),62
) {63
let suggestion = resolution_suggestion(input, candidates);64
set_suggestion(suggestion.unwrap_or_default().into());65
set_suffix(66
suggestion67
.map(|candidate| &candidate[input.trim().len()..])68
.unwrap_or_default()69
.into(),70
);71
}72
73
pub(super) fn set_stream_dimension_suggestion(74
input: &str,75
other_dimension: &str,76
candidates: &[&'static str],77
aspect_numerator: u64,78
aspect_denominator: u64,79
set_suggestion: impl FnOnce(SharedString),80
set_suffix: impl FnOnce(SharedString),81
) {82
let input = input.trim();83
let suggestion = if input.is_empty() {84
scaled_dimension_suggestion(other_dimension, aspect_numerator, aspect_denominator)85
} else {86
resolution_suggestion(input, candidates).map(str::to_owned)87
};88
let suffix = suggestion89
.as_deref()90
.and_then(|candidate| candidate.strip_prefix(input))91
.unwrap_or_default();92
set_suggestion(suggestion.clone().unwrap_or_default().into());93
set_suffix(suffix.into());94
}95
96
pub(super) fn scaled_dimension_suggestion(97
source: &str,98
numerator: u64,99
denominator: u64,100
) -> Option<String> {101
let source = source.trim().parse::<u64>().ok()?;102
if source == 0 || denominator == 0 {103
return None;104
}105
let scaled = source106
.checked_mul(numerator)?107
.checked_add(denominator / 2)?108
/ denominator;109
(scaled > 0 && scaled <= u64::from(MAX_VIRTUAL_DIMENSION)).then(|| scaled.to_string())110
}111
112
pub(super) fn stream_aspect_ratio(width: &str, height: &str) -> Option<String> {113
let width = width.trim().parse::<u32>().ok()?;114
let height = height.trim().parse::<u32>().ok()?;115
if width == 0 || height == 0 {116
return None;117
}118
let divisor = greatest_common_divisor(width, height);119
let reduced_width = width / divisor;120
let reduced_height = height / divisor;121
if (reduced_width, reduced_height) == (8, 5) {122
Some("16:10".into())123
} else {124
Some(format!("{reduced_width}:{reduced_height}"))125
}126
}127
128
pub(super) fn greatest_common_divisor(mut left: u32, mut right: u32) -> u32 {129
while right != 0 {130
(left, right) = (right, left % right);131
}132
left133
}134
135
pub(super) fn update_resolution_suggestions(ui: &QuickAccess) {136
set_resolution_suggestion(137
ui.get_reference_width().as_str(),138
COMMON_WIDTHS,139
|value| ui.set_reference_width_suggestion(value),140
|value| ui.set_reference_width_suffix(value),141
);142
set_resolution_suggestion(143
ui.get_reference_height().as_str(),144
COMMON_HEIGHTS,145
|value| ui.set_reference_height_suggestion(value),146
|value| ui.set_reference_height_suffix(value),147
);148
}149
150
pub(super) fn calculate_geometry(ui: &QuickAccess, displays: &[DisplayOption]) {151
update_geometry_preview(ui, displays);152
if !ui.get_geometry_valid() {153
if ui.get_page() != QuickAccessPage::Geometry && !ui.get_geometry_error().is_empty() {154
surface_geometry_error(ui, ui.get_geometry_error().as_str());155
}156
return;157
}158
if ui.get_stream_only() {159
validate_stream_fields(ui, displays);160
return;161
}162
let request = match geometry_request(ui, displays) {163
Ok(Some(request)) => request,164
Ok(_) => return,165
Err(error) => {166
surface_geometry_error(ui, &error);167
return;168
}169
};170
let result = request171
.calculate()172
.expect("validated geometry request became invalid");173
set_geometry_result(ui, result, true);174
}175
176
pub(super) fn selected_option<'a>(177
ui: &QuickAccess,178
displays: &'a [DisplayOption],179
index: i32,180
) -> Option<&'a DisplayOption> {181
let id = selected_display_id(ui, index)?;182
(id != STREAM_ONLY_ID)183
.then(|| displays.iter().find(|display| display.id == id))184
.flatten()185
}186
187
pub(super) fn update_reference_action(ui: &QuickAccess, displays: &[DisplayOption]) {188
let reference_source = selected_reference_source(ui);189
let reference_manual = matches!(reference_source, Some(ReferenceSource::Manual));190
ui.set_reference_manual(reference_manual);191
let target_ready = if ui.get_stream_only() {192
aspect_ratio_from_index(ui.get_stream_aspect_ratio_index())193
.and_then(|aspect_ratio| {194
let rotation = rotation_from_index(ui.get_stream_rotation())?;195
stream_target_geometry(196
ui.get_stream_width().as_str(),197
ui.get_stream_height().as_str(),198
ui.get_stream_diagonal().as_str(),199
aspect_ratio,200
rotation,201
)202
})203
.is_ok()204
} else {205
automatic_target_geometry(ui, displays, "Target").is_ok()206
};207
let enabled = if reference_manual {208
target_ready209
} else {210
target_ready211
&& selected_reference_display_index(ui, displays)212
.and_then(|index| automatic_display_geometry(displays, index, "Reference").ok())213
.is_some()214
};215
ui.set_reference_action_enabled(enabled);216
}217
218
pub(super) fn surface_geometry_error(ui: &QuickAccess, error: &str) {219
ui.set_geometry_error(error.into());220
if ui.get_page() != QuickAccessPage::Geometry {221
ui.set_error_text(error.into());222
ui.set_state_detail("Geometry calculation needs attention".into());223
}224
}225
226
pub(super) fn geometry_request(227
ui: &QuickAccess,228
displays: &[DisplayOption],229
) -> Result<Option<SizingRequest>, String> {230
let stream = ui231
.get_stream_only()232
.then(|| {233
ValidatedStreamFields::parse(234
ui.get_stream_width().as_str(),235
ui.get_stream_height().as_str(),236
ui.get_stream_diagonal().as_str(),237
ui.get_stream_refresh().as_str(),238
ui.get_stream_aspect_ratio_index(),239
ui.get_stream_rotation(),240
)241
})242
.transpose()?;243
let target = if let Some(stream) = stream {244
stream.target_geometry()?245
} else {246
automatic_target_geometry(ui, displays, "Target")?247
};248
let reference = if ui.get_reference_manual() {249
let values = [250
ui.get_reference_width(),251
ui.get_reference_height(),252
ui.get_reference_diagonal(),253
];254
if values.iter().any(|value| value.trim().is_empty()) {255
return Ok(None);256
}257
DisplayGeometry {258
native_pixels: PixelSize {259
width: parse_u32(&values[0], "Reference width")?,260
height: parse_u32(&values[1], "Reference height")?,261
},262
physical: PhysicalMeasurement::DiagonalMm(263
parse_f64(&values[2], "Reference diagonal")? * MILLIMETERS_PER_INCH,264
),265
aspect_ratio: Some(aspect_ratio_from_index(ui.get_reference_aspect_ratio())?),266
rotation: rotation_from_index(ui.get_reference_rotation())?,267
}268
} else {269
let reference_index = selected_reference_display_index(ui, displays)270
.ok_or_else(|| "Reference display is not selected".to_string())?;271
automatic_display_geometry(displays, reference_index, "Reference")?272
};273
Ok(Some(SizingRequest {274
reference,275
target,276
strategy: sizing_strategy_from_index(ui.get_sizing_strategy())?,277
alignment: 2,278
preferred_refresh_millihz: Some(279
stream280
.map(|fields| fields.screen.refresh_millihz)281
.unwrap_or(240_000),282
),283
}))284
}285
286
pub(super) fn stream_target_geometry(287
width: &str,288
height: &str,289
diagonal: &str,290
aspect_ratio: AspectRatio,291
rotation: Rotation,292
) -> Result<DisplayGeometry, String> {293
ValidatedStreamFields::parse(294
width,295
height,296
diagonal,297
"60",298
aspect_ratio_option_index(aspect_ratio),299
rotation_index(rotation),300
)?301
.target_geometry()302
}303
304
pub(super) fn automatic_display_geometry(305
displays: &[DisplayOption],306
index: i32,307
role: &str,308
) -> Result<DisplayGeometry, String> {309
let display = (index >= 0)310
.then(|| displays.get(index as usize))311
.flatten()312
.ok_or_else(|| format!("{role} display is not selected"))?;313
let (Some(width_mm), Some(height_mm)) = (display.physical_width_mm, display.physical_height_mm)314
else {315
return Err(format!(316
"{role} display physical size is unavailable; choose Manual or reconnect it directly"317
));318
};319
Ok(DisplayGeometry {320
native_pixels: PixelSize {321
width: display.native_width,322
height: display.native_height,323
},324
physical: PhysicalMeasurement::DimensionsMm {325
width: width_mm,326
height: height_mm,327
},328
aspect_ratio: None,329
rotation: display.rotation,330
})331
}332
333
pub(super) fn automatic_target_geometry(334
ui: &QuickAccess,335
displays: &[DisplayOption],336
role: &str,337
) -> Result<DisplayGeometry, String> {338
let display = selected_option(ui, displays, ui.get_selected_display())339
.ok_or_else(|| format!("{role} display is not selected"))?;340
display_geometry(display, role)341
}342
343
pub(super) fn display_geometry(344
display: &DisplayOption,345
role: &str,346
) -> Result<DisplayGeometry, String> {347
let (Some(width_mm), Some(height_mm)) = (display.physical_width_mm, display.physical_height_mm)348
else {349
return Err(format!(350
"{role} display physical size is unavailable; choose Manual or reconnect it directly"351
));352
};353
Ok(DisplayGeometry {354
native_pixels: PixelSize {355
width: display.native_width,356
height: display.native_height,357
},358
physical: PhysicalMeasurement::DimensionsMm {359
width: width_mm,360
height: height_mm,361
},362
aspect_ratio: None,363
rotation: display.rotation,364
})365
}366
367
pub(super) fn selected_reference_display_index(368
ui: &QuickAccess,369
displays: &[DisplayOption],370
) -> Option<i32> {371
let Some(ReferenceSource::Display(id)) = selected_reference_source(ui) else {372
return None;373
};374
displays375
.iter()376
.position(|display| display.id == id)377
.map(|index| index as i32)378
}379
380
pub(super) fn parse_f64(value: &str, name: &str) -> Result<f64, String> {381
let inches = value382
.trim()383
.parse()384
.map_err(|_| format!("{name} must be a number in inches"))?;385
if !valid_physical_millimeters(inches * MILLIMETERS_PER_INCH) {386
return Err(format!("{name} must be between 0.4 and 393.7 inches"));387
}388
Ok(inches)389
}390
391
pub(super) fn sizing_strategy_from_index(index: i32) -> Result<SizingStrategy, String> {392
match index {393
0 => Ok(SizingStrategy::MatchPhysicalSize),394
1 => Ok(SizingStrategy::RoundedScale),395
2 => Ok(SizingStrategy::IntegerScale),396
_ => Err("Choose a supported sizing strategy".into()),397
}398
}399
400
pub(super) fn sizing_strategy_index(strategy: SizingStrategy) -> i32 {401
match strategy {402
SizingStrategy::MatchPhysicalSize => 0,403
SizingStrategy::RoundedScale => 1,404
SizingStrategy::IntegerScale => 2,405
}406
}407
408
pub(super) fn populate_geometry_fields(ui: &QuickAccess, request: SizingRequest) {409
ui.set_reference_width(request.reference.native_pixels.width.to_string().into());410
ui.set_reference_height(request.reference.native_pixels.height.to_string().into());411
ui.set_reference_diagonal(412
format!(413
"{:.1}",414
diagonal_mm(request.reference) / MILLIMETERS_PER_INCH415
)416
.into(),417
);418
ui.set_reference_aspect_ratio(aspect_ratio_index(request.reference));419
ui.set_reference_rotation(rotation_index(request.reference.rotation));420
ui.set_sizing_strategy(sizing_strategy_index(request.strategy));421
}422
423
pub(super) fn diagonal_mm(display: DisplayGeometry) -> f64 {424
match display.physical {425
PhysicalMeasurement::DiagonalMm(value) => value,426
PhysicalMeasurement::DimensionsMm { width, height } => width.hypot(height),427
}428
}429
430
pub(super) fn aspect_ratio_index(display: DisplayGeometry) -> i32 {431
let ratio = display.aspect_ratio.unwrap_or(AspectRatio {432
width: display.native_pixels.width,433
height: display.native_pixels.height,434
});435
aspect_ratio_option_index(ratio)436
}437
438
pub(super) fn screen_override_aspect_ratio(index: i32) -> Result<Option<AspectRatio>, String> {439
if index == 0 {440
Ok(None)441
} else {442
aspect_ratio_from_index(index - 1).map(Some)443
}444
}445
446
pub(super) fn set_geometry_result(ui: &QuickAccess, result: SizingResult, configured: bool) {447
let mode = format!(448
"{} × {}",449
result.virtual_mode.width, result.virtual_mode.height450
);451
ui.set_geometry_result(mode.as_str().into());452
ui.set_geometry_result_detail(453
format!("Scale {:.2}× × {:.2}×", result.scale_x, result.scale_y).into(),454
);455
ui.set_geometry_valid(!ui.get_geometry_save_blocked());456
ui.set_geometry_error("".into());457
if configured {458
let refresh = result459
.preferred_refresh_millihz460
.map(format_refresh_millihz)461
.unwrap_or_else(|| "Driver refresh".into());462
ui.set_geometry_configured(true);463
ui.set_geometry_summary(mode.as_str().into());464
ui.set_geometry_summary_detail(format!("Planned · {refresh}").into());465
ui.set_state_detail("Ready to start".into());466
}467
}468
469
pub(super) fn format_refresh_millihz(refresh: u32) -> String {470
if refresh.is_multiple_of(1_000) {471
format!("{} Hz", refresh / 1_000)472
} else {473
format!("{:.2} Hz", f64::from(refresh) / 1_000.0)474
}475
}476
477
pub(super) fn set_geometry_unconfigured(ui: &QuickAccess) {478
ui.set_geometry_valid(false);479
ui.set_geometry_configured(false);480
ui.set_geometry_summary("3840 × 2160".into());481
ui.set_geometry_summary_detail("Driver · 240 Hz".into());482
ui.set_geometry_result("Enter display measurements".into());483
ui.set_geometry_result_detail("Enter pixels, diagonal inches, and aspect ratio".into());484
}485