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 .anchor (Alignment::pixel())
176 .build_element();
177 let canvas = Canvas::try_ref_mut (&mut screen.view.component).unwrap();
179 canvas.coordinates.modify_dimensions_horizontal (1);
180 canvas.coordinates.modify_dimensions_vertical (1);
181 screen
182 };
183 let mut interface = Interface::<A, Opengl>::with_root (screen);
185 let screen_id = interface.root_id().clone();
187 let screen_tiles = frame::free::Builder::<A>::new (interface.elements(), &screen_id)
188 .layout (layout::Free { size: Size::fill(), .. layout::Free::default_tile() })
189 .clear_color (canvas::ClearColor::Fixed (None))
190 .coord_kind_override (coordinates::Kind::Tile)
191 .build_element();
192 let (_, Event::Create (_, tiles_id, _)) = interface
193 .action (&screen_id, Action::create_singleton (screen_tiles, CreateOrder::Append))
194 .next().unwrap() else { unreachable!() };
195 interface.presentation.screen_tiles = Some (tiles_id);
196 interface
197 }
198
199 fn get_input (&mut self, input_buffer : &mut Vec <Input>) {
200 use winit::platform::pump_events::EventLoopExtPumpEvents;
201 log::trace!("get input...");
202 self.inner.0.pump_app_events (
205 Some (time::Duration::ZERO),
206 &mut InputHandler { input_buffer, input_state: &mut self.input_state });
207 log::trace!("...get input");
208 }
209
210 fn display_view <V : AsRef <View>> (&mut self,
211 view_tree : &Tree <V>,
212 _display_values : std::vec::Drain <(NodeId, Display)>
213 ) {
214 let tileset_id = self.tileset_id;
216 let draw_crosshair = self.draw_crosshair;
217 render::update (&mut self.inner_mut().1, tileset_id, view_tree,
218 draw_crosshair);
219 self.inner.1.do_frame (self.glium_frame.as_mut());
221 self.frame += 1;
222 log::trace!("...display view");
223 }
224}
225
226impl InputHandler <'_> {
227 fn handle_event (&mut self, event : winit::event::Event <()>) {
228 match self.input_state.try_convert_winit_event (event) {
229 Ok (input) => {
230 log::debug!("input: {input:?}");
231 self.input_buffer.extend (input)
232 }
233 Err (event) => log::trace!("ignored winit event: {event:?}")
234 }
235 }
236}
237
238impl InputState {
239 fn new() -> Self {
240 InputState {
241 dimensions: Default::default(),
242 modifiers: Default::default(),
243 pointer_hv: (-1.0, -1.0),
246 pointer_sensitivity: 1.0
247 }
248 }
249 #[expect(clippy::result_large_err)] fn try_convert_winit_event <T> (&mut self, event : winit::event::Event <T>)
254 -> Result <Vec <Input>, winit::event::Event <T>>
255 where T : std::fmt::Debug
256 {
257 use winit::event::{self, Event};
258 use view::input;
259 log::trace!("winit event: {event:?}");
260 let winit_event = event; #[expect(clippy::match_same_arms)]
262 let input = match &winit_event {
263 Event::NewEvents (_) => return Err (winit_event),
264 Event::WindowEvent { event, .. } => match event {
265 event::WindowEvent::ModifiersChanged (modifiers) => {
266 self.modifiers = modifiers.state();
267 return Err (winit_event)
268 }
269 event::WindowEvent::AxisMotion {..} => return Err (winit_event),
270 event::WindowEvent::CloseRequested => input::System::Close.into(),
271 event::WindowEvent::CursorEntered {..} => input::System::CursorEntered.into(),
272 event::WindowEvent::CursorLeft {..} => input::System::CursorLeft.into(),
273 event::WindowEvent::CursorMoved {..} => return Err (winit_event),
278 event::WindowEvent::RedrawRequested => input::System::Redraw.into(),
279 event::WindowEvent::Destroyed => input::System::Destroyed.into(),
280 event::WindowEvent::Focused (focused) =>
281 input::System::Focused (*focused).into(),
282 event::WindowEvent::KeyboardInput { event, is_synthetic: false, .. } => {
283 let input = (event, self.modifiers).into();
284 if event.state == event::ElementState::Pressed &&
285 let Some (mut text) = event.text.clone().map (|text| text.to_string())
286 {
287 let text_input = if text.len() > 1 {
288 input::Text::String (text)
289 } else {
290 debug_assert_eq!(text.len(), 1);
291 input::Text::Char (text.pop().unwrap())
292 }.into();
293 return Ok (vec![input, text_input])
294 }
295 input
296 }
297 event::WindowEvent::KeyboardInput { is_synthetic: true, .. } =>
298 return Err (winit_event),
299 event::WindowEvent::MouseInput { button, state, .. } =>
300 mouse_button (*button, self.modifiers, *state),
301 event::WindowEvent::MouseWheel { delta, .. } =>
302 mouse_wheel (*delta, self.modifiers),
303 event::WindowEvent::Resized (logical_size) => {
304 let (width, height) = (*logical_size).into();
306 let mut out = vec![input::System::Resized { width, height }.into()];
307 self.dimensions = dimensions::Pixel::new_wh (width, height);
308 if self.pointer_hv == (-1.0, -1.0) {
309 self.pointer_hv.0 = width as f32 / 2.0;
310 self.pointer_hv.1 = height as f32 / 2.0;
311 out.push (self.pointer().into());
312 } else {
313 self.clip_pointer().map (|pointer| out.push (pointer.into()));
314 }
315 return Ok (out)
316 }
317 event::WindowEvent::Ime (_) => input::System::InputMethod ().into(),
319 event::WindowEvent::Occluded (occluded) =>
320 input::System::Occluded (*occluded).into(),
321 event::WindowEvent::Moved (_) => return Err (winit_event),
322 _ => {
323 log::error!("TODO: unhandled glutin window event: {event:?}");
324 unimplemented!("TODO: unhandled glutin window event: {:?}", event)
325 }
326 }
327 Event::DeviceEvent { event, .. } => match event {
328 event::DeviceEvent::Button {..} => return Err (winit_event),
330 event::DeviceEvent::Key (_) => return Err (winit_event),
331 event::DeviceEvent::Motion { axis, value } =>
333 input::Axis { axis: *axis, value: value.round() as i32 }.into(),
334 event::DeviceEvent::MouseMotion { delta: (x, y) } => {
336 let motion = input::Motion (x.round() as i32, y.round() as i32).into();
337 let pointer = {
338 self.pointer_hv.0 += (x.abs().powf (self.pointer_sensitivity as f64)
339 * x.signum()) as f32;
340 self.pointer_hv.1 -= (y.abs().powf (self.pointer_sensitivity as f64)
341 * y.signum()) as f32;
342 self.clip_pointer().unwrap_or_else (|| self.pointer()).into()
343 };
344 return Ok (vec![motion, pointer])
345 }
346 event::DeviceEvent::MouseWheel {..} => return Err (winit_event),
347 event::DeviceEvent::Added => return Err (winit_event),
348 event::DeviceEvent::Removed => {
349 log::error!("TODO: unhandled glutin device event: {event:?}");
350 unimplemented!("TODO: unhandled glutin device event: {:?}", event)
351 }
352 }
353 Event::Resumed => return Err (winit_event),
355 Event::AboutToWait => return Err (winit_event),
356 _ => {
357 log::error!("TODO: unhandled winit event: {winit_event:?}");
358 unimplemented!("TODO: unhandld winit event: {:?}", winit_event)
359 }
360 };
361 Ok (vec![input])
362 }
363
364 fn pointer (&self) -> input::Pointer {
365 input::Pointer {
366 position_horizontal: self.pointer_hv.0,
367 position_vertical: self.pointer_hv.1,
368 modifiers: self.modifiers.into()
369 }
370 }
371
372 fn clip_pointer (&mut self) -> Option <input::Pointer> {
374 let hv_before = self.pointer_hv;
375 self.pointer_hv.0 = f32::min (self.pointer_hv.0, self.dimensions.width() as f32);
376 self.pointer_hv.0 = f32::max (self.pointer_hv.0, 0.0);
377 self.pointer_hv.1 = f32::min (self.pointer_hv.1, self.dimensions.height() as f32);
378 self.pointer_hv.1 = f32::max (self.pointer_hv.1, 0.0);
379 if hv_before != self.pointer_hv {
380 Some (self.pointer())
381 } else {
382 None
383 }
384 }
385}
386
387impl winit::application::ApplicationHandler for InputHandler <'_> {
388 fn resumed (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
390 self.handle_event (winit::event::Event::Resumed)
391 }
392 fn window_event (&mut self,
393 _event_loop : &winit::event_loop::ActiveEventLoop,
394 window_id : winit::window::WindowId,
395 event : winit::event::WindowEvent
396 ) {
397 self.handle_event (winit::event::Event::WindowEvent { window_id, event })
398 }
399 fn new_events (&mut self,
401 _event_loop : &winit::event_loop::ActiveEventLoop,
402 cause : winit::event::StartCause
403 ) {
404 self.handle_event (winit::event::Event::NewEvents (cause))
405 }
406 fn user_event (&mut self,
407 _event_loop : &winit::event_loop::ActiveEventLoop,
408 event : ()
409 ) {
410 self.handle_event (winit::event::Event::UserEvent (event))
411 }
412 fn device_event (&mut self,
413 _event_loop : &winit::event_loop::ActiveEventLoop,
414 device_id : winit::event::DeviceId,
415 event : winit::event::DeviceEvent
416 ) {
417 self.handle_event (winit::event::Event::DeviceEvent { device_id, event })
418 }
419 fn about_to_wait (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
420 self.handle_event (winit::event::Event::AboutToWait)
421 }
422 fn suspended (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
423 self.handle_event (winit::event::Event::Suspended)
424 }
425 fn exiting (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
426 self.handle_event (winit::event::Event::LoopExiting)
427 }
428 fn memory_warning (&mut self, _event_loop : &winit::event_loop::ActiveEventLoop) {
429 self.handle_event (winit::event::Event::MemoryWarning)
430 }
431}
432
433impl From <winit::event::MouseButton> for input::button::Mouse {
434 fn from (button : winit::event::MouseButton) -> input::button::Mouse {
435 use winit::event::MouseButton;
436 use view::input::button::Mouse;
437 match button {
438 MouseButton::Left => Mouse::Mouse1,
439 MouseButton::Right => Mouse::Mouse2,
440 MouseButton::Middle => Mouse::Mouse3,
441 MouseButton::Other (4) => Mouse::Mouse4,
442 MouseButton::Other (5) => Mouse::Mouse5,
443 button => {
444 log::error!("unhandled glutin mouse button: {button:?}");
445 unimplemented!()
446 }
447 }
448 }
449}
450
451impl From <(&winit::event::KeyEvent, winit::keyboard::ModifiersState)> for Input {
452 fn from (
453 (input, modifiers) :
454 (&winit::event::KeyEvent, winit::keyboard::ModifiersState)
455 ) -> Input {
456 use std::str::FromStr;
457 use view::{input, Input};
458 use winit::keyboard;
459 let state = input.state.into();
460 let button = {
461 let variant = match &input.logical_key {
462 keyboard::Key::Named (named_key) =>
463 input::button::Keycode::from ((*named_key, input.location)),
464 keyboard::Key::Character (ch) => input::button::Keycode::try_from (
465 char::from_str (ch.as_str()).unwrap()
466 ).unwrap(),
467 keyboard::Key::Unidentified (k) =>
469 unimplemented!("TODO: unidentified key: {:?}", k),
470 keyboard::Key::Dead (k) =>
471 unimplemented!("TODO: dead key: {:?}", k)
472 }.into();
473 input::Button { variant, modifiers: modifiers.into() }
474 };
475 Input::Button (button, state)
476 }
477}
478
479impl From <winit::event::ElementState> for input::button::State {
480 fn from (state : winit::event::ElementState) -> Self {
481 use winit::event::ElementState;
482 use view::input::button::State;
483 match state {
484 ElementState::Pressed => State::Pressed,
485 ElementState::Released => State::Released
486 }
487 }
488}
489
490impl From <winit::keyboard::ModifiersState> for input::Modifiers {
491 fn from (modifiers : winit::keyboard::ModifiersState) -> Self {
492 use view::input::Modifiers;
493 let mut out = Modifiers::empty();
494 out.set (Modifiers::SHIFT, modifiers.shift_key());
495 out.set (Modifiers::CTRL, modifiers.control_key());
496 out.set (Modifiers::ALT, modifiers.alt_key());
497 out.set (Modifiers::SUPER, modifiers.super_key());
498 out
499 }
500}
501
502fn mouse_button (
504 button : winit::event::MouseButton,
505 modifiers : winit::keyboard::ModifiersState,
506 state : winit::event::ElementState
507) -> Input {
508 let state = state.into();
509 let button = {
510 let variant = input::button::Mouse::from (button).into();
511 let modifiers = modifiers.into();
512 input::Button { variant, modifiers }
513 };
514 Input::Button (button, state)
515}
516
517fn mouse_wheel (
520 delta : winit::event::MouseScrollDelta,
521 modifiers : winit::keyboard::ModifiersState
522) -> Input {
523 let delta = match delta {
524 winit::event::MouseScrollDelta::LineDelta (x, y)
525 => (x.round() as i32, y.round() as i32),
526 winit::event::MouseScrollDelta::PixelDelta (
527 winit::dpi::PhysicalPosition { x, y }
528 ) => (x.round() as i32, y.round() as i32)
529 };
530 let modifiers = modifiers.into();
531 input::Wheel { delta, modifiers }.into()
532}