makepad_platform/os/linux/x11/
linux_x11.rs

1use {
2    std::cell::RefCell,
3    std::time::Instant,
4    std::rc::Rc,
5    self::super::opengl_x11::{
6        OpenglWindow,
7        OpenglCx
8    },
9    self::super::super::{
10        egl_sys,
11        gl_sys::LibGl,
12        x11::xlib_event::*,
13        x11::xlib_app::*,
14        x11::x11_sys,
15        linux_media::CxLinuxMedia
16    },
17    crate::{
18        cx_api::{CxOsOp, CxOsApi, OpenUrlInPlace}, 
19        makepad_math::dvec2,
20        makepad_live_id::*,
21        thread::SignalToUI,
22        event::*,
23        pass::CxPassParent,
24        cx::{Cx, OsType,LinuxWindowParams}, 
25        os::cx_stdin::PollTimers,
26        gpu_info::GpuPerformance,
27        os::cx_native::EventFlow,
28    }
29};
30
31impl Cx {
32    pub fn event_loop(cx:Rc<RefCell<Cx>>) {
33        cx.borrow_mut().self_ref = Some(cx.clone());
34        cx.borrow_mut().os_type = OsType::LinuxWindow(LinuxWindowParams{
35            custom_window_chrome: false
36        });
37        cx.borrow_mut().gpu_info.performance = GpuPerformance::Tier1;
38
39        let opengl_windows = Rc::new(RefCell::new(Vec::new()));
40        let is_stdin_loop = std::env::args().find(|v| v=="--stdin-loop").is_some();
41        if is_stdin_loop {
42            cx.borrow_mut().in_makepad_studio = true;
43        }
44        init_xlib_app_global(Box::new({
45            let cx = cx.clone();
46            move | xlib_app,
47            events | {
48                if is_stdin_loop{
49                    return EventFlow::Wait
50                }
51                let mut cx = cx.borrow_mut();
52                let mut opengl_windows = opengl_windows.borrow_mut();
53                cx.xlib_event_callback(xlib_app, events, &mut *opengl_windows)
54            }
55        }));
56        
57        cx.borrow_mut().os.opengl_cx = Some(unsafe {
58            OpenglCx::from_egl_platform_display(
59                egl_sys::EGL_PLATFORM_X11_EXT,
60                get_xlib_app_global().display,
61            )
62        });
63        
64        if is_stdin_loop {
65            cx.borrow_mut().in_makepad_studio = true;
66            return cx.borrow_mut().stdin_event_loop();
67        }
68        
69        cx.borrow_mut().call_event_handler(&Event::Startup);
70        cx.borrow_mut().redraw_all();
71        get_xlib_app_global().start_timer(0,0.008,true);
72        get_xlib_app_global().event_loop();
73    }
74    
75    fn xlib_event_callback(
76        &mut self,
77        xlib_app: &mut XlibApp, 
78        event: XlibEvent,
79        opengl_windows: &mut Vec<OpenglWindow>
80    ) -> EventFlow {
81        if let EventFlow::Exit = self.handle_platform_ops(opengl_windows, xlib_app) {
82            return EventFlow::Exit
83        }
84        
85        //let mut paint_dirty = false;
86        
87        //self.process_desktop_pre_event(&mut event);
88        match event {
89            XlibEvent::AppGotFocus => { // repaint all window passes. Metal sometimes doesnt flip buffers when hidden/no focus
90                for window in opengl_windows.iter_mut() {
91                    if let Some(main_pass_id) = self.windows[window.window_id].main_pass_id {
92                        self.repaint_pass(main_pass_id);
93                    }
94                }
95                //paint_dirty = true;
96                self.call_event_handler(&Event::AppGotFocus);
97            }
98            XlibEvent::AppLostFocus => { 
99                self.call_event_handler(&Event::AppLostFocus);
100            }
101            XlibEvent::WindowGeomChange(mut re) => { // do this here because mac
102                if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == re.window_id) {
103                    if let Some(dpi_override) = self.windows[re.window_id].dpi_override {
104                        re.new_geom.inner_size *= re.new_geom.dpi_factor / dpi_override;
105                        re.new_geom.dpi_factor = dpi_override;
106                    }
107                    
108                    window.window_geom = re.new_geom.clone();
109                    self.windows[re.window_id].window_geom = re.new_geom.clone();
110                    // redraw just this windows root draw list
111                    if re.old_geom.inner_size != re.new_geom.inner_size {
112                        if let Some(main_pass_id) = self.windows[re.window_id].main_pass_id {
113                            self.redraw_pass_and_child_passes(main_pass_id);
114                        }
115                    }
116                }
117                // ok lets not redraw all, just this window
118                self.call_event_handler(&Event::WindowGeomChange(re));
119            }
120            XlibEvent::WindowClosed(wc) => {
121                let window_id = wc.window_id;
122                self.call_event_handler(&Event::WindowClosed(wc));
123                // lets remove the window from the set
124                self.windows[window_id].is_created = false;
125                if let Some(index) = opengl_windows.iter().position( | w | w.window_id == window_id) {
126                    opengl_windows.remove(index);
127                    if opengl_windows.len() == 0 {
128                        xlib_app.terminate_event_loop();
129                        self.call_event_handler(&Event::Shutdown);
130                        return EventFlow::Exit
131                    }
132                }
133            }
134            XlibEvent::Paint => {
135                if self.new_next_frames.len() != 0 {
136                    self.call_next_frame_event(xlib_app.time_now());
137                }
138                if self.need_redrawing() {
139                    self.call_draw_event();
140                    self.os.opengl_cx.as_ref().unwrap().make_current();
141                    self.opengl_compile_shaders();
142                }
143                // ok here we send out to all our childprocesses
144                
145                self.handle_repaint(opengl_windows);
146            }
147            XlibEvent::MouseDown(e) => {
148                self.fingers.process_tap_count(
149                    e.abs,
150                    e.time
151                );
152                self.fingers.mouse_down(e.button, e.window_id);
153                self.call_event_handler(&Event::MouseDown(e.into()))
154            }
155            XlibEvent::MouseMove(e) => {
156                self.call_event_handler(&Event::MouseMove(e.into()));
157                self.fingers.cycle_hover_area(live_id!(mouse).into());
158                self.fingers.switch_captures();
159            }
160            XlibEvent::MouseUp(e) => {
161                let button = e.button;
162                self.call_event_handler(&Event::MouseUp(e.into()));
163                self.fingers.mouse_up(button);
164                self.fingers.cycle_hover_area(live_id!(mouse).into());
165            }
166            XlibEvent::Scroll(e) => {
167                self.call_event_handler(&Event::Scroll(e.into()))
168            }
169            XlibEvent::WindowDragQuery(e) => {
170                self.call_event_handler(&Event::WindowDragQuery(e))
171            }
172            XlibEvent::WindowCloseRequested(e) => {
173                self.call_event_handler(&Event::WindowCloseRequested(e))
174            }
175            XlibEvent::TextInput(e) => {
176                self.call_event_handler(&Event::TextInput(e))
177            }
178            XlibEvent::Drag(e) => {
179                self.call_event_handler(&Event::Drag(e))
180            }
181            XlibEvent::Drop(e) => {
182                self.call_event_handler(&Event::Drop(e))
183            }
184            XlibEvent::DragEnd => {
185                self.call_event_handler(&Event::DragEnd)
186            }
187            XlibEvent::KeyDown(e) => {
188                self.keyboard.process_key_down(e.clone());
189                self.call_event_handler(&Event::KeyDown(e))
190            }
191            XlibEvent::KeyUp(e) => {
192                self.keyboard.process_key_up(e.clone());
193                self.call_event_handler(&Event::KeyUp(e))
194            }
195            XlibEvent::TextCopy(e) => {
196                self.call_event_handler(&Event::TextCopy(e))
197            }
198            XlibEvent::TextCut(e) => {
199                self.call_event_handler(&Event::TextCut(e))
200            }
201            XlibEvent::Timer(e) => {
202                //println!("TIMER! {:?}", std::time::Instant::now());
203                if e.timer_id == 0{
204                    if SignalToUI::check_and_clear_ui_signal(){
205                        self.handle_media_signals();
206                        self.call_event_handler(&Event::Signal);
207                    }
208                    self.handle_action_receiver();
209                }
210                else{
211                    self.call_event_handler(&Event::Timer(e))
212                }
213
214                if self.handle_live_edit() {
215                    self.call_event_handler(&Event::LiveEdit);
216                    self.redraw_all();
217                }
218                return EventFlow::Wait;
219            }
220        }
221        
222        //if self.any_passes_dirty() || self.need_redrawing() || paint_dirty {
223            EventFlow::Poll
224        //} else {
225        //    EventFlow::Wait
226        // }
227        
228    }
229
230    pub(crate) fn handle_networking_events(&mut self) {
231    }
232    
233    pub (crate) fn handle_repaint(&mut self, opengl_windows: &mut Vec<OpenglWindow>) {
234        self.os.opengl_cx.as_ref().unwrap().make_current();
235        let mut passes_todo = Vec::new();
236        self.compute_pass_repaint_order(&mut passes_todo);
237        self.repaint_id += 1;
238        for pass_id in &passes_todo {
239            self.passes[*pass_id].set_time(get_xlib_app_global().time_now() as f32);
240            match self.passes[*pass_id].parent.clone() {
241                CxPassParent::Xr => {}
242                CxPassParent::Window(window_id) => {
243                    if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == window_id) {
244                        //let dpi_factor = window.window_geom.dpi_factor;
245                        window.resize_buffers();
246                        self.draw_pass_to_window(*pass_id, window);
247                    }
248                }
249                CxPassParent::Pass(_) => {
250                    //let dpi_factor = self.get_delegated_dpi_factor(parent_pass_id);
251                    self.draw_pass_to_texture(*pass_id, None);
252                },
253                CxPassParent::None => {
254                    self.draw_pass_to_texture(*pass_id, None);
255                }
256            }
257        }
258    }
259    
260    fn handle_platform_ops(&mut self, opengl_windows: &mut Vec<OpenglWindow>, xlib_app: &mut XlibApp) -> EventFlow {
261        let mut ret = EventFlow::Poll;
262        while let Some(op) = self.platform_ops.pop() {
263            match op {
264                CxOsOp::CreateWindow(window_id) => {
265                    let window = &mut self.windows[window_id];
266                    let opengl_window = OpenglWindow::new(
267                        window_id,
268                        self.os.opengl_cx.as_ref().unwrap(),
269                        window.create_inner_size.unwrap_or(dvec2(800., 600.)),
270                        window.create_position,
271                        &window.create_title,
272                    );
273                    window.window_geom = opengl_window.window_geom.clone();
274                    opengl_windows.push(opengl_window);
275                    window.is_created = true;
276                },
277                CxOsOp::CloseWindow(window_id) => {
278                    self.call_event_handler(&Event::WindowClosed(WindowClosedEvent { window_id }));
279                    if let Some(index) = opengl_windows.iter().position( | w | w.window_id == window_id) {
280                        self.windows[window_id].is_created = false;
281                        opengl_windows[index].xlib_window.close_window();
282                        opengl_windows.remove(index);
283                        if opengl_windows.len() == 0 {
284                            ret = EventFlow::Exit
285                        }
286                    }
287                },
288                CxOsOp::Quit=>{
289                    ret = EventFlow::Exit
290                }
291                CxOsOp::MinimizeWindow(window_id) => {
292                    if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == window_id) {
293                        window.xlib_window.minimize();
294                    }
295                },
296                CxOsOp::Deminiaturize(_window_id) => todo!(),
297                CxOsOp::HideWindow(_window_id) => todo!(),
298                CxOsOp::MaximizeWindow(window_id) => {
299                    if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == window_id) {
300                        window.xlib_window.maximize();
301                    }
302                },
303                CxOsOp::RestoreWindow(window_id) => {
304                    if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == window_id) {
305                        window.xlib_window.restore();
306                    }
307                },
308                CxOsOp::ResizeWindow(window_id, size) => {
309                    if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == window_id) {
310                        window.xlib_window.set_inner_size(size);
311                    }
312                },
313                CxOsOp::RepositionWindow(window_id, size) => {
314                    if let Some(window) = opengl_windows.iter_mut().find( | w | w.window_id == window_id) {
315                        window.xlib_window.set_position(size);
316                    }
317                },
318                CxOsOp::ShowClipboardActions(_) =>{
319                },
320                CxOsOp::CopyToClipboard(content) => {
321                    if let Some(window) = opengl_windows.get(0) {
322                        unsafe {
323                            xlib_app.copy_to_clipboard(&content, window.xlib_window.window.unwrap(), x11_sys::CurrentTime as u64)
324                        }
325                    }
326                }
327                CxOsOp::SetCursor(cursor) => {
328                    xlib_app.set_mouse_cursor(cursor);
329                },
330                CxOsOp::StartTimer {timer_id, interval, repeats} => {
331                    xlib_app.start_timer(timer_id, interval, repeats);
332                },
333                CxOsOp::StopTimer(timer_id) => {
334                    xlib_app.stop_timer(timer_id);
335                },
336                CxOsOp::ShowTextIME(area, pos) => {
337                    let pos = area.clipped_rect(self).pos + pos;
338                    opengl_windows.iter_mut().for_each(|w| {
339                        w.xlib_window.set_ime_spot(pos);
340                    });
341                },
342                CxOsOp::HideTextIME => {
343                    opengl_windows.iter_mut().for_each(|w| {
344                        w.xlib_window.set_ime_spot(dvec2(0.0,0.0));
345                    });
346                },
347                e=>{
348                    crate::error!("Not implemented on this platform: CxOsOp::{:?}", e);
349                }
350            }
351        }
352        ret
353    }
354}
355
356impl CxOsApi for Cx {
357    fn init_cx_os(&mut self) {
358        self.os.start_time = Some(Instant::now());
359        if let Some(item) = std::option_env!("MAKEPAD_PACKAGE_DIR"){
360            self.live_registry.borrow_mut().package_root = Some(item.to_string());
361        }
362        self.live_expand();
363        if !Self::has_studio_web_socket() {
364            self.start_disk_live_file_watcher(100);
365        }
366        self.live_scan_dependencies();
367        self.native_load_dependencies();
368    }
369    
370    fn spawn_thread<F>(&mut self, f: F) where F: FnOnce() + Send + 'static {
371        std::thread::spawn(f);
372    }
373    
374    fn seconds_since_app_start(&self)->f64{
375        Instant::now().duration_since(self.os.start_time.unwrap()).as_secs_f64()
376    }
377    
378    fn open_url(&mut self, _url:&str, _in_place:OpenUrlInPlace){
379        crate::error!("open_url not implemented on this platform");
380    }
381}
382
383#[derive(Default)]
384pub struct CxOs {
385    pub(crate) media: CxLinuxMedia,
386    pub (crate) stdin_timers: PollTimers,
387    pub (crate) start_time: Option<Instant>,
388    // HACK(eddyb) generalize this to EGL, properly.
389    pub(super) opengl_cx: Option<OpenglCx>,
390}
391
392impl CxOs{
393    pub(crate) fn gl(&self)->&LibGl{
394        &self.opengl_cx.as_ref().unwrap().libgl
395    }
396}