use std::{env, thread, time, vec};
use std::sync::{atomic, Arc, LazyLock, Mutex};
use nsys::{self, gl, math};
use nsys::gl::glium::{self, glutin};
use nsys::gl::winit;
use key_vec::KeyVec;
use unbounded_spsc;
use crate::prelude::*;
use super::{render, Graphics, InputHandler, InputState, Presentation};
#[expect(clippy::type_complexity)]
static DISPLAY_RECEIVER
: LazyLock <Mutex <Option <unbounded_spsc::Receiver <Vec <(NodeId, Display)>>>>>
= LazyLock::new (|| Mutex::new (None));
static GLUTIN_WINDOW
: LazyLock <Mutex <Option <(winit::window::Window, glutin::config::Config)>>>
= LazyLock::new (|| Mutex::new (None));
static FRAME : LazyLock <Arc <atomic::AtomicU64>> = LazyLock::new (||
Arc::new (atomic::AtomicU64::new (0)));
pub struct Remote {
pub event_loop : winit::event_loop::EventLoop <()>,
display : unbounded_spsc::Sender <Vec <(NodeId, Display)>>,
input_state : InputState,
screen_tiles : Option <NodeId>
}
pub struct Opengl {
pub draw_crosshair : bool,
pub inner : gl::Render,
tileset_id : Option <gl::render::resource::DefaultTilesetId>,
updates : unbounded_spsc::Receiver <Vec <(NodeId, Display)>>,
id_map : KeyVec <NodeId, NodeId>,
view : Tree <View>,
glium_frame : Option <glium::Frame>,
display_counter : u64
}
pub fn create_screen_tiles <A, P> (interface : &mut Interface <A, P>) where
A : Application,
P : Presentation + presentation::HasGraphics <Remote>
{
let screen_id = interface.root_id().clone();
let screen_tiles = frame::free::Builder::<A>::new (
interface.elements(), &screen_id
) .clear_color (canvas::ClearColor::Fixed (None))
.coord_kind_override (coordinates::Kind::Tile)
.layout (layout::Free { size: Size::fill(), .. layout::Free::default_tile() })
.name ("screen-tiles".to_string())
.build_element();
let (_, Event::Create (_, tiles_id, _)) = interface
.action (&screen_id, Action::create_singleton (screen_tiles, CreateOrder::Append))
.next().unwrap() else { unreachable!() };
interface.presentation.graphics().screen_tiles = Some (tiles_id);
}
impl Default for Remote {
fn default() -> Self {
let event_loop = {
let (event_loop, window, gl_config) =
gl::init::glutin_window ("Gooey Opengl Remote Window");
{
let mut glutin_window_lock = GLUTIN_WINDOW.lock().unwrap();
debug_assert!(glutin_window_lock.is_none());
window.set_cursor_visible (false);
*glutin_window_lock = Some ((window, gl_config));
}
event_loop
};
let display = {
let (sender, receiver) = unbounded_spsc::channel::<Vec <(NodeId, Display)>>();
{
let mut display_receiver_lock = DISPLAY_RECEIVER.lock().unwrap();
debug_assert!(display_receiver_lock.is_none());
*display_receiver_lock = Some (receiver);
}
sender
};
Remote {
event_loop,
display,
input_state: InputState::new(),
screen_tiles: None
}
}
}
pub fn frame() -> u64 {
FRAME.load (atomic::Ordering::SeqCst)
}
impl Remote {
#[inline]
pub const fn screen_tiles_id (&self) -> &NodeId {
self.screen_tiles.as_ref().unwrap()
}
#[inline]
pub const fn dimensions (&self) -> dimensions::Pixel {
self.input_state.dimensions
}
pub const fn pointer_sensitivity (&self) -> f32 {
self.input_state.pointer_sensitivity
}
pub const fn set_pointer_sensitivity (&mut self, pointer_sensitivity : f32) {
self.input_state.pointer_sensitivity = pointer_sensitivity
}
}
impl Graphics for Remote { }
impl Presentation for Remote {
fn make_interface <A : Application> () -> Interface <A, Self> {
let screen = {
let mut screen = frame::screen::PixelBuilder::<A>::new()
.anchor (Alignment::pixel())
.name ("screen".to_string())
.build_element();
let canvas = Canvas::try_ref_mut (&mut screen.view.component).unwrap();
canvas.coordinates.modify_dimensions_horizontal (1);
canvas.coordinates.modify_dimensions_vertical (1);
screen
};
Interface::<A, Remote>::with_root (screen)
}
fn with_root (root : View, root_id : NodeId) -> Self {
let remote = Self::default();
log::debug!("opengl remote with root: {root:?}");
remote.display.send (vec![
(root_id.clone(), Display::Create (root, root_id, CreateOrder::Append))
]).unwrap();
loop {
if (*DISPLAY_RECEIVER).lock().unwrap().is_none() {
break
}
thread::sleep (time::Duration::from_millis (100));
}
remote
}
fn get_input (&mut self, input_buffer : &mut Vec <Input>) {
use winit::platform::pump_events::EventLoopExtPumpEvents;
log::trace!("get input...");
self.event_loop.pump_app_events (
Some (time::Duration::ZERO),
&mut InputHandler { input_buffer, input_state: &mut self.input_state });
log::trace!("...get input");
}
fn display_view <V : AsRef <View>> (&mut self,
_view_tree : &Tree <V>,
display_values : vec::Drain <(NodeId, Display)>
) {
self.display.send (display_values.collect()).unwrap();
}
}
impl Opengl {
pub fn init (tileset_id : Option <gl::render::resource::DefaultTilesetId>) -> Self {
let inner = {
use gl::render::resource::MAIN_VIEWPORT;
let (window, gl_config) = loop {
let maybe_glutin_window = (*GLUTIN_WINDOW).lock().unwrap().take();
if let Some (glutin_window) = maybe_glutin_window {
break glutin_window
}
thread::sleep (time::Duration::from_millis (100));
};
let display = gl::init::glium_display_gl33core (&window, &gl_config);
let mut render =
gl::Render::<gl::render::resource::Default>::new (display, window);
let [tile_width, tile_height] =
render.resource.tile_dimensions (tileset_id.unwrap_or_default());
unsafe {
env::set_var ("GOOEY_TILE_WIDTH", tile_width.to_string());
env::set_var ("GOOEY_TILE_HEIGHT", tile_height.to_string());
}
render.resource.draw2d.viewport_resources_set (MAIN_VIEWPORT, Default::default());
render
};
let updates = loop {
let maybe_receiver = (*DISPLAY_RECEIVER).lock().unwrap().take();
if let Some (receiver) = maybe_receiver {
break receiver
}
thread::sleep (time::Duration::from_millis (100));
};
Opengl {
inner,
tileset_id,
updates,
id_map: KeyVec::new(),
view: Tree::new(),
glium_frame: None,
display_counter: 0,
draw_crosshair: false
}
}
#[inline]
pub const fn inner (&self) -> &gl::Render {
&self.inner
}
#[inline]
pub const fn inner_mut (&mut self) -> &mut gl::Render {
&mut self.inner
}
#[inline]
#[deprecated = "use remote::frame() function instead"]
pub fn frame (&self) -> u64 {
frame()
}
#[inline]
pub const fn display_counter (&self) -> u64 {
self.display_counter
}
#[inline]
pub const fn reset_display_counter (&mut self) {
self.display_counter = 0;
}
pub fn load_pointer (&mut self,
key : gl::render::resource::PointerTextureIndexRepr,
bytes : &[u8],
offset : math::Vector2 <i16>
) {
let render = &mut self.inner;
render::load_pointer (render, key, bytes, offset);
}
pub fn load_textures (&mut self,
textures_16x16 : &[&'static str],
textures_64x64 : &[&'static str],
textures_anysize : &[&'static str]
) {
render::load_textures (
&mut self.inner, textures_16x16, textures_64x64, textures_anysize)
}
#[inline]
pub fn process_display_events (&mut self) {
while self.process_display_events_single() { }
}
pub fn process_display_events_single (&mut self) -> bool {
if let Ok (updates) = self.updates.try_recv() {
for (node_id, display) in updates {
match display {
Display::Create (view, child_id, order) => {
let new_id = if self.id_map.is_empty() {
self.view.insert (Node::new (view),
tree::InsertBehavior::AsRoot).unwrap()
} else {
let parent_id = self.id_map.get (&node_id).unwrap();
let new_id = self.view.insert (
Node::new (view),
tree::InsertBehavior::UnderNode (parent_id)).unwrap();
match order {
CreateOrder::Prepend => {
let _ = self.view.make_first_sibling (&new_id).unwrap();
}
CreateOrder::NthSibling (n) =>
self.view.make_nth_sibling (&new_id, n as usize).unwrap(),
CreateOrder::Append => {}
}
new_id
};
assert!(self.id_map.insert (child_id, new_id).is_none());
}
Display::Update (update) => {
let id = self.id_map.get (&node_id).unwrap();
match update {
Update::View (view) =>
*self.view.get_mut (id).unwrap().data_mut() = view,
Update::FocusTop => {
self.view.make_last_sibling (id).unwrap();
}
}
}
Display::Destroy => {
let id = self.id_map.remove (&node_id).unwrap();
let _ = self.view.remove_node (id, tree::RemoveBehavior::DropChildren)
.unwrap();
}
}
}
self.display_counter += 1;
true
} else {
false
}
}
pub fn update_and_do_frame (&mut self) {
render::update (&mut self.inner, self.tileset_id, &self.view, self.draw_crosshair);
self.inner.do_frame (self.glium_frame.as_mut());
FRAME.fetch_add (1, atomic::Ordering::SeqCst);
}
pub fn display (&mut self) {
self.process_display_events();
self.update_and_do_frame();
}
}
impl AsRef <View> for View {
fn as_ref (&self) -> &View {
self
}
}