diff --git a/docs/changelog.rst b/docs/changelog.rst index f8980c94a..7f3044232 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -50,6 +50,8 @@ Detailed list of changes - A new mouse action ``mouse_selection word_and_line_from_point`` to select the current word under the mouse cursor and extend to end of line (:pull:`6663`) +- Allow using the full range of standard mouse cursor shapes when customizing the mouse cursor + - macOS: When running the default shell with the login program fix :file:`~/.hushlogin` not being respected when opening windows not in the home directory (:iss:`6689`) - ``kitten @ set-background-opacity`` - add a new ``--toggle`` flag to easily switch opacity between the specified value and the default (:iss:`6691`) diff --git a/gen/__main__.py b/gen/__main__.py index 8184f91fb..9945693a5 100644 --- a/gen/__main__.py +++ b/gen/__main__.py @@ -32,6 +32,9 @@ def main(args: List[str]=sys.argv) -> None: elif which == 'wcwidth': from gen.wcwidth import main main(args) + elif which == 'cursors': + from gen.cursors import main + main(args) else: raise SystemExit(f'Unknown which: {which}') diff --git a/gen/cursors.py b/gen/cursors.py new file mode 100755 index 000000000..21753aeb0 --- /dev/null +++ b/gen/cursors.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# License: GPLv3 Copyright: 2023, Kovid Goyal + +import os +import subprocess +import sys +from typing import List + +from .key_constants import patch_file + +# References for these names: +# CSS: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor +# XCursor: https://tronche.com/gui/x/xlib/appendix/b/ + Absolute chaos +# Wayland: https://wayland.app/protocols/cursor-shape-v1 +# Cocoa: https://developer.apple.com/documentation/appkit/nscursor + secret apple selectors + SDL_cocoamouse.m + +# kitty_names CSS_name XCursor_names Wayland_name Cocoa_name +cursors = '''\ +arrow default default,!left_ptr default arrowCursor +beam,text text text,!xterm,ibeam text IBeamCursor +pointer,hand pointer pointing_hand,pointer,!hand2,hand pointer pointingHandCursor +help help help,!question_arrow,whats_this help help:arrowCursor +wait wait wait,!clock,watch wait busybutclickable:arrowCursor +progress progress progress,half-busy,left_ptr_watch progress busybutclickable:arrowCursor +crosshair crosshair crosshair,!tcross crosshair crosshairCursor +vertical-text vertical-text vertical-text vertical-text IBeamCursorForVerticalLayout +move move move,!fleur,pointer-move move move:openHandCursor + +e-resize e-resize e-resize,!right_side e_resize resizeRightCursor +ne-resize ne-resize ne-resize,!top_right_corner ne_resize resizenortheast:_windowResizeNorthEastSouthWestCursor +nw-resize nw-resize nw-resize,!top_left_corner nw_resize resizenorthwest:_windowResizeNorthWestSouthEastCursor +n-resize n-resize n-resize,!top_side n_resize resizeUpCursor +se-resize se-resize se-resize,!bottom_right_corner se_resize resizesoutheast:_windowResizeNorthWestSouthEastCursor +sw-resize sw-resize sw-resize,!bottom_left_corner sw_resize resizesouthwest:_windowResizeNorthEastSouthWestCursor +s-resize s-resize s-resize,!bottom_side s_resize resizeDownCursor +w-resize w-resize w-resize,!left_side w_resize resizeLeftCursor + +ew-resize ew-resize ew-resize,!sb_h_double_arrow,split_h ew_resize resizeLeftRightCursor +ns-resize ns-resize ns-resize,!sb_v_double_arrow,split_v ns_resize resizeUpDownCursor +nesw-resize nesw-resize nesw-resize,size_bdiag,!sb_v_double_arrow nesw_resize _windowResizeNorthEastSouthWestCursor +nwse-resize nwse-resize nwse-resize,size_fdiag,!sb_h_double_arrow nwse_resize _windowResizeNorthWestSouthEastCursor + +zoom-in zoom-in zoom-in,zoom_in zoom_in zoomin:arrowCursor +zoom-out zoom-out zoom-out,zoom_out zoom_out zoomout:arrowCursor + +alias alias dnd-link alias dragLinkCursor +copy copy dnd-copy copy dragCopyCursor +not-allowed not-allowed not-allowed,forbidden,crossed_circle not_allowed operationNotAllowedCursor +no-drop no-drop no-drop,dnd-no-drop no_drop operationNotAllowedCursor +grab grab grab,openhand,!hand1 grab openHandCursor +grabbing grabbing grabbing,closedhand,dnd-none grabbing closedHandCursor +''' + + +def main(args: List[str]=sys.argv) -> None: + glfw_enum = [] + glfw_xc_map = {} + glfw_xfont_map = [] + kitty_to_enum_map = {} + enum_to_glfw_map = {} + for line in cursors.splitlines(): + line = line.strip() + if line: + names_, css, xc_, wayland, cocoa = line.split() + names, xc = names_.split(','), xc_.split(',') + base = css.replace('-', '_').upper() + glfw_name = 'GLFW_' + base + '_CURSOR' + enum_name = base + '_POINTER' + enum_to_glfw_map[enum_name] = glfw_name + for n in names: + kitty_to_enum_map[n] = enum_name + glfw_enum.append(glfw_name) + glfw_xc_map[glfw_name] = ', '.join(f'''"{x.replace('!', '')}"''' for x in xc) + for x in xc: + if x.startswith('!'): + glfw_xfont_map.append(f"case {glfw_name}: return set_cursor_from_font(cursor, {'XC_' + x[1:]});") + break + else: + items = tuple('"' + x.replace('!', '') + '"' for x in xc) + glfw_xfont_map.append(f'case {glfw_name}: return try_cursor_names(cursor, {len(items)}, {", ".join(items)});') + + glfw_enum.append('GLFW_INVALID_CURSOR') + patch_file('glfw/glfw3.h', 'mouse cursor shapes', '\n'.join(f' {x},' for x in glfw_enum)) + patch_file('glfw/wl_window.c', 'glfw to xc mapping', '\n'.join(f' C({g}, {x});' for g, x in glfw_xc_map.items())) + patch_file('glfw/x11_window.c', 'glfw to xc mapping', '\n'.join(f' {x}' for x in glfw_xfont_map)) + patch_file('kitty/data-types.h', 'mouse shapes', '\n'.join(f' {x},' for x in enum_to_glfw_map)) + patch_file( + 'kitty/options/definition.py', 'pointer shape names', '\n'.join(f' {x!r},' for x in kitty_to_enum_map), + start_marker='# ', end_marker='', + ) + patch_file('kitty/options/to-c.h', 'pointer shapes', '\n'.join( + f' else if (strcmp(name, "{k}") == 0) return {v};' for k, v in kitty_to_enum_map.items())) + patch_file('kitty/glfw.c', 'enum to glfw', '\n'.join( + f' case {k}: set_glfw_mouse_cursor(w, {v}); break;' for k, v in enum_to_glfw_map.items())) + patch_file('kitty/glfw.c', 'name to glfw', '\n'.join( + f' if (strcmp(name, "{k}") == 0) return {enum_to_glfw_map[v]};' for k, v in kitty_to_enum_map.items())) + subprocess.check_call(['glfw/glfw.py']) + + +if __name__ == '__main__': + import runpy + m = runpy.run_path(os.path.dirname(os.path.abspath(__file__))) + m['main']([sys.executable, 'cursors']) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index ae78fcc71..7e5cde34c 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1099,17 +1099,38 @@ typedef enum { * @{ */ typedef enum { - GLFW_ARROW_CURSOR, - GLFW_IBEAM_CURSOR, +/* start mouse cursor shapes (auto generated by gen-key-constants.py do not edit) */ + GLFW_DEFAULT_CURSOR, + GLFW_TEXT_CURSOR, + GLFW_POINTER_CURSOR, + GLFW_HELP_CURSOR, + GLFW_WAIT_CURSOR, + GLFW_PROGRESS_CURSOR, GLFW_CROSSHAIR_CURSOR, - GLFW_HAND_CURSOR, - GLFW_HRESIZE_CURSOR, - GLFW_VRESIZE_CURSOR, - GLFW_NW_RESIZE_CURSOR, + GLFW_VERTICAL_TEXT_CURSOR, + GLFW_MOVE_CURSOR, + GLFW_E_RESIZE_CURSOR, GLFW_NE_RESIZE_CURSOR, - GLFW_SW_RESIZE_CURSOR, + GLFW_NW_RESIZE_CURSOR, + GLFW_N_RESIZE_CURSOR, GLFW_SE_RESIZE_CURSOR, - GLFW_INVALID_CURSOR + GLFW_SW_RESIZE_CURSOR, + GLFW_S_RESIZE_CURSOR, + GLFW_W_RESIZE_CURSOR, + GLFW_EW_RESIZE_CURSOR, + GLFW_NS_RESIZE_CURSOR, + GLFW_NESW_RESIZE_CURSOR, + GLFW_NWSE_RESIZE_CURSOR, + GLFW_ZOOM_IN_CURSOR, + GLFW_ZOOM_OUT_CURSOR, + GLFW_ALIAS_CURSOR, + GLFW_COPY_CURSOR, + GLFW_NOT_ALLOWED_CURSOR, + GLFW_NO_DROP_CURSOR, + GLFW_GRAB_CURSOR, + GLFW_GRABBING_CURSOR, + GLFW_INVALID_CURSOR, +/* end mouse cursor shapes */ } GLFWCursorShape; /*! @} */ diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 1d6b54d61..acff040ae 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -177,7 +177,7 @@ static void pointerHandleMotion(void* data UNUSED, wl_fixed_t sy) { _GLFWwindow* window = _glfw.wl.pointerFocus; - GLFWCursorShape cursorShape = GLFW_ARROW_CURSOR; + GLFWCursorShape cursorShape = GLFW_DEFAULT_CURSOR; if (!window) return; @@ -197,21 +197,21 @@ static void pointerHandleMotion(void* data UNUSED, return; case TOP_DECORATION: if (y < window->wl.decorations.metrics.width) - cursorShape = GLFW_VRESIZE_CURSOR; + cursorShape = GLFW_N_RESIZE_CURSOR; else - cursorShape = GLFW_ARROW_CURSOR; + cursorShape = GLFW_DEFAULT_CURSOR; break; case LEFT_DECORATION: if (y < window->wl.decorations.metrics.width) cursorShape = GLFW_NW_RESIZE_CURSOR; else - cursorShape = GLFW_HRESIZE_CURSOR; + cursorShape = GLFW_W_RESIZE_CURSOR; break; case RIGHT_DECORATION: if (y < window->wl.decorations.metrics.width) cursorShape = GLFW_NE_RESIZE_CURSOR; else - cursorShape = GLFW_HRESIZE_CURSOR; + cursorShape = GLFW_E_RESIZE_CURSOR; break; case BOTTOM_DECORATION: if (x < window->wl.decorations.metrics.width) @@ -219,7 +219,7 @@ static void pointerHandleMotion(void* data UNUSED, else if (x > window->wl.width + window->wl.decorations.metrics.width) cursorShape = GLFW_SE_RESIZE_CURSOR; else - cursorShape = GLFW_VRESIZE_CURSOR; + cursorShape = GLFW_S_RESIZE_CURSOR; break; default: assert(0); diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 1674e8b75..bb9752fd6 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -154,7 +154,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image, bool is_opaque, static void setCursorImage(_GLFWwindow* window, bool on_theme_change) { - _GLFWcursorWayland defaultCursor = {.shape = GLFW_ARROW_CURSOR}; + _GLFWcursorWayland defaultCursor = {.shape = GLFW_DEFAULT_CURSOR}; _GLFWcursorWayland* cursorWayland = window->cursor ? &window->cursor->wl : &defaultCursor; struct wl_cursor_image* image = NULL; struct wl_buffer* buffer = NULL; @@ -831,16 +831,37 @@ struct wl_cursor* _glfwLoadCursor(GLFWCursorShape shape, struct wl_cursor_theme* struct wl_cursor* ans = NULL; switch (shape) { - C(GLFW_ARROW_CURSOR, "left_ptr", "arrow", "default") - C(GLFW_IBEAM_CURSOR, "xterm", "ibeam", "text") - C(GLFW_CROSSHAIR_CURSOR, "crosshair", "cross") - C(GLFW_HAND_CURSOR, "hand2", "grab", "grabbing", "closedhand") - C(GLFW_HRESIZE_CURSOR, "sb_h_double_arrow", "h_double_arrow", "col-resize") - C(GLFW_VRESIZE_CURSOR, "sb_v_double_arrow", "v_double_arrow", "row-resize") - C(GLFW_NW_RESIZE_CURSOR, "top_left_corner", "nw-resize") - C(GLFW_NE_RESIZE_CURSOR, "top_right_corner", "ne-resize") - C(GLFW_SW_RESIZE_CURSOR, "bottom_left_corner", "sw-resize") - C(GLFW_SE_RESIZE_CURSOR, "bottom_right_corner", "se-resize") + /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */ + C(GLFW_DEFAULT_CURSOR, "default", "left_ptr"); + C(GLFW_TEXT_CURSOR, "text", "xterm", "ibeam"); + C(GLFW_POINTER_CURSOR, "pointing_hand", "pointer", "hand2", "hand"); + C(GLFW_HELP_CURSOR, "help", "question_arrow", "whats_this"); + C(GLFW_WAIT_CURSOR, "wait", "clock", "watch"); + C(GLFW_PROGRESS_CURSOR, "progress", "half-busy", "left_ptr_watch"); + C(GLFW_CROSSHAIR_CURSOR, "crosshair", "tcross"); + C(GLFW_VERTICAL_TEXT_CURSOR, "vertical-text"); + C(GLFW_MOVE_CURSOR, "move", "fleur", "pointer-move"); + C(GLFW_E_RESIZE_CURSOR, "e-resize", "right_side"); + C(GLFW_NE_RESIZE_CURSOR, "ne-resize", "top_right_corner"); + C(GLFW_NW_RESIZE_CURSOR, "nw-resize", "top_left_corner"); + C(GLFW_N_RESIZE_CURSOR, "n-resize", "top_side"); + C(GLFW_SE_RESIZE_CURSOR, "se-resize", "bottom_right_corner"); + C(GLFW_SW_RESIZE_CURSOR, "sw-resize", "bottom_left_corner"); + C(GLFW_S_RESIZE_CURSOR, "s-resize", "bottom_side"); + C(GLFW_W_RESIZE_CURSOR, "w-resize", "left_side"); + C(GLFW_EW_RESIZE_CURSOR, "ew-resize", "sb_h_double_arrow", "split_h"); + C(GLFW_NS_RESIZE_CURSOR, "ns-resize", "sb_v_double_arrow", "split_v"); + C(GLFW_NESW_RESIZE_CURSOR, "nesw-resize", "size_bdiag", "sb_v_double_arrow"); + C(GLFW_NWSE_RESIZE_CURSOR, "nwse-resize", "size_fdiag", "sb_h_double_arrow"); + C(GLFW_ZOOM_IN_CURSOR, "zoom-in", "zoom_in"); + C(GLFW_ZOOM_OUT_CURSOR, "zoom-out", "zoom_out"); + C(GLFW_ALIAS_CURSOR, "dnd-link"); + C(GLFW_COPY_CURSOR, "dnd-copy"); + C(GLFW_NOT_ALLOWED_CURSOR, "not-allowed", "forbidden", "crossed_circle"); + C(GLFW_NO_DROP_CURSOR, "no-drop", "dnd-no-drop"); + C(GLFW_GRAB_CURSOR, "grab", "openhand", "hand1"); + C(GLFW_GRABBING_CURSOR, "grabbing", "closedhand", "dnd-none"); +/* end glfw to xc mapping */ case GLFW_INVALID_CURSOR: break; } diff --git a/glfw/x11_window.c b/glfw/x11_window.c index f4f95224a..e4fb5cbfc 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -2811,37 +2811,77 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return true; } -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape) -{ - int native = 0; -#define C(name, val) case name: native = val; break; - switch(shape) { - C(GLFW_ARROW_CURSOR, XC_left_ptr); - C(GLFW_IBEAM_CURSOR, XC_xterm); - C(GLFW_CROSSHAIR_CURSOR, XC_crosshair); - C(GLFW_HAND_CURSOR, XC_hand2); - C(GLFW_HRESIZE_CURSOR, XC_sb_h_double_arrow); - C(GLFW_VRESIZE_CURSOR, XC_sb_v_double_arrow); - C(GLFW_NW_RESIZE_CURSOR, XC_top_left_corner); - C(GLFW_NE_RESIZE_CURSOR, XC_top_right_corner); - C(GLFW_SW_RESIZE_CURSOR, XC_bottom_left_corner); - C(GLFW_SE_RESIZE_CURSOR, XC_bottom_right_corner); - case GLFW_INVALID_CURSOR: - return false; - } -#undef C - +static int +set_cursor_from_font(_GLFWcursor* cursor, int native) { cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); - if (!cursor->x11.handle) - { + if (!cursor->x11.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to create standard cursor"); return false; } - return true; } +static bool +try_cursor_names(_GLFWcursor *cursor, int arg_count, ...) { + va_list ap; + va_start(ap, arg_count); + const char *first_name = ""; + for (int i = 0; i < arg_count; i++) { + const char *name = va_arg(ap, const char *); + first_name = name; + cursor->x11.handle = XcursorLibraryLoadCursor(_glfw.x11.display, name); + if (cursor->x11.handle) break; + } + va_end(ap); + if (!cursor->x11.handle) { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to load standard cursor: %s with %d aliases via Xcursor library", first_name, arg_count); + return false; + } + return true; +} + + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape) +{ + switch(shape) { + /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */ + case GLFW_DEFAULT_CURSOR: return set_cursor_from_font(cursor, XC_left_ptr); + case GLFW_TEXT_CURSOR: return set_cursor_from_font(cursor, XC_xterm); + case GLFW_POINTER_CURSOR: return set_cursor_from_font(cursor, XC_hand2); + case GLFW_HELP_CURSOR: return set_cursor_from_font(cursor, XC_question_arrow); + case GLFW_WAIT_CURSOR: return set_cursor_from_font(cursor, XC_clock); + case GLFW_PROGRESS_CURSOR: return try_cursor_names(cursor, 3, "progress", "half-busy", "left_ptr_watch"); + case GLFW_CROSSHAIR_CURSOR: return set_cursor_from_font(cursor, XC_tcross); + case GLFW_VERTICAL_TEXT_CURSOR: return try_cursor_names(cursor, 1, "vertical-text"); + case GLFW_MOVE_CURSOR: return set_cursor_from_font(cursor, XC_fleur); + case GLFW_E_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_right_side); + case GLFW_NE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_right_corner); + case GLFW_NW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_left_corner); + case GLFW_N_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_side); + case GLFW_SE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_right_corner); + case GLFW_SW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_left_corner); + case GLFW_S_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_side); + case GLFW_W_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_left_side); + case GLFW_EW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_h_double_arrow); + case GLFW_NS_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_v_double_arrow); + case GLFW_NESW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_v_double_arrow); + case GLFW_NWSE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_h_double_arrow); + case GLFW_ZOOM_IN_CURSOR: return try_cursor_names(cursor, 2, "zoom-in", "zoom_in"); + case GLFW_ZOOM_OUT_CURSOR: return try_cursor_names(cursor, 2, "zoom-out", "zoom_out"); + case GLFW_ALIAS_CURSOR: return try_cursor_names(cursor, 1, "dnd-link"); + case GLFW_COPY_CURSOR: return try_cursor_names(cursor, 1, "dnd-copy"); + case GLFW_NOT_ALLOWED_CURSOR: return try_cursor_names(cursor, 3, "not-allowed", "forbidden", "crossed_circle"); + case GLFW_NO_DROP_CURSOR: return try_cursor_names(cursor, 2, "no-drop", "dnd-no-drop"); + case GLFW_GRAB_CURSOR: return set_cursor_from_font(cursor, XC_hand1); + case GLFW_GRABBING_CURSOR: return try_cursor_names(cursor, 3, "grabbing", "closedhand", "dnd-none"); +/* end glfw to xc mapping */ + case GLFW_INVALID_CURSOR: return false; + } + return false; +} + void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->x11.handle) diff --git a/kitty/data-types.h b/kitty/data-types.h index ab04467c5..23fc17f27 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -68,7 +68,40 @@ typedef enum { DISABLE_LIGATURES_NEVER, DISABLE_LIGATURES_CURSOR, DISABLE_LIGATU #define ERROR_PREFIX "[PARSE ERROR]" typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MODE } MouseTrackingMode; typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL, SGR_PIXEL_PROTOCOL} MouseTrackingProtocol; -typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; +typedef enum MouseShapes { + INVALID_POINTER, + /* start mouse shapes (auto generated by gen-key-constants.py do not edit) */ + DEFAULT_POINTER, + TEXT_POINTER, + POINTER_POINTER, + HELP_POINTER, + WAIT_POINTER, + PROGRESS_POINTER, + CROSSHAIR_POINTER, + VERTICAL_TEXT_POINTER, + MOVE_POINTER, + E_RESIZE_POINTER, + NE_RESIZE_POINTER, + NW_RESIZE_POINTER, + N_RESIZE_POINTER, + SE_RESIZE_POINTER, + SW_RESIZE_POINTER, + S_RESIZE_POINTER, + W_RESIZE_POINTER, + EW_RESIZE_POINTER, + NS_RESIZE_POINTER, + NESW_RESIZE_POINTER, + NWSE_RESIZE_POINTER, + ZOOM_IN_POINTER, + ZOOM_OUT_POINTER, + ALIAS_POINTER, + COPY_POINTER, + NOT_ALLOWED_POINTER, + NO_DROP_POINTER, + GRAB_POINTER, + GRABBING_POINTER, +/* end mouse shapes */ +} MouseShape; typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn; typedef enum { TILING, SCALED, MIRRORED, CLAMPED, CENTER_CLAMPED, CENTER_SCALED } BackgroundImageLayout; typedef struct ImageAnchorPosition { diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index e32e7e880..ba820ed04 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -43,7 +43,6 @@ IMPERATIVE_CLOSE_REQUESTED: int CLOSE_BEING_CONFIRMED: int ERROR_PREFIX: str GLSL_VERSION: int -GLFW_IBEAM_CURSOR: int # start glfw functional keys (auto generated by gen-key-constants.py do not edit) GLFW_FKEY_ESCAPE: int GLFW_FKEY_ENTER: int @@ -565,7 +564,7 @@ def set_default_window_icon(path: str) -> None: def set_custom_cursor( - cursor_type: int, + cursor_shape: str, images: Tuple[Tuple[bytes, int, int], ...], x: int = 0, y: int = 0 diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index f6baec851..941a164f3 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -837,17 +837,38 @@ typedef enum { * @{ */ typedef enum { - GLFW_ARROW_CURSOR, - GLFW_IBEAM_CURSOR, +/* start mouse cursor shapes (auto generated by gen-key-constants.py do not edit) */ + GLFW_DEFAULT_CURSOR, + GLFW_TEXT_CURSOR, + GLFW_POINTER_CURSOR, + GLFW_HELP_CURSOR, + GLFW_WAIT_CURSOR, + GLFW_PROGRESS_CURSOR, GLFW_CROSSHAIR_CURSOR, - GLFW_HAND_CURSOR, - GLFW_HRESIZE_CURSOR, - GLFW_VRESIZE_CURSOR, - GLFW_NW_RESIZE_CURSOR, + GLFW_VERTICAL_TEXT_CURSOR, + GLFW_MOVE_CURSOR, + GLFW_E_RESIZE_CURSOR, GLFW_NE_RESIZE_CURSOR, - GLFW_SW_RESIZE_CURSOR, + GLFW_NW_RESIZE_CURSOR, + GLFW_N_RESIZE_CURSOR, GLFW_SE_RESIZE_CURSOR, - GLFW_INVALID_CURSOR + GLFW_SW_RESIZE_CURSOR, + GLFW_S_RESIZE_CURSOR, + GLFW_W_RESIZE_CURSOR, + GLFW_EW_RESIZE_CURSOR, + GLFW_NS_RESIZE_CURSOR, + GLFW_NESW_RESIZE_CURSOR, + GLFW_NWSE_RESIZE_CURSOR, + GLFW_ZOOM_IN_CURSOR, + GLFW_ZOOM_OUT_CURSOR, + GLFW_ALIAS_CURSOR, + GLFW_COPY_CURSOR, + GLFW_NOT_ALLOWED_CURSOR, + GLFW_NO_DROP_CURSOR, + GLFW_GRAB_CURSOR, + GLFW_GRABBING_CURSOR, + GLFW_INVALID_CURSOR, +/* end mouse cursor shapes */ } GLFWCursorShape; /*! @} */ diff --git a/kitty/glfw.c b/kitty/glfw.c index f00cc9344..0ab247773 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -33,7 +33,12 @@ extern size_t cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t arr extern monotonic_t cocoa_cursor_blink_interval(void); -static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL; +typedef struct mouse_cursor { + GLFWcursor *glfw; + bool initialized, is_custom; +} mouse_cursor; + +static mouse_cursor cursors[GLFW_INVALID_CURSOR+1] = {0}; static void set_os_window_dpi(OSWindow *w); @@ -675,20 +680,52 @@ draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_heigh #endif // }}} +static void +set_glfw_mouse_cursor(GLFWwindow *w, GLFWCursorShape shape) { + if (!cursors[shape].initialized) { + cursors[shape].initialized = true; + cursors[shape].glfw = glfwCreateStandardCursor(shape); + } + if (cursors[shape].glfw) glfwSetCursor(w, cursors[shape].glfw); +} + void set_mouse_cursor(MouseShape type) { if (global_state.callback_os_window) { GLFWwindow *w = (GLFWwindow*)global_state.callback_os_window->handle; switch(type) { - case HAND: - glfwSetCursor(w, click_cursor); - break; - case ARROW: - glfwSetCursor(w, arrow_cursor); - break; - default: - glfwSetCursor(w, standard_cursor); - break; + case INVALID_POINTER: break; + /* start enum to glfw (auto generated by gen-key-constants.py do not edit) */ + case DEFAULT_POINTER: set_glfw_mouse_cursor(w, GLFW_DEFAULT_CURSOR); break; + case TEXT_POINTER: set_glfw_mouse_cursor(w, GLFW_TEXT_CURSOR); break; + case POINTER_POINTER: set_glfw_mouse_cursor(w, GLFW_POINTER_CURSOR); break; + case HELP_POINTER: set_glfw_mouse_cursor(w, GLFW_HELP_CURSOR); break; + case WAIT_POINTER: set_glfw_mouse_cursor(w, GLFW_WAIT_CURSOR); break; + case PROGRESS_POINTER: set_glfw_mouse_cursor(w, GLFW_PROGRESS_CURSOR); break; + case CROSSHAIR_POINTER: set_glfw_mouse_cursor(w, GLFW_CROSSHAIR_CURSOR); break; + case VERTICAL_TEXT_POINTER: set_glfw_mouse_cursor(w, GLFW_VERTICAL_TEXT_CURSOR); break; + case MOVE_POINTER: set_glfw_mouse_cursor(w, GLFW_MOVE_CURSOR); break; + case E_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_E_RESIZE_CURSOR); break; + case NE_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NE_RESIZE_CURSOR); break; + case NW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NW_RESIZE_CURSOR); break; + case N_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_N_RESIZE_CURSOR); break; + case SE_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_SE_RESIZE_CURSOR); break; + case SW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_SW_RESIZE_CURSOR); break; + case S_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_S_RESIZE_CURSOR); break; + case W_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_W_RESIZE_CURSOR); break; + case EW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_EW_RESIZE_CURSOR); break; + case NS_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NS_RESIZE_CURSOR); break; + case NESW_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NESW_RESIZE_CURSOR); break; + case NWSE_RESIZE_POINTER: set_glfw_mouse_cursor(w, GLFW_NWSE_RESIZE_CURSOR); break; + case ZOOM_IN_POINTER: set_glfw_mouse_cursor(w, GLFW_ZOOM_IN_CURSOR); break; + case ZOOM_OUT_POINTER: set_glfw_mouse_cursor(w, GLFW_ZOOM_OUT_CURSOR); break; + case ALIAS_POINTER: set_glfw_mouse_cursor(w, GLFW_ALIAS_CURSOR); break; + case COPY_POINTER: set_glfw_mouse_cursor(w, GLFW_COPY_CURSOR); break; + case NOT_ALLOWED_POINTER: set_glfw_mouse_cursor(w, GLFW_NOT_ALLOWED_CURSOR); break; + case NO_DROP_POINTER: set_glfw_mouse_cursor(w, GLFW_NO_DROP_CURSOR); break; + case GRAB_POINTER: set_glfw_mouse_cursor(w, GLFW_GRAB_CURSOR); break; + case GRABBING_POINTER: set_glfw_mouse_cursor(w, GLFW_GRABBING_CURSOR); break; +/* end enum to glfw */ } } } @@ -1090,13 +1127,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { PyObject *ret = PyObject_CallFunction(load_programs, "O", is_semi_transparent ? Py_True : Py_False); if (ret == NULL) return NULL; Py_DECREF(ret); -#define CC(dest, shape) {\ - if (!dest##_cursor) { \ - dest##_cursor = glfwCreateStandardCursor(GLFW_##shape##_CURSOR); \ - if (dest##_cursor == NULL) { log_error("Failed to create the %s mouse cursor, using default cursor.", #shape); } \ -}} - CC(standard, IBEAM); CC(click, HAND); CC(arrow, ARROW); -#undef CC get_platform_dependent_config_values(glfw_window); is_first_window = false; } @@ -1121,7 +1151,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { #endif send_prerendered_sprites_for_window(w); if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo); - glfwSetCursor(glfw_window, standard_cursor); + set_glfw_mouse_cursor(glfw_window, GLFW_TEXT_CURSOR); update_os_window_viewport(w, false); glfwSetWindowPosCallback(glfw_window, window_pos_callback); // missing size callback @@ -1310,6 +1340,12 @@ glfw_init(PyObject UNUSED *self, PyObject *args) { static PyObject* glfw_terminate(PYNOARG) { + for (size_t i = 0; i < arraysz(cursors); i++) { + if (cursors[i].is_custom && cursors[i].glfw) { + glfwDestroyCursor(cursors[i].glfw); + cursors[i] = (mouse_cursor){0}; + } + } glfwTerminate(); Py_RETURN_NONE; } @@ -1692,13 +1728,51 @@ cocoa_window_id(PyObject UNUSED *self, PyObject *os_wid) { #endif } +static GLFWCursorShape +pointer_name_to_glfw_name(const char *name) { + /* start name to glfw (auto generated by gen-key-constants.py do not edit) */ + if (strcmp(name, "arrow") == 0) return GLFW_DEFAULT_CURSOR; + if (strcmp(name, "beam") == 0) return GLFW_TEXT_CURSOR; + if (strcmp(name, "text") == 0) return GLFW_TEXT_CURSOR; + if (strcmp(name, "pointer") == 0) return GLFW_POINTER_CURSOR; + if (strcmp(name, "hand") == 0) return GLFW_POINTER_CURSOR; + if (strcmp(name, "help") == 0) return GLFW_HELP_CURSOR; + if (strcmp(name, "wait") == 0) return GLFW_WAIT_CURSOR; + if (strcmp(name, "progress") == 0) return GLFW_PROGRESS_CURSOR; + if (strcmp(name, "crosshair") == 0) return GLFW_CROSSHAIR_CURSOR; + if (strcmp(name, "vertical-text") == 0) return GLFW_VERTICAL_TEXT_CURSOR; + if (strcmp(name, "move") == 0) return GLFW_MOVE_CURSOR; + if (strcmp(name, "e-resize") == 0) return GLFW_E_RESIZE_CURSOR; + if (strcmp(name, "ne-resize") == 0) return GLFW_NE_RESIZE_CURSOR; + if (strcmp(name, "nw-resize") == 0) return GLFW_NW_RESIZE_CURSOR; + if (strcmp(name, "n-resize") == 0) return GLFW_N_RESIZE_CURSOR; + if (strcmp(name, "se-resize") == 0) return GLFW_SE_RESIZE_CURSOR; + if (strcmp(name, "sw-resize") == 0) return GLFW_SW_RESIZE_CURSOR; + if (strcmp(name, "s-resize") == 0) return GLFW_S_RESIZE_CURSOR; + if (strcmp(name, "w-resize") == 0) return GLFW_W_RESIZE_CURSOR; + if (strcmp(name, "ew-resize") == 0) return GLFW_EW_RESIZE_CURSOR; + if (strcmp(name, "ns-resize") == 0) return GLFW_NS_RESIZE_CURSOR; + if (strcmp(name, "nesw-resize") == 0) return GLFW_NESW_RESIZE_CURSOR; + if (strcmp(name, "nwse-resize") == 0) return GLFW_NWSE_RESIZE_CURSOR; + if (strcmp(name, "zoom-in") == 0) return GLFW_ZOOM_IN_CURSOR; + if (strcmp(name, "zoom-out") == 0) return GLFW_ZOOM_OUT_CURSOR; + if (strcmp(name, "alias") == 0) return GLFW_ALIAS_CURSOR; + if (strcmp(name, "copy") == 0) return GLFW_COPY_CURSOR; + if (strcmp(name, "not-allowed") == 0) return GLFW_NOT_ALLOWED_CURSOR; + if (strcmp(name, "no-drop") == 0) return GLFW_NO_DROP_CURSOR; + if (strcmp(name, "grab") == 0) return GLFW_GRAB_CURSOR; + if (strcmp(name, "grabbing") == 0) return GLFW_GRABBING_CURSOR; +/* end name to glfw */ + return GLFW_INVALID_CURSOR; +} + static PyObject* set_custom_cursor(PyObject *self UNUSED, PyObject *args) { - int shape; int x=0, y=0; Py_ssize_t sz; PyObject *images; - if (!PyArg_ParseTuple(args, "iO!|ii", &shape, &PyTuple_Type, &images, &x, &y)) return NULL; + const char *shape; + if (!PyArg_ParseTuple(args, "sO!|ii", &shape, &PyTuple_Type, &images, &x, &y)) return NULL; static GLFWimage gimages[16] = {{0}}; size_t count = MIN((size_t)PyTuple_GET_SIZE(images), arraysz(gimages)); for (size_t i = 0; i < count; i++) { @@ -1708,20 +1782,14 @@ set_custom_cursor(PyObject *self UNUSED, PyObject *args) { return NULL; } } -#define CASE(which, dest) {\ - case which: \ - dest = glfwCreateCursor(gimages, x, y, count); \ - if (dest == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create custom cursor"); return NULL; } \ - break; \ -} - switch(shape) { - CASE(GLFW_IBEAM_CURSOR, standard_cursor); - CASE(GLFW_HAND_CURSOR, click_cursor); - CASE(GLFW_ARROW_CURSOR, arrow_cursor); - default: - PyErr_SetString(PyExc_ValueError, "Unknown cursor shape"); - return NULL; + GLFWCursorShape gshape = pointer_name_to_glfw_name(shape); + if (gshape == GLFW_INVALID_CURSOR) { PyErr_Format(PyExc_KeyError, "Unknown pointer shape: %s", shape); return NULL; } + GLFWcursor *c = glfwCreateCursor(gimages, x, y, count); + if (c == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create custom cursor from specified images"); return NULL; } + if (cursors[gshape].initialized && cursors[gshape].is_custom && cursors[gshape].glfw) { + glfwDestroyCursor(cursors[gshape].glfw); } + cursors[gshape].initialized = true; cursors[gshape].is_custom = true; cursors[gshape].glfw = c; Py_RETURN_NONE; } @@ -2002,7 +2070,6 @@ init_glfw(PyObject *m) { ADDC(GLFW_PRESS); ADDC(GLFW_REPEAT); ADDC(true); ADDC(false); - ADDC(GLFW_IBEAM_CURSOR); ADDC(GLFW_HAND_CURSOR); ADDC(GLFW_ARROW_CURSOR); ADDC(GLFW_PRIMARY_SELECTION); ADDC(GLFW_CLIPBOARD); /* start glfw functional keys (auto generated by gen-key-constants.py do not edit) */ diff --git a/kitty/main.py b/kitty/main.py index c984ae591..dab82ba9f 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -30,7 +30,6 @@ from .constants import ( website_url, ) from .fast_data_types import ( - GLFW_IBEAM_CURSOR, GLFW_MOD_ALT, GLFW_MOD_SHIFT, SingleKey, @@ -76,7 +75,7 @@ def set_custom_ibeam_cursor() -> None: rgba_data2, width2, height2 = load_png_data(data) images = (rgba_data, width, height), (rgba_data2, width2, height2) try: - set_custom_cursor(GLFW_IBEAM_CURSOR, images, 4, 8) + set_custom_cursor("beam", images, 4, 8) except Exception as e: log_error(f'Failed to set custom beam cursor with error: {e}') diff --git a/kitty/mouse.c b/kitty/mouse.c index 3860cfce4..ca38eda54 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -17,7 +17,7 @@ extern PyTypeObject Screen_Type; -static MouseShape mouse_cursor_shape = BEAM; +static MouseShape mouse_cursor_shape = TEXT_POINTER; typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction; #define debug(...) if (OPT(debug_keyboard)) printf(__VA_ARGS__); @@ -288,8 +288,8 @@ do_drag_scroll(Window *w, bool upwards) { if (screen->linebuf == screen->main_linebuf) { screen_history_scroll(screen, SCROLL_LINE, upwards); update_drag(w); - if (mouse_cursor_shape != ARROW) { - mouse_cursor_shape = ARROW; + if (mouse_cursor_shape != DEFAULT_POINTER) { + mouse_cursor_shape = DEFAULT_POINTER; set_mouse_cursor(mouse_cursor_shape); } return true; @@ -347,7 +347,7 @@ detect_url(Screen *screen, unsigned int x, unsigned int y) { int hid = screen_detect_url(screen, x, y); screen->current_hyperlink_under_mouse.id = 0; if (hid != 0) { - mouse_cursor_shape = HAND; + mouse_cursor_shape = POINTER_POINTER; if (hid > 0) { screen->current_hyperlink_under_mouse.id = (hyperlink_id_type)hid; screen->current_hyperlink_under_mouse.x = x; @@ -655,7 +655,7 @@ focus_in_event(void) { // Ensure that no URL is highlighted and the mouse cursor is in default shape bool in_tab_bar; unsigned int window_idx = 0; - mouse_cursor_shape = BEAM; + mouse_cursor_shape = TEXT_POINTER; Window *w = window_for_event(&window_idx, &in_tab_bar); if (w && w->render_data.screen) { screen_mark_url(w->render_data.screen, 0, 0, 0, 0); @@ -817,7 +817,7 @@ mouse_event(const int button, int modifiers, int action) { } w = window_for_event(&window_idx, &in_tab_bar); if (in_tab_bar) { - mouse_cursor_shape = HAND; + mouse_cursor_shape = POINTER_POINTER; handle_tab_bar_mouse(button, modifiers, action); debug("handled by tab bar\n"); } else if (w) { diff --git a/kitty/options/definition.py b/kitty/options/definition.py index f29110110..92a61bcdd 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -588,27 +588,61 @@ Set the active window to the window under the mouse when moving the mouse around ''' ) +pointer_shape_names = ( +# start pointer shape names (auto generated by gen-key-constants.py do not edit) + 'arrow', + 'beam', + 'text', + 'pointer', + 'hand', + 'help', + 'wait', + 'progress', + 'crosshair', + 'vertical-text', + 'move', + 'e-resize', + 'ne-resize', + 'nw-resize', + 'n-resize', + 'se-resize', + 'sw-resize', + 's-resize', + 'w-resize', + 'ew-resize', + 'ns-resize', + 'nesw-resize', + 'nwse-resize', + 'zoom-in', + 'zoom-out', + 'alias', + 'copy', + 'not-allowed', + 'no-drop', + 'grab', + 'grabbing', +# end pointer shape names +) + opt('pointer_shape_when_grabbed', 'arrow', - choices=('arrow', 'beam', 'hand'), ctype='pointer_shape', + choices=pointer_shape_names, ctype='pointer_shape', long_text=''' The shape of the mouse pointer when the program running in the terminal grabs -the mouse. Valid values are: :code:`arrow`, :code:`beam` and :code:`hand`. +the mouse. ''' ) opt('default_pointer_shape', 'beam', - choices=('arrow', 'beam', 'hand'), ctype='pointer_shape', + choices=pointer_shape_names, ctype='pointer_shape', long_text=''' -The default shape of the mouse pointer. Valid values are: :code:`arrow`, -:code:`beam` and :code:`hand`. +The default shape of the mouse pointer. ''' ) opt('pointer_shape_when_dragging', 'beam', - choices=('arrow', 'beam', 'hand'), ctype='pointer_shape', + choices=pointer_shape_names, ctype='pointer_shape', long_text=''' -The default shape of the mouse pointer when dragging across text. Valid values -are: :code:`arrow`, :code:`beam` and :code:`hand`. +The default shape of the mouse pointer when dragging across text. ''' ) diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 60a0bea56..fae488ca1 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -934,7 +934,7 @@ class Parser: raise ValueError(f"The value {val} is not a valid choice for default_pointer_shape") ans["default_pointer_shape"] = val - choices_for_default_pointer_shape = frozenset(('arrow', 'beam', 'hand')) + choices_for_default_pointer_shape = frozenset(('arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing')) def detect_urls(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['detect_urls'] = to_bool(val) @@ -1140,7 +1140,7 @@ class Parser: raise ValueError(f"The value {val} is not a valid choice for pointer_shape_when_dragging") ans["pointer_shape_when_dragging"] = val - choices_for_pointer_shape_when_dragging = frozenset(('arrow', 'beam', 'hand')) + choices_for_pointer_shape_when_dragging = frozenset(('arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing')) def pointer_shape_when_grabbed(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: val = val.lower() @@ -1148,7 +1148,7 @@ class Parser: raise ValueError(f"The value {val} is not a valid choice for pointer_shape_when_grabbed") ans["pointer_shape_when_grabbed"] = val - choices_for_pointer_shape_when_grabbed = frozenset(('arrow', 'beam', 'hand')) + choices_for_pointer_shape_when_grabbed = frozenset(('arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing')) def remember_window_size(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['remember_window_size'] = to_bool(val) diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index abf78e933..9356751ee 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -133,13 +133,41 @@ modify_font(PyObject *mf, Options *opts) { static MouseShape pointer_shape(PyObject *shape_name) { const char *name = PyUnicode_AsUTF8(shape_name); - switch(name[0]) { - case 'a': return ARROW; - case 'h': return HAND; - case 'b': return BEAM; - default: break; - } - return BEAM; + if (!name) return TEXT_POINTER; + /* start pointer shapes (auto generated by gen-key-constants.py do not edit) */ + else if (strcmp(name, "arrow") == 0) return DEFAULT_POINTER; + else if (strcmp(name, "beam") == 0) return TEXT_POINTER; + else if (strcmp(name, "text") == 0) return TEXT_POINTER; + else if (strcmp(name, "pointer") == 0) return POINTER_POINTER; + else if (strcmp(name, "hand") == 0) return POINTER_POINTER; + else if (strcmp(name, "help") == 0) return HELP_POINTER; + else if (strcmp(name, "wait") == 0) return WAIT_POINTER; + else if (strcmp(name, "progress") == 0) return PROGRESS_POINTER; + else if (strcmp(name, "crosshair") == 0) return CROSSHAIR_POINTER; + else if (strcmp(name, "vertical-text") == 0) return VERTICAL_TEXT_POINTER; + else if (strcmp(name, "move") == 0) return MOVE_POINTER; + else if (strcmp(name, "e-resize") == 0) return E_RESIZE_POINTER; + else if (strcmp(name, "ne-resize") == 0) return NE_RESIZE_POINTER; + else if (strcmp(name, "nw-resize") == 0) return NW_RESIZE_POINTER; + else if (strcmp(name, "n-resize") == 0) return N_RESIZE_POINTER; + else if (strcmp(name, "se-resize") == 0) return SE_RESIZE_POINTER; + else if (strcmp(name, "sw-resize") == 0) return SW_RESIZE_POINTER; + else if (strcmp(name, "s-resize") == 0) return S_RESIZE_POINTER; + else if (strcmp(name, "w-resize") == 0) return W_RESIZE_POINTER; + else if (strcmp(name, "ew-resize") == 0) return EW_RESIZE_POINTER; + else if (strcmp(name, "ns-resize") == 0) return NS_RESIZE_POINTER; + else if (strcmp(name, "nesw-resize") == 0) return NESW_RESIZE_POINTER; + else if (strcmp(name, "nwse-resize") == 0) return NWSE_RESIZE_POINTER; + else if (strcmp(name, "zoom-in") == 0) return ZOOM_IN_POINTER; + else if (strcmp(name, "zoom-out") == 0) return ZOOM_OUT_POINTER; + else if (strcmp(name, "alias") == 0) return ALIAS_POINTER; + else if (strcmp(name, "copy") == 0) return COPY_POINTER; + else if (strcmp(name, "not-allowed") == 0) return NOT_ALLOWED_POINTER; + else if (strcmp(name, "no-drop") == 0) return NO_DROP_POINTER; + else if (strcmp(name, "grab") == 0) return GRAB_POINTER; + else if (strcmp(name, "grabbing") == 0) return GRABBING_POINTER; +/* end pointer shapes */ + return TEXT_POINTER; } static int diff --git a/kitty/options/types.py b/kitty/options/types.py index 01155fb01..c520e7910 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -17,13 +17,13 @@ if typing.TYPE_CHECKING: choices_for_allow_cloning = typing.Literal['yes', 'y', 'true', 'no', 'n', 'false', 'ask'] choices_for_allow_remote_control = typing.Literal['password', 'socket-only', 'socket', 'no', 'n', 'false', 'yes', 'y', 'true'] choices_for_background_image_layout = typing.Literal['mirror-tiled', 'scaled', 'tiled', 'clamped', 'centered', 'cscaled'] - choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'hand'] + choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing'] choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11'] choices_for_macos_colorspace = typing.Literal['srgb', 'default', 'displayp3'] choices_for_macos_show_window_title_in = typing.Literal['all', 'menubar', 'none', 'window'] choices_for_placement_strategy = typing.Literal['center', 'top-left'] - choices_for_pointer_shape_when_dragging = typing.Literal['arrow', 'beam', 'hand'] - choices_for_pointer_shape_when_grabbed = typing.Literal['arrow', 'beam', 'hand'] + choices_for_pointer_shape_when_dragging = typing.Literal['arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing'] + choices_for_pointer_shape_when_grabbed = typing.Literal['arrow', 'beam', 'text', 'pointer', 'hand', 'help', 'wait', 'progress', 'crosshair', 'vertical-text', 'move', 'e-resize', 'ne-resize', 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize', 'zoom-in', 'zoom-out', 'alias', 'copy', 'not-allowed', 'no-drop', 'grab', 'grabbing'] choices_for_strip_trailing_spaces = typing.Literal['always', 'never', 'smart'] choices_for_tab_bar_align = typing.Literal['left', 'center', 'right'] choices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separator', 'slant', 'custom']