前言
前段時間在B站發現了一個很可愛的鼠標主題, 出處在這裡, 解壓之後自然發現只能在Windows下使用, 雖然已經在Windows下更換使用了一段時間, 但是覺得沒能在自己的Linux主力機上使用還是有點可惜, 於是打算自己進行移植.
1. 直接轉換
我在深度論壇上發現了這樣一篇文章, 並照着它的步驟進行了移植, 這篇文章寫得很好, 整個過程都相當正常的走完, 成功的將.ani文件移植成X11光標文件. 可當我正打算使用的時候卻發現有一點不對勁.

相比之下, 正常的KDE桌面X11光標大小都在12px-72px之間, 而這個光標主題大小居然高達160px, 大的根本沒法用.
2. 使用Greenfish Icon Editor Pro進行編輯之後安裝
我在嘗試使用GIMP和RealWorld Cursor Editor編輯光標文件後均以失敗告終, 因為GIMP在修改圖像大小時會改變圖像性質, 不能保存為X11光標, RealWorld Cursor Editor改變光標大小的步驟十分繁瑣, 對於單個光標文件需要逐個修改7-8幀的動態光標來說這個工作量已經不太可能一個人完成.
最後我找到了一個更好的圖像編輯器, 能夠相對更方便的編輯光標文件, 並且可以雙向轉換Windows和Linux的光標主題文件
安裝方式:deb下載地址, Arch可以直接在Aur找到, 包名為 gfie-bin.
2.1 修改大小
安裝完成後打開一個ani文件, 右鍵點擊頁, 選擇頁屬性

在其中自定義大小之後點確定保存

另存為X11光標文件

2.2 創建鏈接映射
Windows動態光標只有很少的文件數量, 但是一個X11光標主題卻包含大量不同名字的光標文件, 其中大部分是鏈接文件. 此時需要對照一個主題對原有的文件進行逐一鏈接. 我對照的是默認的breeze主題, 並事先給原有的光標文件改了名字.

此時建立一個這樣的目錄
.
├── cursors/
├── index.theme(主題引導文件)
├── *make(鏈接腳本文件)
└── origin/
└── 原始的光標文件
按照系統中已有的主題編寫鏈接腳本, 以下是鏈接腳本的一部分, 完整的鏈接腳本可以在項目的github主頁獲取
#! /usr/bin/bash
#copy orgin files to cursors dir
cp ./origin/* ./cursors/
#make hard links
ln ./cursors/waiting ./cursors/00000000000000020006000e7e9ffc3f # progress
ln ./cursors/vertical_resize ./cursors/00008160000006810000408080010102 # size_ver
ln ./cursors/forbidden ./cursors/03b6e0fcb3499374a867c041f52298f0 # circle
ln ./cursors/waiting ./cursors/08e8e1c95fe2fc01f976f1e063a24ccd # progress
ln ./cursors/normal ./cursors/1081e37283d90000800003c07f3ef6bf # copy
ln ./cursors/link ./cursors/3085a0e285430894940527032f8b26df # alias
ln ./cursors/waiting ./cursors/3ecb610c1bf2410f44200f48c40d3599 # progress
ln ./cursors/normal ./cursors/4498f0e0c1937ffe01fd06f973665830 # dnd-move
ln ./cursors/person_select ./cursors/5c6cd98b3f3ebcb1f9c7f1c204630408 # help
ln ./cursors/normal ./cursors/6407b0e94181790501fd1e167b474872 # copy
ln ./cursors/link ./cursors/640fb0e74195791501fd1ed57b41487f # alias
ln ./cursors/normal ./cursors/9081237383d90e509aa00f00170e968f # dnd-move
ln ./cursors/link ./cursors/9d800788f1b08800ae810202380a0822 # pointer
ln ./cursors/link ./cursors/a2a266d0498c3104214a47bd64ab0fc8 # alias
ln ./cursors/link ./cursors/alias
ln ./cursors/move ./cursors/all-scroll
ln ./cursors/normal ./cursors/arrow # default
ln ./cursors/normal ./cursors/b66166c04f8c3109214a4fbd64a50fc8 # copy
ln ./cursors/diagonal_resize2 ./cursors/bottom_left_corner
ln ./cursors/diagonal_resize1 ./cursors/bottom_right_corner
ln ./cursors/vertical_resize ./cursors/bottom_side
ln ./cursors/move ./cursors/cell
ln ./cursors/point_hand ./cursors/center_ptr
ln ./cursors/forbidden ./cursors/circle # not-allowed
ln ./cursors/normal ./cursors/closedhand # dnd-move
ln ./cursors/handwriting ./cursors/color-picker
ln ./cursors/horizonal_resize ./cursors/col-resize
ln ./cursors/normal ./cursors/context-menu
ln ./cursors/normal ./cursors/copy
運行腳本就能在cursors目錄中創建完整的主題文件, 此時將這個文件夾放在/usr/share/icons/下, 就能在KDE設置中看到光標主題.
二編
我的確沒想到會有人來請求做另一版本的光標. 並且過了一年, 開始使用 Niri 作為視窗管理器之後由於老版本光標沒法調整大小, 已經 棄用一段時間. 既然有人請求, 那我就恰好做下一版.
設計思路
一年前, 我是直接手工的轉換尺寸, 並手動編寫腳本來做映射. 這樣的成本在今天看來是難以接受的, 我不再有閒功夫來一個個去做轉換.
因此, 這一次我要尋求足夠自動化的解決方案. 這個任務可以被分成多個階段完成, 很適合使用流水線 (pipeline) 的方式實現. 我們
可以這樣描述一個 foo.ani 的處理流程:
-
Extract
foo.ani文件裡面有多個幀的圖片數據, 以及記錄了熱點信息和時間信息. 然而生成 xcursor 的程序xcursorgen非常簡陋, 只能讀取 png 文件和事先寫好的配置文件, 因此需要進行解包. 將裡面的文件和數據分離, 並讀取信息.Note
熱點信息是光標的聚焦點座標, 也就是計算點擊事件的接觸點在整個貼圖上的位置.
-
Resize
我們需要生成多種尺寸的光標, 就需要對光標進行 resize, 同時這一步還需要把解包出的各幀 ico 圖像轉換成 png 格式.
-
GenIn
現在所有材料已經準備完畢, 需要根據解包出的信息生成
xcursor.in. -
GenXcur
調用
xcursorgen程序, 根據解包出的圖片和生成的配置文件生成光標文件. -
Alias
這是整個流水線的最後一步, 由於 xcursor 和 windows 的光標並不是一一對應, 所以需要一個映射方案. 好消息是所有 x 光標 都能被映射到 windows 光標的一個形態. 壞消息是我不知道映射規則, 這只能靠看著形態硬排. 我為了方便修改, 寫了一個 json 格式的配置, 用來方便的儲存映射表.
{ "help": [ "help" ], "work": [ "3ecb610c1bf2410f44200f48c40d3599", "08e8e1c95fe2fc01f976f1e063a24ccd", "00000000000000020006000e7e9ffc3f", "half-busy", "left_ptr_watch", "progress", "waiting" ], "busy": [ "busy", "wait", "watch" ], "cross": [ "cross", "crosshair", "tcross", "not-allowed" ], "text": [ "ibeam", "text", "xterm" ], "handwrt": [ "color-picker", "draft", "handwriting", "pencil" ], ... }這樣在修改映射方案的時候不必頻繁的修改代碼, 也可以寫多套映射表.
實現方案
我在第一步就卡住了, 因為目前並沒有庫能夠解析 windows 的 ani 格式文件. Python 的 Pillow
只能解析 cur 文件, 且不能獲取熱點信息. 但是我在 arch 論壇上找到了一篇文章
於是我也用 c 寫了一個程序, 來解析 ani 文件, 並且能夠以 json 格式輸出信息. 源碼位於這個倉庫

我使用 python 腳本來驅動整個過程, 源碼位於 Ani2Xcur.
TODO
現在的這一套代碼仍然不足以轉換所有的動態光標包, 因為很多是混雜 ico 文件的. 我現在還不能從 ico 文件中獲取
熱點信息. 而且很多 *.ani 文件在解析過程中會有斷言錯誤.
我為了方便做了很多假設, 比如假設幀數量等於序列的步驟數量, 實際上可能並不相等, 可能複用幀.
歡迎大家對我的代碼做出改進, 使其能夠支持更廣泛的光標轉換.