Skip to content

Commit 359011a

Browse files
committedAug 16, 2024
Reimplement graphics using winit and tiny-skia
1 parent 97f01ed commit 359011a

File tree

5 files changed

+2976
-2438
lines changed

5 files changed

+2976
-2438
lines changed
 

‎Cargo.lock

+2,547-2,003
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

+24-57
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,43 @@
1-
[profile.release]
2-
lto = true
3-
strip = true
4-
51
[package]
62
name = "sil"
73
version = "2.0.0"
84
authors = ["Fenhl <fenhl@fenhl.net>"]
95
edition = "2021"
106

7+
[profile.release]
8+
lto = true
9+
strip = true
10+
1111
[build-dependencies]
1212
git2 = "0.15"
1313
thiserror = "1"
1414

1515
[dependencies]
16-
async-trait = "0.1"
16+
async-proto = { version = "0.15", features = ["tokio-tungstenite"] }
17+
chrono = { version = "0.4", features = ["serde"] }
18+
chrono-tz = { version = "0.6", features = ["serde"] }
19+
clap = { version = "4", features = ["derive"] }
1720
enum-iterator = "1"
21+
fontdue = "0.9"
1822
futures = "0.3"
19-
image = "0.23" # transitive ggez dependency
23+
gefolge-websocket = { git = "https://github.com/dasgefolge/gefolge-websocket", branch = "main" }
24+
if_chain = "1"
2025
itertools = "0.10"
21-
serde_json = "1"
26+
png = "0.17"
27+
rand = { version = "0.8", default-features = false, features = ["small_rng"] }
28+
raw-window-handle = "0.6.0"
29+
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "json"] }
30+
serde = { version = "1", features = ["derive"] }
31+
serde_json = { package = "serde_json_path_to_error", version = "0.1" }
32+
softbuffer = "0.4"
33+
text = { git = "https://github.com/fenhl/text", branch = "main" }
2234
thiserror = "1"
35+
tiny-skia = "0.11"
36+
tokio = { version = "1", features = ["fs", "macros", "process", "rt-multi-thread", "sync", "time"] }
37+
tokio-tungstenite = { version = "0.17", features = ["rustls-tls-webpki-roots"] }
38+
wheel = { git = "https://github.com/fenhl/wheel", branch = "main", features = ["chrono"] }
39+
winit = "0.30"
2340
xdg-basedir = "1"
2441

25-
[dependencies.async-proto]
26-
version = "0.15"
27-
features = ["tokio-tungstenite"]
28-
29-
[dependencies.chrono]
30-
version = "0.4"
31-
features = ["serde"]
32-
33-
[dependencies.chrono-tz]
34-
version = "0.6"
35-
features = ["serde"]
36-
37-
[dependencies.clap]
38-
version = "3"
39-
features = ["derive"]
40-
41-
[dependencies.gefolge-websocket]
42-
git = "https://github.com/dasgefolge/gefolge-websocket"
43-
branch = "main"
44-
45-
[dependencies.ggez]
46-
version = "0.7"
47-
default-features = false
48-
49-
[dependencies.rand]
50-
version = "0.8"
51-
default-features = false
52-
features = ["small_rng"]
53-
54-
[dependencies.reqwest]
55-
version = "0.11"
56-
default-features = false
57-
features = ["rustls-tls", "json"]
58-
59-
[dependencies.serde]
60-
version = "1"
61-
features = ["derive"]
62-
63-
[dependencies.tokio]
64-
version = "1"
65-
features = ["fs", "macros", "process", "rt-multi-thread", "sync", "time"]
66-
67-
[dependencies.tokio-tungstenite]
68-
version = "0.17"
69-
features = ["rustls-tls-webpki-roots"]
70-
71-
[dependencies.wheel]
72-
git = "https://github.com/fenhl/wheel"
73-
branch = "main"
74-
7542
[target.'cfg(windows)'.dependencies]
7643
directories = "4"

‎src/main.rs

+365-164
Large diffs are not rendered by default.

‎src/state.rs

+40-53
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,20 @@ use {
2323
},
2424
gefolge_websocket::event::Event,
2525
rand::prelude::*,
26+
tiny_skia::Pixmap,
2627
tokio::{
2728
fs::{
2829
self,
2930
File,
3031
},
3132
io::AsyncReadExt as _,
32-
sync::mpsc,
3333
time::sleep,
3434
},
35-
crate::Error,
35+
winit::event_loop::EventLoopProxy,
36+
crate::{
37+
Error,
38+
UserEvent,
39+
},
3640
};
3741

3842
#[derive(Clone, Copy, PartialEq, Eq, Hash, Sequence)]
@@ -50,7 +54,7 @@ impl Mode {
5054
Self::BinaryTime => {
5155
let timezone = current_event?.timezone;
5256
let now = Utc::now().with_timezone(&timezone);
53-
let tomorrow = now.date().succ();
57+
let tomorrow = now.date_naive().succ_opt().expect("date overflow");
5458
if tomorrow.month() == 1 && tomorrow.day() == 1 {
5559
Some((Priority::Normal, State::BinaryTime(timezone)))
5660
} else {
@@ -74,8 +78,8 @@ impl Mode {
7478
if now.month() == 1 && now.day() == 1 && now.hour() == 1 {
7579
Some(Priority::Programm)
7680
} else {
77-
let tomorrow = now.date().succ();
78-
(tomorrow.month() == 1 && tomorrow.day() == 1).then(|| if tomorrow.and_hms(0, 0, 0) - now < Duration::hours(1).into() {
81+
let tomorrow = now.date_naive().succ_opt().expect("date overflow");
82+
(tomorrow.month() == 1 && tomorrow.day() == 1).then(|| if timezone.from_local_datetime(&tomorrow.and_hms_opt(0, 0, 0).expect("tomorrow has no midnight")).single().expect("failed to determine tomorrow at midnight") - now < Duration::hours(1).into() {
7983
Priority::Programm
8084
} else {
8185
Priority::Normal
@@ -93,6 +97,7 @@ enum Priority {
9397
Programm,
9498
}
9599

100+
#[allow(unused)] //TODO
96101
#[derive(Debug, Clone)]
97102
pub(crate) enum State {
98103
BinaryTime(Tz),
@@ -101,61 +106,44 @@ pub(crate) enum State {
101106
HexagesimalTime(Tz),
102107
Logo {
103108
msg: &'static str,
104-
img: Option<Vec<u8>>,
105109
},
106110
NewYear(Tz),
107111
}
108112

109-
impl State {
110-
fn set(&mut self, new_state: &State) {
111-
if let (State::Logo { img: Some(img), .. }, State::Logo { img: None, msg }) = (&self, new_state) {
112-
*self = State::Logo { img: Some(img.clone()), msg: *msg };
113-
} else {
114-
*self = new_state.clone();
115-
}
113+
async fn load_images_inner(state_tx: EventLoopProxy<UserEvent>) -> Result<(), Error> {
114+
let dirs = stream::iter(xdg_basedir::get_cache_home().into_iter());
115+
let files = dirs.filter_map(|cfg_dir| async move { File::open(cfg_dir.join("fidera/gefolge.png")).await.ok() });
116+
pin_mut!(files);
117+
if let Some(mut file) = files.next().await {
118+
let mut buf = Vec::default();
119+
file.read_to_end(&mut buf).await?;
120+
tokio::task::block_in_place(|| Ok::<_, Error>(state_tx.send_event(UserEvent::Logo(Pixmap::decode_png(&buf)?))?))?;
121+
} else {
122+
let cache_dir = xdg_basedir::get_cache_home()?.join("fidera");
123+
fs::create_dir_all(&cache_dir).await?;
124+
let buf = reqwest::get("https://gefolge.org/static/gefolge.png").await?
125+
.error_for_status()?
126+
.bytes().await?
127+
.to_vec();
128+
fs::write(cache_dir.join("gefolge.png"), &buf).await?;
129+
tokio::task::block_in_place(|| Ok::<_, Error>(state_tx.send_event(UserEvent::Logo(Pixmap::decode_png(&buf)?))?))?;
116130
}
131+
Ok(())
132+
}
117133

118-
fn set_message(&mut self, new_msg: &'static str) {
119-
match self {
120-
State::Logo { msg, .. } => *msg = new_msg,
121-
_ => *self = State::Logo { msg: new_msg, img: None },
122-
}
134+
pub(crate) async fn load_images(state_tx: EventLoopProxy<UserEvent>) {
135+
if let Err(e) = load_images_inner(state_tx).await {
136+
eprintln!("error loading images: {e} (debug: {e:?})"); //TODO send error to event loop?
123137
}
124138
}
125139

126-
async fn maintain_inner(mut rng: impl Rng, current_event: Option<Event>, states_tx: mpsc::Sender<State>) -> Result<Never, Error> {
127-
let mut state = State::Logo {
128-
msg: "loading Gefolge logo",
129-
img: None,
130-
};
131-
states_tx.send(state.clone()).await?;
132-
if let State::Logo { ref mut img, .. } = state {
133-
let dirs = stream::iter(xdg_basedir::get_cache_home().into_iter());
134-
let files = dirs.filter_map(|cfg_dir| async move { File::open(cfg_dir.join("fidera/gefolge.png")).await.ok() });
135-
pin_mut!(files);
136-
if let Some(mut file) = files.next().await {
137-
let mut buf = Vec::default();
138-
file.read_to_end(&mut buf).await?;
139-
*img = Some(buf);
140-
} else {
141-
let cache_dir = xdg_basedir::get_cache_home()?.join("fidera");
142-
fs::create_dir_all(&cache_dir).await?;
143-
let buf = reqwest::get("https://gefolge.org/static/gefolge.png").await?
144-
.error_for_status()?
145-
.bytes().await?
146-
.to_vec();
147-
fs::write(cache_dir.join("gefolge.png"), &buf).await?;
148-
*img = Some(buf);
149-
}
150-
states_tx.send(state.clone()).await?;
151-
}
140+
async fn maintain_inner(mut rng: impl Rng + Send, current_event: Option<Event>, states_tx: EventLoopProxy<UserEvent>) -> Result<Never, Error> {
141+
tokio::task::block_in_place(|| states_tx.send_event(UserEvent::State(State::Logo { msg: "loading Gefolge logo" })))?;
152142
if rng.gen_bool(0.1) {
153-
state.set_message("reticulating splines");
154-
states_tx.send(state.clone()).await?;
143+
tokio::task::block_in_place(|| states_tx.send_event(UserEvent::State(State::Logo { msg: "reticulating splines" })))?;
155144
sleep(StdDuration::from_secs_f64(rng.gen_range(0.5..1.5))).await;
156145
}
157-
state.set_message("determining first mode");
158-
states_tx.send(state.clone()).await?;
146+
tokio::task::block_in_place(|| states_tx.send_event(UserEvent::State(State::Logo { msg: "determining first mode" })))?;
159147
let mut seen_modes = HashSet::new();
160148
loop { //TODO keep listening to WebSocket
161149
let mut available_modes = all::<Mode>().filter_map(|mode| Some((mode, mode.state(current_event.as_ref())?))).collect::<Vec<_>>();
@@ -168,18 +156,17 @@ async fn maintain_inner(mut rng: impl Rng, current_event: Option<Event>, states_
168156
}
169157
if let Some((mode, (_, new_state))) = available_modes.choose(&mut rng) {
170158
seen_modes.insert(*mode);
171-
state.set(new_state); //TODO reload image if necessary
159+
tokio::task::block_in_place(|| states_tx.send_event(UserEvent::State(new_state.clone())))?;
172160
} else {
173-
state.set_message("no modes available");
161+
tokio::task::block_in_place(|| states_tx.send_event(UserEvent::State(State::Logo { msg: "no modes available" })))?;
174162
};
175-
states_tx.send(state.clone()).await?;
176163
sleep(StdDuration::from_secs(10)).await;
177164
}
178165
}
179166

180-
pub(crate) async fn maintain(rng: impl Rng, current_event: Option<Event>, states_tx: mpsc::Sender<State>) {
167+
pub(crate) async fn maintain(rng: impl Rng + Send, current_event: Option<Event>, states_tx: EventLoopProxy<UserEvent>) {
181168
match maintain_inner(rng, current_event, states_tx.clone()).await {
182169
Ok(never) => match never {},
183-
Err(e) => { let _ = states_tx.send(State::Error(Arc::new(e))).await; }
170+
Err(e) => { let _ = tokio::task::block_in_place(|| states_tx.send_event(UserEvent::State(State::Error(Arc::new(e))))); }
184171
}
185172
}

‎src/text.rs

-161
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.