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.
文件
../ bininputuiconfig.rs81.2 KBcontrol.rs4.1 KBcontroller.rs43.6 KBdiagnostics.rs24.2 KBdisplay.rs29.8 KBgeometry.rs21.4 KBgpu_renderer.rs37.4 KBinput.rs79.7 KBlaunch_broker.rs10.3 KBlib.rs350 Blimits.rs714 Bmain.rs24.4 KBmapping.rs46.5 KBnetwork.rs9.2 KBrenderer.rs20.6 KBsession_gate.rs14.9 KBsunshine.rs12.2 KBui.rs68.5 KBvirtual_display.rs7.2 KBwin32_flyout.rs3.4 KBwin32_tray.rs22.3 KBwindow_migration.rs43.8 KBsrc/session_gate.rs14.9 KB
1
use std::error::Error;2
use std::ffi::c_void;3
use std::fmt::{Display, Formatter};4
use std::mem::size_of;5
6
use serde::{Deserialize, Serialize};7
use windows::Win32::Foundation::{8
CloseHandle, ERROR_ALREADY_EXISTS, GetLastError, HANDLE, HLOCAL, INVALID_HANDLE_VALUE,9
LocalFree,10
};11
use windows::Win32::Security::Authorization::{12
ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1,13
};14
use windows::Win32::Security::{15
GetTokenInformation, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, TOKEN_QUERY, TOKEN_USER,16
TokenUser,17
};18
use windows::Win32::System::Memory::{19
CreateFileMappingW, FILE_MAP_WRITE, MapViewOfFile, PAGE_READWRITE, UnmapViewOfFile,20
};21
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};22
use windows::core::{PCWSTR, PWSTR, w};23
24
use crate::limits::{MAX_OUTPUTS, MAX_REFRESH_HZ, MAX_VIRTUAL_DIMENSION, MIN_REFRESH_HZ};25
26
const GATE_MAGIC: u32 = 0x5342_4737;27
const PROTOCOL_VERSION: u32 = 7;28
const GATE_MAPPING: PCWSTR = w!("Global\\SBMSSession-v7");29
30
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]31
pub struct VirtualMode {32
pub width: u32,33
pub height: u32,34
pub refresh_numerator: u32,35
pub refresh_denominator: u32,36
}37
38
impl Default for VirtualMode {39
fn default() -> Self {40
Self {41
width: 3840,42
height: 2160,43
refresh_numerator: 240,44
refresh_denominator: 1,45
}46
}47
}48
49
impl VirtualMode {50
pub fn from_millihz(51
width: u32,52
height: u32,53
refresh_millihz: u32,54
) -> Result<Self, SessionGateError> {55
if refresh_millihz == 0 {56
return Err(SessionGateError("virtual refresh must be non-zero".into()));57
}58
let divisor = greatest_common_divisor(refresh_millihz, 1_000);59
let mode = Self {60
width,61
height,62
refresh_numerator: refresh_millihz / divisor,63
refresh_denominator: 1_000 / divisor,64
};65
mode.validate()?;66
Ok(mode)67
}68
69
pub fn validate(self) -> Result<(), SessionGateError> {70
if self.width == 071
|| self.height == 072
|| self.width > MAX_VIRTUAL_DIMENSION73
|| self.height > MAX_VIRTUAL_DIMENSION74
{75
return Err(SessionGateError(format!(76
"virtual dimensions must be between 1 and {MAX_VIRTUAL_DIMENSION}"77
)));78
}79
if self.refresh_numerator == 0 || self.refresh_denominator == 0 {80
return Err(SessionGateError(81
"virtual refresh numerator and denominator must be non-zero".into(),82
));83
}84
if u64::from(self.refresh_numerator) < MIN_REFRESH_HZ * u64::from(self.refresh_denominator)85
{86
return Err(SessionGateError(format!(87
"virtual refresh must be at least {MIN_REFRESH_HZ} Hz"88
)));89
}90
if u64::from(self.refresh_numerator) > MAX_REFRESH_HZ * u64::from(self.refresh_denominator)91
{92
return Err(SessionGateError(format!(93
"virtual refresh must not exceed {MAX_REFRESH_HZ} Hz"94
)));95
}96
Ok(())97
}98
}99
100
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]101
pub struct VirtualDisplayConfig {102
pub connector_index: u32,103
pub mode: VirtualMode,104
}105
106
impl VirtualDisplayConfig {107
pub const fn new(connector_index: u32, mode: VirtualMode) -> Self {108
Self {109
connector_index,110
mode,111
}112
}113
114
pub fn validate(self) -> Result<(), SessionGateError> {115
if self.connector_index >= MAX_OUTPUTS as u32 {116
return Err(SessionGateError(format!(117
"connector index must be between 0 and {}",118
MAX_OUTPUTS - 1119
)));120
}121
self.mode.validate()122
}123
}124
125
#[repr(C)]126
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]127
struct GateHeader {128
magic: u32,129
version: u32,130
count: u32,131
reserved: u32,132
}133
134
#[repr(C)]135
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]136
struct GateEntry {137
connector_index: u32,138
width: u32,139
height: u32,140
refresh_numerator: u32,141
refresh_denominator: u32,142
}143
144
impl From<VirtualDisplayConfig> for GateEntry {145
fn from(config: VirtualDisplayConfig) -> Self {146
Self {147
connector_index: config.connector_index,148
width: config.mode.width,149
height: config.mode.height,150
refresh_numerator: config.mode.refresh_numerator,151
refresh_denominator: config.mode.refresh_denominator,152
}153
}154
}155
156
#[repr(C)]157
#[derive(Clone, Copy, Debug, Eq, PartialEq)]158
struct GateConfig {159
header: GateHeader,160
entries: [GateEntry; MAX_OUTPUTS],161
}162
163
impl GateConfig {164
fn from_displays(displays: &[VirtualDisplayConfig]) -> Result<Self, SessionGateError> {165
validate_displays(displays)?;166
167
let mut entries = [GateEntry::default(); MAX_OUTPUTS];168
for (entry, display) in entries.iter_mut().zip(displays.iter().copied()) {169
*entry = display.into();170
}171
172
Ok(Self {173
header: GateHeader {174
magic: GATE_MAGIC,175
version: PROTOCOL_VERSION,176
count: displays.len() as u32,177
reserved: 0,178
},179
entries,180
})181
}182
}183
184
pub struct SessionGate {185
gate: HANDLE,186
}187
188
#[derive(Debug)]189
pub struct SessionGateError(String);190
191
impl Display for SessionGateError {192
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {193
formatter.write_str(&self.0)194
}195
}196
197
impl Error for SessionGateError {}198
199
impl SessionGate {200
pub fn create(mode: VirtualMode) -> Result<Self, SessionGateError> {201
Self::create_many(&[VirtualDisplayConfig::new(0, mode)])202
}203
204
pub fn create_many(displays: &[VirtualDisplayConfig]) -> Result<Self, SessionGateError> {205
let config = GateConfig::from_displays(displays)?;206
let descriptor = SecurityDescriptor::for_current_user()?;207
let attributes = descriptor.attributes();208
let gate = unsafe {209
CreateFileMappingW(210
INVALID_HANDLE_VALUE,211
Some(&attributes),212
PAGE_READWRITE,213
0,214
size_of::<GateConfig>() as u32,215
GATE_MAPPING,216
)217
}218
.map_err(|error| SessionGateError(format!("create session gate: {error}")))?;219
if unsafe { GetLastError() } == ERROR_ALREADY_EXISTS {220
unsafe {221
let _ = CloseHandle(gate);222
}223
return Err(SessionGateError(224
"another SBMS mapping session is already running".into(),225
));226
}227
228
let gate_view =229
unsafe { MapViewOfFile(gate, FILE_MAP_WRITE, 0, 0, size_of::<GateConfig>()) };230
if gate_view.Value.is_null() {231
unsafe {232
let _ = CloseHandle(gate);233
}234
return Err(SessionGateError("map session gate failed".into()));235
}236
unsafe {237
gate_view.Value.cast::<GateConfig>().write(config);238
let _ = UnmapViewOfFile(gate_view);239
}240
241
Ok(Self { gate })242
}243
}244
245
fn validate_displays(displays: &[VirtualDisplayConfig]) -> Result<(), SessionGateError> {246
if displays.is_empty() {247
return Err(SessionGateError(248
"at least one virtual display is required".into(),249
));250
}251
if displays.len() > MAX_OUTPUTS {252
return Err(SessionGateError(format!(253
"at most {MAX_OUTPUTS} virtual displays are supported"254
)));255
}256
257
let mut seen_connectors = 0u32;258
for display in displays {259
display.validate()?;260
let connector_bit = 1u32 << display.connector_index;261
if seen_connectors & connector_bit != 0 {262
return Err(SessionGateError(format!(263
"connector index {} is duplicated",264
display.connector_index265
)));266
}267
seen_connectors |= connector_bit;268
}269
Ok(())270
}271
272
const fn greatest_common_divisor(mut left: u32, mut right: u32) -> u32 {273
while right != 0 {274
let remainder = left % right;275
left = right;276
right = remainder;277
}278
left279
}280
281
impl Drop for SessionGate {282
fn drop(&mut self) {283
unsafe {284
let _ = CloseHandle(self.gate);285
}286
}287
}288
289
struct SecurityDescriptor(PSECURITY_DESCRIPTOR);290
291
impl SecurityDescriptor {292
fn for_current_user() -> Result<Self, SessionGateError> {293
let mut token = HANDLE::default();294
unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) }295
.map_err(|error| SessionGateError(format!("OpenProcessToken: {error}")))?;296
let mut length = 0;297
let _ = unsafe { GetTokenInformation(token, TokenUser, None, 0, &mut length) };298
let mut token_user = vec![0u8; length as usize];299
let result = unsafe {300
GetTokenInformation(301
token,302
TokenUser,303
Some(token_user.as_mut_ptr().cast()),304
length,305
&mut length,306
)307
};308
unsafe {309
let _ = CloseHandle(token);310
}311
result.map_err(|error| SessionGateError(format!("GetTokenInformation: {error}")))?;312
313
let user = unsafe { &*token_user.as_ptr().cast::<TOKEN_USER>() };314
let mut sid = PWSTR::null();315
unsafe { ConvertSidToStringSidW(user.User.Sid, &mut sid) }316
.map_err(|error| SessionGateError(format!("ConvertSidToStringSidW: {error}")))?;317
let sid_text = unsafe { sid.to_string() };318
unsafe {319
LocalFree(Some(HLOCAL(sid.0.cast::<c_void>())));320
}321
let sid_text = sid_text.map_err(|error| SessionGateError(format!("user SID: {error}")))?;322
323
let sddl = wide(&format!(324
"D:P(A;;GA;;;{sid_text})(A;;GA;;;SY)(A;;GA;;;LS)(A;;GA;;;BA)"325
));326
let mut descriptor = PSECURITY_DESCRIPTOR::default();327
unsafe {328
ConvertStringSecurityDescriptorToSecurityDescriptorW(329
PCWSTR(sddl.as_ptr()),330
SDDL_REVISION_1,331
&mut descriptor,332
None,333
)334
}335
.map_err(|error| SessionGateError(format!("security descriptor: {error}")))?;336
Ok(Self(descriptor))337
}338
339
fn attributes(&self) -> SECURITY_ATTRIBUTES {340
SECURITY_ATTRIBUTES {341
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,342
lpSecurityDescriptor: self.0.0,343
bInheritHandle: false.into(),344
}345
}346
}347
348
impl Drop for SecurityDescriptor {349
fn drop(&mut self) {350
unsafe {351
LocalFree(Some(HLOCAL(self.0.0)));352
}353
}354
}355
356
fn wide(value: &str) -> Vec<u16> {357
value.encode_utf16().chain(Some(0)).collect()358
}359
360
#[cfg(test)]361
mod tests {362
use super::*;363
364
#[test]365
fn dynamic_mode_header_matches_4640_by_2610() {366
let mode = VirtualMode::from_millihz(4640, 2610, 240_000).unwrap();367
let config = GateConfig::from_displays(&[VirtualDisplayConfig::new(0, mode)]).unwrap();368
369
assert_eq!(mode.refresh_numerator, 240);370
assert_eq!(mode.refresh_denominator, 1);371
assert_eq!(size_of::<GateHeader>(), 16);372
assert_eq!(size_of::<GateEntry>(), 20);373
assert_eq!(size_of::<GateConfig>(), 336);374
assert_eq!(config.header.count, 1);375
assert_eq!(config.entries[0].width, 4640);376
assert_eq!(config.entries[0].height, 2610);377
}378
379
#[test]380
fn invalid_or_excessive_modes_are_rejected() {381
assert!(VirtualMode::from_millihz(0, 2160, 240_000).is_err());382
assert!(VirtualMode::from_millihz(3840, 2160, 0).is_err());383
assert!(VirtualMode::from_millihz(3840, 2160, 500).is_err());384
assert!(VirtualMode::from_millihz(16_385, 16_384, 240_000).is_err());385
assert!(386
VirtualMode {387
width: 3840,388
height: 2160,389
refresh_numerator: 1001,390
refresh_denominator: 1,391
}392
.validate()393
.is_err()394
);395
}396
397
#[test]398
fn multiple_connectors_are_encoded_in_caller_order() {399
let displays = [400
VirtualDisplayConfig::new(5, VirtualMode::default()),401
VirtualDisplayConfig::new(402
2,403
VirtualMode {404
width: 2560,405
height: 1440,406
refresh_numerator: 165,407
refresh_denominator: 1,408
},409
),410
];411
412
let config = GateConfig::from_displays(&displays).unwrap();413
414
assert_eq!(config.header.magic, GATE_MAGIC);415
assert_eq!(config.header.version, PROTOCOL_VERSION);416
assert_eq!(config.header.count, 2);417
assert_eq!(config.header.reserved, 0);418
assert_eq!(config.entries[0].connector_index, 5);419
assert_eq!(config.entries[0].width, 3840);420
assert_eq!(config.entries[1].connector_index, 2);421
assert_eq!(config.entries[1].refresh_numerator, 165);422
assert_eq!(config.entries[2], GateEntry::default());423
}424
425
#[test]426
fn public_display_config_has_stable_json_fields() {427
let display = VirtualDisplayConfig::new(4, VirtualMode::default());428
429
let json = serde_json::to_value(display).unwrap();430
431
assert_eq!(json["connector_index"], 4);432
assert_eq!(json["mode"]["width"], 3840);433
assert_eq!(json["mode"]["height"], 2160);434
assert_eq!(json["mode"]["refresh_numerator"], 240);435
assert_eq!(json["mode"]["refresh_denominator"], 1);436
assert_eq!(437
serde_json::from_value::<VirtualDisplayConfig>(json).unwrap(),438
display439
);440
}441
442
#[test]443
fn display_set_validation_rejects_invalid_shapes() {444
assert!(validate_displays(&[]).is_err());445
446
let too_many = [VirtualDisplayConfig::new(0, VirtualMode::default()); MAX_OUTPUTS + 1];447
assert!(validate_displays(&too_many).is_err());448
449
let duplicate = [450
VirtualDisplayConfig::new(3, VirtualMode::default()),451
VirtualDisplayConfig::new(3, VirtualMode::default()),452
];453
assert!(validate_displays(&duplicate).is_err());454
455
assert!(456
validate_displays(&[VirtualDisplayConfig::new(457
MAX_OUTPUTS as u32,458
VirtualMode::default()459
)])460
.is_err()461
);462
463
assert!(464
validate_displays(&[VirtualDisplayConfig::new(465
0,466
VirtualMode {467
width: 0,468
..VirtualMode::default()469
}470
)])471
.is_err()472
);473
}474
475
#[test]476
fn sixteen_distinct_connectors_are_supported() {477
let displays: [VirtualDisplayConfig; MAX_OUTPUTS] =478
std::array::from_fn(|connector_index| {479
VirtualDisplayConfig::new(connector_index as u32, VirtualMode::default())480
});481
482
let config = GateConfig::from_displays(&displays).unwrap();483
484
assert_eq!(config.header.count, MAX_OUTPUTS as u32);485
assert_eq!(config.entries[15].connector_index, 15);486
}487
}488