add configuration with hot reload

This commit is contained in:
emerald 2022-10-05 14:10:18 -04:00
parent b0d82ea050
commit 56f7e1f11b
Signed by: emerald
GPG Key ID: 5648BBF928C9DC43
6 changed files with 226 additions and 27 deletions

69
src-tauri/Cargo.lock generated
View File

@ -315,6 +315,7 @@ dependencies = [
"env_logger",
"image",
"log",
"notify",
"rand 0.8.5",
"ray_format",
"serde",
@ -322,6 +323,7 @@ dependencies = [
"tauri",
"tauri-build",
"tokio",
"toml",
]
[[package]]
@ -963,6 +965,15 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]]
name = "futf"
version = "0.1.5"
@ -1513,6 +1524,26 @@ dependencies = [
"adler32",
]
[[package]]
name = "inotify"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
"bitflags",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
"libc",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -1643,6 +1674,26 @@ dependencies = [
"treediff",
]
[[package]]
name = "kqueue"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d6112e8f37b59803ac47a42d14f1f3a59bbf72fc6857ffc5be455e28a691f8e"
dependencies = [
"kqueue-sys",
"libc",
]
[[package]]
name = "kqueue-sys"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "kuchiki"
version = "0.8.1"
@ -1922,6 +1973,24 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "notify"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
dependencies = [
"bitflags",
"crossbeam-channel",
"filetime",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
"mio",
"walkdir",
"winapi",
]
[[package]]
name = "num-derive"
version = "0.3.3"

View File

@ -42,6 +42,8 @@ rand = "0.8.5"
tokio = { version = "1.21.0", features = ["full"] }
image = "0.24.3"
base64-url = "1.4.13"
toml = "0.5.9"
notify = "5.0.0"
[features]
# by default Tauri runs in production mode

65
src-tauri/src/config.rs Normal file
View File

@ -0,0 +1,65 @@
use std::fmt::Display;
use log::{debug, error};
use serde::{Deserialize, Serialize};
use tauri::api::path::config_dir;
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct Config {
#[serde(default)]
pub background_color: BGColor,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BGColor {
Transparent,
Green,
Blue,
Pink,
Custom(String),
}
impl Default for BGColor {
fn default() -> Self {
Self::Transparent
}
}
impl Display for BGColor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Custom(c) => {
write!(f, "{}", c)
}
_ => {
write!(f, "{}", self.to_string().to_lowercase())
}
}
}
}
pub fn load_config() -> Config {
if let Some(d) = config_dir() {
use std::fs;
let path = d.join("cathode");
if !path.exists() {
if let Err(e) = fs::create_dir_all(&path) {
debug!("{:?}", e);
error!("Failed to create config directory");
error!("{}", e);
} else {
debug!("Created config dir at {}", path.display())
}
Config::default()
} else {
let raw = fs::read_to_string(path.join("config.toml")).unwrap_or_else(|f| {
error!("Failed to load config file: {}", f);
String::new()
});
toml::from_str::<Config>(&raw).unwrap_or_default()
}
} else {
Config::default()
}
}

View File

@ -4,18 +4,24 @@
)]
use std::{
path::{Path, PathBuf},
path::Path,
sync::{Arc, Mutex},
thread::sleep,
time::Duration,
};
use audio::monitor;
use log::{debug, trace, warn};
use log::{debug, error, trace, warn};
use serde_json::Value;
use tauri::{api::path::cache_dir, Manager, State};
use tauri::{
api::path::{cache_dir, config_dir},
Manager, State,
};
use crate::config::Config;
mod audio;
mod config;
mod fs;
const MIC_THRESHOLD: f32 = 0.5f32;
@ -23,6 +29,7 @@ const MIC_THRESHOLD: f32 = 0.5f32;
struct MicThreshold(Arc<Mutex<f32>>);
struct AudioLevel(Arc<Mutex<f32>>);
struct BlinkInterval(Arc<Mutex<u64>>);
struct CurrentConfig(Arc<Mutex<Config>>);
// struct LastImage(Option<Arc<Path>>);
// struct LastRay(Arc<Mutex<Path>>);
@ -42,10 +49,17 @@ fn main() {
}
}
// Create the config directory if needed
let config = config::load_config();
let current_conf = Arc::new(Mutex::new(config));
let config = current_conf.clone();
tauri::Builder::default()
.manage(MicThreshold(threshold.clone()))
.manage(AudioLevel(level.clone()))
.manage(BlinkInterval(blink_interval.clone()))
.manage(CurrentConfig(current_conf.clone()))
.setup(|app| {
let window = app.get_window("main").unwrap();
tauri::async_runtime::spawn(async move {
@ -66,6 +80,34 @@ fn main() {
}
});
let window = app.get_window("main").unwrap();
tauri::async_runtime::spawn(async move {
use notify::{event::AccessKind, Event, EventKind, RecursiveMode, Result, Watcher};
if let Some(path) = config_dir() {
// let window = app.get_window("main").unwrap();
let mut watcher =
notify::recommended_watcher(move |res: Result<Event>| match res {
Ok(event) => {
if let EventKind::Access(AccessKind::Close(_)) = event.kind {
*config.lock().unwrap() = config::load_config();
window.emit("reload-config", "").unwrap();
}
}
Err(e) => error!("error watching filesystem {:?}", e),
})
.unwrap();
watcher
.watch(
&path.join("cathode").join("config.toml"),
RecursiveMode::NonRecursive,
)
.unwrap();
loop {
sleep(Duration::from_millis(5000));
}
}
});
if let Ok(matches) = app.get_cli_matches() {
if let Some(arg) = matches.args.get("file") {
if let Value::String(path) = arg.value.clone() {
@ -96,6 +138,7 @@ fn main() {
set_mic_threshold,
get_mic_threshold,
get_audio_level,
get_config,
fs::open_image,
fs::save_ray,
fs::open_ray,
@ -123,3 +166,8 @@ fn get_mic_threshold(current: State<'_, MicThreshold>) -> f32 {
fn get_audio_level(level: State<'_, AudioLevel>) -> f32 {
*level.0.lock().unwrap()
}
#[tauri::command]
fn get_config(current: State<'_, CurrentConfig>) -> Config {
current.0.lock().unwrap().clone()
}

View File

@ -1,25 +1,44 @@
<script lang="ts">
import MainView from "./views/main.svelte";
//TODO: load config
<script lang="ts" context="module">
type Config = {
background_color: "transparent" | "blue" | "green" | "pink" | {custom: string};
}
</script>
<main style:background-color="lightblue">
<MainView />
<script lang="ts">
import MainView from "./views/main.svelte";
import {appWindow} from "@tauri-apps/api/window";
import {invoke} from "@tauri-apps/api";
import {onMount} from "svelte";
let config: Config = {background_color: "transparent"};
$: transparent = config.background_color == "transparent";
$: color = typeof config.background_color == "object" ? config.background_color.custom : config.background_color;
onMount(async () => {
config = await invoke("get_config") as Config;
await appWindow.listen("reload-config", async () => {
config = await invoke("get_config") as Config;
});
});
</script>
<main>
<MainView --active-color={transparent ? 'lightblue' : color} --inactive-color={color}/>
</main>
<style lang="scss">
:global(*) {
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
:global(body) {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
:global(body) {
overflow: hidden;
}
</style>

View File

@ -165,7 +165,7 @@
}}
class="buttons"
>
<div on:click={saveRay}><span>Save</span></div>
<div on:click={saveRay}>Save</div>
<div on:click={loadRay}>Load</div>
</div>
{/if}
@ -174,10 +174,6 @@
<svelte:body on:contextmenu|preventDefault />
<style lang="scss">
div {
user-select: none;
}
.buttons {
position: absolute;
top: 2vh;
@ -222,23 +218,23 @@
@keyframes fade-out {
0% {
background-color: inherit;
background-color: var(--active-color);
}
100% {
background-color: transparent;
background-color: var(--inactive-color);
}
}
@keyframes fade-in {
0% {
background-color: transparent;
background-color: var(--inactive-color);
}
100% {
background-color: inherit;
background-color: var(--active-color);
}
}
.container {
background-color: inherit;
background-color: var(--active-color);
animation-name: fade-in;
animation-duration: 0.2s;
}
@ -246,6 +242,6 @@
.container.transparent {
animation-name: fade-out;
animation-duration: 0.2s;
background-color: transparent;
background-color: var(--inactive-color);
}
</style>