From ec5eb3690226c3c01847de827957e4f244ca9616 Mon Sep 17 00:00:00 2001 From: Guanzhong Chen Date: Sun, 4 Oct 2020 02:30:00 -0400 Subject: [PATCH] Remove dependency on xcursorgen (#3) --- .github/workflows/build.yml | 2 +- win2xcur/main/win2xcur.py | 3 -- win2xcur/writer/x11.py | 83 +++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24d28a2..199f27c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: python -m pip install --upgrade pip pip install flake8 flake8-import-order mypy wheel coverage pip install -r requirements.txt - sudo apt-get install x11-apps dmz-cursor-theme + sudo apt-get install dmz-cursor-theme - name: Lint with flake8 run: flake8 . - name: Typecheck with mypy diff --git a/win2xcur/main/win2xcur.py b/win2xcur/main/win2xcur.py index e50b644..a85f634 100644 --- a/win2xcur/main/win2xcur.py +++ b/win2xcur/main/win2xcur.py @@ -10,7 +10,6 @@ from typing import BinaryIO from win2xcur import shadow from win2xcur.parser import open_blob from win2xcur.writer import to_x11 -from win2xcur.writer.x11 import check_xcursorgen def main() -> None: @@ -37,8 +36,6 @@ def main() -> None: args = parser.parse_args() print_lock = Lock() - check_xcursorgen() - def process(file: BinaryIO) -> None: name = file.name blob = file.read() diff --git a/win2xcur/writer/x11.py b/win2xcur/writer/x11.py index 15d8085..3e68fb1 100644 --- a/win2xcur/writer/x11.py +++ b/win2xcur/writer/x11.py @@ -1,54 +1,49 @@ -import os -import subprocess -import sys -from tempfile import TemporaryDirectory +from itertools import chain +from operator import itemgetter from typing import List -from wand.image import Image - from win2xcur.cursor import CursorFrame - -xcursorgen_checked = False - - -def check_xcursorgen() -> None: - global xcursorgen_checked - if xcursorgen_checked: - return - - try: - subprocess.check_call(['xcursorgen', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - except subprocess.CalledProcessError: - raise RuntimeError('xcursorgen must be installed to create X11 cursors!') - else: - xcursorgen_checked = True +from win2xcur.parser import XCursorParser def to_x11(frames: List[CursorFrame]) -> bytes: - check_xcursorgen() + chunks = [] - counter = 0 - configs = [] - with TemporaryDirectory() as png_dir: - for frame in frames: - for cursor in frame: - name = '%d.png' % (counter,) - hx, hy = cursor.hotspot - configs.append('%d %d %d %s %d' % (cursor.image.width, hx, hy, name, int(frame.delay * 1000))) + for frame in frames: + for cursor in frame: + hx, hy = cursor.hotspot + header = XCursorParser.IMAGE_HEADER.pack( + XCursorParser.IMAGE_HEADER.size, + XCursorParser.CHUNK_IMAGE, + cursor.image.width, + 1, + cursor.image.width, + cursor.image.height, + hx, + hy, + int(frame.delay * 1000), + ) + chunks.append(( + XCursorParser.CHUNK_IMAGE, + cursor.image.width, + header + bytes(cursor.image.export_pixels(channel_map='BGRA')) + )) - image = Image(image=cursor.image) - image.save(filename=os.path.join(png_dir, name)) - counter += 1 + header = XCursorParser.FILE_HEADER.pack( + XCursorParser.MAGIC, + XCursorParser.FILE_HEADER.size, + XCursorParser.VERSION, + len(chunks), + ) - output_file = os.path.join(png_dir, 'cursor') - process = subprocess.Popen(['xcursorgen', '-p', png_dir, '-', output_file], stdin=subprocess.PIPE, - stderr=subprocess.PIPE) + offset = XCursorParser.FILE_HEADER.size + len(chunks) * XCursorParser.TOC_CHUNK.size + toc = [] + for chunk_type, chunk_subtype, chunk in chunks: + toc.append(XCursorParser.TOC_CHUNK.pack( + chunk_type, + chunk_subtype, + offset, + )) + offset += len(chunk) - _, error = process.communicate('\n'.join(configs).encode(sys.getfilesystemencoding())) - if process.wait() != 0: - raise RuntimeError('xcursorgen failed: %r' % error) - - with open(output_file, 'rb') as f: - result = f.read() - - return result + return b''.join(chain([header], toc, map(itemgetter(2), chunks)))