ferroui_controls/remote/
remote_widget.rs1use crate::remote::UiThreadHandle;
2use crate::{Control, ControlImpl};
3use ferroui_base::input::InputElementImpl;
4use ferroui_base::interactivity::InteractiveImpl;
5use ferroui_base::layout::{LayoutableImpl, LayoutableImplExt};
6use ferroui_base::media::imaging::WriteableBitmap;
7use ferroui_base::media::DrawingContext;
8use ferroui_base::platform::{PixelFormat, PixelFormats};
9use ferroui_base::threading::DispatcherPriority;
10use ferroui_base::{
11 ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, FerroObjectImplExt, PixelSize, Rect, Ref,
12 StyledElementImpl, Vector, VisualImpl, VisualImplExt, WeakRef,
13};
14use ferroui_remote_protocol::viewport::{
15 ClientSupportedPixelFormatsMessage, ClientViewportAllocatedMessage, FrameMessage, FrameReceivedMessage,
16 PixelFormat as ProtocolPixelFormat,
17};
18use ferroui_remote_protocol::{message_handler, IFerroRemoteTransportConnection, Message};
19use std::cell::{Cell, RefCell};
20use std::sync::Arc;
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24pub enum SizingMode {
25 Local,
26 Remote,
27}
28
29#[repr(C)]
33pub struct RemoteWidget {
34 base: Control,
35 connection: Arc<dyn IFerroRemoteTransportConnection>,
36 last_frame: RefCell<Option<Message>>,
39 bitmap: RefCell<Option<WriteableBitmap>>,
40 mode: Cell<SizingMode>,
41}
42
43ferro_class!(RemoteWidget: Control);
44ferroui_base::ferro_class_info!(RemoteWidget {});
45ferro_impl_classes!(RemoteWidget: StyledElementImpl, InteractiveImpl, InputElementImpl, ControlImpl);
46
47fn platform_pixel_format(format: ProtocolPixelFormat) -> PixelFormat {
50 match format {
51 ProtocolPixelFormat::Rgb565 => PixelFormats::RGB565,
52 ProtocolPixelFormat::Rgba8888 => PixelFormats::RGBA8888,
53 ProtocolPixelFormat::Bgra8888 => PixelFormats::BGRA8888,
54 }
55}
56
57impl FerroObjectImpl for RemoteWidget {
58 fn constructed(this: &Self) {
59 Self::parent_constructed(this);
60
61 let handle: UiThreadHandle<WeakRef<RemoteWidget>> =
66 UiThreadHandle::register(this.to_ref().downgrade(), |widget| widget.upgrade().is_some());
67 this.connection.on_message(message_handler(move |_, msg| {
68 let msg = msg.clone();
69 handle.post(
70 move |widget| {
71 if let Some(widget) = widget.upgrade() {
72 widget.on_message(&msg);
73 }
74 },
75 DispatcherPriority::DEFAULT,
76 );
77 }));
78 this.connection.send(Arc::new(ClientSupportedPixelFormatsMessage {
79 formats: Some(vec![ProtocolPixelFormat::Bgra8888, ProtocolPixelFormat::Rgba8888]),
80 }));
81 }
82}
83
84impl LayoutableImpl for RemoteWidget {
85 fn arrange_core(this: &Self, final_rect: Rect) {
86 if this.mode.get() == SizingMode::Local {
87 this.connection.send(Arc::new(ClientViewportAllocatedMessage {
88 width: final_rect.width,
89 height: final_rect.height,
90 dpi_x: 10.0 * 96.0,
91 dpi_y: 10.0 * 96.0, }));
93 }
94
95 Self::parent_arrange_core(this, final_rect);
96 }
97}
98
99impl VisualImpl for RemoteWidget {
100 fn render(this: &Self, context: &mut DrawingContext) {
101 let last_frame = this.last_frame.borrow().clone();
102 let last_frame = last_frame.as_ref().and_then(|frame| frame.downcast_ref::<FrameMessage>());
103 if let Some(last_frame) = last_frame {
104 if last_frame.width != 0 && last_frame.height != 0 {
105 let fmt = platform_pixel_format(last_frame.format);
106 let mut bitmap = this.bitmap.borrow_mut();
107 let stale = match bitmap.as_ref() {
108 None => true,
109 Some(bitmap) => {
110 bitmap.pixel_size().width != last_frame.width || bitmap.pixel_size().height != last_frame.height
111 }
112 };
113 if stale {
114 if let Some(old) = bitmap.take() {
115 old.dispose();
116 }
117 *bitmap = Some(WriteableBitmap::new(
118 PixelSize::new(last_frame.width, last_frame.height),
119 Vector::new(96.0, 96.0),
120 Some(fmt),
121 None,
122 ));
123 }
124 let bitmap = bitmap.as_ref().expect("the bitmap was created above");
125 {
126 let l = bitmap.lock();
127 let line_len = ((if fmt == PixelFormat::RGB565 { 2 } else { 4 }) * last_frame.width) as usize;
128 let row_bytes = l.row_bytes() as usize;
129 let stride = last_frame.stride as usize;
130 let data = last_frame.data.as_deref().expect("Value cannot be null. (Parameter 'source')");
134 l.with_data(&mut |dest| {
135 for y in 0..last_frame.height as usize {
136 dest[row_bytes * y..row_bytes * y + line_len]
137 .copy_from_slice(&data[y * stride..y * stride + line_len]);
138 }
139 });
140 l.dispose();
141 }
142 context.draw_image_with_rects(
143 bitmap,
144 Rect::new(0.0, 0.0, f64::from(bitmap.pixel_size().width), f64::from(bitmap.pixel_size().height)),
145 Rect::from_size(this.bounds().size()),
146 );
147 }
148 }
149 Self::parent_render(this, context);
150 }
151}
152
153impl RemoteWidget {
154 pub fn construct(connection: Arc<dyn IFerroRemoteTransportConnection>) -> Self {
156 Self {
157 base: Control::construct(),
158 connection,
159 last_frame: RefCell::new(None),
160 bitmap: RefCell::new(None),
161 mode: Cell::new(SizingMode::Local),
162 }
163 }
164
165 pub fn new(connection: Arc<dyn IFerroRemoteTransportConnection>) -> Ref<Self> {
167 instantiate(Self::construct(connection))
168 }
169
170 pub fn mode(&self) -> SizingMode {
171 self.mode.get()
172 }
173
174 pub fn set_mode(&self, value: SizingMode) {
175 self.mode.set(value)
176 }
177
178 fn on_message(&self, msg: &Message) {
179 if let Some(frame) = msg.downcast_ref::<FrameMessage>() {
180 self.connection.send(Arc::new(FrameReceivedMessage { sequence_id: frame.sequence_id }));
181 *self.last_frame.borrow_mut() = Some(msg.clone());
182 self.invalidate_visual();
183 }
184 }
185
186 #[cfg(test)]
188 pub(crate) fn last_frame(&self) -> Option<Message> {
189 self.last_frame.borrow().clone()
190 }
191}