diff --git a/kitty/boss.py b/kitty/boss.py index 5b58ca87f..6afcc6f6b 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -20,9 +20,9 @@ from .fast_data_types import ( ChildMonitor, create_os_window, current_os_window, destroy_global_data, destroy_sprite_map, get_clipboard_string, glfw_post_empty_event, - layout_sprite_map, mark_os_window_for_close, set_clipboard_string, - set_dpi_from_os_window, set_in_sequence_mode, show_window, - toggle_fullscreen, viewport_for_window + layout_sprite_map, mark_os_window_for_close, resolve_key_mods, + set_clipboard_string, set_dpi_from_os_window, set_in_sequence_mode, + show_window, toggle_fullscreen, viewport_for_window ) from .fonts.render import prerender, resize_fonts, set_font_family from .keys import get_shortcut, shortcut_matches @@ -105,6 +105,8 @@ def __init__(self, os_window_id, opts, args, cached_values): initialize_renderer() startup_session = create_session(opts, args) self.add_os_window(startup_session, os_window_id=os_window_id) + self.resolved_keymap = {(resolve_key_mods(mods), key): v for (mods, key), v in self.opts.keymap.items()} + self.resolved_sequence_map = {(resolve_key_mods(mods), key): v for (mods, key), v in self.opts.sequence_map.items()} def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, size=None, startup_id=None): dpi_changed = False @@ -368,9 +370,9 @@ def active_window(self): def dispatch_special_key(self, key, scancode, action, mods): # Handles shortcuts, return True if the key was consumed - key_action = get_shortcut(self.opts.keymap, mods, key, scancode) + key_action = get_shortcut(self.resolved_keymap, mods, key, scancode) if key_action is None: - sequences = get_shortcut(self.opts.sequence_map, mods, key, scancode) + sequences = get_shortcut(self.resolved_sequence_map, mods, key, scancode) if sequences: self.pending_sequences = sequences set_in_sequence_mode(True) diff --git a/kitty/config.py b/kitty/config.py index b56e4e25f..aacd35f4d 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -47,10 +47,13 @@ def to_cursor_shape(x): ) +mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER', '⌥': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'} + + def parse_mods(parts): def map_mod(m): - return {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER', '⌥': 'ALT', 'OPTION': 'ALT'}.get(m, m) + return mod_map.get(m, m) mods = 0 for m in parts: @@ -329,6 +332,7 @@ def url_style(x): 'url_style': url_style, 'copy_on_select': to_bool, 'tab_bar_edge': tab_bar_edge, + 'kitty_mod': to_modifiers, } for name in ( diff --git a/kitty/data-types.h b/kitty/data-types.h index 2b70b5e3a..76c1432a6 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -17,6 +17,7 @@ // Required minimum OpenGL version #define OPENGL_REQUIRED_VERSION_MAJOR 3 #define OPENGL_REQUIRED_VERSION_MINOR 3 +#define GLFW_MOD_KITTY 1024 #define UNUSED __attribute__ ((unused)) #define PYNOARG PyObject *__a1 UNUSED, PyObject *__a2 UNUSED #define EXPORTED __attribute__ ((visibility ("default"))) diff --git a/kitty/glfw.c b/kitty/glfw.c index 5ad0eac49..1e6190bd4 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -935,6 +935,7 @@ init_glfw(PyObject *m) { ADDC(GLFW_MOD_CONTROL); ADDC(GLFW_MOD_ALT); ADDC(GLFW_MOD_SUPER); + ADDC(GLFW_MOD_KITTY); // --- Mouse ------------------------------------------------------------------- ADDC(GLFW_MOUSE_BUTTON_1); diff --git a/kitty/kitty.conf b/kitty/kitty.conf index bc2af1da5..aee55bcc6 100644 --- a/kitty/kitty.conf +++ b/kitty/kitty.conf @@ -101,7 +101,7 @@ url_color #0087BD url_style curly # The modifier keys to press when clicking with the mouse on URLs to open the URL -open_url_modifiers ctrl+shift +open_url_modifiers kitty_mod # The program with which to open URLs that are clicked on. The special value "default" means to # use the operating system's default URL handler. @@ -319,6 +319,11 @@ term xterm-kitty # Keyboard shortcuts {{{ # For a list of key names, see: http://www.glfw.org/docs/latest/group__keys.html # For a list of modifier names, see: http://www.glfw.org/docs/latest/group__mods.html + +# The value of kitty_mod is used as the modifier for all default shortcuts, you +# can change it in your kitty.conf to change the modifiers for all the default +# shortcuts. +kitty_mod ctrl+shift # # You can use the special action no_op to unmap a keyboard shortcut that is # assigned in the default configuration. @@ -327,7 +332,7 @@ term xterm-kitty # syntax below: # map key combine action1 action2 action3 ... # For example: -# map ctrl+shift+e combine : new_window : next_layout +# map kitty_mod+e combine : new_window : next_layout # this will create a new window and switch to the next available layout # # You can use multi-key shortcuts using the syntax shown below: @@ -337,59 +342,59 @@ term xterm-kitty # this will change the font size to 20 points when you press ctrl+f and then 2 # Clipboard {{{ -map ctrl+shift+v paste_from_clipboard -map ctrl+shift+s paste_from_selection -map ctrl+shift+c copy_to_clipboard +map kitty_mod+v paste_from_clipboard +map kitty_mod+s paste_from_selection +map kitty_mod+c copy_to_clipboard map shift+insert paste_from_selection # You can also pass the contents of the current selection to any program using # pass_selection_to_program. By default, the system's open program is used, but # you can specify your own, for example: -# map ctrl+shift+o pass_selection_to_program firefox -map ctrl+shift+o pass_selection_to_program +# map kitty_mod+o pass_selection_to_program firefox +map kitty_mod+o pass_selection_to_program # }}} # Scrolling {{{ -map ctrl+shift+up scroll_line_up -map ctrl+shift+down scroll_line_down -map ctrl+shift+k scroll_line_up -map ctrl+shift+j scroll_line_down -map ctrl+shift+page_up scroll_page_up -map ctrl+shift+page_down scroll_page_down -map ctrl+shift+home scroll_home -map ctrl+shift+end scroll_end -map ctrl+shift+h show_scrollback +map kitty_mod+up scroll_line_up +map kitty_mod+down scroll_line_down +map kitty_mod+k scroll_line_up +map kitty_mod+j scroll_line_down +map kitty_mod+page_up scroll_page_up +map kitty_mod+page_down scroll_page_down +map kitty_mod+home scroll_home +map kitty_mod+end scroll_end +map kitty_mod+h show_scrollback # }}} # Window management {{{ -map ctrl+shift+enter new_window -map ctrl+shift+n new_os_window -map ctrl+shift+w close_window -map ctrl+shift+] next_window -map ctrl+shift+[ previous_window -map ctrl+shift+f move_window_forward -map ctrl+shift+b move_window_backward -map ctrl+shift+` move_window_to_top -map ctrl+shift+1 first_window -map ctrl+shift+2 second_window -map ctrl+shift+3 third_window -map ctrl+shift+4 fourth_window -map ctrl+shift+5 fifth_window -map ctrl+shift+6 sixth_window -map ctrl+shift+7 seventh_window -map ctrl+shift+8 eighth_window -map ctrl+shift+9 ninth_window -map ctrl+shift+0 tenth_window +map kitty_mod+enter new_window +map kitty_mod+n new_os_window +map kitty_mod+w close_window +map kitty_mod+] next_window +map kitty_mod+[ previous_window +map kitty_mod+f move_window_forward +map kitty_mod+b move_window_backward +map kitty_mod+` move_window_to_top +map kitty_mod+1 first_window +map kitty_mod+2 second_window +map kitty_mod+3 third_window +map kitty_mod+4 fourth_window +map kitty_mod+5 fifth_window +map kitty_mod+6 sixth_window +map kitty_mod+7 seventh_window +map kitty_mod+8 eighth_window +map kitty_mod+9 ninth_window +map kitty_mod+0 tenth_window # You can open a new window running an arbitrary program, for example: -# map ctrl+shift+y new_window mutt +# map kitty_mod+y new_window mutt # # You can pass the current selection to the new program by using the @selection placeholder -# map ctrl+shift+y new_window less @selection +# map kitty_mod+y new_window less @selection # # You can even send the contents of the current screen + history buffer as stdin using # the placeholders @text (which is the plain text) and @ansi (which includes text styling escape codes). # For only the current screen, use @screen or @ansi_screen. # For example, the following command opens the scrollback buffer in less in a new window. -# map ctrl+shift+y new_window @ansi less +G -R +# map kitty_mod+y new_window @ansi less +G -R # # You can open a new window with the current working directory set to the # working directory of the current window using @@ -397,14 +402,14 @@ map ctrl+shift+0 tenth_window # }}} # Tab management {{{ -map ctrl+shift+right next_tab -map ctrl+shift+left previous_tab -map ctrl+shift+t new_tab -map ctrl+shift+q close_tab -map ctrl+shift+l next_layout -map ctrl+shift+. move_tab_forward -map ctrl+shift+, move_tab_backward -map ctrl+shift+alt+t set_tab_title +map kitty_mod+right next_tab +map kitty_mod+left previous_tab +map kitty_mod+t new_tab +map kitty_mod+q close_tab +map kitty_mod+l next_layout +map kitty_mod+. move_tab_forward +map kitty_mod+, move_tab_backward +map kitty_mod+alt+t set_tab_title # You can also create shortcuts to go to specific tabs, with 1 being the first tab # map ctrl+alt+1 goto_tab 1 # map ctrl+alt+2 goto_tab 2 @@ -420,12 +425,12 @@ map ctrl+shift+alt+t set_tab_title # }}} # Font sizes {{{ -map ctrl+shift+equal increase_font_size -map ctrl+shift+minus decrease_font_size -map ctrl+shift+backspace restore_font_size +map kitty_mod+equal increase_font_size +map kitty_mod+minus decrease_font_size +map kitty_mod+backspace restore_font_size # To setup shortcuts for specific font sizes, follow the example below: -# map ctrl+shift+f6 set_font_size 10.0 -# map ctrl+shift+f7 set_font_size 20.5 +# map kitty_mod+f6 set_font_size 10.0 +# map kitty_mod+f7 set_font_size 20.5 # }}} # Select and act on visible text {{{ @@ -434,33 +439,33 @@ map ctrl+shift+backspace restore_font_size # # Open a currently visible URL using the keyboard. The program used to open the # URL is specified in open_url_with. -map ctrl+shift+e run_kitten text hints +map kitty_mod+e run_kitten text hints # Select a path/filename and insert it into the terminal. Useful, for instance to # run git commands on a filename output from a previous git command. -map ctrl+shift+p>f run_kitten text hints --type path --program - +map kitty_mod+p>f run_kitten text hints --type path --program - # Select a path/filename and open it with the default open program. -map ctrl+shift+p>shift+f run_kitten text hints --type path +map kitty_mod+p>shift+f run_kitten text hints --type path # Select a line of text and insert it into the terminal. Use for the # output of things like: ls -1 -map ctrl+shift+p>l run_kitten text hints --type line --program - +map kitty_mod+p>l run_kitten text hints --type line --program - # Select words and insert into terminal. -map ctrl+shift+p>w run_kitten text hints --type word --program - +map kitty_mod+p>w run_kitten text hints --type word --program - # The hints kitten has many more modes of operation that you can map to different # shortcuts. For a full description run: kitty +kitten hints --help # }}} # Miscellaneous {{{ -map ctrl+shift+f11 toggle_fullscreen -map ctrl+shift+u input_unicode_character -map ctrl+shift+f2 edit_config_file +map kitty_mod+f11 toggle_fullscreen +map kitty_mod+u input_unicode_character +map kitty_mod+f2 edit_config_file You can customize how the URLs are # Open the kitty shell in a new window/tab/overlay/os_window to control kitty using commands. -map ctrl+shift+escape kitty_shell window +map kitty_mod+escape kitty_shell window # Sending arbitrary text on shortcut key presses # You can tell kitty to send arbitrary (UTF-8) encoded text to diff --git a/kitty/state.c b/kitty/state.c index 38fde1049..ce8d15724 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -303,11 +303,24 @@ repaint_delay(PyObject *val) { PyObject *key, *value; Py_ssize_t pos = 0; \ while (PyDict_Next(d, &pos, &key, &value)) +static inline int +resolve_mods(int mods) { + if (mods & GLFW_MOD_KITTY) { + mods = (mods & ~GLFW_MOD_KITTY) | OPT(kitty_mod); + } + return mods; +} + +static int +convert_mods(PyObject *obj) { + return resolve_mods(PyLong_AsLong(obj)); +} + static inline void set_special_keys(PyObject *dict) { dict_iter(dict) { if (!PyTuple_Check(key)) { PyErr_SetString(PyExc_TypeError, "dict keys for special keys must be tuples"); return; } - int mods = PyLong_AsLong(PyTuple_GET_ITEM(key, 0)); + int mods = resolve_mods(PyLong_AsLong(PyTuple_GET_ITEM(key, 0))); int glfw_key = PyLong_AsLong(PyTuple_GET_ITEM(key, 1)); set_special_key_combo(glfw_key, mods); }} @@ -332,6 +345,7 @@ PYWRAP1(set_options) { global_state.debug_font_fallback = debug_font_fallback ? true : false; #define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL; #define S(name, convert) { GA(name); global_state.opts.name = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; } + S(kitty_mod, PyLong_AsLong); S(visual_bell_duration, PyFloat_AsDouble); S(enable_audio_bell, PyObject_IsTrue); S(focus_follows_mouse, PyObject_IsTrue); @@ -345,8 +359,8 @@ PYWRAP1(set_options) { S(tab_bar_edge, PyLong_AsLong); S(mouse_hide_wait, PyFloat_AsDouble); S(wheel_scroll_multiplier, PyFloat_AsDouble); - S(open_url_modifiers, PyLong_AsUnsignedLong); - S(rectangle_select_modifiers, PyLong_AsUnsignedLong); + S(open_url_modifiers, convert_mods); + S(rectangle_select_modifiers, convert_mods); S(click_interval, PyFloat_AsDouble); S(url_color, color_as_int); S(background, color_as_int); @@ -545,6 +559,7 @@ PYWRAP1(set_display_state) { THREE_ID_OBJ(update_window_title) THREE_ID(remove_window) +PYWRAP1(resolve_key_mods) { return PyLong_FromLong(convert_mods(args)); } PYWRAP1(add_tab) { return PyLong_FromUnsignedLongLong(add_tab(PyLong_AsUnsignedLongLong(args))); } PYWRAP1(add_window) { PyObject *title; id_type a, b; PA("KKO", &a, &b, &title); return PyLong_FromUnsignedLongLong(add_window(a, b, title)); } PYWRAP0(current_os_window) { OSWindow *w = current_os_window(); if (!w) Py_RETURN_NONE; return PyLong_FromUnsignedLongLong(w->id); } @@ -562,6 +577,7 @@ static PyMethodDef module_methods[] = { MW(current_os_window, METH_NOARGS), MW(set_options, METH_VARARGS), MW(set_in_sequence_mode, METH_O), + MW(resolve_key_mods, METH_O), MW(handle_for_window_id, METH_VARARGS), MW(set_logical_dpi, METH_VARARGS), MW(pt_to_px, METH_O), diff --git a/kitty/state.h b/kitty/state.h index 452ddf892..83feb12ea 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -32,6 +32,7 @@ typedef struct { Edge tab_bar_edge; bool sync_to_monitor; bool close_on_child_death; + int kitty_mod; } Options; typedef struct { diff --git a/preprocess-readme.py b/preprocess-readme.py index e315863b7..6b9fb35a6 100755 --- a/preprocess-readme.py +++ b/preprocess-readme.py @@ -16,6 +16,7 @@ for line in open('kitty/kitty.conf'): if line.startswith('map '): _, sc, name = line.split(maxsplit=2) + sc = sc.replace('kitty_mod', 'ctrl+shift') name = name.rstrip().replace(' ', '_').replace('-', '_').replace('___', '_').replace('__', '_').strip('_') defns[name].append('`' + sc.replace('>', ' → ') + '`')