From 8c32cd611abf55d687dbc90e79598f039786026b Mon Sep 17 00:00:00 2001 From: Fl1tzi Date: Tue, 28 Mar 2023 23:33:27 +0200 Subject: [PATCH] initial commit. First working basic setup. --- Cargo.toml | 21 +++ LICENSE | 2 +- README.md | 3 +- fonts/README.md | 5 + fonts/SpaceGrotesk-LICENSE | 94 +++++++++++ fonts/SpaceGrotesk.ttf | Bin 0 -> 86504 bytes src/main.rs | 326 +++++++++++++++++++++++++++++++++++++ src/modules.rs | 227 ++++++++++++++++++++++++++ src/modules/blank.rs | 19 +++ src/modules/counter.rs | 44 +++++ 10 files changed, 739 insertions(+), 2 deletions(-) create mode 100644 Cargo.toml create mode 100644 fonts/README.md create mode 100644 fonts/SpaceGrotesk-LICENSE create mode 100644 fonts/SpaceGrotesk.ttf create mode 100644 src/main.rs create mode 100644 src/modules.rs create mode 100644 src/modules/blank.rs create mode 100644 src/modules/counter.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4394696 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "dach-decker" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] +hidapi = "2.2.0" +elgato-streamdeck = { version = "0.2.4", features = ["async"] } +toml = "0.7.2" +tokio = { version = "1", features = ["full"] } +log = "0.4" +dirs = "4.0.0" +serde = { version = "1.0", features = ["derive"] } +simple_logger = "4.0.0" +image = "0.24.5" +async-trait = "0.1.66" +futures-util = "0.3.27" +lazy_static = "1.4.0" +imageproc = "0.23.0" +rusttype = "0.9.3" diff --git a/LICENSE b/LICENSE index 2071b23..8a30ea5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) +Fl1tzi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index 7434c26..e03f9f5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # dach-decker -Dach-Decker or Dachdecker (eng. roofer) is a software to configure your Stream Deck with an easy to use configuration file. \ No newline at end of file +Dach-Decker or Dachdecker (eng. roofer) is a software to configure your Stream Deck on Linux with an easy to use configuration file. + diff --git a/fonts/README.md b/fonts/README.md new file mode 100644 index 0000000..cf2bc20 --- /dev/null +++ b/fonts/README.md @@ -0,0 +1,5 @@ +# Fonts + +This folder holds all fonts which may be used by the modules. + +Thanks to the creators of these awesome fonts! diff --git a/fonts/SpaceGrotesk-LICENSE b/fonts/SpaceGrotesk-LICENSE new file mode 100644 index 0000000..579cdbf --- /dev/null +++ b/fonts/SpaceGrotesk-LICENSE @@ -0,0 +1,94 @@ +Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + diff --git a/fonts/SpaceGrotesk.ttf b/fonts/SpaceGrotesk.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d0e5eff1ff43b884f3507892a489da65a1ae71d1 GIT binary patch literal 86504 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 =cQ1iXvnlcLl7F76Y}%) z+w@X?zm};a0LVspszvivt$O_$V)JC=4h;q(4H|h!*67#!Wj%C`v_R*|rq;H`R@vOz z*3v559?)~!_1pBs^}fm&Uu#BH)GsNJTq1ZB_`yXrwd9vq)I(QPR~7j4E2|b4L?t!V zE8x^p23oVcAX0fki^lJz>0N1Bga)U*qVkCpyELR~1BQL=dF6lj$>ZK9`yOjVk7IWv z<@(coY`?%)d8Oz7A*ly%g@nH>Nu%`qs65@zBx!t_^4XmCgJfT}q{q)DDc%psbtWaL z?@rV2pa=3|ou1#H9RIQAJTv&8iRG@VGxW+z3j&=G=;T1d zbIX|2qoV>H6KHmz;{#Q{9E+8qR1r>#rJ%GZ(60ts9B7auX!d$Ec`~mI`TRf&p=veY zdjdM7-@VTYxm@fEU5TPSpgg$v%PL2|CVydl*l6sP;gw+%uoW(>4*N1Th>a(yy&D$` z?HLzf%i4Gxy0Oja2|0=LXv5>!toBQlM&fVB^hM6UjE{5T+&Dj;29J?r@prQS!1j~4 zkiE&aP=D-gF1FMaF10f2Ly<{LVm2g31?ju&SD%#r&Esl;o9gNzoAcXkWx_zKT`PkdywZF z{83(#!W+&15~G<668rpB;-0@o%<~gOJ0GGfwM9=-@8WPa>t@Jn>-D5{B%GVzJe_h_ zaTC>G49wR)!U~01Iy(7N0~E#HEApaS z?;52G&)B)35n5X^lMRp7Y+CxW#_Q$I8TAFu$@v23sQv$0_a^>BGY%W=Ft*u#d(0kW zo_X2+)b0Z<>;W0<#RmHcZKA$TTd)IP#oF7!{L~lxsUNl9x9#?E+lejqx*d|max-N= z;CU^VPIDv3)qJq33UHt@5T1qTg$rB;97WNUcF-B@H+nPI^}7<+;kL?QIpEg0dt{f3 znK63gh|6*_+`X>ZJ?b8J1C%~X=|1qHc2N3W*^kcLAe}C{d}((E+-rucM8m0(TDhGV z8cniR2tzI#Si`!B2pWS#&p6Af+2w9KSMLV7WEJV>I_G(#7Tz2gF9lK|v!zTXU@OgY zv#|9hV%J@R4fhr7wIXb@Y1m<3!{#c*vRX)edrfs{)pP}`+n2WPrM(f_`_ftxDdoD| z3vR!gWrzFYqTWmUW`w)ZzDuMXIM(nJC;mB8C(C5HER|c_*IlXGAWP&9xk;XJ{#r+YN4s zyO}-$*<)K1w)@oylNhx!;N5Zi+lG{zH?^p0M^<93qf0nlQevg0N z_xN6NFZv_?nC~Zd+z^h zf9k@O{ZaTTDe^;#CweG7JBQRZ#kN)SG=r7d&J(IpfJ%8=k!L4@a~=fKJOqCEWB6lPAF)Dob$DY~72ZZnniIsM z`IY>W)!*B=KJNp9F41fT%+Yo61rzq)z zyjgmPw@Bb}b}R2%9^)O$PSB=)dr}rjB_(rZ7qaF6vgt6=DMmh(AffI-My*3iZADIX zAgO3$Elc~BF{9nU40j{5+d^i$3b2sNwejW4?xLo{)KWqX>$rM{Qw?>Zj+D>U28z%Q znxC6D%tf)~vK8=4COw1B#*L(AwKlFzs- zO3j`nV^_AjG(NZw*Z|B^9%PW~hx%U>DWQRw&4 z?h2k0?frRfv8!anH-vGJ2_s=6Pmhj { + match $res { + Some(v) => v, + None => continue, + } + }; +} + +/// The config structure +#[derive(Deserialize, Debug)] +pub struct Config { + global: Option, + device: Vec, +} + +#[derive(Deserialize, Debug)] +struct GlobalConfig { + default_font: Option +} + +fn main() { + simple_logger::init_with_env().unwrap(); + + let config_file: PathBuf = match env::var_os("DACH_DECKER_CONFIG") { + Some(path) => PathBuf::from(path), + None => { + if let Some(mut path) = config_dir() { + path.push(CONFIG_FOLDER_NAME); + path.push("config.toml"); + path + } else { + error!("Please use the \"DACH_DECKER_CONFIG\" environment variable to provide a path to your config"); + exit(1); + } + } + }; + + info!("Loading configuration from \"{}\"", config_file.display()); + + let config: Config = match fs::read_to_string(config_file) { + Ok(content) => match toml::from_str(&content) { + Ok(c) => c, + Err(e) => { + error!("Error detected in configuration:\n{}", e); + exit(1); + } + }, + Err(file_error) => { + if file_error.kind() == ErrorKind::NotFound { + error!("Unable to load configuration because the file does not exist. Please create the configuration file."); + } else { + error!("Cannot open the configuration file: {}", file_error); + } + exit(1); + } + }; + debug!("{:#?}", config); + // hidapi + let hid = match streamdeck::new_hidapi() { + Ok(v) => v, + Err(e) => { + error!("HidApi Error:\n{}", e); + exit(1); + } + }; + // list devices + // TODO: allow hotplug + let devices = streamdeck::list_devices(&hid); + // lets start some async + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(start(config, hid, devices)) +} + +pub async fn start(config: Config, hid: HidApi, hw_devices: Vec<(streamdeck::info::Kind, String)>) { + init_devices(config, hid, hw_devices).await; + + // TODO: PLEASE IMPROVE THIS!! + // Issue is that tokio sleeps are not kept running while they are sleeping which results in the + // program exiting... + // + // However, this will stay open even if the program is nothing doing anymore. + loop { + tokio::time::sleep(Duration::from_secs(2000)).await; + } +} + +/// This is the entry point for the application. This will check all devices for their config, +/// start the bridges and the device button listeners. +async fn init_devices(config: Config, hid: HidApi, devices: Vec<(streamdeck::info::Kind, String)>) { + // check if configuration is correct for device + if devices.len() == 0 { + error!("There are no Decks connected"); + exit(1); + } + info!("There are {} Decks connected", devices.len()); + 'outer: for device in devices { + // no pedals are supported + if !device.0.is_visual() { + continue; + } + // device.1 is the serial number + if let Some(device_conf) = config.device.iter().find(|s| s.serial == device.1) { + // connect to deck or continue to next + let deck = match AsyncStreamDeck::connect(&hid, device.0, &device.1) { + Ok(deck) => { + info!("Successfully connected to {}", device.1); + deck + } + Err(e) => { + error!("Failed to connect to Deck {}:\n{}", device.1, e); + continue 'outer; + } + }; + // set brightness + deck.set_brightness(device_conf.brightness).await.unwrap(); + // reset + deck.reset().await.unwrap(); + // initialize buttons + // let mut bridges: Vec = Vec::new(); + let button_count = device.0.key_count(); + let mut buttons_key = HashMap::new(); + for button in device_conf.buttons.clone().into_iter() { + // if the index of the button is higher than the button count + if button_count < button.index { + warn!( + "The button {} does not exist on Deck {}; skipping", + button.index, device.1 + ); + continue 'outer; + } + // check if the action has the correct syntax + for key in vec![&button.on_click, &button.on_release] { + if let Some(a) = key { + for action in a { + if !action.starts_with("bash:") && !action.starts_with("sh:") { + error!( + "Unknown action in button {} on Deck {}; skipping", + button.index, device.1 + ); + continue 'outer; + } + } + } + } + // create a watch channel for the module to receive device events + let (button_sender, button_receiver) = mpsc::channel(4); + buttons_key.insert( + button.index, + ( + button_sender, + (button.on_click.clone(), button.on_release.clone()), + ), + ); + // spawn the module + let b = button.clone(); + let rx = Arc::new(Mutex::new(button_receiver)); + let dev = deck.clone(); + tokio::spawn(async move { + start_module(b, dev, rx).await; + }); + } + // start the device key listener + tokio::spawn(async move { + device_key_listener(deck, buttons_key).await; + }); + } else { + info!("Deck {} is not configured; skipping", device.1); + } + } +} + +/// listener for button press changes on the device. Also executes the scripts. +pub async fn device_key_listener( + device: Arc, + mut keys: HashMap< + u8, + ( + mpsc::Sender, + (Option>, Option>), + ), + >, +) { + loop { + match device.get_reader().read(7.0).await { + Ok(v) => { + trace!("Received Keypress: {:?}", v); + for update in v { + match update { + ButtonStateUpdate::ButtonDown(i) => { + let options = skip_if_none!(keys.get(&i)); + let actions = &options.1 .0; + if send_key_event(options, actions, HostEvent::ButtonPressed).await == false { + debug!("Removed key {} from listeners (receiver dropped)", &i); + keys.remove(&i); + } + } + ButtonStateUpdate::ButtonUp(i) => { + let options = skip_if_none!(keys.get(&i)); + let actions = &options.1.1; + /* let sender = &options.0; + let on_release = &options.1 .1; + if let Some(actions) = on_release { + execute_button_action(actions).await; + } else { + if sender.try_send(HostEvent::ButtonReleased).is_err() { + keys.remove(&i); + debug!("Removed key {} from listeners (does not respond)", &i); + } + }*/ + if send_key_event(options, actions, HostEvent::ButtonReleased).await == false { + debug!("Removed key {} from listeners (receiver dropped)", &i); + keys.remove(&i); + } + } + } + } + } + Err(e) => { + error!("Error while retrieving key status: {:?}", e); + } + } + } +} + +/// manually sends the script event or try to send it to the module. +/// Returns false if the receiver is dead and can therefore be removed. +pub async fn send_key_event(options: &(mpsc::Sender, (Option>, Option>)), actions: &Option>, event: HostEvent) -> bool { + let sender = &options.0; + if let Some(actions) = actions { + execute_button_action(actions).await; + } else { + if let Err(e) = sender.try_send(event) { + match e { + TrySendError::Full(_) => trace!("Buffer full: {:?}", e), + TrySendError::Closed(_) => { + return false + } + } + } + } + true +} + +/// executes a shell script +pub async fn execute_button_action(actions: &Vec) { + for a in actions { + if let Some(v) = a.strip_prefix("bash:") { + execute_bash(v).await; + } else if let Some(v) = a.strip_prefix("sh:") { + execute_sh(v).await; + } else { + unreachable!() + } + } +} + +pub async fn execute_bash(command: &str) { + match Command::new("/bin/bash").arg(command).output().await { + Ok(o) => debug!("Command \'{}\' returned: {}", command, o.status), + Err(e) => error!("Command \'{}\' failed: {}", command, e), + } +} + +pub async fn execute_sh(command: &str) { + match Command::new("sh").arg(command).output().await { + Ok(o) => debug!("Command \'{}\' returned: {}", command, o.status), + Err(e) => error!("Command \'{}\' failed: {}", command, e), + } +} + +#[derive(Deserialize, Debug, Clone)] +pub struct DeviceConfig { + pub serial: String, + #[serde(default = "default_brightness")] + pub brightness: u8, + pub buttons: Vec