cursors.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #!/usr/bin/env python
  2. # License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>
  3. import os
  4. import subprocess
  5. import sys
  6. from typing import List
  7. if __name__ == '__main__' and not __package__:
  8. import __main__
  9. __main__.__package__ = 'gen'
  10. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  11. from .key_constants import patch_file
  12. # References for these names:
  13. # CSS:choices_for_{option.name} https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
  14. # XCursor: https://tronche.com/gui/x/xlib/appendix/b/ + Absolute chaos
  15. # Wayland: https://wayland.app/protocols/cursor-shape-v1
  16. # Cocoa: https://developer.apple.com/documentation/appkit/nscursor + secret apple selectors + SDL_cocoamouse.m
  17. # kitty_names CSS_name XCursor_names Wayland_name Cocoa_name
  18. cursors = '''\
  19. arrow default default,!left_ptr default arrowCursor
  20. beam,text text text,!xterm,ibeam text IBeamCursor
  21. pointer,hand pointer pointing_hand,pointer,!hand2,hand pointer pointingHandCursor
  22. help help help,!question_arrow,whats_this help help:arrowCursor
  23. wait wait wait,!clock,watch wait busybutclickable:arrowCursor
  24. progress progress progress,half-busy,left_ptr_watch progress busybutclickable:arrowCursor
  25. crosshair crosshair crosshair,!tcross crosshair crosshairCursor
  26. cell cell cell,!plus,!cross cell cell:crosshairCursor
  27. vertical-text vertical-text vertical-text vertical-text IBeamCursorForVerticalLayout
  28. move move move,!fleur,pointer-move move move:openHandCursor
  29. e-resize e-resize e-resize,!right_side e_resize resizeRightCursor
  30. ne-resize ne-resize ne-resize,!top_right_corner ne_resize resizenortheast:_windowResizeNorthEastSouthWestCursor
  31. nw-resize nw-resize nw-resize,!top_left_corner nw_resize resizenorthwest:_windowResizeNorthWestSouthEastCursor
  32. n-resize n-resize n-resize,!top_side n_resize resizeUpCursor
  33. se-resize se-resize se-resize,!bottom_right_corner se_resize resizesoutheast:_windowResizeNorthWestSouthEastCursor
  34. sw-resize sw-resize sw-resize,!bottom_left_corner sw_resize resizesouthwest:_windowResizeNorthEastSouthWestCursor
  35. s-resize s-resize s-resize,!bottom_side s_resize resizeDownCursor
  36. w-resize w-resize w-resize,!left_side w_resize resizeLeftCursor
  37. ew-resize ew-resize ew-resize,!sb_h_double_arrow,split_h ew_resize resizeLeftRightCursor
  38. ns-resize ns-resize ns-resize,!sb_v_double_arrow,split_v ns_resize resizeUpDownCursor
  39. nesw-resize nesw-resize nesw-resize,size_bdiag,size-bdiag nesw_resize _windowResizeNorthEastSouthWestCursor
  40. nwse-resize nwse-resize nwse-resize,size_fdiag,size-fdiag nwse_resize _windowResizeNorthWestSouthEastCursor
  41. zoom-in zoom-in zoom-in,zoom_in zoom_in zoomin:arrowCursor
  42. zoom-out zoom-out zoom-out,zoom_out zoom_out zoomout:arrowCursor
  43. alias alias dnd-link alias dragLinkCursor
  44. copy copy dnd-copy copy dragCopyCursor
  45. not-allowed not-allowed not-allowed,forbidden,crossed_circle not_allowed operationNotAllowedCursor
  46. no-drop no-drop no-drop,dnd-no-drop no_drop operationNotAllowedCursor
  47. grab grab grab,openhand,!hand1 grab openHandCursor
  48. grabbing grabbing grabbing,closedhand,dnd-none grabbing closedHandCursor
  49. '''
  50. def main(args: List[str]=sys.argv) -> None:
  51. glfw_enum = []
  52. css_names = []
  53. glfw_xc_map = {}
  54. glfw_xfont_map = []
  55. kitty_to_enum_map = {}
  56. enum_to_glfw_map = {}
  57. enum_to_css_map = {}
  58. glfw_cocoa_map = {}
  59. glfw_css_map = {}
  60. css_to_enum = {}
  61. xc_to_enum = {}
  62. glfw_wayland = {}
  63. for line in cursors.splitlines():
  64. line = line.strip()
  65. if line:
  66. names_, css, xc_, wayland, cocoa = line.split()
  67. names, xc = names_.split(','), xc_.split(',')
  68. base = css.replace('-', '_').upper()
  69. glfw_name = 'GLFW_' + base + '_CURSOR'
  70. enum_name = base + '_POINTER'
  71. enum_to_glfw_map[enum_name] = glfw_name
  72. enum_to_css_map[enum_name] = css
  73. glfw_css_map[glfw_name] = css
  74. css_to_enum[css] = enum_name
  75. css_names.append(css)
  76. glfw_wayland[glfw_name] = 'WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_' + wayland.replace('-', '_').upper()
  77. for n in names:
  78. kitty_to_enum_map[n] = enum_name
  79. glfw_enum.append(glfw_name)
  80. glfw_xc_map[glfw_name] = ', '.join(f'''"{x.replace('!', '')}"''' for x in xc)
  81. for x in xc:
  82. if x.startswith('!'):
  83. glfw_xfont_map.append(f"case {glfw_name}: return set_cursor_from_font(cursor, {'XC_' + x[1:]});")
  84. break
  85. else:
  86. items = tuple('"' + x.replace('!', '') + '"' for x in xc)
  87. glfw_xfont_map.append(f'case {glfw_name}: return try_cursor_names(cursor, {len(items)}, {", ".join(items)});')
  88. for x in xc:
  89. x = x.lstrip('!')
  90. xc_to_enum[x] = enum_name
  91. parts = cocoa.split(':', 1)
  92. if len(parts) == 1:
  93. if parts[0].startswith('_'):
  94. glfw_cocoa_map[glfw_name] = f'U({glfw_name}, {parts[0]});'
  95. else:
  96. glfw_cocoa_map[glfw_name] = f'C({glfw_name}, {parts[0]});'
  97. else:
  98. glfw_cocoa_map[glfw_name] = f'S({glfw_name}, {parts[0]}, {parts[1]});'
  99. for x, v in xc_to_enum.items():
  100. if x not in css_to_enum:
  101. css_to_enum[x] = v
  102. glfw_enum.append('GLFW_INVALID_CURSOR')
  103. patch_file('glfw/glfw3.h', 'mouse cursor shapes', '\n'.join(f' {x},' for x in glfw_enum))
  104. patch_file('glfw/wl_window.c', 'glfw to wayland mapping', '\n'.join(f' C({g}, {x});' for g, x in glfw_wayland.items()))
  105. patch_file('glfw/wl_window.c', 'glfw to xc mapping', '\n'.join(f' C({g}, {x});' for g, x in glfw_xc_map.items()))
  106. patch_file('glfw/x11_window.c', 'glfw to xc mapping', '\n'.join(f' {x}' for x in glfw_xfont_map))
  107. patch_file('kitty/data-types.h', 'mouse shapes', '\n'.join(f' {x},' for x in enum_to_glfw_map))
  108. patch_file(
  109. 'kitty/options/definition.py', 'pointer shape names', '\n'.join(f' {x!r},' for x in kitty_to_enum_map),
  110. start_marker='# ', end_marker='',
  111. )
  112. patch_file('kitty/options/to-c.h', 'pointer shapes', '\n'.join(
  113. f' else if (strcmp(name, "{k}") == 0) return {v};' for k, v in kitty_to_enum_map.items()))
  114. patch_file('kitty/glfw.c', 'enum to glfw', '\n'.join(
  115. f' case {k}: set_glfw_mouse_cursor(w, {v}); break;' for k, v in enum_to_glfw_map.items()))
  116. patch_file('kitty/glfw.c', 'name to glfw', '\n'.join(
  117. f' if (strcmp(name, "{k}") == 0) return {enum_to_glfw_map[v]};' for k, v in kitty_to_enum_map.items()))
  118. patch_file('kitty/glfw.c', 'glfw to css', '\n'.join(
  119. f' case {g}: return "{c}";' for g, c in glfw_css_map.items()
  120. ))
  121. patch_file('kitty/screen.c', 'enum to css', '\n'.join(
  122. f' case {e}: ans = "{c}"; break;' for e, c in enum_to_css_map.items()))
  123. patch_file('kitty/screen.c', 'css to enum', '\n'.join(
  124. f' else if (strcmp("{c}", css_name) == 0) s = {e};' for c, e in css_to_enum.items()))
  125. patch_file('glfw/cocoa_window.m', 'glfw to cocoa', '\n'.join(f' {x}' for x in glfw_cocoa_map.values()))
  126. patch_file('docs/pointer-shapes.rst', 'list of shape css names', '\n'.join(
  127. f'#. {x}' if x else '' for x in [''] + sorted(css_names) + ['']), start_marker='.. ', end_marker='')
  128. patch_file('tools/tui/loop/mouse.go', 'pointer shape enum', '\n'.join(
  129. f'\t{x} PointerShape = {i}' for i, x in enumerate(enum_to_glfw_map)), start_marker='// ', end_marker='')
  130. patch_file('tools/tui/loop/mouse.go', 'pointer shape tostring', '\n'.join(
  131. f'''\tcase {x}: return "{x.lower().rpartition('_')[0].replace('_', '-')}"''' for x in enum_to_glfw_map), start_marker='// ', end_marker='')
  132. patch_file('tools/cmd/mouse_demo/main.go', 'all pointer shapes', '\n'.join(
  133. f'\tloop.{x},' for x in enum_to_glfw_map), start_marker='// ', end_marker='')
  134. subprocess.check_call(['glfw/glfw.py'])
  135. if __name__ == '__main__':
  136. import runpy
  137. m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))
  138. m['main']([sys.executable, 'cursors'])