Evan's Blog
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.

上游 GitHub
READ-ONLY MIRROR

README、源码与 Release 已保存到本站;源码视图固定到 commit a37d6c4b。

main/ root / src
a37d6c4b· 固定源码快照
src/session_gate.rs14.9 KB
1use std::error::Error;
2use std::ffi::c_void;
3use std::fmt::{Display, Formatter};
4use std::mem::size_of;
5
6use serde::{Deserialize, Serialize};
7use windows::Win32::Foundation::{
8 CloseHandle, ERROR_ALREADY_EXISTS, GetLastError, HANDLE, HLOCAL, INVALID_HANDLE_VALUE,
9 LocalFree,
10};
11use windows::Win32::Security::Authorization::{
12 ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1,
13};
14use windows::Win32::Security::{
15 GetTokenInformation, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, TOKEN_QUERY, TOKEN_USER,
16 TokenUser,
17};
18use windows::Win32::System::Memory::{
19 CreateFileMappingW, FILE_MAP_WRITE, MapViewOfFile, PAGE_READWRITE, UnmapViewOfFile,
20};
21use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
22use windows::core::{PCWSTR, PWSTR, w};
23
24use crate::limits::{MAX_OUTPUTS, MAX_REFRESH_HZ, MAX_VIRTUAL_DIMENSION, MIN_REFRESH_HZ};
25
26const GATE_MAGIC: u32 = 0x5342_4737;
27const PROTOCOL_VERSION: u32 = 7;
28const GATE_MAPPING: PCWSTR = w!("Global\\SBMSSession-v7");
29
30#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
31pub struct VirtualMode {
32 pub width: u32,
33 pub height: u32,
34 pub refresh_numerator: u32,
35 pub refresh_denominator: u32,
36}
37
38impl 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
49impl 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 == 0
71 || self.height == 0
72 || self.width > MAX_VIRTUAL_DIMENSION
73 || self.height > MAX_VIRTUAL_DIMENSION
74 {
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)]
101pub struct VirtualDisplayConfig {
102 pub connector_index: u32,
103 pub mode: VirtualMode,
104}
105
106impl 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 - 1
119 )));
120 }
121 self.mode.validate()
122 }
123}
124
125#[repr(C)]
126#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
127struct 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)]
136struct GateEntry {
137 connector_index: u32,
138 width: u32,
139 height: u32,
140 refresh_numerator: u32,
141 refresh_denominator: u32,
142}
143
144impl 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)]
158struct GateConfig {
159 header: GateHeader,
160 entries: [GateEntry; MAX_OUTPUTS],
161}
162
163impl 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
184pub struct SessionGate {
185 gate: HANDLE,
186}
187
188#[derive(Debug)]
189pub struct SessionGateError(String);
190
191impl Display for SessionGateError {
192 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
193 formatter.write_str(&self.0)
194 }
195}
196
197impl Error for SessionGateError {}
198
199impl 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
245fn 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_index
265 )));
266 }
267 seen_connectors |= connector_bit;
268 }
269 Ok(())
270}
271
272const 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 left
279}
280
281impl Drop for SessionGate {
282 fn drop(&mut self) {
283 unsafe {
284 let _ = CloseHandle(self.gate);
285 }
286 }
287}
288
289struct SecurityDescriptor(PSECURITY_DESCRIPTOR);
290
291impl 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
348impl Drop for SecurityDescriptor {
349 fn drop(&mut self) {
350 unsafe {
351 LocalFree(Some(HLOCAL(self.0.0)));
352 }
353 }
354}
355
356fn wide(value: &str) -> Vec<u16> {
357 value.encode_utf16().chain(Some(0)).collect()
358}
359
360#[cfg(test)]
361mod 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 display
439 );
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