summaryrefslogtreecommitdiffstats
path: root/src/xcursor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/xcursor.rs')
-rw-r--r--src/xcursor.rs101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/xcursor.rs b/src/xcursor.rs
new file mode 100644
index 0000000..ff6c3d6
--- /dev/null
+++ b/src/xcursor.rs
@@ -0,0 +1,101 @@
+#[derive(Debug, Clone)]
+pub struct ImageInfo {
+ pub r#type: u32,
+ pub subtype: u32,
+ pub width: u32,
+ pub height: u32,
+ pub xhot: u32,
+ pub yhot: u32,
+ pub delay: u32,
+ pub data: Vec<u8>,
+}
+
+pub struct XCursorEncoder {
+ pub images: Vec<ImageInfo>,
+}
+
+const MAGIC: [u8; 4] = [0x58, 0x63, 0x75, 0x72];
+const IMAGE_HEADER: [u8; 4] = [0x02, 0x00, 0xfd, 0xff];
+
+impl XCursorEncoder {
+ pub fn new(images: Vec<ImageInfo>) -> Self {
+ XCursorEncoder { images }
+ }
+
+ fn image_pos(&self, position: usize) -> usize {
+ let mut val = 12 * self.images.len() + 16;
+ for n in 0..position {
+ val += 36 + ((self.images[n].width as usize) * (self.images[n].height as usize) * 4)
+ }
+ val
+ }
+
+ pub fn pack(&mut self) -> Vec<u8> {
+ fn insert_bytes(data: &mut Vec<u8>, new_data: impl IntoIterator<Item = u8>) {
+ for int in new_data {
+ data.push(int);
+ }
+ }
+ fn insert_int(data: &mut Vec<u8>, int: u32) {
+ data.push((int & 0xff) as u8);
+ data.push(((int >> 8) & 0xff) as u8);
+ data.push(((int >> 16) & 0xff) as u8);
+ data.push(((int >> 24) & 0xff) as u8);
+ }
+
+ let mut data: Vec<u8> = Vec::new();
+
+ // File Header
+ {
+ // MAGIC string ("Xcur")
+ insert_bytes(&mut data, MAGIC);
+ // CARD32 bytes in this header
+ insert_int(&mut data, 16);
+ // CARD32 file version
+ insert_int(&mut data, 1);
+ // CARD32 number of toc entries
+ insert_int(&mut data, self.images.len().try_into().unwrap());
+ }
+
+ // ntoc entries
+ {
+ let mut img_idx: usize = 0;
+ for img in self.images.clone().into_iter() {
+ // Some header
+ insert_bytes(&mut data, IMAGE_HEADER);
+ // CARD32 type-specific label - size for images
+ insert_int(&mut data, img.r#type);
+ // CARD32 absolute byte position of table in file
+ insert_int(&mut data, self.image_pos(img_idx).try_into().unwrap());
+
+ img_idx += 1;
+ }
+ }
+
+ // images
+ {
+ for img in self.images.clone().into_iter() {
+ // Header Size (36)
+ insert_int(&mut data, 36);
+ // Image Type
+ insert_bytes(&mut data, IMAGE_HEADER);
+ // Subtype, for nominal size
+ insert_int(&mut data, img.subtype);
+ // Version
+ insert_int(&mut data, 1);
+ // Image dimensions
+ insert_int(&mut data, img.width);
+ insert_int(&mut data, img.height);
+ // Cursor positioning
+ insert_int(&mut data, img.xhot);
+ insert_int(&mut data, img.yhot);
+ // Milliseconds till next frame
+ insert_int(&mut data, img.delay);
+ // Raw image data
+ insert_bytes(&mut data, img.data);
+ }
+ }
+
+ data
+ }
+}