summaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authorLibravatarLarge Libravatar memdmp <memdmpmemewarenet>2025-01-13 16:54:34 +0100
committerLibravatarLarge Libravatar memdmp <memdmpmemewarenet>2025-01-13 16:54:34 +0100
commit6e3b3c8013e6d8814dbf70c854e55d062bedbdf4 (patch)
tree5b07a6d26c349293bc61a71a32d6e368fed11c4e /src/main.rs
downloadbibata-cursor-cli-6e3b3c8013e6d8814dbf70c854e55d062bedbdf4.tar.gz
bibata-cursor-cli-6e3b3c8013e6d8814dbf70c854e55d062bedbdf4.tar.bz2
bibata-cursor-cli-6e3b3c8013e6d8814dbf70c854e55d062bedbdf4.tar.lz
bibata-cursor-cli-6e3b3c8013e6d8814dbf70c854e55d062bedbdf4.zip

chore: initial commit

HEADmaster
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..fed8090
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,199 @@
+use std::{fs, io::Result, os::unix};
+
+use serde::{Deserialize, Serialize};
+use tiny_skia::Pixmap;
+use xcursor::{ImageInfo, XCursorEncoder};
+
+mod xcursor;
+
+#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
+struct Xy {
+ x: u32,
+ y: u32,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+struct BibataPlatformNames {
+ windows: Option<String>,
+ xorg: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+struct BibataFrame {
+ idx: i32,
+ id: String,
+ url: String,
+ delay: u32,
+ sha512: String,
+ svg: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+struct BibataCursor {
+ animated: bool,
+ id: String,
+ name: String,
+ aliases: Vec<String>,
+ hot: Xy,
+ frames: Vec<BibataFrame>,
+ frame_count: i32,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct BibataCursorStyle {
+ kind: String,
+ cursors: Vec<BibataCursor>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct BibataColourScheme<'a> {
+ pub base: &'a str,
+ pub outline: &'a str,
+ pub watch_background: &'a str,
+ pub watch_color: Vec<&'a str>,
+ /** Value between 0 and 1 - Bibata defaults to 0.8 */
+ pub watch_opacity: f32,
+}
+
+fn vector_to_pixmap(svg_data: &[u8]) -> Pixmap {
+ let tree = {
+ let mut opt = usvg::Options::default();
+ // Get file's absolute directory.
+ // opt.resources_dir = std::fs::canonicalize(&args[1])
+ // .ok()
+ // .and_then(|p| p.parent().map(|p| p.to_path_buf()));
+
+ opt.fontdb_mut().load_system_fonts();
+
+ usvg::Tree::from_data(svg_data, &opt).unwrap()
+ };
+
+ let pixmap_size = tree.size().to_int_size();
+ let mut pixmap = tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap();
+ resvg::render(&tree, tiny_skia::Transform::default(), &mut pixmap.as_mut());
+ pixmap
+}
+fn resize_cursvg(svg: &str, new_size: &u16) -> String {
+ svg.to_string().replace(
+ "width=\"256\" height=\"256\"",
+ format!("width=\"{new_size}\" height=\"{new_size}\"").as_str(),
+ )
+}
+fn apply_colours(unpatched_svg: String, colours: &BibataColourScheme) -> String {
+ if colours.watch_color.len() != 4 {
+ panic!("Must specify 4 watch colours!");
+ };
+ unpatched_svg
+ .replace("#00FF00", colours.base)
+ .replace("#0000FF", colours.outline)
+ .replace("#FF0000", colours.watch_background)
+ .replace("#F05024", colours.watch_color[0])
+ .replace("#FCB813", colours.watch_color[1])
+ .replace("#7EBA41", colours.watch_color[2])
+ .replace("#32A0DA", colours.watch_color[3])
+ .replace(
+ "fill-opacity=\"0.8\"",
+ format!("fill-opacity=\"{}\"", colours.watch_opacity).as_str(),
+ )
+}
+fn process_xcursor(
+ cursor: BibataCursor,
+ desired_colours: BibataColourScheme,
+ desired_sizes: Vec<u16>,
+ time_scale: u32,
+) -> Vec<u8> {
+ let mut image_info_vec: Vec<ImageInfo> = Vec::new();
+ for cursor_frame in cursor.frames {
+ for desired_size in &desired_sizes {
+ let svg_data = apply_colours(
+ resize_cursvg(&cursor_frame.svg, desired_size),
+ &desired_colours,
+ );
+ let pixmap = vector_to_pixmap(svg_data.as_bytes());
+
+ let pixels = {
+ let pixels = pixmap.pixels();
+ let mut otherpixels: Vec<u8> = Vec::new();
+ for elem in pixels.iter() {
+ otherpixels.push(elem.red());
+ otherpixels.push(elem.green());
+ otherpixels.push(elem.blue());
+ otherpixels.push(elem.alpha());
+ }
+ otherpixels
+ };
+ let image_info = ImageInfo {
+ r#type: *desired_size as u32,
+ subtype: *desired_size as u32,
+ width: pixmap.width().try_into().unwrap(),
+ height: pixmap.height().try_into().unwrap(),
+ xhot: (cursor.hot.x * pixmap.width() / 256).try_into().unwrap(),
+ yhot: (cursor.hot.y * pixmap.height() / 256).try_into().unwrap(),
+ data: pixels.try_into().unwrap(),
+ delay: (time_scale / cursor_frame.delay).try_into().unwrap(),
+ };
+ image_info_vec.push(image_info);
+ }
+ }
+ let mut encoder = XCursorEncoder::new(image_info_vec);
+ encoder.pack()
+}
+
+fn main() -> Result<()> {
+ let cursor_style: Vec<BibataCursorStyle> =
+ serde_json::from_str(include_str!("../.cursors.min")).unwrap();
+
+ // TODO: proper arg parsing, handling shit cutely, not hardcoding everything, etc
+ if fs::exists("xcursor-out")? {
+ fs::remove_dir_all("xcursor-out")?;
+ }
+ fs::create_dir("xcursor-out")?;
+ for style in cursor_style {
+ fs::create_dir(format!("xcursor-out/{}", style.kind))?;
+ for cursor in style.cursors {
+ println!("xcursor-out/{}/{}", style.kind, cursor.name.clone());
+ // shove these files in a dir named `cursors` inside the theme dir
+ // the theme dir should have these 2 files in it:
+ //
+ // cursor.theme:
+ // [Icon Theme]
+ // Name=Bibata
+ // Inherits="Bibata"
+ //
+ // index.theme:
+ // [Icon Theme]
+ // Name=Bibata
+ // Comment=Generated Bibata Theme
+ // Inherits="hicolor"
+ //
+ // the theme dir should be ~/.local/share/icons/Bibata
+ //
+ // to apply on gnome: gsettings set org.gnome.desktop.interface cursor-theme Bibata
+ fs::write(
+ format!("xcursor-out/{}/{}", style.kind, cursor.name.clone()),
+ process_xcursor(
+ cursor.clone(),
+ BibataColourScheme {
+ base: "#ffffff",
+ outline: "#000000",
+ watch_background: "#ffffff",
+ watch_color: vec!["#ff5e9c", "#65c7e0", "#f69a59", "#6789f0"],
+ watch_opacity: 1.0,
+ },
+ vec![
+ 4, 6, 8, 10, 12, 16, 18, 24, 30, 32, 36, 42, 48, 54, 60, 66, 72, 128,
+ ],
+ 750,
+ ),
+ )?;
+ for alias in cursor.aliases {
+ unix::fs::symlink(
+ cursor.name.clone(),
+ format!("xcursor-out/{}/{}", style.kind, alias),
+ )?;
+ }
+ }
+ }
+
+ Ok(())
+}