parent
9edfafcac2
commit
530fd61125
@ -19,11 +19,11 @@ 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, show_window, toggle_fullscreen,
|
||||
viewport_for_window
|
||||
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
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
from .remote_control import handle_cmd
|
||||
from .session import create_session
|
||||
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
|
||||
@ -79,6 +79,7 @@ class Boss:
|
||||
|
||||
def __init__(self, os_window_id, opts, args, cached_values):
|
||||
self.window_id_map = WeakValueDictionary()
|
||||
self.pending_sequences = None
|
||||
self.cached_values = cached_values
|
||||
self.os_window_map = {}
|
||||
self.cursor_blinking = True
|
||||
@ -352,8 +353,37 @@ class Boss:
|
||||
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)
|
||||
self.current_key_press_info = key, scancode, action, mods
|
||||
return self.dispatch_action(key_action)
|
||||
if key_action is None:
|
||||
sequences = get_shortcut(self.opts.sequence_map, mods, key, scancode)
|
||||
if sequences:
|
||||
self.pending_sequences = sequences
|
||||
set_in_sequence_mode(True)
|
||||
return True
|
||||
else:
|
||||
self.current_key_press_info = key, scancode, action, mods
|
||||
return self.dispatch_action(key_action)
|
||||
|
||||
def process_sequence(self, key, scancode, action, mods):
|
||||
if not self.pending_sequences:
|
||||
set_in_sequence_mode(False)
|
||||
|
||||
remaining = {}
|
||||
matched_action = None
|
||||
for seq, key_action in self.pending_sequences.items():
|
||||
if shortcut_matches(seq[0], mods, key, scancode):
|
||||
seq = seq[1:]
|
||||
if seq:
|
||||
remaining[seq] = key_action
|
||||
else:
|
||||
matched_action = key_action
|
||||
|
||||
if remaining:
|
||||
self.pending_sequences = remaining
|
||||
else:
|
||||
self.pending_sequences = None
|
||||
set_in_sequence_mode(False)
|
||||
if matched_action is not None:
|
||||
self.dispatch_action(matched_action)
|
||||
|
||||
def default_bg_changed_for(self, window_id):
|
||||
w = self.window_id_map.get(window_id)
|
||||
|
||||
@ -120,16 +120,36 @@ def parse_key_action(action):
|
||||
return KeyAction(func, args)
|
||||
|
||||
|
||||
def parse_key(val, keymap):
|
||||
all_key_actions = set()
|
||||
sequence_sep = '>'
|
||||
|
||||
|
||||
def parse_key(val, keymap, sequence_map):
|
||||
sc, action = val.partition(' ')[::2]
|
||||
sc, action = sc.strip(), action.strip()
|
||||
sc, action = sc.strip().strip(sequence_sep), action.strip()
|
||||
if not sc or not action:
|
||||
return
|
||||
mods, key = parse_shortcut(sc)
|
||||
if key is None:
|
||||
log_error('Shortcut: {} has unknown key, ignoring'.format(
|
||||
val))
|
||||
return
|
||||
is_sequence = sequence_sep in sc
|
||||
if is_sequence:
|
||||
trigger = None
|
||||
rest = []
|
||||
for part in sc.split(sequence_sep):
|
||||
mods, key = parse_shortcut(part)
|
||||
if key is None:
|
||||
log_error('Shortcut: {} has unknown key, ignoring'.format(
|
||||
sc))
|
||||
return
|
||||
if trigger is None:
|
||||
trigger = mods, key
|
||||
else:
|
||||
rest.append((mods, key))
|
||||
rest = tuple(rest)
|
||||
else:
|
||||
mods, key = parse_shortcut(sc)
|
||||
if key is None:
|
||||
log_error('Shortcut: {} has unknown key, ignoring'.format(
|
||||
sc))
|
||||
return
|
||||
try:
|
||||
paction = parse_key_action(action)
|
||||
except Exception:
|
||||
@ -137,7 +157,12 @@ def parse_key(val, keymap):
|
||||
action))
|
||||
else:
|
||||
if paction is not None:
|
||||
keymap[(mods, key)] = paction
|
||||
all_key_actions.add(paction.func)
|
||||
if is_sequence:
|
||||
s = sequence_map.setdefault(trigger, {})
|
||||
s[rest] = paction
|
||||
else:
|
||||
keymap[(mods, key)] = paction
|
||||
|
||||
|
||||
def parse_symbol_map(val):
|
||||
@ -177,7 +202,7 @@ def parse_send_text_bytes(text):
|
||||
).encode('utf-8')
|
||||
|
||||
|
||||
def parse_send_text(val, keymap):
|
||||
def parse_send_text(val, keymap, sequence_map):
|
||||
parts = val.split(' ')
|
||||
|
||||
def abort(msg):
|
||||
@ -190,7 +215,7 @@ def parse_send_text(val, keymap):
|
||||
mode, sc = parts[:2]
|
||||
text = ' '.join(parts[2:])
|
||||
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
||||
return parse_key(key_str, keymap)
|
||||
return parse_key(key_str, keymap, sequence_map)
|
||||
|
||||
|
||||
def to_modifiers(val):
|
||||
@ -317,14 +342,14 @@ for a in ('active', 'inactive'):
|
||||
|
||||
def special_handling(key, val, ans):
|
||||
if key == 'map':
|
||||
parse_key(val, ans['keymap'])
|
||||
parse_key(val, ans['keymap'], ans['sequence_map'])
|
||||
return True
|
||||
if key == 'symbol_map':
|
||||
ans['symbol_map'].update(parse_symbol_map(val))
|
||||
return True
|
||||
if key == 'send_text':
|
||||
# For legacy compatibility
|
||||
parse_send_text(val, ans['keymap'])
|
||||
parse_send_text(val, ans['keymap'], ans['sequence_map'])
|
||||
return True
|
||||
|
||||
|
||||
@ -337,6 +362,7 @@ default_config_path = os.path.join(
|
||||
def parse_config(lines, check_keys=True):
|
||||
ans = {
|
||||
'keymap': {},
|
||||
'sequence_map': {},
|
||||
'symbol_map': {},
|
||||
}
|
||||
parse_config_base(
|
||||
@ -351,7 +377,7 @@ def parse_config(lines, check_keys=True):
|
||||
|
||||
|
||||
Options, defaults = init_config(default_config_path, parse_config)
|
||||
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset(
|
||||
actions = frozenset(all_key_actions) | frozenset(
|
||||
'combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
||||
split()
|
||||
)
|
||||
@ -370,6 +396,20 @@ def merge_keymaps(defaults, newvals):
|
||||
return ans
|
||||
|
||||
|
||||
def merge_sequence_maps(defaults, newvals):
|
||||
ans = {t: r.copy() for t, r in defaults.items()}
|
||||
for trigger, rest_map in newvals.items():
|
||||
s = ans.setdefault(trigger, {})
|
||||
for k, v in rest_map.items():
|
||||
f = v.func
|
||||
if f in no_op_actions:
|
||||
s.pop(k, None)
|
||||
continue
|
||||
if f in actions:
|
||||
s[k] = v
|
||||
return {k: v for k, v in ans.items() if v}
|
||||
|
||||
|
||||
def merge_dicts(defaults, newvals):
|
||||
ans = defaults.copy()
|
||||
ans.update(newvals)
|
||||
@ -383,6 +423,8 @@ def merge_configs(defaults, vals):
|
||||
newvals = vals.get(k, {})
|
||||
if k == 'keymap':
|
||||
ans['keymap'] = merge_keymaps(v, newvals)
|
||||
elif k == 'sequence_map':
|
||||
ans['sequence_map'] = merge_sequence_maps(v, newvals)
|
||||
else:
|
||||
ans[k] = merge_dicts(v, newvals)
|
||||
else:
|
||||
|
||||
@ -101,6 +101,10 @@ void
|
||||
on_key_input(int key, int scancode, int action, int mods, const char* text, int state UNUSED) {
|
||||
Window *w = active_window();
|
||||
if (!w) return;
|
||||
if (global_state.in_sequence_mode) {
|
||||
if (action != GLFW_RELEASE) call_boss(process_sequence, "iiii", key, scancode, action, mods);
|
||||
return;
|
||||
}
|
||||
Screen *screen = w->render_data.screen;
|
||||
bool has_text = text && !is_ascii_control_char(text[0]);
|
||||
#ifdef __APPLE__
|
||||
|
||||
@ -251,6 +251,10 @@ def get_shortcut(keymap, mods, key, scancode):
|
||||
return keymap.get((mods & 0b1111, key))
|
||||
|
||||
|
||||
def shortcut_matches(s, mods, key, scancode):
|
||||
return s[0] & 0b1111 == mods & 0b1111 and s[1] == key
|
||||
|
||||
|
||||
def generate_key_table():
|
||||
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
||||
import os
|
||||
|
||||
@ -323,6 +323,12 @@ term xterm-kitty
|
||||
# For example:
|
||||
# map ctrl+shift+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:
|
||||
# map key1>key2>key3 action
|
||||
# For example:
|
||||
# map ctrl+f>2 set_font_size 20
|
||||
# this will change the font size to 20 points when you press ctrl+f and then 3
|
||||
|
||||
# Clipboard {{{
|
||||
map ctrl+shift+v paste_from_clipboard
|
||||
|
||||
@ -284,6 +284,7 @@ os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) {
|
||||
#define KKI(name) PYWRAP1(name) { id_type a, b; unsigned int c; PA("KKI", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
||||
#define KKII(name) PYWRAP1(name) { id_type a, b; unsigned int c, d; PA("KKII", &a, &b, &c, &d); name(a, b, c, d); Py_RETURN_NONE; }
|
||||
#define KK5I(name) PYWRAP1(name) { id_type a, b; unsigned int c, d, e, f, g; PA("KKIIIII", &a, &b, &c, &d, &e, &f, &g); name(a, b, c, d, e, f, g); Py_RETURN_NONE; }
|
||||
#define BOOL_SET(name) PYWRAP1(set_##name) { global_state.name = PyObject_IsTrue(args); Py_RETURN_NONE; }
|
||||
|
||||
static inline color_type
|
||||
color_as_int(PyObject *color) {
|
||||
@ -367,6 +368,8 @@ PYWRAP1(set_options) {
|
||||
|
||||
GA(keymap); set_special_keys(ret);
|
||||
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
||||
GA(sequence_map); set_special_keys(ret);
|
||||
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
||||
|
||||
#define read_adjust(name) { \
|
||||
PyObject *al = PyObject_GetAttrString(opts, #name); \
|
||||
@ -386,6 +389,8 @@ PYWRAP1(set_options) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
BOOL_SET(in_sequence_mode)
|
||||
|
||||
PYWRAP1(set_tab_bar_render_data) {
|
||||
ScreenRenderData d = {0};
|
||||
id_type os_window_id;
|
||||
@ -555,6 +560,7 @@ KK5I(add_borders_rect)
|
||||
static PyMethodDef module_methods[] = {
|
||||
MW(current_os_window, METH_NOARGS),
|
||||
MW(set_options, METH_VARARGS),
|
||||
MW(set_in_sequence_mode, METH_O),
|
||||
MW(handle_for_window_id, METH_VARARGS),
|
||||
MW(set_logical_dpi, METH_VARARGS),
|
||||
MW(pt_to_px, METH_O),
|
||||
|
||||
@ -132,6 +132,7 @@ typedef struct {
|
||||
bool is_wayland;
|
||||
bool debug_gl, debug_font_fallback;
|
||||
bool has_pending_resizes;
|
||||
bool in_sequence_mode;
|
||||
} GlobalState;
|
||||
|
||||
extern GlobalState global_state;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user