gooey/presentation/opengl/
remote.rs1use std::{env, thread, time, vec};
11use std::sync::{atomic, Arc, LazyLock, Mutex};
12use nsys::{self, gl, math};
13use nsys::gl::glium::{self, glutin};
14use nsys::gl::winit;
15use key_vec::KeyVec;
16use unbounded_spsc;
17
18use crate::prelude::*;
19use super::{render, Graphics, InputHandler, InputState, Presentation};
20
21#[expect(clippy::type_complexity)]
23static DISPLAY_RECEIVER
24  : LazyLock <Mutex <Option <unbounded_spsc::Receiver <Vec <(NodeId, Display)>>>>>
25  = LazyLock::new (|| Mutex::new (None));
26static GLUTIN_WINDOW
28  : LazyLock <Mutex <Option <(winit::window::Window, glutin::config::Config)>>>
29  = LazyLock::new (|| Mutex::new (None));
30static FRAME : LazyLock <Arc <atomic::AtomicU64>> = LazyLock::new (||
32  Arc::new (atomic::AtomicU64::new (0)));
33
34pub struct Remote {
36  pub event_loop : winit::event_loop::EventLoop <()>,
37  display        : unbounded_spsc::Sender <Vec <(NodeId, Display)>>,
38  input_state    : InputState,
39  screen_tiles   : Option <NodeId>
40}
41
42pub struct Opengl {
44  pub draw_crosshair : bool,
45  pub inner       : gl::Render,
46  tileset_id      : Option <gl::render::resource::DefaultTilesetId>,
47  updates         : unbounded_spsc::Receiver <Vec <(NodeId, Display)>>,
48  id_map          : KeyVec <NodeId, NodeId>,
49  view            : Tree <View>,
50  glium_frame     : Option <glium::Frame>,
51  display_counter : u64
53}
54
55pub fn create_screen_tiles <A, P> (interface : &mut Interface <A, P>) where
59  A : Application,
60  P : Presentation + presentation::HasGraphics <Remote>
61{
62  let screen_id     = interface.root_id().clone();
63  let screen_tiles  = frame::free::Builder::<A>::new (
64    interface.elements(), &screen_id
65  ) .layout (layout::Free { size: Size::fill(), .. layout::Free::default_tile() })
66    .clear_color (canvas::ClearColor::Fixed (None))
67    .coord_kind_override (coordinates::Kind::Tile)
68    .build_element();
69  let (_, Event::Create (_, tiles_id, _)) = interface
70    .action (&screen_id, Action::create_singleton (screen_tiles, CreateOrder::Append))
71    .next().unwrap() else { unreachable!() };
72  interface.presentation.graphics().screen_tiles = Some (tiles_id);
73}
74
75impl Default for Remote {
76  fn default() -> Self {
77    let event_loop = {
78      let (event_loop, window, gl_config) =
79        gl::init::glutin_window ("Gooey Opengl Remote Window");
80      {
81        let mut glutin_window_lock = GLUTIN_WINDOW.lock().unwrap();
82        debug_assert!(glutin_window_lock.is_none());
83        window.set_cursor_visible (false);
88        *glutin_window_lock = Some ((window, gl_config));
92      }
93      event_loop
94    };
95    let display = {
96      let (sender, receiver) =
97        unbounded_spsc::channel::<Vec <(NodeId, Display)>>();
98      {
99        let mut display_receiver_lock = DISPLAY_RECEIVER.lock().unwrap();
100        debug_assert!(display_receiver_lock.is_none());
101        *display_receiver_lock = Some (receiver);
102      }
103      sender
104    };
105    Remote {
106      event_loop,
107      display,
108      input_state:  InputState::new(),
109      screen_tiles: None
110    }
111  }
112}
113
114pub fn frame() -> u64 {
116  FRAME.load (atomic::Ordering::SeqCst)
117}
118
119impl Remote {
120  #[inline]
121  pub const fn screen_tiles_id (&self) -> &NodeId {
122    self.screen_tiles.as_ref().unwrap()
123  }
124  #[inline]
125  pub const fn dimensions (&self) -> dimensions::Pixel {
126    self.input_state.dimensions
127  }
128  pub const fn pointer_sensitivity (&self) -> f32 {
129    self.input_state.pointer_sensitivity
130  }
131  pub const fn set_pointer_sensitivity (&mut self, pointer_sensitivity : f32) {
132    self.input_state.pointer_sensitivity = pointer_sensitivity
133  }
134}
135
136impl Graphics     for Remote { }
137impl Presentation for Remote {
138  fn with_root (root : View, root_id : NodeId) -> Self {
142    let remote = Self::default();
143    log::debug!("opengl remote with root: {root:?}");
144    remote.display.send (vec![
145      (root_id.clone(), Display::Create (root, root_id, CreateOrder::Append))
146    ]).unwrap();
147    loop {
150      if (*DISPLAY_RECEIVER).lock().unwrap().is_none() {
151        break
152      }
153      thread::sleep (time::Duration::from_millis (100));
154    }
155    remote
156  }
157
158  fn make_interface <A : Application> () -> Interface <A, Self> {
162    let screen = {
163      let mut screen = frame::screen::PixelBuilder::<A>::new()
166        .anchor (Alignment::pixel())
167        .build_element();
168      let canvas = Canvas::try_ref_mut (&mut screen.view.component).unwrap();
170      canvas.coordinates.modify_dimensions_horizontal (1);
171      canvas.coordinates.modify_dimensions_vertical (1);
172      screen
173    };
174    Interface::<A, Remote>::with_root (screen)
175  }
176
177  fn get_input (&mut self, input_buffer : &mut Vec <Input>) {
178    use winit::platform::pump_events::EventLoopExtPumpEvents;
179    log::trace!("get input...");
180    self.event_loop.pump_app_events (
183      Some (time::Duration::ZERO),
184      &mut InputHandler { input_buffer, input_state: &mut self.input_state });
185    log::trace!("...get input");
186  }
187
188  fn display_view <V : AsRef <View>> (&mut self,
189    _view_tree      : &Tree <V>,
190    display_values : vec::Drain <(NodeId, Display)>
191  ) {
192    self.display.send (display_values.collect()).unwrap();
193  }
194}
195
196impl Opengl {
197  pub fn init (tileset_id : Option <gl::render::resource::DefaultTilesetId>) -> Self {
200    let inner = {
201      use gl::render::resource::MAIN_VIEWPORT;
202      let (window, gl_config) = loop {
204        let maybe_glutin_window = (*GLUTIN_WINDOW).lock().unwrap().take();
205        if let Some (glutin_window) = maybe_glutin_window {
206          break glutin_window
207        }
208        thread::sleep (time::Duration::from_millis (100));
209      };
210      let display = gl::init::glium_display_gl33core (&window, &gl_config);
216      let mut render =
217        gl::Render::<gl::render::resource::Default>::new (display, window);
218      let [tile_width, tile_height] =
220        render.resource.tile_dimensions (tileset_id.unwrap_or_default());
221      unsafe {
222        env::set_var ("GOOEY_TILE_WIDTH", tile_width.to_string());
223        env::set_var ("GOOEY_TILE_HEIGHT", tile_height.to_string());
224      }
225      render.resource.draw2d
227        .viewport_resources_set (MAIN_VIEWPORT, Default::default());
228      render
229    };
230    let updates = loop {
231      let maybe_receiver = (*DISPLAY_RECEIVER).lock().unwrap().take();
232      if let Some (receiver) = maybe_receiver {
233        break receiver
234      }
235      thread::sleep (time::Duration::from_millis (100));
236    };
237    Opengl {
238      inner,
239      tileset_id,
240      updates,
241      id_map:          KeyVec::new(),
242      view:            Tree::new(),
243      glium_frame:     None,
244      display_counter: 0,
245      draw_crosshair:  false
246    }
247  }
248
249  #[inline]
250  pub const fn inner (&self) -> &gl::Render {
251    &self.inner
252  }
253  #[inline]
254  pub const fn inner_mut (&mut self) -> &mut gl::Render {
255    &mut self.inner
256  }
257  #[inline]
258  #[deprecated = "use remote::frame() function instead"]
259  pub fn frame (&self) -> u64 {
260    frame()
261  }
262  #[inline]
263  pub const fn display_counter (&self) -> u64 {
264    self.display_counter
265  }
266  #[inline]
267  pub const fn reset_display_counter (&mut self) {
268    self.display_counter = 0;
269  }
270  pub fn load_pointer (&mut self,
271    key    : gl::render::resource::PointerTextureIndexRepr,
272    bytes  : &[u8],
273    offset : math::Vector2 <i16>
274  ) {
275    let render = &mut self.inner;
276    render::load_pointer (render, key, bytes, offset);
277  }
278  pub fn load_textures (&mut self,
279    textures_16x16   : &[&'static str],
280    textures_64x64   : &[&'static str],
281    textures_anysize : &[&'static str]
282  ) {
283    render::load_textures (
284      &mut self.inner, textures_16x16, textures_64x64, textures_anysize)
285  }
286  pub fn process_display_events (&mut self) {
287    while let Ok (updates) = self.updates.try_recv() {
288      for (node_id, display) in updates {
289        match display {
290          Display::Create (view, child_id, order) => {
291            let new_id = if self.id_map.is_empty() {
292              self.view.insert (Node::new (view),
294                tree::InsertBehavior::AsRoot).unwrap()
295            } else {
296              let parent_id = self.id_map.get (&node_id).unwrap();
297              let new_id    = self.view.insert (
298                Node::new (view),
299                tree::InsertBehavior::UnderNode (parent_id)).unwrap();
300              match order {
301                CreateOrder::Prepend => {
302                  let _ = self.view.make_first_sibling (&new_id).unwrap();
303                }
304                CreateOrder::NthSibling (n) =>
305                  self.view.make_nth_sibling (&new_id, n as usize).unwrap(),
306                CreateOrder::Append => {}
307              }
308              new_id
309            };
310            assert!(self.id_map.insert (child_id, new_id).is_none());
311          }
312          Display::Update (update) => {
313            let id = self.id_map.get (&node_id).unwrap();
314            match update {
315              Update::View (view) =>
316                *self.view.get_mut (id).unwrap().data_mut() = view,
317              Update::FocusTop => {
318                self.view.make_last_sibling (id).unwrap();
319              }
320            }
321          }
322          Display::Destroy => {
323            let id = self.id_map.remove (&node_id).unwrap();
324            let _ = self.view.remove_node (id, tree::RemoveBehavior::DropChildren)
325              .unwrap();
326          }
327        }
328      }
329      self.display_counter += 1;
330    }
331  }
332  pub fn update_and_do_frame (&mut self) {
333    render::update (&mut self.inner, self.tileset_id, &self.view, self.draw_crosshair);
335    self.inner.do_frame (self.glium_frame.as_mut());
337    FRAME.fetch_add (1, atomic::Ordering::SeqCst);
338  }
339  pub fn display (&mut self) {
341    self.process_display_events();
342    self.update_and_do_frame();
343  }
344}
345
346impl AsRef <View> for View {
348  fn as_ref (&self) -> &View {
349    self
350  }
351}