2024-03-15 14:49:25 +00:00
/*
Copyright 2024 , Savanni D ' Gerinel < savanni @ luminescent - dreams . com >
2024-03-22 03:48:48 +00:00
This file is part of On the Grid .
2024-03-15 14:49:25 +00:00
2024-03-22 03:48:48 +00:00
On the Grid is free software : you can redistribute it and / or modify it under the terms of the GNU
2024-03-15 14:49:25 +00:00
General Public License as published by the Free Software Foundation , either version 3 of the
License , or ( at your option ) any later version .
2024-03-22 03:48:48 +00:00
On the Grid is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without
2024-03-15 14:49:25 +00:00
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
2024-03-22 03:48:48 +00:00
You should have received a copy of the GNU General Public License along with On the Grid . If not , see < https ://www.gnu.org/licenses/>.
2024-03-15 14:49:25 +00:00
* /
2024-03-22 02:39:28 +00:00
pub mod components ;
2024-02-27 13:34:17 +00:00
2024-03-15 14:49:25 +00:00
mod app_window ;
pub use app_window ::AppWindow ;
2024-02-27 13:34:17 +00:00
mod views ;
2024-04-05 14:06:52 +00:00
use async_std ::task ::yield_now ;
use gio ::resources_lookup_data ;
use gtk ::gdk_pixbuf ::{ Colorspace , InterpType , Pixbuf } ;
use image ::{ io ::Reader as ImageReader , ImageError } ;
use otg_core ::{ Core , CoreRequest , CoreResponse , Observable } ;
use std ::{ cell ::RefCell , collections ::HashMap , io ::Cursor , rc ::Rc } ;
2023-04-07 01:52:39 +00:00
#[ derive(Clone) ]
pub struct CoreApi {
2024-02-27 13:34:17 +00:00
pub core : Core ,
2023-04-07 01:52:39 +00:00
}
impl CoreApi {
2024-03-21 23:20:09 +00:00
pub async fn dispatch ( & self , request : CoreRequest ) -> CoreResponse {
self . core . dispatch ( request ) . await
2023-04-07 01:52:39 +00:00
}
}
2023-06-15 03:47:12 +00:00
2024-04-05 14:06:52 +00:00
#[ derive(Clone) ]
pub enum Resource {
Image ( Pixbuf ) ,
}
pub struct ResourceManager {
resources : Rc < RefCell < HashMap < String , Resource > > > ,
}
impl ResourceManager {
pub fn new ( ) -> Self {
let mut resources = HashMap ::new ( ) ;
for ( path , xres , yres , transparency ) in [
(
" /com/luminescent-dreams/otg-gtk/wood_texture.jpg " ,
840 ,
840 ,
false ,
) ,
(
" /com/luminescent-dreams/otg-gtk/black_stone.png " ,
40 ,
40 ,
true ,
) ,
(
" /com/luminescent-dreams/otg-gtk/white_stone.png " ,
40 ,
40 ,
true ,
) ,
] {
match perftrace ( & format! ( " loading {} " , path ) , | | {
Self ::load_image ( path , transparency , xres , yres )
} ) {
Ok ( Some ( image ) ) = > {
resources . insert ( path . to_owned ( ) , Resource ::Image ( image ) ) ;
}
Ok ( None ) = > println! ( " no image in resource bundle for {} " , path ) ,
Err ( err ) = > println! ( " failed to load image {} : {} " , path , err ) ,
}
}
Self {
resources : Rc ::new ( RefCell ::new ( resources ) ) ,
}
}
pub fn resource ( & self , path : & str ) -> Option < Resource > {
self . resources . borrow ( ) . get ( path ) . cloned ( )
}
fn load_image (
path : & str ,
transparency : bool ,
width : i32 ,
height : i32 ,
) -> Result < Option < Pixbuf > , ImageError > {
let image_bytes = resources_lookup_data ( path , gio ::ResourceLookupFlags ::NONE ) . unwrap ( ) ;
let image = ImageReader ::new ( Cursor ::new ( image_bytes ) )
. with_guessed_format ( )
. unwrap ( )
. decode ( ) ;
image . map ( | image | {
let stride = if transparency {
image . to_rgba8 ( ) . sample_layout ( ) . height_stride
} else {
image . to_rgb8 ( ) . sample_layout ( ) . height_stride
} ;
Pixbuf ::from_bytes (
& glib ::Bytes ::from ( image . as_bytes ( ) ) ,
Colorspace ::Rgb ,
transparency ,
8 ,
image . width ( ) as i32 ,
image . height ( ) as i32 ,
stride as i32 ,
)
. scale_simple ( width , height , InterpType ::Nearest )
} )
}
}
2023-06-15 03:47:12 +00:00
pub fn perftrace < F , A > ( trace_name : & str , f : F ) -> A
where
F : FnOnce ( ) -> A ,
{
let start = std ::time ::Instant ::now ( ) ;
let result = f ( ) ;
let end = std ::time ::Instant ::now ( ) ;
println! ( " [Trace: {} ] {:?} " , trace_name , end - start ) ;
2023-10-05 16:19:57 +00:00
result
2023-06-15 03:47:12 +00:00
}
2024-02-28 04:21:54 +00:00
2024-02-28 04:36:27 +00:00
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
///
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
2024-03-26 12:53:24 +00:00
#[ allow(dead_code) ]
2024-02-28 04:21:54 +00:00
struct LocalObserver < T > {
join_handle : glib ::JoinHandle < ( ) > ,
handler : Rc < dyn Fn ( T ) > ,
}
impl < T : 'static > LocalObserver < T > {
2024-02-28 04:36:27 +00:00
/// Construct a new LocalObserver and start it running.
///
/// observable -- any object which emits events
/// handler -- a function which can process events
2024-03-26 12:53:24 +00:00
#[ allow(dead_code) ]
2024-02-28 04:21:54 +00:00
fn new ( observable : & dyn Observable < T > , handler : impl Fn ( T ) + 'static ) -> Self {
let listener = observable . subscribe ( ) ;
let handler = Rc ::new ( handler ) ;
let join_handle = glib ::spawn_future_local ( {
let handler = handler . clone ( ) ;
async move {
loop {
match listener . recv ( ) . await {
Ok ( msg ) = > handler ( msg ) ,
2024-02-28 04:36:27 +00:00
Err ( _ ) = > {
// recv only fails if the channel has been closed and no other notifications are pending. This will break out of the loop and terminate the observer.
return ;
2024-02-28 04:21:54 +00:00
}
}
yield_now ( ) . await ;
}
}
} ) ;
Self {
join_handle ,
handler ,
}
}
}
impl < T > Drop for LocalObserver < T > {
fn drop ( & mut self ) {
2024-02-28 04:36:27 +00:00
// Abort the task when the observer goes out of scope.
2024-02-28 04:21:54 +00:00
self . join_handle . abort ( ) ;
}
}