Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5117a2c17a | ||
|
|
ebce1d7c61 | ||
|
|
e4668c1aff | ||
|
|
2519c49c02 | ||
|
|
9e512ff58a | ||
|
|
ed50595aca | ||
|
|
9d62d087e0 | ||
|
|
6b933e33f5 | ||
|
|
a86931f401 | ||
|
|
fc4e1595e8 | ||
|
|
b36c3f3425 | ||
|
|
e427fd1233 | ||
|
|
dd3af45043 | ||
|
|
304d42d4c2 | ||
|
|
abca9280e7 |
@@ -28,7 +28,7 @@ from .fonts.render import set_font_family
|
|||||||
from .borders import BordersProgram
|
from .borders import BordersProgram
|
||||||
from .char_grid import cursor_shader, cell_shader
|
from .char_grid import cursor_shader, cell_shader
|
||||||
from .constants import is_key_pressed
|
from .constants import is_key_pressed
|
||||||
from .keys import interpret_text_event, interpret_key_event, get_shortcut
|
from .keys import interpret_text_event, interpret_key_event, get_shortcut, get_sent_data
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .shaders import Sprites, ShaderProgram
|
from .shaders import Sprites, ShaderProgram
|
||||||
from .tabs import TabManager, SpecialWindow
|
from .tabs import TabManager, SpecialWindow
|
||||||
@@ -308,7 +308,7 @@ class Boss(Thread):
|
|||||||
return
|
return
|
||||||
if window.char_grid.scrolled_by and key not in MODIFIER_KEYS and action == GLFW_PRESS:
|
if window.char_grid.scrolled_by and key not in MODIFIER_KEYS and action == GLFW_PRESS:
|
||||||
window.scroll_end()
|
window.scroll_end()
|
||||||
data = interpret_key_event(key, scancode, mods, window, action)
|
data = get_sent_data(self.opts.send_text_map, key, scancode, mods, window, action) or interpret_key_event(key, scancode, mods, window, action)
|
||||||
if data:
|
if data:
|
||||||
window.write_to_child(data)
|
window.write_to_child(data)
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ class CharGrid:
|
|||||||
if raw is None:
|
if raw is None:
|
||||||
return 0
|
return 0
|
||||||
val = to_color(raw)
|
val = to_color(raw)
|
||||||
return None if val is None else (color_as_int(val) << 8) | 1
|
return None if val is None else (color_as_int(val) << 8) | 2
|
||||||
|
|
||||||
for which, val in changes.items():
|
for which, val in changes.items():
|
||||||
val = item(val)
|
val = item(val)
|
||||||
@@ -322,10 +322,9 @@ class CharGrid:
|
|||||||
sprites = get_boss().sprites
|
sprites = get_boss().sprites
|
||||||
is_dirty = self.screen.is_dirty()
|
is_dirty = self.screen.is_dirty()
|
||||||
with sprites.lock:
|
with sprites.lock:
|
||||||
fg = self.screen.default_fg
|
fg, bg = self.screen.default_fg, self.screen.default_bg
|
||||||
fg = fg >> 8 if fg & 1 else self.default_fg
|
fg = fg >> 8 if fg & 2 else self.default_fg
|
||||||
bg = self.screen.default_bg
|
bg = bg >> 8 if bg & 2 else self.default_bg
|
||||||
bg = bg >> 8 if bg & 1 else self.default_bg
|
|
||||||
cursor_changed, history_line_added_count = self.screen.update_cell_data(
|
cursor_changed, history_line_added_count = self.screen.update_cell_data(
|
||||||
sprites.backend, self.color_profile, addressof(self.main_sprite_map), fg, bg, force_full_refresh)
|
sprites.backend, self.color_profile, addressof(self.main_sprite_map), fg, bg, force_full_refresh)
|
||||||
if self.scrolled_by:
|
if self.scrolled_by:
|
||||||
@@ -475,9 +474,9 @@ class CharGrid:
|
|||||||
if self.render_buf_is_dirty or sel != self.last_rendered_selection:
|
if self.render_buf_is_dirty or sel != self.last_rendered_selection:
|
||||||
memmove(buf, self.render_buf, sizeof(type(buf)))
|
memmove(buf, self.render_buf, sizeof(type(buf)))
|
||||||
fg = self.screen.highlight_fg
|
fg = self.screen.highlight_fg
|
||||||
fg = fg >> 8 if fg & 1 else self.highlight_fg
|
fg = fg >> 8 if fg & 2 else self.highlight_fg
|
||||||
bg = self.screen.highlight_bg
|
bg = self.screen.highlight_bg
|
||||||
bg = bg >> 8 if bg & 1 else self.highlight_bg
|
bg = bg >> 8 if bg & 2 else self.highlight_bg
|
||||||
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], fg, bg)
|
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], fg, bg)
|
||||||
if self.render_buf_is_dirty or self.last_rendered_selection != sel:
|
if self.render_buf_is_dirty or self.last_rendered_selection != sel:
|
||||||
sprites.set_sprite_map(self.buffer_id, buf)
|
sprites.set_sprite_map(self.buffer_id, buf)
|
||||||
@@ -503,7 +502,7 @@ class CharGrid:
|
|||||||
left = sg.xstart + cursor.x * sg.dx
|
left = sg.xstart + cursor.x * sg.dx
|
||||||
top = sg.ystart - cursor.y * sg.dy
|
top = sg.ystart - cursor.y * sg.dy
|
||||||
cc = self.screen.cursor_color
|
cc = self.screen.cursor_color
|
||||||
col = color_from_int(cc >> 8) if cc & 1 else self.opts.cursor
|
col = color_from_int(cc >> 8) if cc & 2 else self.opts.cursor
|
||||||
shape = cursor.shape or self.default_cursor.shape
|
shape = cursor.shape or self.default_cursor.shape
|
||||||
alpha = self.opts.cursor_opacity
|
alpha = self.opts.cursor_opacity
|
||||||
if alpha < 1.0 and shape == CURSOR_BLOCK:
|
if alpha < 1.0 and shape == CURSOR_BLOCK:
|
||||||
|
|||||||
@@ -125,14 +125,14 @@ def write_osc(code, string=''):
|
|||||||
write(OSC + str(code) + string + '\x07')
|
write(OSC + str(code) + string + '\x07')
|
||||||
|
|
||||||
|
|
||||||
set_dynamic_color = write_osc
|
set_dynamic_color = set_color_table_color = write_osc
|
||||||
|
|
||||||
|
|
||||||
def replay(raw):
|
def replay(raw):
|
||||||
for line in raw.splitlines():
|
for line in raw.splitlines():
|
||||||
if line.strip():
|
if line.strip():
|
||||||
cmd, rest = line.partition(' ')[::2]
|
cmd, rest = line.partition(' ')[::2]
|
||||||
if cmd in {'draw', 'set_title', 'set_icon'}:
|
if cmd in {'draw', 'set_title', 'set_icon', 'set_dynamic_color', 'set_color_table_color'}:
|
||||||
globals()[cmd](rest)
|
globals()[cmd](rest)
|
||||||
else:
|
else:
|
||||||
rest = map(int, rest.split()) if rest else ()
|
rest = map(int, rest.split()) if rest else ()
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
|
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include <AvailabilityMacros.h>
|
||||||
|
|
||||||
#ifndef NSWindowStyleMaskTitled
|
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200)
|
||||||
#define NSWindowStyleMaskTitled NSTitledWindowMask
|
#define NSWindowStyleMaskTitled NSTitledWindowMask
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
117
kitty/config.py
117
kitty/config.py
@@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import ast
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -84,16 +85,22 @@ named_keys = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_key(val, keymap):
|
def parse_shortcut(sc):
|
||||||
sc, action = val.partition(' ')[::2]
|
|
||||||
action = action.strip()
|
|
||||||
sc = sc.strip()
|
|
||||||
if not sc or not action:
|
|
||||||
return
|
|
||||||
parts = sc.split('+')
|
parts = sc.split('+')
|
||||||
mods = parse_mods(parts[:-1])
|
mods = parse_mods(parts[:-1])
|
||||||
key = parts[-1].upper()
|
key = parts[-1].upper()
|
||||||
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
key = getattr(defines, 'GLFW_KEY_' + named_keys.get(key, key), None)
|
||||||
|
if key is not None:
|
||||||
|
return mods, key
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_key(val, keymap):
|
||||||
|
sc, action = val.partition(' ')[::2]
|
||||||
|
sc, action = sc.strip(), action.strip()
|
||||||
|
if not sc or not action:
|
||||||
|
return
|
||||||
|
mods, key = parse_shortcut(sc)
|
||||||
if key is None:
|
if key is None:
|
||||||
safe_print(
|
safe_print(
|
||||||
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
'Shortcut: {} has an unknown key, ignoring'.format(val),
|
||||||
@@ -137,6 +144,39 @@ def parse_symbol_map(val):
|
|||||||
return symbol_map
|
return symbol_map
|
||||||
|
|
||||||
|
|
||||||
|
def parse_send_text(val):
|
||||||
|
parts = val.split(' ')
|
||||||
|
|
||||||
|
def abort(msg):
|
||||||
|
safe_print(
|
||||||
|
'Send text: {} is invalid ({}), ignoring'.format(val, msg), file=sys.stderr
|
||||||
|
)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if len(parts) < 3:
|
||||||
|
return abort('Incomplete')
|
||||||
|
|
||||||
|
text = ' '.join(parts[2:])
|
||||||
|
mode, sc = parts[:2]
|
||||||
|
mods, key = parse_shortcut(sc.strip())
|
||||||
|
if key is None:
|
||||||
|
return abort('Invalid shortcut')
|
||||||
|
text = ast.literal_eval("'''" + text + "'''").encode('utf-8')
|
||||||
|
if not text:
|
||||||
|
return abort('Empty text')
|
||||||
|
|
||||||
|
if mode in ('all', '*'):
|
||||||
|
modes = parse_send_text.all_modes
|
||||||
|
else:
|
||||||
|
modes = frozenset(mode.split(',')).intersection(parse_send_text.all_modes)
|
||||||
|
if not modes:
|
||||||
|
return abort('Invalid keyboard modes')
|
||||||
|
return {mode: {(mods, key): text} for mode in modes}
|
||||||
|
|
||||||
|
|
||||||
|
parse_send_text.all_modes = frozenset({'normal', 'application', 'kitty'})
|
||||||
|
|
||||||
|
|
||||||
def to_open_url_modifiers(val):
|
def to_open_url_modifiers(val):
|
||||||
return parse_mods(val.split('+'))
|
return parse_mods(val.split('+'))
|
||||||
|
|
||||||
@@ -198,8 +238,10 @@ for a in ('active', 'inactive'):
|
|||||||
type_map['%s_tab_%s' % (a, b)] = lambda x: to_color(x, validate=True)
|
type_map['%s_tab_%s' % (a, b)] = lambda x: to_color(x, validate=True)
|
||||||
|
|
||||||
|
|
||||||
def parse_config(lines):
|
def parse_config(lines, check_keys=True):
|
||||||
ans = {'keymap': {}, 'symbol_map': {}}
|
ans = {'keymap': {}, 'symbol_map': {}, 'send_text_map': {'kitty': {}, 'normal': {}, 'application': {}}}
|
||||||
|
if check_keys:
|
||||||
|
all_keys = defaults._asdict()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line or line.startswith('#'):
|
if not line or line.startswith('#'):
|
||||||
@@ -213,6 +255,15 @@ def parse_config(lines):
|
|||||||
if key == 'symbol_map':
|
if key == 'symbol_map':
|
||||||
ans['symbol_map'].update(parse_symbol_map(val))
|
ans['symbol_map'].update(parse_symbol_map(val))
|
||||||
continue
|
continue
|
||||||
|
if key == 'send_text':
|
||||||
|
stvals = parse_send_text(val)
|
||||||
|
for k, v in ans['send_text_map'].items():
|
||||||
|
v.update(stvals.get(k, {}))
|
||||||
|
continue
|
||||||
|
if check_keys:
|
||||||
|
if key not in all_keys:
|
||||||
|
safe_print('Ignoring unknown config key: {}'.format(key), file=sys.stderr)
|
||||||
|
continue
|
||||||
tm = type_map.get(key)
|
tm = type_map.get(key)
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
val = tm(val)
|
val = tm(val)
|
||||||
@@ -223,38 +274,42 @@ def parse_config(lines):
|
|||||||
with open(
|
with open(
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty.conf')
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'kitty.conf')
|
||||||
) as f:
|
) as f:
|
||||||
defaults = parse_config(f.readlines())
|
defaults = parse_config(f.readlines(), check_keys=False)
|
||||||
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
||||||
defaults = Options(**defaults)
|
defaults = Options(**defaults)
|
||||||
actions = frozenset(defaults.keymap.values())
|
actions = frozenset(defaults.keymap.values())
|
||||||
|
|
||||||
|
|
||||||
def update_dict(a, b):
|
def merge_keymaps(defaults, newvals):
|
||||||
a.update(b)
|
ans = defaults.copy()
|
||||||
return a
|
for k, v in newvals.items():
|
||||||
|
if v in {'noop', 'no-op', 'no_op'}:
|
||||||
|
ans.pop(k, None)
|
||||||
|
continue
|
||||||
|
if v in actions:
|
||||||
|
ans[k] = v
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def merge_dicts(vals, defaults):
|
def merge_dicts(defaults, newvals):
|
||||||
return {
|
ans = defaults.copy()
|
||||||
k: update_dict(v, vals.get(k, {}))
|
ans.update(newvals)
|
||||||
if isinstance(v, dict) else vals.get(k, v)
|
return ans
|
||||||
for k, v in defaults.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def merge_configs(ans, vals):
|
def merge_configs(defaults, vals):
|
||||||
vals['keymap'] = {
|
ans = {}
|
||||||
k: v
|
for k, v in defaults.items():
|
||||||
for k, v in vals.get('keymap', {}).items() if v in actions
|
if isinstance(v, dict):
|
||||||
}
|
newvals = vals.get(k, {})
|
||||||
remove_keys = {
|
if k == 'keymap':
|
||||||
k
|
ans['keymap'] = merge_keymaps(v, newvals)
|
||||||
for k, v in vals.get('keymap', {}).items()
|
elif k == 'send_text_map':
|
||||||
if v in ('noop', 'no-op', 'no_op')
|
ans[k] = {m: merge_dicts(mm, newvals.get(m, {})) for m, mm in v.items()}
|
||||||
}
|
else:
|
||||||
ans = merge_dicts(vals, ans)
|
ans[k] = merge_dicts(v, newvals)
|
||||||
for k in remove_keys:
|
else:
|
||||||
ans['keymap'].pop(k, None)
|
ans[k] = vals.get(k, v)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from .fast_data_types import (
|
|||||||
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
|
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
|
||||||
|
|
||||||
appname = 'kitty'
|
appname = 'kitty'
|
||||||
version = (0, 2, 7)
|
version = (0, 2, 8)
|
||||||
str_version = '.'.join(map(str, version))
|
str_version = '.'.join(map(str, version))
|
||||||
_plat = sys.platform.lower()
|
_plat = sys.platform.lower()
|
||||||
isosx = 'darwin' in _plat
|
isosx = 'darwin' in _plat
|
||||||
|
|||||||
@@ -31,6 +31,17 @@ change_wcwidth_wrap(PyObject UNUSED *self, PyObject *use9) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
redirect_std_streams(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
char *devnull = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &devnull)) return NULL;
|
||||||
|
if (freopen(devnull, "r", stdin) == NULL) return PyErr_SetFromErrno(PyExc_EnvironmentError);
|
||||||
|
if (freopen(devnull, "w", stdout) == NULL) return PyErr_SetFromErrno(PyExc_EnvironmentError);
|
||||||
|
if (freopen(devnull, "w", stderr) == NULL) return PyErr_SetFromErrno(PyExc_EnvironmentError);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
GL_METHODS
|
GL_METHODS
|
||||||
{"drain_read", (PyCFunction)drain_read, METH_O, ""},
|
{"drain_read", (PyCFunction)drain_read, METH_O, ""},
|
||||||
@@ -38,6 +49,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
|
||||||
{"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""},
|
{"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""},
|
||||||
{"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""},
|
{"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""},
|
||||||
|
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||||
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
||||||
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
|
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
|
|||||||
16
kitty/glfw.c
16
kitty/glfw.c
@@ -208,6 +208,18 @@ glfw_get_key_name(PyObject UNUSED *self, PyObject *args) {
|
|||||||
return Py_BuildValue("s", glfwGetKeyName(key, scancode));
|
return Py_BuildValue("s", glfwGetKeyName(key, scancode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
glfw_init_hint_string(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
int hint_id;
|
||||||
|
char *hint;
|
||||||
|
if (!PyArg_ParseTuple(args, "is", &hint_id, &hint)) return NULL;
|
||||||
|
#ifdef glfwInitHintString
|
||||||
|
glfwInitHintString(hint_id, hint);
|
||||||
|
#endif
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -448,6 +460,10 @@ init_glfw(PyObject *m) {
|
|||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
glfwSetErrorCallback(cb_error_callback);
|
glfwSetErrorCallback(cb_error_callback);
|
||||||
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
|
||||||
|
#ifdef GLFW_X11_WM_CLASS_NAME
|
||||||
|
ADDC(GLFW_X11_WM_CLASS_NAME)
|
||||||
|
ADDC(GLFW_X11_WM_CLASS_CLASS)
|
||||||
|
#endif
|
||||||
ADDC(GLFW_RELEASE);
|
ADDC(GLFW_RELEASE);
|
||||||
ADDC(GLFW_PRESS);
|
ADDC(GLFW_PRESS);
|
||||||
ADDC(GLFW_REPEAT);
|
ADDC(GLFW_REPEAT);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ PyObject* glfw_wait_events(PyObject UNUSED *self, PyObject*);
|
|||||||
PyObject* glfw_post_empty_event(PyObject UNUSED *self);
|
PyObject* glfw_post_empty_event(PyObject UNUSED *self);
|
||||||
PyObject* glfw_get_physical_dpi(PyObject UNUSED *self);
|
PyObject* glfw_get_physical_dpi(PyObject UNUSED *self);
|
||||||
PyObject* glfw_get_key_name(PyObject UNUSED *self, PyObject *args);
|
PyObject* glfw_get_key_name(PyObject UNUSED *self, PyObject *args);
|
||||||
|
PyObject* glfw_init_hint_string(PyObject UNUSED *self, PyObject *args);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
PyObject* cocoa_hide_titlebar(PyObject UNUSED *self, PyObject *window_id);
|
PyObject* cocoa_hide_titlebar(PyObject UNUSED *self, PyObject *window_id);
|
||||||
@@ -38,6 +39,7 @@ PyObject* cocoa_get_lang(PyObject UNUSED *self);
|
|||||||
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""}, \
|
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""}, \
|
||||||
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""}, \
|
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""}, \
|
||||||
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, \
|
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""}, \
|
||||||
|
{"glfw_init_hint_string", (PyCFunction)glfw_init_hint_string, METH_VARARGS, ""}, \
|
||||||
COCOA_HIDE_TITLEBAR \
|
COCOA_HIDE_TITLEBAR \
|
||||||
COCOA_GET_LANG
|
COCOA_GET_LANG
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ def get_key_map(screen):
|
|||||||
return cursor_key_mode_map[screen.cursor_key_mode]
|
return cursor_key_mode_map[screen.cursor_key_mode]
|
||||||
|
|
||||||
|
|
||||||
|
def keyboard_mode_name(screen):
|
||||||
|
if screen.extended_keyboard:
|
||||||
|
return 'kitty'
|
||||||
|
return 'application' if screen.cursor_key_mode else 'normal'
|
||||||
|
|
||||||
|
|
||||||
valid_localized_key_names = {
|
valid_localized_key_names = {
|
||||||
k: getattr(defines, 'GLFW_KEY_' + k)
|
k: getattr(defines, 'GLFW_KEY_' + k)
|
||||||
for k in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
for k in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||||
@@ -195,3 +201,11 @@ def interpret_text_event(codepoint, mods, window):
|
|||||||
def get_shortcut(keymap, mods, key, scancode):
|
def get_shortcut(keymap, mods, key, scancode):
|
||||||
key = get_localized_key(key, scancode)
|
key = get_localized_key(key, scancode)
|
||||||
return keymap.get((mods & 0b1111, key))
|
return keymap.get((mods & 0b1111, key))
|
||||||
|
|
||||||
|
|
||||||
|
def get_sent_data(send_text_map, key, scancode, mods, window, action):
|
||||||
|
if action in (defines.GLFW_PRESS, defines.GLFW_REPEAT):
|
||||||
|
key = get_localized_key(key, scancode)
|
||||||
|
m = keyboard_mode_name(window.screen)
|
||||||
|
keymap = send_text_map[m]
|
||||||
|
return keymap.get((mods & 0b1111, key))
|
||||||
|
|||||||
@@ -231,6 +231,23 @@ map ctrl+shift+equal increase_font_size
|
|||||||
map ctrl+shift+minus decrease_font_size
|
map ctrl+shift+minus decrease_font_size
|
||||||
map ctrl+shift+backspace restore_font_size
|
map ctrl+shift+backspace restore_font_size
|
||||||
|
|
||||||
|
# Sending arbitrary text on shortcut key presses
|
||||||
|
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
||||||
|
# the client program when pressing specified shortcut keys. For example:
|
||||||
|
# send_text all ctrl+alt+a Special text
|
||||||
|
# This will send "Special text" when you press the Ctrl+Alt+a key combination.
|
||||||
|
# The text to be sent is a python string literal so you can use escapes like
|
||||||
|
# \x1b to send control codes or \u21fb to send unicode characters (or you can
|
||||||
|
# just input the unicode characters directly as UTF-8 text). The first argument
|
||||||
|
# to send_text is the keyboard modes in which to activate the shortcut. The possible
|
||||||
|
# values are normal or application or kitty or a comma separated combination of them.
|
||||||
|
# The special keyword all means all modes. The modes normal and application refer to
|
||||||
|
# the DECCKM cursor key mode for terminals, and kitty refers to the special kitty
|
||||||
|
# extended keyboard protocol. Another example, that outputs a word and then moves the cursor
|
||||||
|
# to the start of the line (same as pressing the Home key):
|
||||||
|
# send_text normal ctrl+alt+a Word\x1b[H
|
||||||
|
# send_text application ctrl+alt+a Word\x1bOH
|
||||||
|
|
||||||
# Symbol mapping (special font for specified unicode code points). Map the
|
# Symbol mapping (special font for specified unicode code points). Map the
|
||||||
# specified unicode codepoints to a particular font. Useful if you need special
|
# specified unicode codepoints to a particular font. Useful if you need special
|
||||||
# rendering for some symbols, such as for Powerline. Avoids the need for
|
# rendering for some symbols, such as for Powerline. Avoids the need for
|
||||||
|
|||||||
@@ -25,11 +25,15 @@ from .fast_data_types import (
|
|||||||
GLFW_STENCIL_BITS, Window, change_wcwidth,
|
GLFW_STENCIL_BITS, Window, change_wcwidth,
|
||||||
enable_automatic_opengl_error_checking, glClear, glClearColor, glewInit,
|
enable_automatic_opengl_error_checking, glClear, glClearColor, glewInit,
|
||||||
glfw_init, glfw_set_error_callback, glfw_swap_interval, glfw_terminate,
|
glfw_init, glfw_set_error_callback, glfw_swap_interval, glfw_terminate,
|
||||||
glfw_wait_events, glfw_window_hint
|
glfw_wait_events, glfw_window_hint, glfw_init_hint_string
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
from .fast_data_types import GLFW_X11_WM_CLASS_NAME, GLFW_X11_WM_CLASS_CLASS
|
||||||
|
except ImportError:
|
||||||
|
GLFW_X11_WM_CLASS_NAME = GLFW_X11_WM_CLASS_CLASS = None
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .shaders import GL_VERSION
|
from .shaders import GL_VERSION
|
||||||
from .utils import safe_print
|
from .utils import safe_print, detach
|
||||||
|
|
||||||
|
|
||||||
defconf = os.path.join(config_dir, 'kitty.conf')
|
defconf = os.path.join(config_dir, 'kitty.conf')
|
||||||
@@ -95,6 +99,13 @@ def option_parser():
|
|||||||
default=False,
|
default=False,
|
||||||
help=_('Output commands received from child process to stdout')
|
help=_('Output commands received from child process to stdout')
|
||||||
)
|
)
|
||||||
|
if not isosx:
|
||||||
|
a(
|
||||||
|
'--detach',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help=_('Detach from the controlling terminal, if any')
|
||||||
|
)
|
||||||
a(
|
a(
|
||||||
'--replay-commands',
|
'--replay-commands',
|
||||||
default=None,
|
default=None,
|
||||||
@@ -188,7 +199,13 @@ def run_app(opts, args):
|
|||||||
else:
|
else:
|
||||||
viewport_size.width = opts.initial_window_width
|
viewport_size.width = opts.initial_window_width
|
||||||
viewport_size.height = opts.initial_window_height
|
viewport_size.height = opts.initial_window_height
|
||||||
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
try:
|
||||||
|
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
||||||
|
except ValueError:
|
||||||
|
safe_print('Failed to create GLFW window with initial size:', viewport_size)
|
||||||
|
viewport_size.width = 640
|
||||||
|
viewport_size.height = 400
|
||||||
|
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
||||||
window.set_title(appname)
|
window.set_title(appname)
|
||||||
window.make_context_current()
|
window.make_context_current()
|
||||||
if isosx:
|
if isosx:
|
||||||
@@ -241,11 +258,25 @@ def ensure_osx_locale():
|
|||||||
def main():
|
def main():
|
||||||
if isosx:
|
if isosx:
|
||||||
ensure_osx_locale()
|
ensure_osx_locale()
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except Exception:
|
||||||
|
if not isosx:
|
||||||
|
raise
|
||||||
|
print('Failed to set locale with LANG:', os.environ.get('LANG'), file=sys.stderr)
|
||||||
|
os.environ.pop('LANG')
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
except Exception:
|
||||||
|
print('Failed to set locale with no LANG, ignoring', file=sys.stderr)
|
||||||
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES',
|
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES',
|
||||||
None) == '1' and getattr(sys, 'frozen', True):
|
None) == '1' and getattr(sys, 'frozen', True):
|
||||||
os.chdir(os.path.expanduser('~'))
|
os.chdir(os.path.expanduser('~'))
|
||||||
|
if not os.path.isdir(os.getcwd()):
|
||||||
|
os.chdir(os.path.expanduser('~'))
|
||||||
args = option_parser().parse_args()
|
args = option_parser().parse_args()
|
||||||
|
if getattr(args, 'detach', False):
|
||||||
|
detach()
|
||||||
if args.cmd:
|
if args.cmd:
|
||||||
exec(args.cmd)
|
exec(args.cmd)
|
||||||
return
|
return
|
||||||
@@ -259,6 +290,8 @@ def main():
|
|||||||
change_wcwidth(not opts.use_system_wcwidth)
|
change_wcwidth(not opts.use_system_wcwidth)
|
||||||
glfw_set_error_callback(on_glfw_error)
|
glfw_set_error_callback(on_glfw_error)
|
||||||
enable_automatic_opengl_error_checking(args.debug_gl)
|
enable_automatic_opengl_error_checking(args.debug_gl)
|
||||||
|
if GLFW_X11_WM_CLASS_CLASS is not None:
|
||||||
|
glfw_init_hint_string(GLFW_X11_WM_CLASS_CLASS, opts.cls)
|
||||||
if not glfw_init():
|
if not glfw_init():
|
||||||
raise SystemExit('GLFW initialization failed')
|
raise SystemExit('GLFW initialization failed')
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -58,5 +58,5 @@ def encode_mouse_event(tracking_mode, tracking_protocol, button, action, mods, x
|
|||||||
ans = bytes(ans)
|
ans = bytes(ans)
|
||||||
else:
|
else:
|
||||||
if x <= 223 and y <= 223:
|
if x <= 223 and y <= 223:
|
||||||
ans = bytearray([0o33, ord('['), cb + 32, x + 32, y + 32])
|
ans = bytearray([0o33, ord('['), ord('M'), cb + 32, x + 32, y + 32])
|
||||||
return ans
|
return ans
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include <structmember.h>
|
#include <structmember.h>
|
||||||
|
#include <limits.h>
|
||||||
#include "unicode-data.h"
|
#include "unicode-data.h"
|
||||||
#include "tracker.h"
|
#include "tracker.h"
|
||||||
#include "modes.h"
|
#include "modes.h"
|
||||||
@@ -1332,6 +1333,14 @@ static PyGetSetDef getsetters[] = {
|
|||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if UINT_MAX == UINT32_MAX
|
||||||
|
#define T_COL T_UINT
|
||||||
|
#elif ULONG_MAX == UINT32_MAX
|
||||||
|
#define T_COL T_ULONG
|
||||||
|
#else
|
||||||
|
#error Neither int nor long is 4-bytes in size
|
||||||
|
#endif
|
||||||
|
|
||||||
static PyMemberDef members[] = {
|
static PyMemberDef members[] = {
|
||||||
{"callbacks", T_OBJECT_EX, offsetof(Screen, callbacks), 0, "callbacks"},
|
{"callbacks", T_OBJECT_EX, offsetof(Screen, callbacks), 0, "callbacks"},
|
||||||
{"cursor", T_OBJECT_EX, offsetof(Screen, cursor), READONLY, "cursor"},
|
{"cursor", T_OBJECT_EX, offsetof(Screen, cursor), READONLY, "cursor"},
|
||||||
@@ -1341,11 +1350,11 @@ static PyMemberDef members[] = {
|
|||||||
{"columns", T_UINT, offsetof(Screen, columns), READONLY, "columns"},
|
{"columns", T_UINT, offsetof(Screen, columns), READONLY, "columns"},
|
||||||
{"margin_top", T_UINT, offsetof(Screen, margin_top), READONLY, "margin_top"},
|
{"margin_top", T_UINT, offsetof(Screen, margin_top), READONLY, "margin_top"},
|
||||||
{"margin_bottom", T_UINT, offsetof(Screen, margin_bottom), READONLY, "margin_bottom"},
|
{"margin_bottom", T_UINT, offsetof(Screen, margin_bottom), READONLY, "margin_bottom"},
|
||||||
{"default_fg", T_ULONG, offsetof(Screen, default_fg), 0, "default_fg"},
|
{"default_fg", T_COL, offsetof(Screen, default_fg), 0, "default_fg"},
|
||||||
{"default_bg", T_ULONG, offsetof(Screen, default_bg), 0, "default_bg"},
|
{"default_bg", T_COL, offsetof(Screen, default_bg), 0, "default_bg"},
|
||||||
{"highlight_fg", T_ULONG, offsetof(Screen, highlight_fg), 0, "highlight_fg"},
|
{"highlight_fg", T_COL, offsetof(Screen, highlight_fg), 0, "highlight_fg"},
|
||||||
{"highlight_bg", T_ULONG, offsetof(Screen, highlight_bg), 0, "highlight_bg"},
|
{"highlight_bg", T_COL, offsetof(Screen, highlight_bg), 0, "highlight_bg"},
|
||||||
{"cursor_color", T_ULONG, offsetof(Screen, cursor_color), 0, "cursor_color"},
|
{"cursor_color", T_COL, offsetof(Screen, cursor_color), 0, "cursor_color"},
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,12 @@ class Tab:
|
|||||||
self.borders = Borders(opts)
|
self.borders = Borders(opts)
|
||||||
self.windows = deque()
|
self.windows = deque()
|
||||||
self.active_window_idx = 0
|
self.active_window_idx = 0
|
||||||
|
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
|
||||||
|
setattr(self, which + '_window', partial(self.nth_window, num=i))
|
||||||
if session_tab is None:
|
if session_tab is None:
|
||||||
self.cwd = args.directory
|
self.cwd = args.directory
|
||||||
l = self.enabled_layouts[0]
|
l = self.enabled_layouts[0]
|
||||||
|
self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows)
|
||||||
if special_window is None:
|
if special_window is None:
|
||||||
queue_action(self.new_window)
|
queue_action(self.new_window)
|
||||||
else:
|
else:
|
||||||
@@ -42,10 +45,8 @@ class Tab:
|
|||||||
else:
|
else:
|
||||||
self.cwd = session_tab.cwd or args.directory
|
self.cwd = session_tab.cwd or args.directory
|
||||||
l = session_tab.layout
|
l = session_tab.layout
|
||||||
|
self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows)
|
||||||
queue_action(self.startup, session_tab)
|
queue_action(self.startup, session_tab)
|
||||||
self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows)
|
|
||||||
for i, which in enumerate('first second third fourth fifth sixth seventh eighth ninth tenth'.split()):
|
|
||||||
setattr(self, which + '_window', partial(self.nth_window, num=i))
|
|
||||||
|
|
||||||
def startup(self, session_tab):
|
def startup(self, session_tab):
|
||||||
for cmd in session_tab.windows:
|
for cmd in session_tab.windows:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from functools import lru_cache
|
|||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
from .constants import isosx
|
from .constants import isosx
|
||||||
from .fast_data_types import glfw_get_physical_dpi, wcwidth as wcwidth_impl
|
from .fast_data_types import glfw_get_physical_dpi, wcwidth as wcwidth_impl, redirect_std_streams
|
||||||
from .rgb import Color, to_color
|
from .rgb import Color, to_color
|
||||||
|
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ def get_primary_selection():
|
|||||||
return '' # There is no primary selection on OS X
|
return '' # There is no primary selection on OS X
|
||||||
# glfw has no way to get the primary selection
|
# glfw has no way to get the primary selection
|
||||||
# https://github.com/glfw/glfw/issues/894
|
# https://github.com/glfw/glfw/issues/894
|
||||||
return subprocess.check_output(['xsel', '-p']).decode('utf-8')
|
return subprocess.check_output(['xsel', '-p'], stderr=open(os.devnull, 'wb'), stdin=open(os.devnull, 'rb')).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def base64_encode(
|
def base64_encode(
|
||||||
@@ -203,7 +203,7 @@ def set_primary_selection(text):
|
|||||||
return # There is no primary selection on OS X
|
return # There is no primary selection on OS X
|
||||||
if isinstance(text, str):
|
if isinstance(text, str):
|
||||||
text = text.encode('utf-8')
|
text = text.encode('utf-8')
|
||||||
p = subprocess.Popen(['xsel', '-i', '-p'], stdin=subprocess.PIPE)
|
p = subprocess.Popen(['xsel', '-i', '-p'], stdin=subprocess.PIPE, stdout=open(os.devnull, 'wb'), stderr=subprocess.STDOUT)
|
||||||
p.stdin.write(text), p.stdin.close()
|
p.stdin.write(text), p.stdin.close()
|
||||||
p.wait()
|
p.wait()
|
||||||
|
|
||||||
@@ -215,3 +215,14 @@ def open_url(url, program='default'):
|
|||||||
cmd = shlex.split(program)
|
cmd = shlex.split(program)
|
||||||
cmd.append(url)
|
cmd.append(url)
|
||||||
subprocess.Popen(cmd).wait()
|
subprocess.Popen(cmd).wait()
|
||||||
|
|
||||||
|
|
||||||
|
def detach(fork=True, setsid=True, redirect=True):
|
||||||
|
if fork:
|
||||||
|
# Detach from the controlling process.
|
||||||
|
if os.fork() != 0:
|
||||||
|
raise SystemExit(0)
|
||||||
|
if setsid:
|
||||||
|
os.setsid()
|
||||||
|
if redirect:
|
||||||
|
redirect_std_streams(os.devnull)
|
||||||
|
|||||||
Reference in New Issue
Block a user