makepad_platform/os/
cx_stdin.rs

1#![allow(dead_code)]
2use {
3    std::cell::Cell,
4    std::collections::HashMap,
5    crate::{
6        cx::Cx,
7        cursor::MouseCursor,
8        makepad_micro_serde::*,
9        makepad_math::{dvec2,DVec2},
10        window::WindowId,
11        area::Area,
12        event::{
13            KeyModifiers,
14            Event,
15            TextInputEvent,
16            TimerEvent,
17            KeyEvent,
18            ScrollEvent,
19            MouseButton,
20            MouseDownEvent,
21            MouseUpEvent,
22            MouseMoveEvent,
23        }
24    }
25};
26
27// HACK(eddyb) more or less `<[T; N]>::each_ref`, which is still unstable.
28fn ref_array_to_array_of_refs<T, const N: usize>(ref_array: &[T; N]) -> [&T; N] {
29    let mut out_refs = std::mem::MaybeUninit::<[&T; N]>::uninit();
30    for (i, ref_elem) in ref_array.iter().enumerate() {
31        unsafe { *out_refs.as_mut_ptr().cast::<&T>().add(i) = ref_elem; }
32    }
33    unsafe { out_refs.assume_init() }
34}
35
36pub const SWAPCHAIN_IMAGE_COUNT: usize = match () {
37    // HACK(eddyb) done like this so that we can override each target easily.
38    _ if cfg!(target_os = "linux")   => 3,
39    _ if cfg!(target_os = "macos")   => 1,
40    _ if cfg!(target_os = "windows") => 2,
41    _ => 2,
42};
43
44/// "Swapchains" group together some number (i.e. `SWAPCHAIN_IMAGE_COUNT` here)
45/// of "presentable images", to form a queue of render targets which can be
46/// "presented" (to a surface, like a display, window, etc.) independently of
47/// rendering being done onto *other* "presentable images" in the "swapchain".
48///
49/// Certain configurations of swapchains often have older/more specific names,
50/// e.g. "double buffering" for `SWAPCHAIN_IMAGE_COUNT == 2` (or "triple" etc.).
51#[derive(Copy, Clone, Debug, PartialEq, SerBin, DeBin, SerJson, DeJson)]
52pub struct Swapchain<I>
53    // HACK(eddyb) hint `{Ser,De}{Bin,Json}` derivers to add their own bounds.
54    where I: Sized
55{
56    pub window_id: usize,
57    pub alloc_width: u32,
58    pub alloc_height: u32,
59    pub presentable_images: [PresentableImage<I>; SWAPCHAIN_IMAGE_COUNT],
60}
61
62impl Swapchain<()> {
63    pub fn new(window_id: usize, alloc_width: u32, alloc_height: u32) -> Self {
64        let presentable_images = [(); SWAPCHAIN_IMAGE_COUNT].map(|()| PresentableImage {
65            id: PresentableImageId::alloc(),
66            image: (),
67        });
68        Self { window_id, alloc_width, alloc_height, presentable_images }
69    }
70}
71
72impl<I> Swapchain<I> {
73    pub fn get_image(&self, id: PresentableImageId) -> Option<&PresentableImage<I>> {
74        self.presentable_images.iter().find(|pi| pi.id == id)
75    }
76    pub fn images_as_ref(&self) -> Swapchain<&I> {
77        let Swapchain { window_id, alloc_width, alloc_height, ref presentable_images } = *self;
78        let presentable_images = ref_array_to_array_of_refs(presentable_images)
79            .map(|&PresentableImage { id, ref image }| PresentableImage { id, image });
80        Swapchain { window_id, alloc_width, alloc_height, presentable_images }
81    }
82    pub fn images_map<I2>(self, mut f: impl FnMut(PresentableImage<I>) -> I2) -> Swapchain<I2> {
83        let Swapchain { window_id, alloc_width, alloc_height, presentable_images } = self;
84        let presentable_images = presentable_images
85            .map(|pi| PresentableImage { id: pi.id, image: f(pi) });
86        Swapchain { window_id, alloc_width, alloc_height, presentable_images }
87    }
88}
89
90/// One of the "presentable images" of a [`SharedSwapchain`].
91#[derive(Copy, Clone, Debug, PartialEq, SerBin, DeBin, SerJson, DeJson)]
92pub struct PresentableImage<I>
93    // HACK(eddyb) hint `{Ser,De}{Bin,Json}` derivers to add their own bounds.
94    where I: Sized
95{
96    pub id: PresentableImageId,
97    pub image: I,
98}
99
100/// Cross-process-unique (on best-effort) ID of a [`SharedPresentableImage`],
101/// such that multiple processes on the same system should be able to share
102/// swapchains with each-other and (effectively) never observe collisions.
103#[derive(Copy, Clone, Debug, PartialEq, SerBin, DeBin, SerJson, DeJson)]
104pub struct PresentableImageId {
105    /// PID of the originating process (which allocated this ID).
106    origin_pid: u32,
107
108    /// The atomically-acquired value of a (private) counter, during allocation,
109    /// in the originating process, which will guarantee that the same process
110    /// continuously generating new swapchains will not overlap with itself,
111    /// unless it generates billions of swapchains, mixing old and new ones.
112    per_origin_counter: u32,
113}
114
115impl PresentableImageId {
116    pub fn alloc() -> Self {
117        use std::sync::atomic::{AtomicU32, Ordering};
118
119        static COUNTER: AtomicU32 = AtomicU32::new(0);
120
121        Self {
122            origin_pid: std::process::id(),
123            per_origin_counter: COUNTER.fetch_add(1, Ordering::Relaxed),
124        }
125    }
126
127    pub fn as_u64(self) -> u64 {
128        let Self { origin_pid, per_origin_counter } = self;
129        (u64::from(origin_pid) << 32) | u64::from(per_origin_counter)
130    }
131
132    // NOT public intentionally! (while not too dangerous, this could be misused)
133    fn from_u64(pid_and_counter: u64) -> Self {
134        Self {
135            origin_pid: (pid_and_counter >> 32) as u32,
136            per_origin_counter: pid_and_counter as u32,
137        }
138    }
139}
140
141pub type SharedSwapchain = Swapchain<SharedPresentableImageOsHandle>;
142
143// FIXME(eddyb) move these type aliases into `os::{linux,apple,windows}`.
144
145/// [DMA-BUF](crate::os::linux::dma_buf)-backed image from `eglExportDMABUFImageMESA`.
146#[cfg(all(target_os = "linux", not(target_env="ohos")))]
147pub type SharedPresentableImageOsHandle =
148    crate::os::linux::dma_buf::Image<aux_chan::AuxChannedImageFd>;
149
150// HACK(eddyb) the macOS helper XPC service (in `os/apple/metal_xpc.{m,rs}`)
151// doesn't need/want any form of "handle passing", as the `id` field contains
152// all the disambiguating information it may need (however, long-term it'd
153// probably be better to use something like `IOSurface` + mach ports).
154#[cfg(target_os = "macos")]
155#[derive(Copy, Clone, Debug, PartialEq, SerBin, DeBin, SerJson, DeJson)]
156pub struct SharedPresentableImageOsHandle {
157    // HACK(eddyb) non-`()` field working around deriving limitations.
158    pub _dummy_for_macos: Option<u32>,
159}
160
161/// DirectX 11 `HANDLE` from `IDXGIResource::GetSharedHandle`.
162#[cfg(target_os = "windows")]
163// FIXME(eddyb) actually use a newtype of `HANDLE` with manual trait impls.
164pub type SharedPresentableImageOsHandle = u64;
165
166// FIXME(eddyb) use `enum Foo {}` here ideally, when the derives are fixed.
167#[cfg(not(any(all(target_os = "linux", not(target_env="ohos")), target_os = "macos", target_os = "windows")))]
168#[derive(Copy, Clone, Debug, PartialEq, SerBin, DeBin, SerJson, DeJson)]
169pub struct SharedPresentableImageOsHandle {
170    // HACK(eddyb) non-`()` field working around deriving limitations.
171    pub _dummy_for_unsupported: Option<u32>,
172}
173
174/// Auxiliary communication channel, besides stdin (only on Linux).
175#[cfg(all(target_os = "linux", not(target_env="ohos")))]
176pub mod aux_chan {
177    use super::*;
178    use crate::os::linux::ipc::{self as linux_ipc, FixedSizeEncoding};
179    use std::{io, os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd}};
180
181    // HACK(eddyb) `io::Error::other` stabilization is too recent.
182    fn io_error_other(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
183        io::Error::new(io::ErrorKind::Other, error)
184    }
185
186    // Host->Client and Client->Host message types.
187    pub type H2C = (PresentableImageId, OwnedFd);
188    pub type C2H = linux_ipc::Never;
189
190    impl FixedSizeEncoding<{u64::BYTE_LEN}, 0> for PresentableImageId {
191        fn encode(&self) -> ([u8; Self::BYTE_LEN], [std::os::fd::BorrowedFd<'_>; 0]) {
192            let (bytes, []) = self.as_u64().encode();
193            (bytes, [])
194        }
195        fn decode(bytes: [u8; Self::BYTE_LEN], fds: [OwnedFd; 0]) -> Self {
196            Self::from_u64(u64::decode(bytes, fds))
197        }
198    }
199
200    pub type HostEndpoint = linux_ipc::Channel<H2C, C2H>;
201    pub type ClientEndpoint = linux_ipc::Channel<C2H, H2C>;
202    pub fn make_host_and_client_endpoint_pair() -> io::Result<(HostEndpoint, ClientEndpoint)> {
203        linux_ipc::channel()
204    }
205
206    pub type InheritableClientEndpoint = linux_ipc::InheritableChannel<C2H, H2C>;
207    impl InheritableClientEndpoint {
208        pub fn extra_args_for_client_spawning(&self) -> [String; 1] {
209            [format!("--stdin-loop-aux-chan-fd={}", self.as_fd().as_raw_fd())]
210        }
211        pub fn from_process_args_in_client() -> io::Result<Self> {
212            for arg in std::env::args() {
213                if let Some(fd) = arg.strip_prefix("--stdin-loop-aux-chan-fd=") {
214                    let raw_fd = fd.parse().map_err(io_error_other)?;
215                    let owned_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
216                    return Ok(Self::from(owned_fd));
217                }
218            }
219            Err(io_error_other("missing --stdin-loop-aux-chan-fd argument"))
220        }
221    }
222
223    // HACK(eddyb) this type being serialized/deserialized doesn't really ensure
224    // anything in and of itself, it's only used here to guide correct usage
225    // through types - ideally host<->client (de)serialization itself would
226    // handle all the file descriptors passing necessary, but for now this helps.
227    #[derive(Copy, Clone, Debug, PartialEq, SerBin, DeBin, SerJson, DeJson)]
228    pub struct AuxChannedImageFd {
229        // HACK(eddyb) non-`()` field working around deriving limitations.
230        _private: Option<u32>,
231    }
232    type PrDmaBufImg<FD> = PresentableImage<crate::os::linux::dma_buf::Image<FD>>;
233    impl PrDmaBufImg<OwnedFd> {
234        pub fn send_fds_to_aux_chan(self, host_endpoint: &HostEndpoint)
235            -> io::Result<PrDmaBufImg<AuxChannedImageFd>>
236        {
237            let Self { id, image } = self;
238            let mut plane_idx = 0;
239            let mut success = Ok(());
240            let image = image.planes_fd_map(|fd| {
241                assert_eq!(plane_idx, 0, "only images with one DMA-BUF plane are supported");
242                plane_idx += 1;
243                if success.is_ok() {
244                    success = host_endpoint.send((self.id, fd));
245                }
246                AuxChannedImageFd { _private: None }
247            });
248            success?;
249            Ok(PresentableImage { id, image })
250        }
251    }
252    impl PrDmaBufImg<AuxChannedImageFd> {
253        pub fn recv_fds_from_aux_chan(self, client_endpoint: &ClientEndpoint)
254            -> io::Result<PrDmaBufImg<OwnedFd>>
255        {
256            let Self { id, image } = self;
257            let mut plane_idx = 0;
258            let mut success = Ok(());
259            let image = image.planes_fd_map(|_| {
260                assert_eq!(plane_idx, 0, "only images with one DMA-BUF plane are supported");
261                plane_idx += 1;
262
263                client_endpoint.recv().and_then(|(recv_id, recv_fd)|
264                if recv_id != id {
265                    Err(io_error_other(format!(
266                        "recv_fds_from_aux_chan: ID mismatch \
267                         (expected {id:?}, got {recv_id:?}",
268                    )))
269                } else {
270                    Ok(recv_fd)
271                }).map_err(|err| if success.is_ok() { success = Err(err); })
272            });
273            success?;
274            Ok(PresentableImage {
275                id,
276                image: image.planes_fd_map(Result::unwrap)
277            })
278        }
279    }
280}
281#[cfg(not(all(target_os = "linux", not(target_env="ohos"))))]
282pub mod aux_chan {
283    use std::io;
284
285    #[derive(Clone)]
286    pub struct HostEndpoint { _private: () }
287    pub struct ClientEndpoint { _private: () }
288    pub fn make_host_and_client_endpoint_pair() -> io::Result<(HostEndpoint, ClientEndpoint)> {
289        Ok((HostEndpoint { _private: () }, ClientEndpoint { _private: () }))
290    }
291
292    pub struct InheritableClientEndpoint(ClientEndpoint);
293    impl ClientEndpoint {
294        pub fn into_child_process_inheritable(
295            self,
296        ) -> io::Result<InheritableClientEndpoint> {
297            Ok(InheritableClientEndpoint(self))
298        }
299    }
300    impl InheritableClientEndpoint {
301        pub fn into_uninheritable(self) -> io::Result<ClientEndpoint> {
302            Ok(self.0)
303        }
304        pub fn extra_args_for_client_spawning(&self) -> [String; 0] {
305            []
306        }
307    }
308}
309
310
311#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
312pub struct StdinKeyModifiers{
313    pub shift: bool,
314    pub control: bool,
315    pub alt: bool,
316    pub logo: bool
317}
318
319impl StdinKeyModifiers{
320    pub fn into_key_modifiers(&self)->KeyModifiers{
321        KeyModifiers{
322            shift: self.shift,
323            control: self.control,
324            alt: self.alt,
325            logo: self.logo,
326        }
327    }
328    pub fn from_key_modifiers(km:&KeyModifiers)->Self{
329        Self{
330            shift: km.shift,
331            control: km.control,
332            alt: km.alt,
333            logo: km.logo,
334        }
335    }
336}
337
338
339#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
340pub struct StdinMouseDown {
341   pub button_raw_bits: u32,
342   pub x: f64,
343   pub y: f64,
344   pub time: f64,
345   pub modifiers: StdinKeyModifiers
346}
347
348impl StdinMouseDown {
349    pub fn into_event(self, window_id: WindowId, pos: DVec2) -> MouseDownEvent {
350        MouseDownEvent {
351            abs: dvec2(self.x - pos.x, self.y - pos.y),
352            button: MouseButton::from_bits_retain(self.button_raw_bits),
353            window_id,
354            modifiers: self.modifiers.into_key_modifiers(),
355            time: self.time,
356            handled: Cell::new(Area::Empty),
357        }
358    }
359}
360
361#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
362pub struct StdinMouseMove{
363   pub time: f64,
364   pub x: f64,
365   pub y: f64,
366   pub modifiers: StdinKeyModifiers
367}
368
369impl StdinMouseMove {
370    pub fn into_event(self, window_id: WindowId, pos: DVec2) -> MouseMoveEvent {
371        MouseMoveEvent{
372            abs: dvec2(self.x - pos.x, self.y - pos.y),
373            window_id,
374            modifiers: self.modifiers.into_key_modifiers(),
375            time: self.time,
376            handled: Cell::new(Area::Empty),
377        }
378    }
379}
380
381#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
382pub struct StdinMouseUp {
383   pub time: f64,
384   pub button_raw_bits: u32,
385   pub x: f64,
386   pub y: f64,
387   pub modifiers: StdinKeyModifiers
388}
389
390#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
391pub struct StdinTextInput{
392    pub time: f64,
393    pub window_id: usize,
394    pub raw_button: usize,
395    pub x: f64,
396    pub y: f64
397}
398
399impl StdinMouseUp {
400   pub fn into_event(self, window_id: WindowId, pos: DVec2) -> MouseUpEvent {
401        MouseUpEvent {
402            abs: dvec2(self.x - pos.x, self.y - pos.y),
403            button: MouseButton::from_bits_retain(self.button_raw_bits),
404            window_id,
405            modifiers: self.modifiers.into_key_modifiers(),
406            time: self.time,
407        }
408    }
409}
410
411
412#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
413pub struct StdinScroll{
414   pub time: f64,
415   pub sx: f64,
416   pub sy: f64,
417   pub x: f64,
418   pub y: f64,
419   pub is_mouse: bool,
420   pub modifiers: StdinKeyModifiers
421}
422
423impl StdinScroll {
424    pub fn into_event(self, window_id: WindowId, pos: DVec2) -> ScrollEvent {
425        ScrollEvent{
426            abs: dvec2(self.x - pos.x, self.y - pos.y),
427            scroll: dvec2(self.sx, self.sy),
428            window_id,
429            modifiers: self.modifiers.into_key_modifiers(),
430            handled_x: Cell::new(false),
431            handled_y: Cell::new(false),
432            is_mouse: self.is_mouse,
433            time: self.time,
434        }
435    }
436}
437
438#[derive(Clone, Debug, SerBin, DeBin, SerJson, DeJson)]
439pub enum HostToStdin{
440    Swapchain(SharedSwapchain),
441    WindowGeomChange {
442        dpi_factor: f64,
443        window_id: usize,
444        // HACK(eddyb) `DVec` (like `WindowGeom`'s `inner_size` field) can't
445        // be used here due to it not implementing (de)serialization traits.
446        left: f64,
447        top: f64,
448        width: f64,
449        height: f64,
450    },
451    Tick,
452    /*
453    Tick{
454        buffer_id: u64,
455        frame: u64,
456        time: f64,
457    },
458    */
459    
460    MouseDown(StdinMouseDown),
461    MouseUp(StdinMouseUp),
462    MouseMove(StdinMouseMove),
463    KeyDown(KeyEvent),
464    KeyUp(KeyEvent),
465    TextInput(TextInputEvent),
466    Scroll(StdinScroll),
467    /*ReloadFile{
468        file:String,
469        contents:String
470    },*/
471}
472
473/// After a successful client-side draw, all the host needs to know, so it can
474/// present the result, is the swapchain image used, and the sub-area within
475/// that image that was being used to draw the entire client window (with the
476/// whole allocated area rarely used, except just before needing a new swapchain).
477#[derive(Copy, Clone, Debug, SerBin, DeBin, SerJson, DeJson)]
478pub struct PresentableDraw {
479    pub window_id: usize,
480    pub target_id: PresentableImageId,
481    pub width: u32,
482    pub height: u32,
483}
484
485#[repr(usize)]
486pub enum WindowKindId{
487    Main = 0,
488    Design = 1,
489    Outline = 2
490}
491
492impl WindowKindId{
493    pub fn from_usize(d:usize)->Self{
494        match d{
495            0=>Self::Main,
496            1=>Self::Design,
497            2=>Self::Outline,
498            _=>panic!()
499        }
500    }
501}
502
503#[derive(Clone, Debug, SerBin, DeBin, SerJson, DeJson)]
504pub enum StdinToHost {
505    CreateWindow{window_id: usize, kind_id:usize},
506    ReadyToStart,
507    SetCursor(MouseCursor),
508    // the client is done drawing, and the texture is completely updated
509    DrawCompleteAndFlip(PresentableDraw)
510}
511
512impl StdinToHost{
513    pub fn to_json(&self)->String{
514        let mut json = self.serialize_json();
515        json.push('\n');
516        json
517    }
518}
519
520impl HostToStdin{
521    pub fn to_json(&self)->String{
522        let mut json = self.serialize_json();
523        json.push('\n');
524        json
525    }
526}
527
528impl Cx {
529    
530}
531
532
533use std::time::Instant;
534use std::time::Duration;
535
536pub struct PollTimer {
537    pub start_time: Instant,
538    pub interval: Duration,
539    pub repeats: bool,
540    pub step: u64,
541}
542
543impl PollTimer {
544    pub fn new(interval_s: f64, repeats: bool) -> Self {
545        Self {
546            start_time: Instant::now(),
547            interval: Duration::from_secs_f64(interval_s),
548            repeats,
549            step: 0,
550        }
551    }
552}
553
554pub struct PollTimers{
555    pub timers: HashMap<u64, PollTimer>,
556    pub time_start: Instant,
557    pub last_time: Instant,
558}
559impl Default for PollTimers{
560    fn default()->Self{
561        Self{
562            time_start: Instant::now(),
563            last_time: Instant::now(),
564            timers: Default::default()
565        }
566    }
567}
568impl PollTimers{
569   
570    pub fn time_now(&self) -> f64 {
571        let time_now = Instant::now(); //unsafe {mach_absolute_time()};
572        (time_now.duration_since(self.time_start)).as_secs_f64()
573    }
574
575    pub fn get_dispatch(&mut self)->Vec<Event>{
576        let mut to_be_dispatched = Vec::with_capacity(self.timers.len());
577        let mut to_be_removed = Vec::with_capacity(self.timers.len());
578        let now = Instant::now();
579        let time = self.time_now();
580        for (id, timer) in self.timers.iter_mut() {
581            let elapsed_time = now - timer.start_time;
582            let next_due_time = Duration::from_nanos(timer.interval.as_nanos() as u64 * (timer.step + 1));
583            
584            if elapsed_time > next_due_time {
585                
586                to_be_dispatched.push(Event::Timer(TimerEvent {timer_id: *id, time:Some(time)}));
587                if timer.repeats {
588                    timer.step += 1;
589                } else {
590                    to_be_removed.push(*id);
591                }
592            }
593        }
594        
595        for id in to_be_removed {
596            self.timers.remove(&id);
597        }
598
599        self.last_time = now;
600        to_be_dispatched
601    }
602}