1use std;
4use std::convert::TryFrom;
5use std::time;
6use log;
7
8use nsys::{self, gl, math::Vector2};
9use nsys::gl::glium::{self, glutin};
10use nsys::gl::winit;
11
12use crate::prelude::*;
13use super::{Graphics, Presentation};
14
15pub mod remote;
16pub mod render;
17
18pub use self::remote::Remote;
19
20pub const RESOURCE_TEXTURES_16X16 : u16 = 0;
21pub const RESOURCE_TEXTURES_64X64 : u16 = 1;
22pub const RESOURCE_TEXTURES_ANYSIZE : u16 = 2;
23
24pub const POINTER_TEXTURE_INDEX : u16 = 0;
25
26pub struct Opengl {
28 pub draw_crosshair : bool,
29 pub inner : (winit::event_loop::EventLoop <()>, gl::Render),
30 input_state : InputState,
31 tileset_id : Option <gl::render::resource::DefaultTilesetId>,
32 glium_frame : Option <glium::Frame>,
33 frame : u64,
34 screen_tiles : Option <NodeId>
35}
36
37pub (crate) struct InputState {
38 pub dimensions : dimensions::Pixel,
39 pub modifiers : winit::keyboard::ModifiersState,
42 pub pointer_hv : (f32, f32),
46 pub pointer_sensitivity : f32
47}
48
49pub (crate) struct InputHandler <'a> {
50 pub input_buffer : &'a mut Vec <Input>,
51 pub input_state : &'a mut InputState,
52}
53
54pub fn log_glium_info (
56 glium_display : &glium::Display <glutin::surface::WindowSurface>
57) {
58 let glium_info = gl::info::Glium::new (glium_display);
59 log::info!("{glium_info:#?}");
60}
61
62impl Opengl {
63 #[inline]
64 pub const fn inner (&self) -> &(winit::event_loop::EventLoop <()>, gl::Render) {
65 &self.inner
66 }
67 #[inline]
68 pub const fn inner_mut (&mut self)
69 -> &mut (winit::event_loop::EventLoop <()>, gl::Render)
70 {
71 &mut self.inner
72 }
73 #[inline]
74 pub const fn frame (&self) -> u64 {
75 self.frame
76 }
77 #[inline]
78 pub const fn screen_tiles_id (&self) -> &NodeId {
79 self.screen_tiles.as_ref().unwrap()
80 }
81 #[inline]
82 pub fn dimensions (&self) -> dimensions::Pixel {
83 let inner_size : (u32, u32) =
84 self.inner.1.window.inner_size().into();
85 Vector2::from (inner_size).into()
86 }
87 pub const fn pointer_sentivity (&self) -> f32 {
88 self.input_state.pointer_sensitivity
89 }
90 pub const fn set_pointer_sensitivity (&mut self, pointer_sensitivity : f32) {
91 self.input_state.pointer_sensitivity = pointer_sensitivity
92 }
93 pub fn load_pointer (&mut self,
94 key : gl::render::resource::PointerTextureIndexRepr,
95 bytes : &[u8],
96 offset : Vector2 <i16>
97 ) {
98 let render = &mut self.inner_mut().1;
99 render::load_pointer (render, key, bytes, offset);
100 }
101 pub fn load_textures (&mut self,
102 textures_16x16 : &[&'static str],
103 textures_64x64 : &[&'static str],
104 textures_anysize : &[&'static str]
105 ) {
106 render::load_textures (
107 &mut self.inner.1, textures_16x16, textures_64x64, textures_anysize)
108 }
109}
110
111impl std::fmt::Debug for Opengl {
112 fn fmt (&self, f : &mut std::fmt::Formatter) -> Result <(), std::fmt::Error> {
113 write!(f, "Opengl {{ inner: ({:?}, Render), frame: {} }}",
114 self.inner.0, self.frame)
115 }
116}
117
118impl Default for Opengl {
119 fn default() -> Self {
120 let tileset_id = gl::render::resource::DefaultTilesetId::EasciiAcorn128;
122 let input_state = InputState::new();
123 let inner = {
124 let (event_loop, window, _config, display) =
125 gl::init::glium_init_gl33core ("Gooey Opengl Window");
126 window.set_cursor_visible (false);
128 window.set_cursor_grab (winit::window::CursorGrabMode::Confined).unwrap();
129 let render = gl::Render::<gl::render::resource::Default>::new (display, window);
130 let [tile_width, tile_height] = render.resource.tile_dimensions (tileset_id);
132 unsafe {
133 std::env::set_var ("GOOEY_TILE_WIDTH", tile_width.to_string());
134 std::env::set_var ("GOOEY_TILE_HEIGHT", tile_height.to_string());
135 }
136 (event_loop, render)
137 };
138 Opengl {
139 inner, input_state,
140 tileset_id: Some (tileset_id),
141 glium_frame: None,
142 frame: 0,
143 screen_tiles: None,
144 draw_crosshair: false
145 }
146 }
147}
148
149impl Graphics for Opengl { }
150impl Presentation for Opengl {
151 fn with_root (root : View, _id : NodeId) -> Self {
153 use gl::render::resource::{draw2d, MAIN_VIEWPORT};
154 let mut opengl = Self::default();
155 {
156 let draw_crosshair = opengl.draw_crosshair;
157 let render = &mut opengl.inner_mut().1;
158 render::set_screen_view (render, &root);
159 let resources = draw2d::ViewportResources {
160 draw_crosshair, .. Default::default()
161 };
162 render.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT, resources);
163 }
164 log::debug!("opengl with root: {opengl:?}");
165 opengl
166 }
167
168 fn make_interface <A : Application> () -> Interface <A, Self> {
171 let screen = {
172 let mut screen = frame::screen::PixelBuilder::<A>::new()
175 .name ("screen".to_string())
176 .anchor (Alignment::pixel())
177 .build_element();
178 let canvas = Canvas::try_ref_mut (&mut screen.view.component).unwrap();
180 canvas.coordinates.modify_dimensions_horizontal (1);
181 canvas.coordinates.modify_dimensions_vertical (1);
182 screen
183 };
184 let mut interface = Interface::<A, Opengl>::with_root (screen);
186 let screen_id = interface.root_id().clone();
188 let screen_tiles = frame::free::Builder::<A>::new (interface.elements(), &screen_id)
189 .name ("screen-tiles".to_string())
190 .layout (layout::Free { size: Size::fill(), .. layout::Free::default_tile() })
191 .clear_color (canvas::ClearColor::Fixed (None))
192 .coord_kind_override (coordinates::Kind::Tile)
193 .build_element();
194 let (_, Event::Create (_, tiles_id, _)) = interface
195 .action (&screen_id, Action::create_singleton (screen_tiles, CreateOrder::Append))
196 .next().unwrap() else { unreachable!() };
197 interface.presentation.screen_tiles = Some (tiles_id);
198 interface
199 }
200
201 fn get_input (&mut self, input_buffer : &mut Vec <Input>) {
202 use winit::platform::pump_events::EventLoopExtPumpEvents;
203 log::trace!("get input...");
204 self.inner.0.pump_app_events (
207 Some (time::Duration::ZERO),
208 &mut InputHandler { input_buffer, input_state: &mut self.input_state });
209 log::trace!("...get input");
210 }
211
212 fn display_view <V : AsRef <View>> (&mut self,
213 view_tree : &Tree <V>,
214 _display_values : std::vec::Drain <(NodeId, Display)>
215 ) {
216 let tileset_id = self.tileset_id;
218 let draw_crosshair = self.draw_crosshair;
219 render::update (&mut self.inner_mut().1, tileset_id, view_tree,
220 draw_crosshair);
221 self.inner.1.do_frame (self.glium_frame.as_mut());
223 self.frame += 1;
224 log::trace!("...display view");
225 }
226}
227
228impl InputHandler <'_> {
229 fn handle_event (&mut self, event : winit::event::Event <()>) {
230 match self.input_state.try_convert_winit_event (event) {
231 Ok (input) => {
232 log::debug!("input: {input:?}");
233 self.input_buffer.extend (input)
234 }
235 Err (event) => log::trace!("ignored winit event: {event:?}")
236 }
237 }
238}
239
240impl InputState {
241 fn new() -> Self {
242 InputState {
243 dimensions: Default::default(),
244 modifiers: Default::default(),
245 pointer_hv: (-1.0, -1.0),
248 pointer_sensitivity: 1.0
249 }
250 }
251 #[expect(clippy::result_large_err)] fn try_convert_winit_event <T> (&mut self, event : winit::event::Event <T>)
256 -> Result <Vec <Input>, winit::event::Event <T>>
257 where T : std::fmt::Debug
258 {
259 use winit::event::{self, Event};
260 use view::input;
261 log::trace!("winit event: {event:?}");
262 let winit_event = event; #[expect(clippy::match_same_arms)]
264 let input = match &winit_event {
265 Event::NewEvents (_) => return Err (winit_event),
266 Event::WindowEvent { event, .. } => match event {
267 event::WindowEvent::ModifiersChanged (modifiers) => {
268 self.modifiers = modifiers.state();
269 return Err (winit_event)
270 }
271 event::WindowEvent::AxisMotion {..} => return Err (winit_event),
272 event::WindowEvent::CloseRequested => input::System::Close.into(),
273 event::WindowEvent::CursorEntered {..} => input::System::CursorEntered.into(),
274 event::WindowEvent::CursorLeft {..} => input::System::CursorLeft.into(),
275 event::WindowEvent::CursorMoved {..} => return Err (winit_event),
280 event::WindowEvent::RedrawRequested => input::System::Redraw.into(),
281 event::WindowEvent::Destroyed => input::System::Destroyed.into(),
282 event::WindowEvent::Focused (focused) =>
283 input::System::Focused (*focused).into(),
284 event::WindowEvent::KeyboardInput { event, is_synthetic: false, .. } => {
285 let input = (event, self.modifiers).into();
286 if event.state == event::ElementState::Pressed &&
287 let Some (mut text) = event.text.clone().map (|text| text.to_string())
288 {
289 let text_input = if text.len() > 1 {
290 input::Text::String (text)
291 } else {
292 debug_assert_eq!(text.len(), 1);
293 input::Text::Char (text.pop().unwrap())
294 }.into();
295 return Ok (vec![input, text_input])
296 }
297 input
298 }
299 event::WindowEvent::KeyboardInput { is_synthetic: true, .. } =>
300 return Err (winit_event),
301 event::WindowEvent::MouseInput { button, state, .. } =>
302 mouse_button (*button, self.modifiers, *state),
303 event::WindowEvent::MouseWheel { delta, .. } =>
304 mouse_wheel (*delta, self.modifiers),
305 event::WindowEvent::Resized (logical_size) => {
306 let (width, height) = (*logical_size).into();
308 let mut out = vec![input::System::Resized { width, height }.into()];
309 self.dimensions = dimensions::Pixel::new_wh (width, height);
310 if self.pointer_hv == (-1.0, -1.0) {
311 self.pointer_hv.0 = width as f32 / 2.0;
312 self.pointer_hv.1 = height as f32 / 2.0;
313 out.push (self.pointer().into());
314 } else {
315 self.clip_pointer().map (|pointer| out.push (pointer.into()));
316 }
317 return Ok (out)
318 }
319 event::WindowEvent::Ime (_) => input::System::InputMethod ().into(),
321 event::WindowEvent::Occluded (occluded) =>
322 input::System::Occluded (*occluded).into(),
323 event::WindowEvent::Moved (_) => return Err (winit_event),
324 _ => {
325 log::error!("TODO: unhandled glutin window event: {event:?}");
326 unimplemented!("TODO: unhandled glutin window event: {:?}", event)
327 }
328 }
329 Event::DeviceEvent { event, .. } => match event {
330 event::DeviceEvent::Button {..} => return Err (winit_event),
332 event::DeviceEvent::Key (_) => return Err (winit_event),
333 event::DeviceEvent::Motion { axis, value } =>
335 input::Axis { axis: *axis, value: value.round() as i32 }.into(),
336 event::DeviceEvent::MouseMotion { delta: (x, y) } => {
338 let motion = input::Motion (x.round() as i32, y.round() as i32).into();
339 let pointer = {
340 self.pointer_hv.0 += (x.abs().powf (self.pointer_sensitivity as f64)
341 * x.signum()) as f32;
342 self.pointer_hv.1 -= (y.abs().powf (self.pointer_sensitivity as f64)
343 * y.signum()) as f32;
344 self.clip_pointer().unwrap_or_else (|| self.pointer()).into()
345 };
346 return Ok (vec![motion, pointer])
347 }
348 event::DeviceEvent::MouseWheel {..} => return Err (winit_event),
349 event::DeviceEvent::Added => return Err (winit_event),
350 event::DeviceEvent::Removed => {
351 log::error!("TODO: unhandled glutin device event: {event:?}");
352 unimplemented!("TODO: unhandled glutin device event: {:?}", event)
353 }
354 }
355 Event::Resumed => return Err (winit_event),
357 Event::AboutToWait => return Err (winit_event),
358 _ => {
359 log::error!("TODO: unhandled winit event: {winit_event:?}");
360 unimplemented!("TODO: unhandld winit event: {:?}", winit_event)
361 }
362 };
363 Ok (vec![input])
364 }
365
366 fn pointer (&self) -> input::Pointer {
367 input::Pointer {
368 position_horizontal: self.pointer_hv.0,
369 position_vertical: self.pointer_hv.1,
370 modifiers: self.modifiers.into()
371 }
372 }
373
374 fn clip_pointer (&mut self) -> Option <input::Pointer> {
376 let hv_before = self.pointer_hv;
377 self.pointer_hv.0 = f32::min (self.pointer_hv.0, self.dimensions.width() as f32);
378 self.pointer_hv.0 = f32::max (self.pointer_hv.0, 0.0);
379 self.pointer_hv.1 = f32::min (self.pointer_hv.1, self.dimensions.height() as f32);
380 self.pointer_hv.1 = f32::max (self.pointer_hv.1, 0.0);
381 if hv_before != self.pointer_hv {
382 Some (self.pointer())
383 } else {
384 None
385 }
386 }
387}
388
389impl winit::application::ApplicationHandler for InputHandler <'_> {
390 fn resumed (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
392 self.handle_event (winit::event::Event::Resumed)
393 }
394 fn window_event (&mut self,
395 _event_loop : &winit::event_loop::ActiveEventLoop,
396 window_id : winit::window::WindowId,
397 event : winit::event::WindowEvent
398 ) {
399 self.handle_event (winit::event::Event::WindowEvent { window_id, event })
400 }
401 fn new_events (&mut self,
403 _event_loop : &winit::event_loop::ActiveEventLoop,
404 cause : winit::event::StartCause
405 ) {
406 self.handle_event (winit::event::Event::NewEvents (cause))
407 }
408 fn user_event (&mut self,
409 _event_loop : &winit::event_loop::ActiveEventLoop,
410 event : ()
411 ) {
412 self.handle_event (winit::event::Event::UserEvent (event))
413 }
414 fn device_event (&mut self,
415 _event_loop : &winit::event_loop::ActiveEventLoop,
416 device_id : winit::event::DeviceId,
417 event : winit::event::DeviceEvent
418 ) {
419 self.handle_event (winit::event::Event::DeviceEvent { device_id, event })
420 }
421 fn about_to_wait (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
422 self.handle_event (winit::event::Event::AboutToWait)
423 }
424 fn suspended (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
425 self.handle_event (winit::event::Event::Suspended)
426 }
427 fn exiting (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
428 self.handle_event (winit::event::Event::LoopExiting)
429 }
430 fn memory_warning (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
431 self.handle_event (winit::event::Event::MemoryWarning)
432 }
433}
434
435impl From <winit::event::MouseButton> for input::button::Mouse {
436 fn from (button : winit::event::MouseButton) -> input::button::Mouse {
437 use winit::event::MouseButton;
438 use view::input::button::Mouse;
439 match button {
440 MouseButton::Left => Mouse::Mouse1,
441 MouseButton::Right => Mouse::Mouse2,
442 MouseButton::Middle => Mouse::Mouse3,
443 MouseButton::Other (4) => Mouse::Mouse4,
444 MouseButton::Other (5) => Mouse::Mouse5,
445 button => {
446 log::error!("unhandled glutin mouse button: {button:?}");
447 unimplemented!()
448 }
449 }
450 }
451}
452
453impl From <(&winit::event::KeyEvent, winit::keyboard::ModifiersState)> for Input {
454 fn from (
455 (input, modifiers) :
456 (&winit::event::KeyEvent, winit::keyboard::ModifiersState)
457 ) -> Input {
458 use std::str::FromStr;
459 use view::{input, Input};
460 use winit::keyboard;
461 let state = input.state.into();
462 let button = {
463 let variant = match &input.logical_key {
464 keyboard::Key::Named (named_key) =>
465 input::button::Keycode::from ((*named_key, input.location)),
466 keyboard::Key::Character (ch) => input::button::Keycode::try_from (
467 char::from_str (ch.as_str()).unwrap()
468 ).unwrap(),
469 keyboard::Key::Unidentified (k) =>
471 unimplemented!("TODO: unidentified key: {:?}", k),
472 keyboard::Key::Dead (k) =>
473 unimplemented!("TODO: dead key: {:?}", k)
474 }.into();
475 input::Button { variant, modifiers: modifiers.into() }
476 };
477 Input::Button (button, state)
478 }
479}
480
481impl From <winit::event::ElementState> for input::button::State {
482 fn from (state : winit::event::ElementState) -> Self {
483 use winit::event::ElementState;
484 use view::input::button::State;
485 match state {
486 ElementState::Pressed => State::Pressed,
487 ElementState::Released => State::Released
488 }
489 }
490}
491
492impl From <winit::keyboard::ModifiersState> for input::Modifiers {
493 fn from (modifiers : winit::keyboard::ModifiersState) -> Self {
494 use view::input::Modifiers;
495 let mut out = Modifiers::empty();
496 out.set (Modifiers::SHIFT, modifiers.shift_key());
497 out.set (Modifiers::CTRL, modifiers.control_key());
498 out.set (Modifiers::ALT, modifiers.alt_key());
499 out.set (Modifiers::SUPER, modifiers.super_key());
500 out
501 }
502}
503
504fn mouse_button (
506 button : winit::event::MouseButton,
507 modifiers : winit::keyboard::ModifiersState,
508 state : winit::event::ElementState
509) -> Input {
510 let state = state.into();
511 let button = {
512 let variant = input::button::Mouse::from (button).into();
513 let modifiers = modifiers.into();
514 input::Button { variant, modifiers }
515 };
516 Input::Button (button, state)
517}
518
519fn mouse_wheel (
522 delta : winit::event::MouseScrollDelta,
523 modifiers : winit::keyboard::ModifiersState
524) -> Input {
525 let delta = match delta {
526 winit::event::MouseScrollDelta::LineDelta (x, y)
527 => (x.round() as i32, y.round() as i32),
528 winit::event::MouseScrollDelta::PixelDelta (
529 winit::dpi::PhysicalPosition { x, y }
530 ) => (x.round() as i32, y.round() as i32)
531 };
532 let modifiers = modifiers.into();
533 input::Wheel { delta, modifiers }.into()
534}