Skip to content

Commit 2cd8d45

Browse files
Support png images for icons
1 parent e3a5a86 commit 2cd8d45

File tree

2 files changed

+60
-28
lines changed

2 files changed

+60
-28
lines changed

share/tiny-dfr/config.toml

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ FontTemplate = ":bold"
3232
PrimaryLayerKeys = [
3333
# Action defines the key code to send when the button is pressed
3434
# Text defines the button label
35-
# Svg specifies the icon to be used for the button.
36-
# Do not include the .svg extension in the file name.
37-
# Svgs are looked up in /etc/tiny-dfr first and then in /usr/share/tiny-dfr
38-
# Only one of Text or Svg is allowed,
35+
# Icon specifies the icon to be used for the button.
36+
# Icons can either be svgs or pngs, with svgs being preferred
37+
# For best results with pngs, they should be 48x48
38+
# Do not include the extension in the file name.
39+
# Icons are looked up in /etc/tiny-dfr first and then in /usr/share/tiny-dfr
40+
# Only one of Text or Icon is allowed,
3941
# if both are present, the behavior is undefined.
4042
# For the list of supported key codes see
4143
# https://docs.rs/input-linux/latest/input_linux/enum.Key.html
@@ -55,17 +57,17 @@ PrimaryLayerKeys = [
5557

5658
# This key defines the contents of the media key layer
5759
MediaLayerKeys = [
58-
{ Svg = "brightness_low", Action = "BrightnessDown" },
59-
{ Svg = "brightness_high", Action = "BrightnessUp" },
60-
{ Svg = "mic_off", Action = "MicMute" },
61-
{ Svg = "search", Action = "Search" },
62-
{ Svg = "backlight_low", Action = "IllumDown" },
63-
{ Svg = "backlight_high", Action = "IllumUp" },
64-
{ Svg = "fast_rewind", Action = "PreviousSong" },
65-
{ Svg = "play_pause", Action = "PlayPause" },
66-
{ Svg = "fast_forward", Action = "NextSong" },
67-
{ Svg = "volume_off", Action = "Mute" },
68-
{ Svg = "volume_down", Action = "VolumeDown" },
69-
{ Svg = "volume_up", Action = "VolumeUp" }
60+
{ Icon = "brightness_low", Action = "BrightnessDown" },
61+
{ Icon = "brightness_high", Action = "BrightnessUp" },
62+
{ Icon = "mic_off", Action = "MicMute" },
63+
{ Icon = "search", Action = "Search" },
64+
{ Icon = "backlight_low", Action = "IllumDown" },
65+
{ Icon = "backlight_high", Action = "IllumUp" },
66+
{ Icon = "fast_rewind", Action = "PreviousSong" },
67+
{ Icon = "play_pause", Action = "PlayPause" },
68+
{ Icon = "fast_forward", Action = "NextSong" },
69+
{ Icon = "volume_off", Action = "Mute" },
70+
{ Icon = "volume_down", Action = "VolumeDown" },
71+
{ Icon = "volume_up", Action = "VolumeUp" }
7072
]
7173

src/main.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99
cmp::min,
1010
panic::{self, AssertUnwindSafe}
1111
};
12-
use cairo::{ImageSurface, Format, Context, Surface, Rectangle, FontFace};
12+
use cairo::{ImageSurface, Format, Context, Surface, Rectangle, FontFace, Antialias};
1313
use rsvg::{Loader, CairoRenderer, SvgHandle};
1414
use drm::control::ClipRect;
1515
use anyhow::{Error, Result};
@@ -70,7 +70,8 @@ struct ConfigProxy {
7070
#[derive(Deserialize)]
7171
#[serde(rename_all = "PascalCase")]
7272
struct ButtonConfig {
73-
svg: Option<String>,
73+
#[serde(alias = "Svg")]
74+
icon: Option<String>,
7475
text: Option<String>,
7576
action: Key
7677
}
@@ -83,7 +84,8 @@ struct Config {
8384

8485
enum ButtonImage {
8586
Text(String),
86-
Svg(SvgHandle)
87+
Svg(SvgHandle),
88+
Bitmap(ImageSurface)
8789
}
8890

8991
struct Button {
@@ -93,14 +95,38 @@ struct Button {
9395
action: Key
9496
}
9597

98+
fn try_load_svg(path: &str) -> Result<ButtonImage> {
99+
let handle = Loader::new().read_path(format!("/etc/tiny-dfr/{}.svg", path)).or_else(|_| {
100+
Loader::new().read_path(format!("/usr/share/tiny-dfr/{}.svg", path))
101+
})?;
102+
Ok(ButtonImage::Svg(handle))
103+
}
104+
105+
fn try_load_png(path: &str) -> Result<ButtonImage> {
106+
let mut file = File::open(format!("/etc/tiny-dfr/{}.png", path)).or_else(|_| {
107+
File::open(format!("/usr/share/tiny-dfr/{}.png", path))
108+
})?;
109+
let surf = ImageSurface::create_from_png(&mut file)?;
110+
if surf.height() == ICON_SIZE && surf.width() == ICON_SIZE {
111+
return Ok(ButtonImage::Bitmap(surf));
112+
}
113+
let resized = ImageSurface::create(Format::ARgb32, ICON_SIZE, ICON_SIZE).unwrap();
114+
let c = Context::new(&resized).unwrap();
115+
c.scale(ICON_SIZE as f64 / surf.width() as f64, ICON_SIZE as f64 / surf.height() as f64);
116+
c.set_source_surface(surf, 0.0, 0.0).unwrap();
117+
c.set_antialias(Antialias::Best);
118+
c.paint().unwrap();
119+
return Ok(ButtonImage::Bitmap(resized));
120+
}
121+
96122
impl Button {
97123
fn with_config(cfg: ButtonConfig) -> Button {
98124
if let Some(text) = cfg.text {
99125
Button::new_text(text, cfg.action)
100-
} else if let Some(svg) = cfg.svg {
101-
Button::new_svg(&svg, cfg.action)
126+
} else if let Some(icon) = cfg.icon {
127+
Button::new_icon(&icon, cfg.action)
102128
} else {
103-
panic!("Invalid config, a button must have either Text or Svg")
129+
panic!("Invalid config, a button must have either Text or Icon")
104130
}
105131
}
106132
fn new_text(text: String, action: Key) -> Button {
@@ -111,15 +137,12 @@ impl Button {
111137
image: ButtonImage::Text(text)
112138
}
113139
}
114-
fn new_svg(path: &str, action: Key) -> Button {
115-
let svg = Loader::new().read_path(format!("/etc/tiny-dfr/{}.svg", path)).or_else(|_| {
116-
Loader::new().read_path(format!("/usr/share/tiny-dfr/{}.svg", path))
117-
}).unwrap();
140+
fn new_icon(path: &str, action: Key) -> Button {
141+
let image = try_load_svg(path).or_else(|_| try_load_png(path)).unwrap();
118142
Button {
119-
action,
143+
action, image,
120144
active: false,
121145
changed: false,
122-
image: ButtonImage::Svg(svg)
123146
}
124147
}
125148
fn render(&self, c: &Context, button_left_edge: f64, button_width: u64, y_shift: f64) {
@@ -141,6 +164,13 @@ impl Button {
141164
&Rectangle::new(x, y, ICON_SIZE as f64, ICON_SIZE as f64)
142165
).unwrap();
143166
}
167+
ButtonImage::Bitmap(surf) => {
168+
let x = button_left_edge + (button_width as f64 / 2.0 - (ICON_SIZE / 2) as f64).round();
169+
let y = y_shift + ((DFR_HEIGHT as f64 - ICON_SIZE as f64) / 2.0).round();
170+
c.set_source_surface(surf, x, y).unwrap();
171+
c.rectangle(x, y, ICON_SIZE as f64, ICON_SIZE as f64);
172+
c.fill().unwrap();
173+
}
144174
}
145175
}
146176
fn set_active<F>(&mut self, uinput: &mut UInputHandle<F>, active: bool) where F: AsRawFd {

0 commit comments

Comments
 (0)