makepad_platform/os/linux/x11/
linux_x11_stdin.rs

1use {
2    crate::{
3        cx::Cx, cx_api::CxOsOp, event::{Event, WindowGeom}, makepad_live_id::*, makepad_math::*, makepad_micro_serde::*, os::cx_stdin::{aux_chan, HostToStdin, PollTimer, PresentableDraw, StdinToHost, Swapchain}, pass::{CxPassColorTexture, CxPassParent, PassClearColor}, texture::{Texture, TextureFormat}, thread::SignalToUI, window::CxWindowPool, CxOsApi,
4    }, std::io::{self, prelude::*, BufReader} 
5};
6
7#[derive(Default)]
8pub(crate) struct StdinWindow{
9    swapchain: Option<Swapchain<Texture>>,
10    present_index: usize
11}
12
13impl Cx {
14    
15    pub (crate) fn stdin_handle_repaint(
16        &mut self,
17        windows: &mut Vec<StdinWindow>,
18    ) {
19        self.os.opengl_cx.as_ref().unwrap().make_current();
20        let mut passes_todo = Vec::new();
21        self.compute_pass_repaint_order(&mut passes_todo);
22        self.repaint_id += 1;
23        for &pass_id in &passes_todo {
24            match self.passes[pass_id].parent.clone() {
25                CxPassParent::Xr => {}
26                CxPassParent::Window(window_id) => {
27                    // only render to swapchain if swapchain exists
28                    let window = &mut windows[window_id.id()];
29                    if let Some(swapchain) = &window.swapchain {
30                        let current_image = &swapchain.presentable_images[window.present_index];
31                        window.present_index = (window.present_index + 1) % swapchain.presentable_images.len();
32
33                        // render to swapchain
34                        self.draw_pass_to_texture(pass_id, Some(&current_image.image));
35
36                        // wait for GPU to finish rendering
37                        unsafe { (self.os.gl().glFinish)(); }
38
39                        let dpi_factor = self.passes[pass_id].dpi_factor.unwrap();
40                        let pass_rect = self.get_pass_rect(pass_id, dpi_factor).unwrap();
41                        let presentable_draw = PresentableDraw {
42                            window_id: window_id.id(),
43                            target_id: current_image.id,
44                            width: (pass_rect.size.x * dpi_factor) as u32,
45                            height: (pass_rect.size.y * dpi_factor) as u32,
46                        };
47
48                        // inform host that frame is ready
49                        let _ = io::stdout().write_all(StdinToHost::DrawCompleteAndFlip(presentable_draw).to_json().as_bytes());
50                    }
51                }
52                CxPassParent::Pass(_) => {
53                    //let dpi_factor = self.get_delegated_dpi_factor(parent_pass_id);
54                    self.draw_pass_to_texture(pass_id, None);
55                },
56                CxPassParent::None => {
57                    self.draw_pass_to_texture(pass_id, None);
58                }
59            }
60        }
61    }
62    
63    pub fn stdin_event_loop(&mut self) {
64        let aux_chan_client_endpoint =
65            aux_chan::InheritableClientEndpoint::from_process_args_in_client()
66                .and_then(|chan| chan.into_uninheritable())
67                .expect("failed to acquire auxiliary channel");
68
69
70        let (json_msg_tx, json_msg_rx) = std::sync::mpsc::channel();
71        {
72            std::thread::spawn(move || {
73                let mut reader = BufReader::new(std::io::stdin().lock());
74                let mut line = String::new();
75                loop {
76                    line.clear();
77                    if let Ok(0) | Err(_) = reader.read_line(&mut line) {
78                        break;
79                    }
80                    // alright lets put the line in a json parser
81                    match HostToStdin::deserialize_json(&line) {
82                        Ok(msg) => {
83                            if json_msg_tx.send(msg).is_err() {
84                                break;
85                            }
86                        }
87                        Err(err) => {
88                            // we should output a log string
89                            crate::error!("Cant parse stdin-JSON {} {:?}", line, err)
90                        }
91                    }
92                }
93                println!("Terminating STDIN reader loop")
94            });
95        }
96
97        let _ = io::stdout().write_all(StdinToHost::ReadyToStart.to_json().as_bytes());
98        
99        let mut stdin_windows:Vec<StdinWindow> = Vec::new();
100 
101        self.call_event_handler(&Event::Startup);
102
103        while let Ok(msg) = json_msg_rx.recv(){
104            match msg {
105                HostToStdin::KeyDown(e) => {
106                    self.call_event_handler(&Event::KeyDown(e));
107                }
108                HostToStdin::KeyUp(e) => {
109                    self.call_event_handler(&Event::KeyUp(e));
110                }
111                HostToStdin::TextInput(e) => {
112                    self.call_event_handler(&Event::TextInput(e));
113                }
114                HostToStdin::MouseDown(e) => {
115                    self.fingers.process_tap_count(
116                        dvec2(e.x,e.y),
117                        e.time
118                    );
119                    let (window_id,pos) = self.windows.window_id_contains(dvec2(e.x, e.y));
120                    let mouse_down_event = e.into_event(window_id, pos);
121                    self.fingers.mouse_down(mouse_down_event.button, window_id);
122                    self.call_event_handler(&Event::MouseDown(mouse_down_event));
123                }
124                HostToStdin::MouseMove(e) => {
125                    let (window_id, pos) = if let Some((_, window_id)) = self.fingers.first_mouse_button{
126                        (window_id, self.windows[window_id].window_geom.position)
127                    }
128                    else{
129                        self.windows.window_id_contains(dvec2(e.x, e.y))
130                    };
131                    self.call_event_handler(&Event::MouseMove(e.into_event(window_id,pos)));
132                    self.fingers.cycle_hover_area(live_id!(mouse).into());
133                    self.fingers.switch_captures();
134                }
135                HostToStdin::MouseUp(e) => {
136                    let (window_id, pos) = if let Some((_, window_id)) = self.fingers.first_mouse_button{
137                        (window_id, self.windows[window_id].window_geom.position)
138                    }
139                    else{
140                        self.windows.window_id_contains(dvec2(e.x, e.y))
141                    };
142                    let mouse_up_event = e.into_event(window_id, pos);
143                    let button = mouse_up_event.button;
144                    self.call_event_handler(&Event::MouseUp(mouse_up_event));
145                    self.fingers.mouse_up(button);
146                    self.fingers.cycle_hover_area(live_id!(mouse).into());
147                }
148                HostToStdin::Scroll(e) => {
149                    let (window_id,pos) = self.windows.window_id_contains(dvec2(e.x, e.y));
150                    self.call_event_handler(&Event::Scroll(e.into_event(window_id,pos)))
151                }
152                HostToStdin::WindowGeomChange { dpi_factor, left, top, width, height, window_id } => {
153                    self.windows[CxWindowPool::from_usize(window_id)].window_geom = WindowGeom {
154                        dpi_factor,
155                        position: dvec2(left, top),
156                        inner_size: dvec2(width, height),
157                        ..Default::default()
158                    };
159                    self.redraw_all();
160                }
161                HostToStdin::Swapchain(new_swapchain) => {
162                    let new_swapchain = new_swapchain.images_map(|pi| {
163                        let mut new_texture = Texture::new(self);
164                        match pi.recv_fds_from_aux_chan(&aux_chan_client_endpoint) {
165                            Ok(pi) => {
166                                // update texture
167                                let desc = TextureFormat::SharedBGRAu8{
168                                    id: pi.id,
169                                    width: new_swapchain.alloc_width as usize,
170                                    height: new_swapchain.alloc_height as usize,
171                                    initial: true,
172                                };
173                                new_texture = Texture::new_with_format(self, desc);
174                                self.textures[new_texture.texture_id()]
175                                .update_from_shared_dma_buf_image(
176                                    self.os.gl(),
177                                    self.os.opengl_cx.as_ref().unwrap(),
178                                    &pi.image,
179                                );
180                            }
181                            Err(err) => {
182                                crate::error!("failed to receive new swapchain on auxiliary channel: {err:?}");
183                            }
184                        }
185                        new_texture
186                    });
187                    let window_id = new_swapchain.window_id;
188                    let stdin_window = &mut stdin_windows[window_id];
189                    stdin_window.swapchain = Some(new_swapchain);
190                    
191                    // reset present_index
192                    stdin_window.present_index = 0;
193                                        
194                    // lets set up our render pass target
195                    let window = &mut self.windows[CxWindowPool::from_usize(window_id)];
196                    let pass = &mut self.passes[window.main_pass_id.unwrap()];
197                    if let Some(swapchain) = &stdin_window.swapchain {
198                        pass.color_textures = vec![CxPassColorTexture {
199                            clear_color: PassClearColor::ClearWith(vec4(1.0,1.0,0.0,1.0)),
200                            //clear_color: PassClearColor::ClearWith(pass.clear_color),
201                            texture: swapchain.presentable_images[stdin_window.present_index].image.clone(),
202                        }];
203                    }
204                    
205
206                    self.redraw_all();
207                    self.stdin_handle_platform_ops(&mut stdin_windows);
208                }
209
210                HostToStdin::Tick  =>  {
211
212                    // poll the service for updates
213                    // check signals
214                    if SignalToUI::check_and_clear_ui_signal(){
215                        self.handle_media_signals();
216                        self.call_event_handler(&Event::Signal);
217                    }
218                    if SignalToUI::check_and_clear_action_signal() {
219                        self.handle_action_receiver();
220                    }
221                    
222                    for event in self.os.stdin_timers.get_dispatch() {
223                        self.call_event_handler(&event);
224                    }                    
225                    if self.handle_live_edit(){
226                        self.call_event_handler(&Event::LiveEdit);
227                        self.redraw_all();
228                    }
229                    self.handle_networking_events();
230                    
231                    // we should poll our runloop
232                    self.stdin_handle_platform_ops(&mut stdin_windows);
233
234                    // alright a tick.
235                    // we should now run all the stuff.
236                    if self.new_next_frames.len() != 0 {
237                        self.call_next_frame_event(self.seconds_since_app_start());
238                    }
239                    
240                    if self.need_redrawing() {
241                        self.call_draw_event();
242                        self.opengl_compile_shaders();
243                    }
244
245                    self.stdin_handle_repaint(&mut stdin_windows);
246                }
247            }
248        }
249    }
250    
251    
252    fn stdin_handle_platform_ops(
253        &mut self,
254        stdin_windows: &mut Vec<StdinWindow>,
255    ) {
256        while let Some(op) = self.platform_ops.pop() {
257            match op {
258                CxOsOp::CreateWindow(window_id) => {
259                    while window_id.id() >= stdin_windows.len(){
260                        stdin_windows.push(StdinWindow::default());
261                    }
262                    //let stdin_window = &mut stdin_windows[window_id.id()];
263                    let window = &mut self.windows[window_id];
264                    window.is_created = true;
265                    let _ = io::stdout().write_all(StdinToHost::CreateWindow{window_id:window_id.id(),kind_id:window.kind_id}.to_json().as_bytes());
266                },
267                CxOsOp::SetCursor(cursor) => {
268                    let _ = io::stdout().write_all(StdinToHost::SetCursor(cursor).to_json().as_bytes());
269                },
270                CxOsOp::StartTimer {timer_id, interval, repeats} => {
271                    self.os.stdin_timers.timers.insert(timer_id, PollTimer::new(interval, repeats));
272                },
273                CxOsOp::StopTimer(timer_id) => {
274                    self.os.stdin_timers.timers.remove(&timer_id);
275                },
276                _ => ()
277                /*
278                CxOsOp::CloseWindow(_window_id) => {},
279                CxOsOp::MinimizeWindow(_window_id) => {},
280                CxOsOp::MaximizeWindow(_window_id) => {},
281                CxOsOp::RestoreWindow(_window_id) => {},
282                CxOsOp::FullscreenWindow(_window_id) => {},
283                CxOsOp::NormalizeWindow(_window_id) => {}
284                CxOsOp::SetTopmost(_window_id, _is_topmost) => {}
285                CxOsOp::XrStartPresenting(_) => {},
286                CxOsOp::XrStopPresenting(_) => {},
287                CxOsOp::ShowTextIME(_area, _pos) => {},
288                CxOsOp::HideTextIME => {},
289                CxOsOp::SetCursor(_cursor) => {},
290                CxOsOp::StartTimer {timer_id, interval, repeats} => {},
291                CxOsOp::StopTimer(timer_id) => {},
292                CxOsOp::StartDragging(dragged_item) => {}
293                CxOsOp::UpdateMenu(menu) => {}*/
294            }
295        }
296    }
297    
298}