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 ) .clear_color (canvas::ClearColor::Fixed (None))
66 .coord_kind_override (coordinates::Kind::Tile)
67 .layout (layout::Free { size: Size::fill(), .. layout::Free::default_tile() })
68 .name ("screen-tiles".to_string())
69 .build_element();
70 let (_, Event::Create (_, tiles_id, _)) = interface
71 .action (&screen_id, Action::create_singleton (screen_tiles, CreateOrder::Append))
72 .next().unwrap() else { unreachable!() };
73 interface.presentation.graphics().screen_tiles = Some (tiles_id);
74}
75
76impl Default for Remote {
77 fn default() -> Self {
78 let event_loop = {
79 let (event_loop, window, gl_config) =
80 gl::init::glutin_window ("Gooey Opengl Remote Window");
81 {
82 let mut glutin_window_lock = GLUTIN_WINDOW.lock().unwrap();
83 debug_assert!(glutin_window_lock.is_none());
84 window.set_cursor_visible (false);
89 *glutin_window_lock = Some ((window, gl_config));
93 }
94 event_loop
95 };
96 let display = {
97 let (sender, receiver) = 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 make_interface <A : Application> () -> Interface <A, Self> {
142 let screen = {
143 let mut screen = frame::screen::PixelBuilder::<A>::new()
146 .anchor (Alignment::pixel())
147 .name ("screen".to_string())
148 .build_element();
149 let canvas = Canvas::try_ref_mut (&mut screen.view.component).unwrap();
151 canvas.coordinates.modify_dimensions_horizontal (1);
152 canvas.coordinates.modify_dimensions_vertical (1);
153 screen
154 };
155 Interface::<A, Remote>::with_root (screen)
156 }
157
158 fn with_root (root : View, root_id : NodeId) -> Self {
162 let remote = Self::default();
163 log::debug!("opengl remote with root: {root:?}");
164 remote.display.send (vec![
165 (root_id.clone(), Display::Create (root, root_id, CreateOrder::Append))
166 ]).unwrap();
167 loop {
170 if (*DISPLAY_RECEIVER).lock().unwrap().is_none() {
171 break
172 }
173 thread::sleep (time::Duration::from_millis (100));
174 }
175 remote
176 }
177
178 fn get_input (&mut self, input_buffer : &mut Vec <Input>) {
179 use winit::platform::pump_events::EventLoopExtPumpEvents;
180 log::trace!("get input...");
181 self.event_loop.pump_app_events (
184 Some (time::Duration::ZERO),
185 &mut InputHandler { input_buffer, input_state: &mut self.input_state });
186 log::trace!("...get input");
187 }
188
189 fn display_view <V : AsRef <View>> (&mut self,
190 _view_tree : &Tree <V>,
191 display_values : vec::Drain <(NodeId, Display)>
192 ) {
193 self.display.send (display_values.collect()).unwrap();
194 }
195}
196
197impl Opengl {
198 pub fn init (tileset_id : Option <gl::render::resource::DefaultTilesetId>) -> Self {
203 let inner = {
204 use gl::render::resource::MAIN_VIEWPORT;
205 let (window, gl_config) = loop {
207 let maybe_glutin_window = (*GLUTIN_WINDOW).lock().unwrap().take();
208 if let Some (glutin_window) = maybe_glutin_window {
209 break glutin_window
210 }
211 thread::sleep (time::Duration::from_millis (100));
212 };
213 let display = gl::init::glium_display_gl33core (&window, &gl_config);
219 let mut render =
220 gl::Render::<gl::render::resource::Default>::new (display, window);
221 let [tile_width, tile_height] =
223 render.resource.tile_dimensions (tileset_id.unwrap_or_default());
224 unsafe {
225 env::set_var ("GOOEY_TILE_WIDTH", tile_width.to_string());
226 env::set_var ("GOOEY_TILE_HEIGHT", tile_height.to_string());
227 }
228 render.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT, Default::default());
230 render
231 };
232 let updates = loop {
233 let maybe_receiver = (*DISPLAY_RECEIVER).lock().unwrap().take();
234 if let Some (receiver) = maybe_receiver {
235 break receiver
236 }
237 thread::sleep (time::Duration::from_millis (100));
238 };
239 Opengl {
240 inner,
241 tileset_id,
242 updates,
243 id_map: KeyVec::new(),
244 view: Tree::new(),
245 glium_frame: None,
246 display_counter: 0,
247 draw_crosshair: false
248 }
249 }
250
251 #[inline]
252 pub const fn inner (&self) -> &gl::Render {
253 &self.inner
254 }
255
256 #[inline]
257 pub const fn inner_mut (&mut self) -> &mut gl::Render {
258 &mut self.inner
259 }
260
261 #[inline]
262 #[deprecated = "use remote::frame() function instead"]
263 pub fn frame (&self) -> u64 {
264 frame()
265 }
266
267 #[inline]
268 pub const fn display_counter (&self) -> u64 {
269 self.display_counter
270 }
271
272 #[inline]
273 pub const fn reset_display_counter (&mut self) {
274 self.display_counter = 0;
275 }
276
277 pub fn load_pointer (&mut self,
278 key : gl::render::resource::PointerTextureIndexRepr,
279 bytes : &[u8],
280 offset : math::Vector2 <i16>
281 ) {
282 let render = &mut self.inner;
283 render::load_pointer (render, key, bytes, offset);
284 }
285
286 pub fn load_textures (&mut self,
287 textures_16x16 : &[&'static str],
288 textures_64x64 : &[&'static str],
289 textures_anysize : &[&'static str]
290 ) {
291 render::load_textures (
292 &mut self.inner, textures_16x16, textures_64x64, textures_anysize)
293 }
294
295 #[inline]
296 pub fn process_display_events (&mut self) {
297 while self.process_display_events_single() { }
298 }
299
300 pub fn process_display_events_single (&mut self) -> bool {
302 if let Ok (updates) = self.updates.try_recv() {
303 for (node_id, display) in updates {
304 match display {
305 Display::Create (view, child_id, order) => {
306 let new_id = if self.id_map.is_empty() {
307 self.view.insert (Node::new (view),
309 tree::InsertBehavior::AsRoot).unwrap()
310 } else {
311 let parent_id = self.id_map.get (&node_id).unwrap();
312 let new_id = self.view.insert (
313 Node::new (view),
314 tree::InsertBehavior::UnderNode (parent_id)).unwrap();
315 match order {
316 CreateOrder::Prepend => {
317 let _ = self.view.make_first_sibling (&new_id).unwrap();
318 }
319 CreateOrder::NthSibling (n) =>
320 self.view.make_nth_sibling (&new_id, n as usize).unwrap(),
321 CreateOrder::Append => {}
322 }
323 new_id
324 };
325 assert!(self.id_map.insert (child_id, new_id).is_none());
326 }
327 Display::Update (update) => {
328 let id = self.id_map.get (&node_id).unwrap();
329 match update {
330 Update::View (view) =>
331 *self.view.get_mut (id).unwrap().data_mut() = view,
332 Update::FocusTop => {
333 self.view.make_last_sibling (id).unwrap();
334 }
335 }
336 }
337 Display::Destroy => {
338 let id = self.id_map.remove (&node_id).unwrap();
339 let _ = self.view.remove_node (id, tree::RemoveBehavior::DropChildren)
340 .unwrap();
341 }
342 }
343 }
344 self.display_counter += 1;
345 true
346 } else {
347 false
348 }
349 }
350 pub fn update_and_do_frame (&mut self) {
351 render::update (&mut self.inner, self.tileset_id, &self.view, self.draw_crosshair);
353 self.inner.do_frame (self.glium_frame.as_mut());
355 FRAME.fetch_add (1, atomic::Ordering::SeqCst);
356 }
357 pub fn display (&mut self) {
359 self.process_display_events();
360 self.update_and_do_frame();
361 }
362}
363
364impl AsRef <View> for View {
366 fn as_ref (&self) -> &View {
367 self
368 }
369}