Tray, fullscreen-only hiding, peeks, Ethernet details, wider maps
- tray.rs: the shell serves org.kde.StatusNotifierWatcher (only when no other watcher runs) and is its host; icons from the theme or from IconPixmap (png.rs, a stdlib png writer); left/middle/right click and scroll; dbusmenu menus drawn in the island's look; tray IPC verbs for testing - edge.rs: the bar hides only on monitors showing a fullscreen window, driven by Hyprland's event socket; peeks of the workspaces (with the disc sliding) and the island (song change, new player), with a pop on the island - prompt.rs: Ethernet details popup (addresses, gateway, DNS, MAC copyable) - map.rs: 1100x560 maps, every other node a ring further in - tools/fake_tray.py: a fake tray app, by bus name or by path
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* a png from raw rgba pixels, for tray icons that come as pixels (IconPixmap) rather
|
||||
* than a name: amane draws images from files. stdlib only, so the deflate stream is
|
||||
* stored, not compressed; tray icons are a few kB either way
|
||||
*/
|
||||
|
||||
// the image, straight rgba (not premultiplied), rows top to bottom
|
||||
pub fn encode(width: u32, height: u32, rgba: &[u8]) -> Vec<u8> {
|
||||
let row = width as usize * 4;
|
||||
|
||||
// every row starts with its filter byte, 0: none
|
||||
let mut raw = Vec::with_capacity((row + 1) * height as usize);
|
||||
|
||||
for line in rgba.chunks(row).take(height as usize) {
|
||||
raw.push(0);
|
||||
raw.extend_from_slice(line);
|
||||
}
|
||||
|
||||
let mut header = Vec::with_capacity(13);
|
||||
header.extend_from_slice(&width.to_be_bytes());
|
||||
header.extend_from_slice(&height.to_be_bytes());
|
||||
// 8 bits per channel, colour type 6 (rgba), deflate, adaptive filtering, no interlace
|
||||
header.extend_from_slice(&[8, 6, 0, 0, 0]);
|
||||
|
||||
let mut png = vec![0x89, b'P', b'N', b'G', b'\r', b'\n', 0x1a, b'\n'];
|
||||
|
||||
chunk(&mut png, b"IHDR", &header);
|
||||
chunk(&mut png, b"IDAT", &zlib_stored(&raw));
|
||||
chunk(&mut png, b"IEND", &[]);
|
||||
|
||||
png
|
||||
}
|
||||
|
||||
/*
|
||||
* IconPixmap's pixels: argb32 in network byte order, so a, r, g, b per pixel; the
|
||||
* same as rgba for amane
|
||||
*/
|
||||
pub fn argb_to_rgba(argb: &[u8]) -> Vec<u8> {
|
||||
argb.chunks_exact(4).flat_map(|pixel| [pixel[1], pixel[2], pixel[3], pixel[0]]).collect()
|
||||
}
|
||||
|
||||
fn chunk(png: &mut Vec<u8>, kind: &[u8; 4], data: &[u8]) {
|
||||
png.extend_from_slice(&(data.len() as u32).to_be_bytes());
|
||||
png.extend_from_slice(kind);
|
||||
png.extend_from_slice(data);
|
||||
|
||||
let mut checked = kind.to_vec();
|
||||
checked.extend_from_slice(data);
|
||||
|
||||
png.extend_from_slice(&crc32(&checked).to_be_bytes());
|
||||
}
|
||||
|
||||
// zlib around deflate's stored blocks, at most 65535 bytes each
|
||||
fn zlib_stored(data: &[u8]) -> Vec<u8> {
|
||||
let mut out = vec![0x78, 0x01];
|
||||
let blocks: Vec<&[u8]> = if data.is_empty() { vec![&[]] } else { data.chunks(65_535).collect() };
|
||||
|
||||
for (index, block) in blocks.iter().enumerate() {
|
||||
let last = index + 1 == blocks.len();
|
||||
let length = block.len() as u16;
|
||||
|
||||
out.push(u8::from(last));
|
||||
out.extend_from_slice(&length.to_le_bytes());
|
||||
out.extend_from_slice(&(!length).to_le_bytes());
|
||||
out.extend_from_slice(block);
|
||||
}
|
||||
|
||||
out.extend_from_slice(&adler32(data).to_be_bytes());
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn crc32(data: &[u8]) -> u32 {
|
||||
let mut crc = 0xffff_ffff_u32;
|
||||
|
||||
for byte in data {
|
||||
crc ^= u32::from(*byte);
|
||||
|
||||
for _ in 0..8 {
|
||||
crc = if crc & 1 == 1 { (crc >> 1) ^ 0xedb8_8320 } else { crc >> 1 };
|
||||
}
|
||||
}
|
||||
|
||||
!crc
|
||||
}
|
||||
|
||||
fn adler32(data: &[u8]) -> u32 {
|
||||
let (mut a, mut b) = (1_u32, 0_u32);
|
||||
|
||||
for byte in data {
|
||||
a = (a + u32::from(*byte)) % 65_521;
|
||||
b = (b + a) % 65_521;
|
||||
}
|
||||
|
||||
(b << 16) | a
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn checksums_and_layout() {
|
||||
// the well-known values: IEND's crc, and adler32 of "Wikipedia"
|
||||
assert_eq!(crc32(b"IEND"), 0xae42_6082);
|
||||
assert_eq!(adler32(b"Wikipedia"), 0x11e6_0398);
|
||||
|
||||
let png = encode(2, 1, &[255, 0, 0, 255, 0, 0, 255, 128]);
|
||||
|
||||
assert_eq!(&png[..8], &[0x89, b'P', b'N', b'G', b'\r', b'\n', 0x1a, b'\n']);
|
||||
assert_eq!(&png[12..16], b"IHDR");
|
||||
assert_eq!(&png[png.len() - 12..], &[0, 0, 0, 0, b'I', b'E', b'N', b'D', 0xae, 0x42, 0x60, 0x82]);
|
||||
|
||||
// one row: filter byte, then the pixels, in one final stored block
|
||||
let idat = &png[33 + 8..];
|
||||
assert_eq!(&idat[..2], &[0x78, 0x01]);
|
||||
assert_eq!(&idat[2..7], &[1, 9, 0, 0xf6, 0xff]);
|
||||
|
||||
assert_eq!(argb_to_rgba(&[128, 1, 2, 3]), [1, 2, 3, 128]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user