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 bd87fa1e。

main/ root / src/ui
bd87fa1e· 固定源码快照
src/ui/projection.rs6.7 KB
1use std::rc::Rc;
2
3use slint::{Model, ModelRc, SharedString, VecModel};
4
5use crate::controller::DisplayOption;
6
7use super::state::{GroupDraft, SunshineState};
8use super::{
9 QuickAccess, STREAM_ONLY_ID, geometry_request, group_tab_detail, populate_geometry_fields,
10 rebuild_reference_options, selected_display_id, selected_reference_source, set_geometry_result,
11 set_geometry_unconfigured, update_reference_action, update_stream_suggestions,
12 validate_stream_fields,
13};
14
15pub(super) fn update_tab_projection(ui: &QuickAccess, groups: &[GroupDraft], active: usize) {
16 let first = if groups.len() <= 3 {
17 0
18 } else {
19 active.saturating_sub(1).min(groups.len() - 3)
20 };
21 let visible = groups.iter().enumerate().skip(first).take(3);
22 let mut labels = Vec::new();
23 let mut details = Vec::new();
24 let mut indices = Vec::new();
25 for (index, group) in visible {
26 labels.push(SharedString::from((group.id + 1).to_string()));
27 details.push(SharedString::from(group.tab_detail.as_str()));
28 indices.push(index as i32);
29 }
30 ui.set_tab_labels(ModelRc::new(Rc::new(VecModel::from(labels))));
31 ui.set_tab_details(ModelRc::new(Rc::new(VecModel::from(details))));
32 ui.set_tab_indices(ModelRc::new(Rc::new(VecModel::from(indices))));
33 ui.set_active_group_index(active as i32);
34 ui.set_group_count(groups.len() as i32);
35}
36
37pub(super) fn project_group_telemetry(ui: &QuickAccess, groups: &[GroupDraft], active: usize) {
38 let Some(group) = groups.get(active) else {
39 return;
40 };
41 let telemetry = &group.telemetry;
42 ui.set_mapping_fps(telemetry.fps.unwrap_or_default() as i32);
43 ui.set_mapping_fps_valid(telemetry.fps.is_some());
44 ui.set_mapping_fps_error(telemetry.error);
45 ui.set_mapping_fps_nan(ui.get_stream_only() && !telemetry.error);
46 ui.set_sunshine_state(telemetry.sunshine_state.as_i32());
47 ui.set_sunshine_port(telemetry.sunshine_port.unwrap_or_default().into());
48 ui.set_sunshine_panel_enabled(
49 telemetry.ready
50 && telemetry.sunshine_state == SunshineState::Ready
51 && telemetry.sunshine_port.is_some()
52 && group.stream_only,
53 );
54}
55
56pub(super) fn set_target_options(
57 ui: &QuickAccess,
58 displays: &[DisplayOption],
59 groups: &[GroupDraft],
60 active: usize,
61) {
62 let used = groups
63 .iter()
64 .enumerate()
65 .filter(|(index, group)| *index != active && !group.stream_only)
66 .filter_map(|(_, group)| group.target_id.as_deref())
67 .collect::<Vec<_>>();
68 let candidates = displays
69 .iter()
70 .filter(|display| !used.contains(&display.id.as_str()))
71 .collect::<Vec<_>>();
72 let mut names = candidates
73 .iter()
74 .map(|display| SharedString::from(display.name.as_str()))
75 .collect::<Vec<_>>();
76 let mut labels = candidates
77 .iter()
78 .map(|display| {
79 SharedString::from(
80 if display.physical_width_mm.is_some() && display.physical_height_mm.is_some() {
81 display.label.clone()
82 } else {
83 format!("{} · Size unavailable", display.label)
84 },
85 )
86 })
87 .collect::<Vec<_>>();
88 let mut ids = candidates
89 .iter()
90 .map(|display| SharedString::from(display.id.as_str()))
91 .collect::<Vec<_>>();
92 names.push("Stream only".into());
93 labels.push("Stream only · Create a virtual display".into());
94 ids.push(STREAM_ONLY_ID.into());
95 ui.set_display_names(ModelRc::new(Rc::new(VecModel::from(names))));
96 ui.set_display_labels(ModelRc::new(Rc::new(VecModel::from(labels))));
97 ui.set_display_ids(ModelRc::new(Rc::new(VecModel::from(ids))));
98
99 let group = &groups[active];
100 let desired = if group.stream_only {
101 STREAM_ONLY_ID
102 } else {
103 group.target_id.as_deref().unwrap_or("")
104 };
105 let selected = (0..ui.get_display_ids().row_count())
106 .find(|index| {
107 ui.get_display_ids()
108 .row_data(*index)
109 .is_some_and(|id| id.as_str() == desired)
110 })
111 .or_else(|| (!candidates.is_empty()).then_some(0))
112 .unwrap_or(candidates.len());
113 ui.set_selected_display(selected as i32);
114 ui.set_stream_only(
115 ui.get_display_ids()
116 .row_data(selected)
117 .is_some_and(|id| id.as_str() == STREAM_ONLY_ID),
118 );
119}
120
121pub(super) fn hydrate_group(
122 ui: &QuickAccess,
123 displays: &[DisplayOption],
124 groups: &[GroupDraft],
125 active: usize,
126) {
127 let Some(group) = groups.get(active) else {
128 return;
129 };
130 set_target_options(ui, displays, groups, active);
131 ui.set_stream_width(group.stream_width.as_str().into());
132 ui.set_stream_height(group.stream_height.as_str().into());
133 ui.set_stream_diagonal(group.stream_diagonal.as_str().into());
134 ui.set_stream_refresh(group.stream_refresh.as_str().into());
135 ui.set_stream_aspect_ratio_index(group.stream_aspect_ratio_index);
136 ui.set_stream_rotation(group.stream_rotation_index);
137 if let Some(request) = group.sizing {
138 populate_geometry_fields(ui, request);
139 match request.calculate() {
140 Ok(result) => set_geometry_result(ui, result, true),
141 Err(error) => {
142 set_geometry_unconfigured(ui);
143 ui.set_geometry_error(error.to_string().into());
144 }
145 }
146 } else {
147 set_geometry_unconfigured(ui);
148 }
149 rebuild_reference_options(ui, displays, group.reference_source.clone());
150 update_reference_action(ui, displays);
151 update_stream_suggestions(ui);
152 if group.stream_only {
153 validate_stream_fields(ui, displays);
154 }
155 update_tab_projection(ui, groups, active);
156 project_group_telemetry(ui, groups, active);
157}
158
159pub(super) fn snapshot_group(
160 ui: &QuickAccess,
161 displays: &[DisplayOption],
162 groups: &mut [GroupDraft],
163 active: usize,
164) {
165 let Some(group) = groups.get_mut(active) else {
166 return;
167 };
168 let target_id = selected_display_id(ui, ui.get_selected_display());
169 group.stream_only = target_id.as_deref() == Some(STREAM_ONLY_ID);
170 group.target_id = (!group.stream_only).then_some(target_id).flatten();
171 group.reference_source = selected_reference_source(ui);
172 group.sizing = geometry_request(ui, displays)
173 .ok()
174 .flatten()
175 .filter(|request| request.calculate().is_ok());
176 group.stream_width = ui.get_stream_width().to_string();
177 group.stream_height = ui.get_stream_height().to_string();
178 group.stream_diagonal = ui.get_stream_diagonal().to_string();
179 group.stream_refresh = ui.get_stream_refresh().to_string();
180 group.stream_aspect_ratio_index = ui.get_stream_aspect_ratio_index();
181 group.stream_rotation_index = ui.get_stream_rotation();
182 group.tab_detail = group_tab_detail(group, displays);
183}
184