Compare commits

..

13 Commits

Author SHA1 Message Date
Kovid Goyal
339dd9b412 version 0.2.3 2017-04-28 09:48:27 +05:30
Kovid Goyal
3f272d102b Test for toggling IUTF8 2017-04-28 09:41:47 +05:30
Kovid Goyal
29988c94fa Fix #70 2017-04-28 09:21:40 +05:30
Kovid Goyal
35f2b3254a A spot of refactoring 2017-04-28 08:54:06 +05:30
Kovid Goyal
898a8075be Fix #69 2017-04-28 08:31:07 +05:30
Kovid Goyal
f5d957e8ff Merge branch 'binor-patch-1' of https://github.com/binor/kitty 2017-04-07 08:24:33 +05:30
binor
2cc3cabd3f Fix mouse coordinates off on macOS Retina display 2017-04-06 19:53:14 +02:00
Kovid Goyal
be5185ab1a version 0.2.2 2017-04-05 09:12:10 +05:30
Kovid Goyal
01c289e440 Add ST test to OTH tests as well 2017-04-05 09:09:41 +05:30
Kovid Goyal
db2d14d9ed Fix backslashes in OSC codes not being parsed correctly
Fixes #61
2017-04-05 09:07:55 +05:30
Kovid Goyal
c504b96b60 Add a --dump-bytes option, useful for reproducing problems from users computers 2017-04-05 00:13:31 +05:30
Kovid Goyal
15c4b1961e Add a few missing commands to replay 2017-04-04 23:51:20 +05:30
Kovid Goyal
2cc20e4b27 Allow changing font size in a running terminal using keyboard shortcuts.
Fixes #57
2017-03-31 10:00:56 +05:30
16 changed files with 149 additions and 28 deletions

View File

@@ -14,6 +14,7 @@ from time import monotonic
from queue import Queue, Empty
from gettext import gettext as _
from .config import MINIMUM_FONT_SIZE
from .constants import (
viewport_size, set_boss, wakeup, cell_size, MODIFIER_KEYS,
main_thread, mouse_button_pressed, mouse_cursor_pos
@@ -86,6 +87,7 @@ class Boss(Thread):
self.pending_ui_thread_calls = Queue()
self.write_dispatch_map = {}
set_boss(self)
self.current_font_size = opts.font_size
cell_size.width, cell_size.height = set_font_family(opts)
self.opts, self.args = opts, args
self.glfw_window = glfw_window
@@ -228,6 +230,28 @@ class Boss(Thread):
self.pending_resize = False
glfw_post_empty_event()
def increase_font_size(self):
self.change_font_size(min(self.opts.font_size * 5, self.current_font_size + self.opts.font_size_delta))
def decrease_font_size(self):
self.change_font_size(max(MINIMUM_FONT_SIZE, self.current_font_size - self.opts.font_size_delta))
def restore_font_size(self):
self.change_font_size(self.opts.font_size)
def change_font_size(self, new_size):
if new_size == self.current_font_size:
return
self.current_font_size = new_size
cell_size.width, cell_size.height = set_font_family(
self.opts, override_font_size=self.current_font_size)
self.sprites.do_layout(cell_size.width, cell_size.height)
self.queue_action(self.resize_windows_after_font_size_change)
def resize_windows_after_font_size_change(self):
self.tab_manager.resize()
glfw_post_empty_event()
def tabbar_visibility_changed(self):
self.tab_manager.resize(only_tabs=True)
glfw_post_empty_event()
@@ -340,7 +364,7 @@ class Boss(Thread):
@callback
def on_mouse_move(self, window, xpos, ypos):
mouse_cursor_pos[:2] = int(xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio)
mouse_cursor_pos[:2] = xpos, ypos = int(xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio)
self.show_mouse_cursor()
w = self.window_for_pos(xpos, ypos)
if w is not None:

View File

@@ -10,6 +10,7 @@ import signal
from threading import Thread
from .constants import terminfo_dir
import kitty.fast_data_types as fast_data_types
def remove_cloexec(fd):
@@ -33,6 +34,7 @@ class Child:
self.forked = True
master, slave = os.openpty() # Note that master and slave are in blocking mode
remove_cloexec(slave)
self.set_iutf8(fd=master)
stdin, self.stdin = self.stdin, None
if stdin is not None:
stdin_read_fd, stdin_write_fd = os.pipe()
@@ -79,6 +81,16 @@ class Child:
if self.child_fd is not None:
fcntl.ioctl(self.child_fd, termios.TIOCSWINSZ, struct.pack('4H', h, w, ww, wh))
def set_iutf8(self, on=True, fd=None):
fd = fd or self.child_fd
if fd is not None and hasattr(fast_data_types, 'IUTF8'):
attrs = termios.tcgetattr(fd)
if on:
attrs[0] |= fast_data_types.IUTF8
else:
attrs[0] &= ~fast_data_types.IUTF8
termios.tcsetattr(fd, termios.TCSANOW, attrs)
def hangup(self):
if self.pid is not None:
pid, self.pid = self.pid, None

View File

@@ -19,10 +19,30 @@ def write(x):
sys.stdout.flush()
def set_title(*args):
pass
def set_icon(*args):
pass
def screen_bell():
pass
def screen_cursor_position(y, x):
write(CSI + '%s;%sH' % (y, x))
def screen_cursor_forward(amt):
write(CSI + '%sC' % amt)
def screen_cursor_back1(amt):
write(CSI + '%sD' % amt)
def screen_designate_charset(which, to):
which = '()'[int(which)]
to = chr(int(to))
@@ -57,6 +77,10 @@ def screen_erase_in_display(how, private):
write(CSI + ('?' if private else '') + str(how) + 'J')
def screen_erase_in_line(how, private):
write(CSI + ('?' if private else '') + str(how) + 'K')
def screen_cursor_up2(count):
write(CSI + '%dA' % count)
@@ -65,6 +89,10 @@ def screen_carriage_return():
write('\r')
def screen_linefeed():
write('\n')
def screen_backspace():
write('\x08')
@@ -77,8 +105,8 @@ def replay(raw):
for line in raw.splitlines():
if line.strip():
cmd, rest = line.partition(' ')[::2]
if cmd == 'draw':
draw(rest)
if cmd in {'draw', 'set_title', 'set_icon'}:
globals()[cmd](rest)
else:
rest = map(int, rest.split()) if rest else ()
globals()[cmd](*rest)

View File

@@ -17,10 +17,11 @@ from .layout import all_layouts
from .utils import safe_print, to_color
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
MINIMUM_FONT_SIZE = 6
def to_font_size(x):
return max(6, float(x))
return max(MINIMUM_FONT_SIZE, float(x))
cshapes = {
@@ -153,6 +154,7 @@ type_map = {
'scrollback_pager': shlex.split,
'scrollback_in_new_tab': to_bool,
'font_size': to_font_size,
'font_size_delta': float,
'cursor_shape': to_cursor_shape,
'cursor_opacity': to_opacity,
'open_url_modifiers': to_open_url_modifiers,

View File

@@ -15,7 +15,7 @@ from .fast_data_types import (
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
appname = 'kitty'
version = (0, 2, 1)
version = (0, 2, 3)
str_version = '.'.join(map(str, version))
_plat = sys.platform.lower()
isosx = 'darwin' in _plat

View File

@@ -56,6 +56,8 @@ static struct PyModuleDef module = {
.m_methods = module_methods
};
#include <termios.h>
PyMODINIT_FUNC
PyInit_fast_data_types(void) {
PyObject *m;
@@ -105,6 +107,9 @@ PyInit_fast_data_types(void) {
PyModule_AddIntMacro(m, NORMAL_PROTOCOL);
PyModule_AddIntMacro(m, URXVT_PROTOCOL);
PyModule_AddIntMacro(m, UTF8_PROTOCOL);
#ifdef IUTF8
PyModule_AddIntMacro(m, IUTF8);
#endif
}
return m;

View File

@@ -253,7 +253,7 @@ PyTypeObject ScreenModes_Type;
#define SAVEPOINTS_SZ 256
typedef struct {
uint32_t utf8_state, *g0_charset, *g1_charset, *g_charset;
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
bool use_latin1;
Cursor cursor;
bool mDECOM, mDECAWM, mDECSCNM;
@@ -274,7 +274,7 @@ typedef struct {
PyObject_HEAD
unsigned int columns, lines, margin_top, margin_bottom, charset;
uint32_t utf8_state, *g0_charset, *g1_charset, *g_charset;
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
bool use_latin1;
Cursor *cursor;
SavepointBuffer main_savepoints, alt_savepoints;
@@ -396,6 +396,7 @@ void screen_erase_characters(Screen *self, unsigned int count);
void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom);
void screen_change_charset(Screen *, uint32_t to);
void screen_designate_charset(Screen *, uint32_t which, uint32_t as);
void screen_use_latin1(Screen *, bool);
void set_title(Screen *self, PyObject*);
void set_icon(Screen *self, PyObject*);
void set_dynamic_color(Screen *self, unsigned int code, PyObject*);

View File

@@ -19,7 +19,7 @@ def install_symbol_map(val, font_size, dpi):
symbol_map[ch] = family_map[family]
def set_font_family(opts, ignore_dpi_failure=False):
def set_font_family(opts, override_font_size=None, ignore_dpi_failure=False):
global cell_width, cell_height, baseline, CellTexture, WideCellTexture, underline_thickness, underline_position
try:
dpi = get_logical_dpi()
@@ -37,11 +37,12 @@ def set_font_family(opts, ignore_dpi_failure=False):
if ans == 'auto' and (bold or italic):
ans = get_family(False, False)
return ans
font_size = override_font_size or opts.font_size
for bold in (False, True):
for italic in (False, True):
main_font[(bold, italic)] = Face(get_family(bold, italic), bold, italic, True, opts.font_size, dpi)
install_symbol_map(opts.symbol_map, opts.font_size, dpi)
main_font[(bold, italic)] = Face(get_family(bold, italic), bold, italic, True, font_size, dpi)
install_symbol_map(opts.symbol_map, font_size, dpi)
mf = main_font[(False, False)]
cell_width, cell_height = mf.cell_size()
CellTexture = ctypes.c_ubyte * (cell_width * cell_height)

View File

@@ -71,10 +71,10 @@ def font_units_to_pixels(x, units_per_em, size_in_pts, dpi):
return ceil_int(x * ((size_in_pts * dpi) / (72 * units_per_em)))
def set_font_family(opts):
def set_font_family(opts, override_font_size=None):
global current_font_family, current_font_family_name, cff_size, cell_width, cell_height, CharTexture, baseline
global underline_position, underline_thickness
size_in_pts = opts.font_size
size_in_pts = override_font_size or opts.font_size
current_font_family = get_font_files(opts)
current_font_family_name = opts.font_family
dpi = get_logical_dpi()

View File

@@ -15,6 +15,10 @@ bold_italic_font auto
# Font size (in pts)
font_size 11.0
# The amount the font size is changed by (in pts) when increasing/decreasing
# the font size in a running terminal.
font_size_delta 2
# The foreground color
foreground #dddddd
@@ -215,6 +219,10 @@ map ctrl+shift+l next_layout
map ctrl+shift+. move_tab_forward
map ctrl+shift+, move_tab_backward
# Miscellaneous
map ctrl+shift+equal increase_font_size
map ctrl+shift+minus decrease_font_size
map ctrl+shift+backspace restore_font_size
# Symbol mapping (special font for specified unicode code points). Map the
# specified unicode codepoints to a particular font. Useful if you need special

View File

@@ -98,6 +98,11 @@ def option_parser():
default=None,
help=_('Replay previously dumped commands')
)
a(
'--dump-bytes',
help=_('Path to file in which to store the raw bytes received from the'
' child process. Useful for debugging.')
)
a(
'--debug-gl',
action='store_true',

View File

@@ -225,10 +225,12 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal
switch(ch) {
case '@':
REPORT_COMMAND(screen_use_latin1, 1);
screen->use_latin1 = true; screen->utf8_state = 0; break;
screen_use_latin1(screen, true);
break;
case 'G':
REPORT_COMMAND(screen_use_latin1, 0);
screen->use_latin1 = false; screen->utf8_state = 0; break;
screen_use_latin1(screen, false);
break;
default:
REPORT_ERROR("Unhandled Esc %% code: 0x%x", ch); break;
}
@@ -519,16 +521,16 @@ accumulate_osc(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback)
switch(ch) {
case ST:
return true;
case ESC_ST:
if (screen->parser_buf_pos > 0 && screen->parser_buf[screen->parser_buf_pos - 1] == ESC) {
screen->parser_buf_pos--;
return true;
}
case BEL:
return true;
case NUL:
case DEL:
break;
case ESC_ST:
if (screen->parser_buf_pos > 0 && screen->parser_buf[screen->parser_buf_pos - 1] == ESC) {
screen->parser_buf_pos--;
return true;
}
default:
if (screen->parser_buf_pos >= PARSER_BUF_SZ - 1) {
REPORT_ERROR("OSC sequence too long, truncating.");
@@ -688,17 +690,17 @@ dispatch_unicode_char(Screen *screen, uint32_t codepoint, PyObject DUMP_UNUSED *
static inline void
_parse_bytes(Screen *screen, uint8_t *buf, Py_ssize_t len, PyObject DUMP_UNUSED *dump_callback) {
uint32_t prev = screen->utf8_state, codepoint = 0;
uint32_t prev = screen->utf8_state;
for (unsigned int i = 0; i < len; i++) {
if (screen->use_latin1) dispatch_unicode_char(screen, latin1_charset[buf[i]], dump_callback);
else {
switch (decode_utf8(&screen->utf8_state, &codepoint, buf[i])) {
switch (decode_utf8(&screen->utf8_state, &screen->utf8_codepoint, buf[i])) {
case UTF8_ACCEPT:
dispatch_unicode_char(screen, codepoint, dump_callback);
dispatch_unicode_char(screen, screen->utf8_codepoint, dump_callback);
break;
case UTF8_REJECT:
screen->utf8_state = UTF8_ACCEPT;
if (prev != UTF8_ACCEPT) i--;
if (prev != UTF8_ACCEPT && i > 0) i--;
break;
}
prev = screen->utf8_state;
@@ -753,6 +755,11 @@ FNAME(read_bytes)(PyObject UNUSED *self, PyObject *args) {
/* PyObject_Print(Py_BuildValue("y#", screen->read_buf, len), stderr, 0); */
break;
}
#ifdef DUMP_COMMANDS
if (len > 0) {
Py_XDECREF(PyObject_CallFunction(dump_callback, "sy#", "bytes", screen->read_buf, len)); PyErr_Clear();
}
#endif
_parse_bytes(screen, screen->read_buf, len, dump_callback);
if(len > 0) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;

View File

@@ -29,6 +29,7 @@ init_tabstops(bool *tabstops, index_type count) {
self->g1_charset = self->g0_charset; \
self->g_charset = self->g0_charset; \
self->utf8_state = 0; \
self->utf8_codepoint = 0; \
self->use_latin1 = false;
static PyObject*
@@ -638,6 +639,7 @@ savepoints_pop(SavepointBuffer *self) {
#define COPY_CHARSETS(self, sp) \
sp->utf8_state = self->utf8_state; \
sp->utf8_codepoint = self->utf8_codepoint; \
sp->g0_charset = self->g0_charset; \
sp->g1_charset = self->g1_charset; \
sp->g_charset = self->g_charset; \
@@ -855,6 +857,14 @@ void screen_erase_characters(Screen *self, unsigned int count) {
// Device control {{{
void
screen_use_latin1(Screen *self, bool on) {
self->use_latin1 = on; self->utf8_state = 0; self->utf8_codepoint = 0;
PyObject_CallMethod(self->callbacks, "use_utf8", "O", on ? Py_False : Py_True);
if (PyErr_Occurred()) PyErr_Print();
PyErr_Clear();
}
void
screen_bell(Screen UNUSED *self) {
PyObject_CallMethod(self->callbacks, "bell", NULL);

View File

@@ -39,7 +39,9 @@ class Window:
self.child_fd = child.child_fd
self.start_visual_bell_at = None
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
self.read_bytes = partial(read_bytes_dump, self.dump_commands) if args.dump_commands else read_bytes
self.read_bytes = partial(read_bytes_dump, self.dump_commands) if args.dump_commands or args.dump_bytes else read_bytes
if args.dump_bytes:
self.dump_bytes_to = open(args.dump_bytes, 'ab')
self.draw_dump_buf = []
self.write_buf = memoryview(b'')
self.char_grid = CharGrid(self.screen, opts)
@@ -118,6 +120,9 @@ class Window:
self.start_visual_bell_at = monotonic()
glfw_post_empty_event()
def use_utf8(self, on):
self.child.set_iutf8(on)
def update_screen(self):
self.char_grid.update_cell_data()
glfw_post_empty_event()
@@ -347,6 +352,9 @@ class Window:
self.draw_dump_buf = []
else:
self.draw_dump_buf.append(a[1])
elif a[0] == 'bytes':
self.dump_bytes_to.write(a[1])
self.dump_bytes_to.flush()
else:
if self.draw_dump_buf:
safe_print('draw', ''.join(self.draw_dump_buf))

View File

@@ -33,10 +33,14 @@ class Callbacks:
def buf_toggled(self, is_alt):
self.is_alt = is_alt
def use_utf8(self, on):
self.iutf8 = on
def clear(self):
self.wtcbuf = b''
self.iconbuf = self.titlebuf = self.colorbuf = self.qbuf = self.ctbuf = ''
self.is_alt = False
self.iutf8 = True
def filled_line_buf(ynum=5, xnum=5, cursor=Cursor()):

View File

@@ -72,11 +72,17 @@ class TestParser(BaseTest):
self.ae(str(s.line(0)), '123 ')
def test_charsets(self):
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
pb(b'\xc3')
pb(b'\xa1', ('draw', b'\xc3\xa1'.decode('utf-8')))
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
pb('\033)0\x0e/_', ('screen_designate_charset', 1, ord('0')), ('screen_change_charset', 1), '/_')
self.ae(str(s.line(0)), '/\xa0 ')
pb('\033%G_', ('screen_use_latin1', 0), '_')
self.assertTrue(s.callbacks.iutf8)
pb('\033%@_', ('screen_use_latin1', 1), '_')
self.assertFalse(s.callbacks.iutf8)
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
pb('\033(0/_', ('screen_designate_charset', 0, ord('0')), '/_')
@@ -156,9 +162,9 @@ class TestParser(BaseTest):
s = self.create_screen()
pb = partial(self.parse_bytes_dump, s)
c = s.callbacks
pb('a\033]2;xyz\x9cbcde', 'a', ('set_title', 'xyz'), 'bcde')
pb('a\033]2;x\\ryz\x9cbcde', 'a', ('set_title', 'x\\ryz'), 'bcde')
self.ae(str(s.line(0)), 'abcde')
self.ae(c.titlebuf, 'xyz')
self.ae(c.titlebuf, 'x\\ryz')
c.clear()
pb('\033]\x07', ('set_title', ''), ('set_icon', ''))
self.ae(c.titlebuf, ''), self.ae(c.iconbuf, '')
@@ -181,4 +187,4 @@ class TestParser(BaseTest):
pb = partial(self.parse_bytes_dump, s)
for prefix in '\033_', '\033^', '\u009e', '\u009f':
for suffix in '\u009c', '\033\\':
pb('a{}+++{}bcde'.format(prefix, suffix), 'abcde')
pb('a{}+\\++{}bcde'.format(prefix, suffix), 'abcde')