mirror of
https://codeberg.org/Fl1tzi/microdeck.git
synced 2024-05-08 06:30:44 +00:00
complete folder switching, temporary icon
This commit is contained in:
parent
5ddff29c87
commit
41420d3080
|
@ -1,5 +0,0 @@
|
|||
# Fonts
|
||||
|
||||
This folder holds all fonts which may be used by the modules.
|
||||
|
||||
Thanks to the creators of these awesome fonts!
|
|
@ -1,94 +0,0 @@
|
|||
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.
|
||||
|
Binary file not shown.
|
@ -16,7 +16,7 @@ use tokio::{
|
|||
runtime::Runtime,
|
||||
sync::mpsc::{self, error::TrySendError},
|
||||
};
|
||||
use tracing::{debug, error, info_span, trace};
|
||||
use tracing::{debug, error, info_span, trace, warn};
|
||||
|
||||
/// A module controller in holding the information of a Module
|
||||
pub type ModuleController = (Arc<Button>, Option<mpsc::Sender<HostEvent>>);
|
||||
|
@ -42,6 +42,7 @@ pub struct Device {
|
|||
modules_runtime: Option<Runtime>,
|
||||
config: DeviceConfig,
|
||||
spaces: Arc<HashMap<String, Space>>,
|
||||
selected_space: Option<String>,
|
||||
serial: String,
|
||||
}
|
||||
|
||||
|
@ -81,6 +82,7 @@ impl Device {
|
|||
modules_runtime: None,
|
||||
config: device_conf,
|
||||
spaces,
|
||||
selected_space: None,
|
||||
serial,
|
||||
})
|
||||
}
|
||||
|
@ -91,8 +93,17 @@ impl Device {
|
|||
if self.modules_runtime.is_none() {
|
||||
self.modules_runtime = Some(Runtime::new().unwrap());
|
||||
}
|
||||
for i in 0..self.config.buttons.len() {
|
||||
let button = self.config.buttons.get(i).unwrap().to_owned();
|
||||
// TODO: DO THIS WITHOUT CLONING! Currently takes up a big amount of memory.
|
||||
let button_config = match &self.selected_space {
|
||||
Some(s) => self.spaces.get(s).unwrap_or_else(|| {
|
||||
warn!("The space \"{}\" was not found", s);
|
||||
&self.config.buttons
|
||||
}
|
||||
).to_owned(),
|
||||
None => self.config.buttons.to_owned()
|
||||
};
|
||||
for i in 0..button_config.len() {
|
||||
let button = button_config.get(i).unwrap().to_owned();
|
||||
unwrap_or_error!(self._create_module(button).await);
|
||||
}
|
||||
}
|
||||
|
@ -173,16 +184,16 @@ impl Device {
|
|||
|
||||
/// Switch to a space. This will tear down the whole runtime of the current space.
|
||||
#[tracing::instrument(skip_all, fields(serial = self.serial))]
|
||||
async fn switch_to_space(&mut self, name: &String) {
|
||||
async fn switch_to_space(&mut self, name: String) {
|
||||
debug!("Switching to space {}", name);
|
||||
self.drop();
|
||||
if let Some(space) = self.spaces.get(name) {
|
||||
self.config.buttons = space.clone();
|
||||
self.device.reset().await.unwrap();
|
||||
self.init_modules().await;
|
||||
if name.to_lowercase() == "home" {
|
||||
self.selected_space = None
|
||||
} else {
|
||||
error!("Space {} was not found", name);
|
||||
};
|
||||
self.selected_space = Some(name)
|
||||
}
|
||||
self.drop();
|
||||
self.device.reset().await.unwrap();
|
||||
self.init_modules().await;
|
||||
}
|
||||
|
||||
/// Handle all incoming button state updates from the listener (shell actions, module sender)
|
||||
|
@ -219,7 +230,7 @@ impl Device {
|
|||
Some(n) => n.clone(),
|
||||
None => return,
|
||||
};
|
||||
self.switch_to_space(&name).await;
|
||||
self.switch_to_space(name).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,21 @@ mod space;
|
|||
use self::counter::Counter;
|
||||
use self::space::Space;
|
||||
|
||||
use crate::GLOBAL_FONT;
|
||||
// other things
|
||||
use crate::GLOBAL_FONT;
|
||||
use crate::config::Button;
|
||||
use async_trait::async_trait;
|
||||
pub use deck_driver as streamdeck;
|
||||
use futures_util::Future;
|
||||
use image::{DynamicImage, Rgb, RgbImage};
|
||||
use image::imageops::{resize, self};
|
||||
use image::io::Reader;
|
||||
use image::{DynamicImage, Rgb, RgbImage, ImageBuffer};
|
||||
use imageproc::drawing::draw_text_mut;
|
||||
use imageproc::filter;
|
||||
use lazy_static::lazy_static;
|
||||
use rusttype::Scale;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{BufReader, self};
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
@ -25,7 +29,7 @@ use streamdeck::info::Kind;
|
|||
use streamdeck::AsyncStreamDeck;
|
||||
pub use streamdeck::StreamDeckError;
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, error, info};
|
||||
use tracing::{debug, error, info, trace};
|
||||
|
||||
lazy_static! {
|
||||
static ref MODULE_MAP: HashMap<&'static str, ModuleFunction> = {
|
||||
|
@ -114,26 +118,44 @@ impl DeviceAccess {
|
|||
}
|
||||
|
||||
/// Draw an image with text
|
||||
///
|
||||
/// 60% image
|
||||
/// 40% text
|
||||
pub fn image_with_text(&self, image: Vec<u8>, text: String, config: &Button) -> DynamicImage {
|
||||
let res = self.resolution();
|
||||
let mut image = RgbImage::new(res.0 as u32, res.1 as u32);
|
||||
#[tracing::instrument(skip_all, fields(index = config.index))]
|
||||
pub fn image_with_text(&self, image: DynamicImage, text: String, config: &Button) -> DynamicImage {
|
||||
trace!("Render start");
|
||||
let (w, h) = self.resolution();
|
||||
|
||||
let image_scaling = parse_config(&config, &"IMAGE_SCALE".into(), 65.0);
|
||||
|
||||
// TODO: lots of parsing. This can probbably be improved.
|
||||
let new_h = (h as f32 * (image_scaling * 0.01)) as u32;
|
||||
let new_w = (w as f32 * (image_scaling * 0.01)) as u32;
|
||||
|
||||
// Calculate percentage of which we can scale down to the button resolution.
|
||||
// By taking the smallest it keeps the aspect ratio.
|
||||
// let percentage = f32::min(deck_w / image.width() as f32, deck_h / image.height() as f32);
|
||||
|
||||
let image = image.resize_to_fill(new_w, new_h, image::imageops::FilterType::Nearest);
|
||||
|
||||
let mut base_image = RgbImage::new(h as u32, w as u32);
|
||||
draw_text_mut(
|
||||
&mut image,
|
||||
&mut base_image,
|
||||
Rgb([255, 255, 255]),
|
||||
-10,
|
||||
10,
|
||||
Scale::uniform(parse_config(config, &"FONT_SIZE".into(), 20.0)),
|
||||
0,
|
||||
h as i32 - 20,
|
||||
Scale::uniform(parse_config(config, &"FONT_SIZE".into(), 15.0)),
|
||||
&GLOBAL_FONT.get().unwrap(),
|
||||
&text,
|
||||
);
|
||||
image::DynamicImage::ImageRgb8(image)
|
||||
// position at the middle
|
||||
let free_space = w - image.width() as usize;
|
||||
imageops::overlay(&mut base_image, &image.to_rgb8(), (free_space/2) as i64, 0);
|
||||
trace!("Render end");
|
||||
image::DynamicImage::ImageRgb8(base_image)
|
||||
}
|
||||
|
||||
/// Draw text
|
||||
#[tracing::instrument(skip_all, fields(index = config.index))]
|
||||
pub fn text(&self, text: String, config: &Button) -> DynamicImage {
|
||||
trace!("Render start");
|
||||
let res = self.resolution();
|
||||
let mut image = RgbImage::new(res.0 as u32, res.1 as u32);
|
||||
draw_text_mut(
|
||||
|
@ -141,16 +163,41 @@ impl DeviceAccess {
|
|||
Rgb([255, 255, 255]),
|
||||
10,
|
||||
10,
|
||||
Scale::uniform(parse_config(config, &"FONT_SIZE".into(), 20.0)),
|
||||
Scale::uniform(parse_config(config, &"FONT_SIZE".into(), 15.0)),
|
||||
&GLOBAL_FONT.get().unwrap(),
|
||||
&text,
|
||||
);
|
||||
trace!("Render end");
|
||||
image::DynamicImage::ImageRgb8(image)
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads the image from the `IMAGE` option.
|
||||
/// Displays [create_error_image] if it does not exist or cannot be loeded.
|
||||
pub fn load_image(config: &Button) -> io::Result<DynamicImage> {
|
||||
// TODO: maybe us an Option (faster?)
|
||||
let file_path = parse_config(config, &"IMAGE".into(), "None".to_string());
|
||||
|
||||
if file_path == "None" {
|
||||
return Ok(create_error_image());
|
||||
}
|
||||
|
||||
Ok(Reader::open(file_path)?.decode().expect("Unable to decode image"))
|
||||
}
|
||||
|
||||
/// A smooth red image which should represent an empty space
|
||||
pub fn create_error_image() -> DynamicImage {
|
||||
let mut error_img: RgbImage = ImageBuffer::new(1, 1);
|
||||
|
||||
for pixel in error_img.enumerate_pixels_mut() {
|
||||
*pixel.2 = image::Rgb([240, 128, 128]);
|
||||
}
|
||||
|
||||
DynamicImage::ImageRgb8(error_img)
|
||||
}
|
||||
|
||||
/// reads a key from the config and parses the config in the given type
|
||||
fn parse_config<T>(config: &Button, key: &String, if_wrong_type: T) -> T
|
||||
pub fn parse_config<T>(config: &Button, key: &String, if_wrong_type: T) -> T
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
|
|
|
@ -3,6 +3,9 @@ use super::ChannelReceiver;
|
|||
use super::DeviceAccess;
|
||||
use super::Module;
|
||||
use super::ReturnError;
|
||||
use super::create_error_image;
|
||||
use super::load_image;
|
||||
use super::parse_config;
|
||||
use async_trait::async_trait;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -12,10 +15,16 @@ pub struct Space;
|
|||
#[async_trait]
|
||||
impl Module for Space {
|
||||
async fn run(
|
||||
_streamdeck: DeviceAccess,
|
||||
streamdeck: DeviceAccess,
|
||||
_button_receiver: ChannelReceiver,
|
||||
_config: Arc<Button>,
|
||||
config: Arc<Button>,
|
||||
) -> Result<(), ReturnError> {
|
||||
// let icon = load_image(&config).unwrap();
|
||||
let icon = create_error_image();
|
||||
|
||||
let image = streamdeck.image_with_text(icon, parse_config(&config, &"NAME".into(), "Unknown".to_string()), &config);
|
||||
|
||||
streamdeck.write_img(image).await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue