|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import os |
| 4 | +import math |
| 5 | +import subprocess |
| 6 | +import logging |
| 7 | + |
| 8 | +# Configure logging |
| 9 | +logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s') |
| 10 | + |
| 11 | +# Define the nominal size and a dictionary for cursor names and their hotspots (24x24) |
| 12 | +nominal_size = 24 |
| 13 | +cursors = { |
| 14 | + "alias": (10, 10),# |
| 15 | + "all-resize": (11, 11),# |
| 16 | + "all-scroll": (11, 11),# |
| 17 | + "cell": (11, 11),# |
| 18 | + "col-resize": (11, 11),# |
| 19 | + "context-menu": (2, 1),# |
| 20 | + "copy": (2, 1),# |
| 21 | + "crosshair": (11, 11),# |
| 22 | + "default": (3, 3),# |
| 23 | + "e-resize": (11, 11),# |
| 24 | + "ew-resize": (11, 11),# |
| 25 | + "grab": (11, 2),# |
| 26 | + "grabbing": (11, 9),# |
| 27 | + "help": (11, 19),# |
| 28 | + "ne-resize": (11, 11),# |
| 29 | + "nesw-resize": (11, 11),# |
| 30 | + "no-drop": (2, 1),# |
| 31 | + "not-allowed": (11, 11),# |
| 32 | + "n-resize": (11, 11),# |
| 33 | + "ns-resize": (11, 11),# |
| 34 | + "nw-resize": (11, 11),# |
| 35 | + "nwse-resize": (11, 11),# |
| 36 | + "pointer": (8, 3),# |
| 37 | + "progress": {"hotspot": (2, 1), "frames": 60, "duration": 16},# |
| 38 | + "row-resize": (11, 11),# |
| 39 | + "se-resize": (11, 11),# |
| 40 | + "s-resize": (11, 11),# |
| 41 | + "sw-resize": (11, 11),# |
| 42 | + "text": (10, 11),# |
| 43 | + "vertical-text": (11, 10),# |
| 44 | + "text": (10, 11),# |
| 45 | + "wait": {"hotspot": (11, 11), "frames": 60, "duration": 16},# |
| 46 | + "w-resize": (11, 11),# |
| 47 | + "zoom-in": (10, 10),# |
| 48 | + "zoom-out": (10, 10),# |
| 49 | + "X_cursor": (11, 11) |
| 50 | +} |
| 51 | + |
| 52 | +# Define the sizes we want to scale to |
| 53 | +sizes = [24, 30, 36, 48, 72, 96] |
| 54 | + |
| 55 | +# Base directories |
| 56 | +png_dir = './pngs' # Updated to use "pngs" |
| 57 | +output_dir = '../../Yaru/cursors/' |
| 58 | + |
| 59 | +# Function to generate .in file content |
| 60 | +def generate_in_file_content(cursor_name, cursor_info): |
| 61 | + in_content = [] |
| 62 | + is_animated = isinstance(cursor_info, dict) |
| 63 | + |
| 64 | + frame_range = range(1, cursor_info["frames"] + 1) if is_animated else [1] |
| 65 | + hotspot = cursor_info["hotspot"] if is_animated else cursor_info |
| 66 | + |
| 67 | + for frame in frame_range: |
| 68 | + for size in sizes: |
| 69 | + file_name = f"{cursor_name}_{frame:04d}.png" if is_animated else f"{cursor_name}.png" |
| 70 | + png_file_relative = f"pngs/{size}x{size}/{file_name}" |
| 71 | + png_file = os.path.join(png_dir, f"{size}x{size}", file_name) |
| 72 | + |
| 73 | + if not os.path.exists(png_file): |
| 74 | + logging.error(f"PNG file not found: {png_file}") |
| 75 | + continue |
| 76 | + |
| 77 | + hotspot_x = math.floor(hotspot[0] * size / nominal_size) |
| 78 | + hotspot_y = math.floor(hotspot[1] * size / nominal_size) |
| 79 | + |
| 80 | + entry = f"{size} {hotspot_x} {hotspot_y} {png_file_relative}" |
| 81 | + if is_animated: |
| 82 | + entry += f" {cursor_info['duration']}" |
| 83 | + in_content.append(entry + "\n") |
| 84 | + |
| 85 | + return ''.join(in_content) |
| 86 | + |
| 87 | +# Function to generate all .in files and call xcursorgen |
| 88 | +def generate_cursors(): |
| 89 | + for cursor_name, cursor_info in cursors.items(): |
| 90 | + logging.info(f"Processing cursor: {cursor_name}") |
| 91 | + |
| 92 | + # Generate the full .in file content |
| 93 | + in_file_content = generate_in_file_content(cursor_name, cursor_info) |
| 94 | + in_file_path = os.path.join(png_dir, f'{cursor_name}.in') |
| 95 | + |
| 96 | + # Write the .in file in ./pngs/ directory |
| 97 | + try: |
| 98 | + with open(in_file_path, 'w') as in_file: |
| 99 | + in_file.write(in_file_content) |
| 100 | + logging.info(f"Generated .in file: {in_file_path}") |
| 101 | + except Exception as e: |
| 102 | + logging.error(f"Error writing .in file: {in_file_path} - {str(e)}") |
| 103 | + continue |
| 104 | + |
| 105 | + # Call xcursorgen to generate the cursor file |
| 106 | + cursor_output = os.path.join(output_dir, cursor_name) |
| 107 | + command = ['xcursorgen', in_file_path, cursor_output] |
| 108 | + logging.debug(f"Running command: {' '.join(command)}") |
| 109 | + try: |
| 110 | + subprocess.run(command, check=True) |
| 111 | + logging.info(f"Generated cursor: {cursor_output}") |
| 112 | + except subprocess.CalledProcessError as e: |
| 113 | + logging.error(f"xcursorgen failed for {cursor_name}: {str(e)}") |
| 114 | + |
| 115 | +if __name__ == "__main__": |
| 116 | + # Ensure output directory exists |
| 117 | + if not os.path.exists(output_dir): |
| 118 | + logging.info(f"Creating output directory: {output_dir}") |
| 119 | + os.makedirs(output_dir) |
| 120 | + |
| 121 | + # Call the main function to generate cursors |
| 122 | + generate_cursors() |
| 123 | + |
0 commit comments