Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d2a720fb9 | ||
|
|
9ed980a511 | ||
|
|
1365eafce5 | ||
|
|
07b70f8c95 | ||
|
|
f16bf2a136 | ||
|
|
0f2bcfabb3 | ||
|
|
d3e8c616b0 | ||
|
|
99888996a0 | ||
|
|
4c94e16936 | ||
|
|
235f3940f2 | ||
|
|
e86c2f2000 | ||
|
|
65b212fa1b | ||
|
|
4b0ff03868 | ||
|
|
8961eaaa9d | ||
|
|
356722b9a6 | ||
|
|
89e5ae28bb | ||
|
|
b40e1e6492 | ||
|
|
4dc6918b13 |
@@ -3,6 +3,23 @@ Changelog
|
|||||||
|
|
||||||
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||||
|
|
||||||
|
version 0.5.1 [2017-12-01]
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
- Add an option to control the thickness of lines in box drawing characters
|
||||||
|
|
||||||
|
- Increase max. allowed ligature length to nine characters
|
||||||
|
|
||||||
|
- Fix text not vertically centered when adjusting line height
|
||||||
|
|
||||||
|
- Fix unicode block characters not being rendered properly
|
||||||
|
|
||||||
|
- Fix shift+up/down not generating correct escape codes
|
||||||
|
|
||||||
|
- Image display: Fix displaying images taller than two screen heights not
|
||||||
|
scrolling properly
|
||||||
|
|
||||||
|
|
||||||
version 0.5.0 [2017-11-19]
|
version 0.5.0 [2017-11-19]
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from .fast_data_types import (
|
|||||||
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
|
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
|
||||||
)
|
)
|
||||||
from .fonts.render import prerender, resize_fonts, set_font_family
|
from .fonts.render import prerender, resize_fonts, set_font_family
|
||||||
from .keys import get_key_map, get_sent_data, get_shortcut
|
from .keys import get_key_map, get_shortcut
|
||||||
from .session import create_session
|
from .session import create_session
|
||||||
from .tabs import SpecialWindow, TabManager
|
from .tabs import SpecialWindow, TabManager
|
||||||
from .utils import (
|
from .utils import (
|
||||||
@@ -199,13 +199,6 @@ class Boss:
|
|||||||
passthrough = f(*key_action.args)
|
passthrough = f(*key_action.args)
|
||||||
if passthrough is not True:
|
if passthrough is not True:
|
||||||
return True
|
return True
|
||||||
key, scancode, action, mods = self.current_key_press_info
|
|
||||||
data = get_sent_data(
|
|
||||||
self.opts.send_text_map, key, scancode, mods, window, action
|
|
||||||
)
|
|
||||||
if data:
|
|
||||||
window.write_to_child(data)
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def combine(self, *actions):
|
def combine(self, *actions):
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ resize_pty(ChildMonitor *self, PyObject *args) {
|
|||||||
if (fd == -1) FIND(add_queue, add_queue_count);
|
if (fd == -1) FIND(add_queue, add_queue_count);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
|
if (!pty_resize(fd, &dim)) PyErr_SetFromErrno(PyExc_OSError);
|
||||||
} else fprintf(stderr, "Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %lu)\n", window_id, self->count, add_queue_count);
|
} else fprintf(stderr, "Failed to send resize signal to child with id: %lu (children count: %u) (add queue: %zu)\n", window_id, self->count, add_queue_count);
|
||||||
children_mutex(unlock);
|
children_mutex(unlock);
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from .rgb import to_color
|
|||||||
from .utils import safe_print
|
from .utils import safe_print
|
||||||
|
|
||||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||||
MINIMUM_FONT_SIZE = 6
|
MINIMUM_FONT_SIZE = 4
|
||||||
|
|
||||||
|
|
||||||
def to_font_size(x):
|
def to_font_size(x):
|
||||||
@@ -175,7 +175,7 @@ def parse_send_text_bytes(text):
|
|||||||
return ast.literal_eval("'''" + text + "'''").encode('utf-8')
|
return ast.literal_eval("'''" + text + "'''").encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def parse_send_text(val):
|
def parse_send_text(val, keymap):
|
||||||
parts = val.split(' ')
|
parts = val.split(' ')
|
||||||
|
|
||||||
def abort(msg):
|
def abort(msg):
|
||||||
@@ -187,28 +187,10 @@ def parse_send_text(val):
|
|||||||
|
|
||||||
if len(parts) < 3:
|
if len(parts) < 3:
|
||||||
return abort('Incomplete')
|
return abort('Incomplete')
|
||||||
|
|
||||||
text = ' '.join(parts[2:])
|
|
||||||
mode, sc = parts[:2]
|
mode, sc = parts[:2]
|
||||||
mods, key = parse_shortcut(sc.strip())
|
text = ' '.join(parts[2:])
|
||||||
if key is None:
|
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
||||||
return abort('Invalid shortcut')
|
return parse_key(key_str, keymap)
|
||||||
text = parse_send_text_bytes(text)
|
|
||||||
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):
|
||||||
@@ -238,6 +220,13 @@ def adjust_line_height(x):
|
|||||||
return int(x)
|
return int(x)
|
||||||
|
|
||||||
|
|
||||||
|
def box_drawing_scale(x):
|
||||||
|
ans = tuple(float(x.strip()) for x in x.split(','))
|
||||||
|
if len(ans) != 4:
|
||||||
|
raise ValueError('Invalid box_drawing scale, must have four entries')
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
type_map = {
|
type_map = {
|
||||||
'adjust_line_height': adjust_line_height,
|
'adjust_line_height': adjust_line_height,
|
||||||
'scrollback_lines': positive_int,
|
'scrollback_lines': positive_int,
|
||||||
@@ -267,6 +256,7 @@ type_map = {
|
|||||||
'use_system_wcwidth': to_bool,
|
'use_system_wcwidth': to_bool,
|
||||||
'macos_hide_titlebar': to_bool,
|
'macos_hide_titlebar': to_bool,
|
||||||
'macos_option_as_alt': to_bool,
|
'macos_option_as_alt': to_bool,
|
||||||
|
'box_drawing_scale': box_drawing_scale,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in (
|
for name in (
|
||||||
@@ -285,11 +275,6 @@ def parse_config(lines, check_keys=True):
|
|||||||
ans = {
|
ans = {
|
||||||
'keymap': {},
|
'keymap': {},
|
||||||
'symbol_map': {},
|
'symbol_map': {},
|
||||||
'send_text_map': {
|
|
||||||
'kitty': {},
|
|
||||||
'normal': {},
|
|
||||||
'application': {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if check_keys:
|
if check_keys:
|
||||||
all_keys = defaults._asdict()
|
all_keys = defaults._asdict()
|
||||||
@@ -307,9 +292,8 @@ def parse_config(lines, check_keys=True):
|
|||||||
ans['symbol_map'].update(parse_symbol_map(val))
|
ans['symbol_map'].update(parse_symbol_map(val))
|
||||||
continue
|
continue
|
||||||
if key == 'send_text':
|
if key == 'send_text':
|
||||||
stvals = parse_send_text(val)
|
# For legacy compatibility
|
||||||
for k, v in ans['send_text_map'].items():
|
parse_send_text(val, ans['keymap'])
|
||||||
v.update(stvals.get(k, {}))
|
|
||||||
continue
|
continue
|
||||||
if check_keys:
|
if check_keys:
|
||||||
if key not in all_keys:
|
if key not in all_keys:
|
||||||
@@ -360,11 +344,6 @@ def merge_configs(defaults, vals):
|
|||||||
newvals = vals.get(k, {})
|
newvals = vals.get(k, {})
|
||||||
if k == 'keymap':
|
if k == 'keymap':
|
||||||
ans['keymap'] = merge_keymaps(v, newvals)
|
ans['keymap'] = merge_keymaps(v, newvals)
|
||||||
elif k == 'send_text_map':
|
|
||||||
ans[k] = {
|
|
||||||
m: merge_dicts(mm, newvals.get(m, {}))
|
|
||||||
for m, mm in v.items()
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
ans[k] = merge_dicts(v, newvals)
|
ans[k] = merge_dicts(v, newvals)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from collections import namedtuple
|
|||||||
from .fast_data_types import set_boss as set_c_boss
|
from .fast_data_types import set_boss as set_c_boss
|
||||||
|
|
||||||
appname = 'kitty'
|
appname = 'kitty'
|
||||||
version = (0, 5, 0)
|
version = (0, 5, 1)
|
||||||
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
|
||||||
@@ -67,7 +67,11 @@ cell_size = ViewportSize()
|
|||||||
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
terminfo_dir = os.path.join(base_dir, 'terminfo')
|
terminfo_dir = os.path.join(base_dir, 'terminfo')
|
||||||
logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
|
logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
|
||||||
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
try:
|
||||||
|
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
||||||
|
except KeyError:
|
||||||
|
print('Failed to read login shell from /etc/passwd for current user, falling back to /bin/sh', file=sys.stderr)
|
||||||
|
shell_path = '/bin/sh'
|
||||||
|
|
||||||
GLint = ctypes.c_int if ctypes.sizeof(ctypes.c_int) == 4 else ctypes.c_long
|
GLint = ctypes.c_int if ctypes.sizeof(ctypes.c_int) == 4 else ctypes.c_long
|
||||||
GLuint = ctypes.c_uint if ctypes.sizeof(ctypes.c_uint) == 4 else ctypes.c_ulong
|
GLuint = ctypes.c_uint if ctypes.sizeof(ctypes.c_uint) == 4 else ctypes.c_ulong
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
#define MISSING_GLYPH 4
|
#define MISSING_GLYPH 4
|
||||||
|
#define MAX_NUM_EXTRA_GLYPHS 8
|
||||||
|
|
||||||
typedef uint16_t glyph_index;
|
typedef uint16_t glyph_index;
|
||||||
typedef void (*send_sprite_to_gpu_func)(unsigned int, unsigned int, unsigned int, uint8_t*);
|
typedef void (*send_sprite_to_gpu_func)(unsigned int, unsigned int, unsigned int, uint8_t*);
|
||||||
@@ -21,13 +22,17 @@ typedef struct SpecialGlyphCache SpecialGlyphCache;
|
|||||||
enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
|
enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
glyph_index data[MAX_NUM_EXTRA_GLYPHS];
|
||||||
|
} ExtraGlyphs;
|
||||||
|
|
||||||
struct SpritePosition {
|
struct SpritePosition {
|
||||||
SpritePosition *next;
|
SpritePosition *next;
|
||||||
bool filled, rendered;
|
bool filled, rendered;
|
||||||
sprite_index x, y, z;
|
sprite_index x, y, z;
|
||||||
uint8_t ligature_index;
|
uint8_t ligature_index;
|
||||||
glyph_index glyph;
|
glyph_index glyph;
|
||||||
uint64_t extra_glyphs;
|
ExtraGlyphs extra_glyphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpecialGlyphCache {
|
struct SpecialGlyphCache {
|
||||||
@@ -70,6 +75,7 @@ typedef struct {
|
|||||||
|
|
||||||
static Fonts fonts = {0};
|
static Fonts fonts = {0};
|
||||||
|
|
||||||
|
|
||||||
// Sprites {{{
|
// Sprites {{{
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@@ -104,15 +110,25 @@ do_increment(int *error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
extra_glyphs_equal(ExtraGlyphs *a, ExtraGlyphs *b) {
|
||||||
|
for (size_t i = 0; i < MAX_NUM_EXTRA_GLYPHS; i++) {
|
||||||
|
if (a->data[i] != b->data[i]) return false;
|
||||||
|
if (a->data[i] == 0) return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static SpritePosition*
|
static SpritePosition*
|
||||||
sprite_position_for(Font *font, glyph_index glyph, uint64_t extra_glyphs, uint8_t ligature_index, int *error) {
|
sprite_position_for(Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, uint8_t ligature_index, int *error) {
|
||||||
glyph_index idx = glyph & 0x3ff;
|
glyph_index idx = glyph & 0x3ff;
|
||||||
SpritePosition *s = font->sprite_map + idx;
|
SpritePosition *s = font->sprite_map + idx;
|
||||||
// Optimize for the common case of glyph under 1024 already in the cache
|
// Optimize for the common case of glyph under 1024 already in the cache
|
||||||
if (LIKELY(s->glyph == glyph && s->filled && s->extra_glyphs == extra_glyphs && s->ligature_index == ligature_index)) return s; // Cache hit
|
if (LIKELY(s->glyph == glyph && s->filled && extra_glyphs_equal(&s->extra_glyphs, extra_glyphs) && s->ligature_index == ligature_index)) return s; // Cache hit
|
||||||
while(true) {
|
while(true) {
|
||||||
if (s->filled) {
|
if (s->filled) {
|
||||||
if (s->glyph == glyph && s->extra_glyphs == extra_glyphs && s->ligature_index == ligature_index) return s; // Cache hit
|
if (s->glyph == glyph && extra_glyphs_equal(&s->extra_glyphs, extra_glyphs) && s->ligature_index == ligature_index) return s; // Cache hit
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -123,7 +139,7 @@ sprite_position_for(Font *font, glyph_index glyph, uint64_t extra_glyphs, uint8_
|
|||||||
s = s->next;
|
s = s->next;
|
||||||
}
|
}
|
||||||
s->glyph = glyph;
|
s->glyph = glyph;
|
||||||
s->extra_glyphs = extra_glyphs;
|
memcpy(&s->extra_glyphs, extra_glyphs, sizeof(ExtraGlyphs));
|
||||||
s->ligature_index = ligature_index;
|
s->ligature_index = ligature_index;
|
||||||
s->filled = true;
|
s->filled = true;
|
||||||
s->rendered = false;
|
s->rendered = false;
|
||||||
@@ -179,7 +195,7 @@ free_maps(Font *font) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
clear_sprite_map(Font *font) {
|
clear_sprite_map(Font *font) {
|
||||||
#define CLEAR(s) s->filled = false; s->rendered = false; s->glyph = 0; s->extra_glyphs = 0; s->x = 0; s->y = 0; s->z = 0; s->ligature_index = 0;
|
#define CLEAR(s) s->filled = false; s->rendered = false; s->glyph = 0; memset(&s->extra_glyphs, 0, sizeof(ExtraGlyphs)); s->x = 0; s->y = 0; s->z = 0; s->ligature_index = 0;
|
||||||
SpritePosition *s;
|
SpritePosition *s;
|
||||||
for (size_t i = 0; i < sizeof(font->sprite_map)/sizeof(font->sprite_map[0]); i++) {
|
for (size_t i = 0; i < sizeof(font->sprite_map)/sizeof(font->sprite_map[0]); i++) {
|
||||||
s = font->sprite_map + i;
|
s = font->sprite_map + i;
|
||||||
@@ -246,7 +262,7 @@ del_font(Font *f) {
|
|||||||
|
|
||||||
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
|
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
|
||||||
static uint8_t *canvas = NULL;
|
static uint8_t *canvas = NULL;
|
||||||
#define CELLS_IN_CANVAS 16
|
#define CELLS_IN_CANVAS ((MAX_NUM_EXTRA_GLYPHS + 1) * 3)
|
||||||
static inline void
|
static inline void
|
||||||
clear_canvas(void) { memset(canvas, 0, CELLS_IN_CANVAS * cell_width * cell_height); }
|
clear_canvas(void) { memset(canvas, 0, CELLS_IN_CANVAS * cell_width * cell_height); }
|
||||||
|
|
||||||
@@ -267,11 +283,17 @@ update_cell_metrics() {
|
|||||||
CALL(fonts.bold_font_idx, 0, false); CALL(fonts.italic_font_idx, 0, false); CALL(fonts.bi_font_idx, 0, false);
|
CALL(fonts.bold_font_idx, 0, false); CALL(fonts.italic_font_idx, 0, false); CALL(fonts.bi_font_idx, 0, false);
|
||||||
cell_metrics(fonts.fonts[fonts.medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
|
cell_metrics(fonts.fonts[fonts.medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness);
|
||||||
if (!cell_width) { PyErr_SetString(PyExc_ValueError, "Failed to calculate cell width for the specified font."); return NULL; }
|
if (!cell_width) { PyErr_SetString(PyExc_ValueError, "Failed to calculate cell width for the specified font."); return NULL; }
|
||||||
|
unsigned int before_cell_height = cell_height;
|
||||||
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
|
if (OPT(adjust_line_height_px) != 0) cell_height += OPT(adjust_line_height_px);
|
||||||
if (OPT(adjust_line_height_frac) != 0.f) cell_height *= OPT(adjust_line_height_frac);
|
if (OPT(adjust_line_height_frac) != 0.f) cell_height *= OPT(adjust_line_height_frac);
|
||||||
|
int line_height_adjustment = cell_height - before_cell_height;
|
||||||
if (cell_height < 4) { PyErr_SetString(PyExc_ValueError, "line height too small after adjustment"); return NULL; }
|
if (cell_height < 4) { PyErr_SetString(PyExc_ValueError, "line height too small after adjustment"); return NULL; }
|
||||||
if (cell_height > 1000) { PyErr_SetString(PyExc_ValueError, "line height too large after adjustment"); return NULL; }
|
if (cell_height > 1000) { PyErr_SetString(PyExc_ValueError, "line height too large after adjustment"); return NULL; }
|
||||||
underline_position = MIN(cell_height - 1, underline_position);
|
underline_position = MIN(cell_height - 1, underline_position);
|
||||||
|
if (line_height_adjustment > 1) {
|
||||||
|
baseline += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2);
|
||||||
|
underline_position += MIN(cell_height - 1, (unsigned)line_height_adjustment / 2);
|
||||||
|
}
|
||||||
sprite_tracker_set_layout(cell_width, cell_height);
|
sprite_tracker_set_layout(cell_width, cell_height);
|
||||||
global_state.cell_width = cell_width; global_state.cell_height = cell_height;
|
global_state.cell_width = cell_width; global_state.cell_height = cell_height;
|
||||||
free(canvas); canvas = malloc(CELLS_IN_CANVAS * cell_width * cell_height);
|
free(canvas); canvas = malloc(CELLS_IN_CANVAS * cell_width * cell_height);
|
||||||
@@ -360,7 +382,7 @@ START_ALLOW_CASE_RANGE
|
|||||||
case ' ':
|
case ' ':
|
||||||
return BLANK_FONT;
|
return BLANK_FONT;
|
||||||
case 0x2500 ... 0x2570:
|
case 0x2500 ... 0x2570:
|
||||||
case 0x2574 ... 0x257f:
|
case 0x2574 ... 0x259f:
|
||||||
case 0xe0b0:
|
case 0xe0b0:
|
||||||
case 0xe0b2:
|
case 0xe0b2:
|
||||||
return BOX_FONT;
|
return BOX_FONT;
|
||||||
@@ -393,14 +415,14 @@ static inline glyph_index
|
|||||||
box_glyph_id(char_type ch) {
|
box_glyph_id(char_type ch) {
|
||||||
START_ALLOW_CASE_RANGE
|
START_ALLOW_CASE_RANGE
|
||||||
switch(ch) {
|
switch(ch) {
|
||||||
case 0x2500 ... 0x257f:
|
case 0x2500 ... 0x259f:
|
||||||
return ch - 0x2500;
|
return ch - 0x2500;
|
||||||
case 0xe0b0:
|
case 0xe0b0:
|
||||||
return 0x80;
|
return 0xfa;
|
||||||
case 0xe0b2:
|
case 0xe0b2:
|
||||||
return 0x81;
|
return 0xfb;
|
||||||
default:
|
default:
|
||||||
return 0x82;
|
return 0xff;
|
||||||
}
|
}
|
||||||
END_ALLOW_CASE_RANGE
|
END_ALLOW_CASE_RANGE
|
||||||
}
|
}
|
||||||
@@ -411,7 +433,8 @@ static void
|
|||||||
render_box_cell(Cell *cell) {
|
render_box_cell(Cell *cell) {
|
||||||
int error = 0;
|
int error = 0;
|
||||||
glyph_index glyph = box_glyph_id(cell->ch);
|
glyph_index glyph = box_glyph_id(cell->ch);
|
||||||
SpritePosition *sp = sprite_position_for(&fonts.fonts[BOX_FONT], glyph, 0, false, &error);
|
static ExtraGlyphs extra_glyphs = {{0}};
|
||||||
|
SpritePosition *sp = sprite_position_for(&fonts.fonts[BOX_FONT], glyph, &extra_glyphs, false, &error);
|
||||||
if (sp == NULL) {
|
if (sp == NULL) {
|
||||||
sprite_map_set_error(error); PyErr_Print();
|
sprite_map_set_error(error); PyErr_Print();
|
||||||
set_sprite(cell, 0, 0, 0);
|
set_sprite(cell, 0, 0, 0);
|
||||||
@@ -462,7 +485,7 @@ extract_cell_from_canvas(unsigned int i, unsigned int num_cells) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, uint64_t extra_glyphs) {
|
render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) {
|
||||||
static SpritePosition* sprite_position[16];
|
static SpritePosition* sprite_position[16];
|
||||||
int error = 0;
|
int error = 0;
|
||||||
num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells);
|
num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells);
|
||||||
@@ -550,7 +573,7 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline glyph_index
|
static inline glyph_index
|
||||||
next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_glyphs, Cell *cells, hb_glyph_info_t *info, unsigned int max_num_glyphs, unsigned int max_num_cells, uint64_t *extra_glyphs) {
|
next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_glyphs, Cell *cells, hb_glyph_info_t *info, unsigned int max_num_glyphs, unsigned int max_num_cells, ExtraGlyphs *extra_glyphs) {
|
||||||
// See https://github.com/behdad/harfbuzz/issues/615 for a discussion of
|
// See https://github.com/behdad/harfbuzz/issues/615 for a discussion of
|
||||||
// how to break text into cells. In addition, we have to deal with
|
// how to break text into cells. In addition, we have to deal with
|
||||||
// monospace ligature fonts that use dummy glyphs of zero size to implement
|
// monospace ligature fonts that use dummy glyphs of zero size to implement
|
||||||
@@ -558,7 +581,7 @@ next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_gl
|
|||||||
|
|
||||||
CellData cell_data;
|
CellData cell_data;
|
||||||
cell_data.cell = cells; cell_data.num_codepoints = num_codepoints_in_cell(cells); cell_data.codepoints_consumed = 0; cell_data.current_codepoint = cells->ch;
|
cell_data.cell = cells; cell_data.num_codepoints = num_codepoints_in_cell(cells); cell_data.codepoints_consumed = 0; cell_data.current_codepoint = cells->ch;
|
||||||
#define LIMIT 5
|
#define LIMIT (MAX_NUM_EXTRA_GLYPHS + 1)
|
||||||
glyph_index glyphs_in_group[LIMIT];
|
glyph_index glyphs_in_group[LIMIT];
|
||||||
unsigned int ncells = 0, nglyphs = 0, n;
|
unsigned int ncells = 0, nglyphs = 0, n;
|
||||||
uint32_t previous_cluster = UINT32_MAX, cluster;
|
uint32_t previous_cluster = UINT32_MAX, cluster;
|
||||||
@@ -584,27 +607,16 @@ next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_gl
|
|||||||
}
|
}
|
||||||
*num_group_cells = MAX(1, MIN(ncells, cell_limit));
|
*num_group_cells = MAX(1, MIN(ncells, cell_limit));
|
||||||
*num_group_glyphs = MAX(1, MIN(nglyphs, glyph_limit));
|
*num_group_glyphs = MAX(1, MIN(nglyphs, glyph_limit));
|
||||||
|
memset(extra_glyphs, 0, sizeof(ExtraGlyphs));
|
||||||
|
|
||||||
#define G(n) ((uint64_t)(glyphs_in_group[n] & 0xffff))
|
|
||||||
switch(nglyphs) {
|
switch(nglyphs) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
*extra_glyphs = 0;
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
default:
|
||||||
*extra_glyphs = G(1);
|
memcpy(&extra_glyphs->data, glyphs_in_group + 1, sizeof(glyph_index) * MIN(MAX_NUM_EXTRA_GLYPHS, nglyphs - 1));
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
*extra_glyphs = G(1) | (G(2) << 16);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
*extra_glyphs = G(1) | (G(2) << 16) | (G(3) << 32);
|
|
||||||
break;
|
|
||||||
default: // we only support a maximum of four extra glyphs per cell
|
|
||||||
*extra_glyphs = G(1) | (G(2) << 16) | (G(3) << 32) | (G(4) << 48);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#undef G
|
|
||||||
#undef LIMIT
|
#undef LIMIT
|
||||||
return glyphs_in_group[0];
|
return glyphs_in_group[0];
|
||||||
}
|
}
|
||||||
@@ -633,11 +645,12 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) {
|
|||||||
clear_canvas();
|
clear_canvas();
|
||||||
#endif
|
#endif
|
||||||
unsigned int run_pos = 0, cell_pos = 0, num_group_glyphs, num_group_cells;
|
unsigned int run_pos = 0, cell_pos = 0, num_group_glyphs, num_group_cells;
|
||||||
uint64_t extra_glyphs; glyph_index first_glyph;
|
ExtraGlyphs extra_glyphs;
|
||||||
|
glyph_index first_glyph;
|
||||||
while(run_pos < num_glyphs && cell_pos < num_cells) {
|
while(run_pos < num_glyphs && cell_pos < num_cells) {
|
||||||
first_glyph = next_group(font, &num_group_cells, &num_group_glyphs, first_cell + cell_pos, info + run_pos, num_glyphs - run_pos, num_cells - cell_pos, &extra_glyphs);
|
first_glyph = next_group(font, &num_group_cells, &num_group_glyphs, first_cell + cell_pos, info + run_pos, num_glyphs - run_pos, num_cells - cell_pos, &extra_glyphs);
|
||||||
/* printf("Group: num_group_cells: %u num_group_glyphs: %u\n", num_group_cells, num_group_glyphs); */
|
/* printf("Group: num_group_cells: %u num_group_glyphs: %u\n", num_group_cells, num_group_glyphs); */
|
||||||
render_group(num_group_cells, num_group_glyphs, first_cell + cell_pos, info + run_pos, positions + run_pos, font, first_glyph, extra_glyphs);
|
render_group(num_group_cells, num_group_glyphs, first_cell + cell_pos, info + run_pos, positions + run_pos, font, first_glyph, &extra_glyphs);
|
||||||
run_pos += num_group_glyphs; cell_pos += num_group_cells;
|
run_pos += num_group_glyphs; cell_pos += num_group_cells;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -665,10 +678,13 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
|
|||||||
|
|
||||||
PyObject *ans = PyList_New(0);
|
PyObject *ans = PyList_New(0);
|
||||||
unsigned int run_pos = 0, cell_pos = 0, num_group_glyphs, num_group_cells;
|
unsigned int run_pos = 0, cell_pos = 0, num_group_glyphs, num_group_cells;
|
||||||
uint64_t extra_glyphs; glyph_index first_glyph;
|
ExtraGlyphs extra_glyphs;
|
||||||
|
glyph_index first_glyph;
|
||||||
while(run_pos < num_glyphs && cell_pos < num) {
|
while(run_pos < num_glyphs && cell_pos < num) {
|
||||||
first_glyph = next_group(font, &num_group_cells, &num_group_glyphs, line->cells + cell_pos, info + run_pos, num_glyphs - run_pos, num - cell_pos, &extra_glyphs);
|
first_glyph = next_group(font, &num_group_cells, &num_group_glyphs, line->cells + cell_pos, info + run_pos, num_glyphs - run_pos, num - cell_pos, &extra_glyphs);
|
||||||
PyList_Append(ans, Py_BuildValue("IIIK", num_group_cells, num_group_glyphs, first_glyph, extra_glyphs));
|
PyObject *eg = PyTuple_New(MAX_NUM_EXTRA_GLYPHS);
|
||||||
|
for (size_t g = 0; g < MAX_NUM_EXTRA_GLYPHS; g++) PyTuple_SET_ITEM(eg, g, Py_BuildValue("H", extra_glyphs.data[g]));
|
||||||
|
PyList_Append(ans, Py_BuildValue("IIIN", num_group_cells, num_group_glyphs, first_glyph, eg));
|
||||||
run_pos += num_group_glyphs; cell_pos += num_group_cells;
|
run_pos += num_group_glyphs; cell_pos += num_group_cells;
|
||||||
}
|
}
|
||||||
if (face) { Py_CLEAR(face); free(font); }
|
if (face) { Py_CLEAR(face); free(font); }
|
||||||
@@ -778,10 +794,10 @@ sprite_map_set_layout(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
||||||
glyph_index glyph;
|
glyph_index glyph;
|
||||||
uint64_t extra_glyphs = 0;
|
ExtraGlyphs extra_glyphs = {{0}};
|
||||||
if (!PyArg_ParseTuple(args, "H|I", &glyph, &extra_glyphs)) return NULL;
|
if (!PyArg_ParseTuple(args, "H|I", &glyph, &extra_glyphs.data)) return NULL;
|
||||||
int error;
|
int error;
|
||||||
SpritePosition *pos = sprite_position_for(&fonts.fonts[fonts.medium_font_idx], glyph, extra_glyphs, 0, &error);
|
SpritePosition *pos = sprite_position_for(&fonts.fonts[fonts.medium_font_idx], glyph, &extra_glyphs, 0, &error);
|
||||||
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
|
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
|
||||||
return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
|
return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,23 @@
|
|||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
from functools import partial as p
|
from functools import partial as p
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
|
|
||||||
from kitty.utils import get_logical_dpi
|
from kitty.utils import get_logical_dpi
|
||||||
|
|
||||||
|
scale = (0.001, 1, 1.5, 2)
|
||||||
|
|
||||||
|
|
||||||
|
def set_scale(new_scale):
|
||||||
|
global scale
|
||||||
|
scale = tuple(new_scale)
|
||||||
|
|
||||||
|
|
||||||
def thickness(level=1, horizontal=True):
|
def thickness(level=1, horizontal=True):
|
||||||
dpi = get_logical_dpi()[0 if horizontal else 1]
|
dpi = get_logical_dpi()[0 if horizontal else 1]
|
||||||
pts = (0.001, 1, 1.5, 2)[level]
|
pts = scale[level]
|
||||||
return int(math.ceil(pts * dpi / 72.0))
|
return int(math.ceil(pts * dpi / 72.0))
|
||||||
|
|
||||||
|
|
||||||
@@ -264,6 +272,49 @@ def inner_corner(buf, width, height, which='tl', level=1):
|
|||||||
draw_vline(buf, width, y1, y2, width // 2 + (xd * hgap), level)
|
draw_vline(buf, width, y1, y2, width // 2 + (xd * hgap), level)
|
||||||
|
|
||||||
|
|
||||||
|
def vblock(buf, width, height, frac=1, gravity='top'):
|
||||||
|
num_rows = min(height, round(frac * height))
|
||||||
|
start = 0 if gravity == 'top' else height - num_rows
|
||||||
|
for r in range(start, start + num_rows):
|
||||||
|
off = r * width
|
||||||
|
for c in range(off, off + width):
|
||||||
|
buf[c] = 255
|
||||||
|
|
||||||
|
|
||||||
|
def hblock(buf, width, height, frac=1, gravity='left'):
|
||||||
|
num_cols = min(width, round(frac * width))
|
||||||
|
start = 0 if gravity == 'left' else width - num_cols
|
||||||
|
for r in range(height):
|
||||||
|
off = r * width + start
|
||||||
|
for c in range(off, off + num_cols):
|
||||||
|
buf[c] = 255
|
||||||
|
|
||||||
|
|
||||||
|
def shade(buf, width, height, frac=1/4):
|
||||||
|
rand = bytearray(os.urandom(width * height))
|
||||||
|
cutoff = int(frac * 255)
|
||||||
|
|
||||||
|
for r in range(height):
|
||||||
|
off = width * r
|
||||||
|
for c in range(width):
|
||||||
|
q = off + c
|
||||||
|
if rand[q] < cutoff:
|
||||||
|
buf[q] = 255
|
||||||
|
|
||||||
|
|
||||||
|
def quad(buf, width, height, x=0, y=0):
|
||||||
|
num_cols = width // 2
|
||||||
|
left = x * num_cols
|
||||||
|
right = width if x else num_cols
|
||||||
|
num_rows = height // 2
|
||||||
|
top = y * num_rows
|
||||||
|
bottom = height if y else num_rows
|
||||||
|
for r in range(top, bottom):
|
||||||
|
off = r * width
|
||||||
|
for c in range(left, right):
|
||||||
|
buf[off + c] = 255
|
||||||
|
|
||||||
|
|
||||||
box_chars = {
|
box_chars = {
|
||||||
'─': [hline],
|
'─': [hline],
|
||||||
'━': [p(hline, level=3)],
|
'━': [p(hline, level=3)],
|
||||||
@@ -308,6 +359,38 @@ box_chars = {
|
|||||||
'╣': [p(inner_corner, which='tl'), p(inner_corner, which='bl'), p(dvline, only='right')],
|
'╣': [p(inner_corner, which='tl'), p(inner_corner, which='bl'), p(dvline, only='right')],
|
||||||
'╦': [p(inner_corner, which='bl'), p(inner_corner, which='br'), p(dhline, only='top')],
|
'╦': [p(inner_corner, which='bl'), p(inner_corner, which='br'), p(dhline, only='top')],
|
||||||
'╩': [p(inner_corner, which='tl'), p(inner_corner, which='tr'), p(dhline, only='bottom')],
|
'╩': [p(inner_corner, which='tl'), p(inner_corner, which='tr'), p(dhline, only='bottom')],
|
||||||
|
'▀': [p(vblock, frac=1/2)],
|
||||||
|
'▁': [p(vblock, frac=1/8, gravity='bottom')],
|
||||||
|
'▂': [p(vblock, frac=1/4, gravity='bottom')],
|
||||||
|
'▃': [p(vblock, frac=3/8, gravity='bottom')],
|
||||||
|
'▄': [p(vblock, frac=1/2, gravity='bottom')],
|
||||||
|
'▅': [p(vblock, frac=5/8, gravity='bottom')],
|
||||||
|
'▆': [p(vblock, frac=3/4, gravity='bottom')],
|
||||||
|
'▇': [p(vblock, frac=7/8, gravity='bottom')],
|
||||||
|
'█': [p(vblock, frac=1, gravity='bottom')],
|
||||||
|
'▉': [p(hblock, frac=7/8)],
|
||||||
|
'▊': [p(hblock, frac=3/4)],
|
||||||
|
'▋': [p(hblock, frac=5/8)],
|
||||||
|
'▌': [p(hblock, frac=1/2)],
|
||||||
|
'▍': [p(hblock, frac=3/8)],
|
||||||
|
'▎': [p(hblock, frac=1/4)],
|
||||||
|
'▏': [p(hblock, frac=1/8)],
|
||||||
|
'▐': [p(hblock, frac=1/2, gravity='right')],
|
||||||
|
'░': [p(shade, frac=1/4)],
|
||||||
|
'▒': [p(shade, frac=2/4)],
|
||||||
|
'▓': [p(shade, frac=3/4)],
|
||||||
|
'▔': [p(vblock, frac=1/8)],
|
||||||
|
'▕': [p(hblock, frac=1/8, gravity='right')],
|
||||||
|
'▖': [p(quad, y=1)],
|
||||||
|
'▗': [p(quad, x=1, y=1)],
|
||||||
|
'▘': [quad],
|
||||||
|
'▙': [quad, p(quad, y=1), p(quad, x=1, y=1)],
|
||||||
|
'▚': [quad, p(quad, x=1, y=1)],
|
||||||
|
'▛': [quad, p(quad, x=1), p(quad, y=1)],
|
||||||
|
'▜': [quad, p(quad, x=1, y=1), p(quad, x=1)],
|
||||||
|
'▝': [p(quad, x=1)],
|
||||||
|
'▞': [p(quad, x=1), p(quad, y=1)],
|
||||||
|
'▟': [p(quad, x=1), p(quad, y=1), p(quad, x=1, y=1)],
|
||||||
}
|
}
|
||||||
|
|
||||||
t, f = 1, 3
|
t, f = 1, 3
|
||||||
@@ -336,8 +419,6 @@ for chars, func in (('╒╕╘╛', dvcorner), ('╓╖╙╜', dhcorner), ('
|
|||||||
for ch in chars:
|
for ch in chars:
|
||||||
box_chars[ch] = [p(func, which=ch)]
|
box_chars[ch] = [p(func, which=ch)]
|
||||||
|
|
||||||
is_renderable_box_char = box_chars.__contains__
|
|
||||||
|
|
||||||
|
|
||||||
def render_box_char(ch, buf, width, height):
|
def render_box_char(ch, buf, width, height):
|
||||||
for func in box_chars[ch]:
|
for func in box_chars[ch]:
|
||||||
@@ -354,34 +435,41 @@ def render_missing_glyph(buf, width, height):
|
|||||||
draw_vline(buf, width, vgap, height - vgap + 1, width - hgap, 0)
|
draw_vline(buf, width, vgap, height - vgap + 1, width - hgap, 0)
|
||||||
|
|
||||||
|
|
||||||
def join_rows(width, height, rows):
|
|
||||||
import ctypes
|
|
||||||
ans = (ctypes.c_ubyte * (width * height * len(rows)))()
|
|
||||||
pos = ctypes.addressof(ans)
|
|
||||||
row_sz = width * height
|
|
||||||
for row in rows:
|
|
||||||
ctypes.memmove(pos, ctypes.addressof(row), row_sz)
|
|
||||||
pos += row_sz
|
|
||||||
return ans
|
|
||||||
|
|
||||||
|
|
||||||
def test_drawing(sz=32, family='monospace'):
|
def test_drawing(sz=32, family='monospace'):
|
||||||
from .render import join_cells, display_bitmap, render_cell, set_font_family
|
from .render import display_bitmap, setup_for_testing
|
||||||
from kitty.config import defaults
|
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
|
||||||
opts = defaults._replace(font_family=family, font_size=sz)
|
|
||||||
width, height = set_font_family(opts)
|
width, height = setup_for_testing(family, sz)[1:]
|
||||||
|
space = bytearray(width * height)
|
||||||
|
|
||||||
|
def join_cells(cells):
|
||||||
|
cells = tuple(bytes(x) for x in cells)
|
||||||
|
return concat_cells(width, height, cells)
|
||||||
|
|
||||||
|
def render_chr(ch):
|
||||||
|
if ch in box_chars:
|
||||||
|
cell = bytearray(len(space))
|
||||||
|
render_box_char(ch, cell, width, height)
|
||||||
|
return cell
|
||||||
|
return space
|
||||||
|
|
||||||
pos = 0x2500
|
pos = 0x2500
|
||||||
rows = []
|
rows = []
|
||||||
space = render_cell()[0]
|
space_row = join_cells(repeat(space, 32))
|
||||||
space_row = join_cells(width, height, *repeat(space, 32))
|
|
||||||
|
|
||||||
for r in range(8):
|
try:
|
||||||
row = []
|
for r in range(10):
|
||||||
for i in range(16):
|
row = []
|
||||||
row.append(render_cell(chr(pos))[0]), row.append(space)
|
for i in range(16):
|
||||||
pos += 1
|
row.append(render_chr(chr(pos)))
|
||||||
rows.append(join_cells(width, height, *row))
|
row.append(space)
|
||||||
rows.append(space_row)
|
pos += 1
|
||||||
width *= 32
|
rows.append(join_cells(row))
|
||||||
im = join_rows(width, height, rows)
|
rows.append(space_row)
|
||||||
display_bitmap(im, width, height * len(rows))
|
rgb_data = b''.join(rows)
|
||||||
|
width *= 32
|
||||||
|
height *= len(rows)
|
||||||
|
assert len(rgb_data) == width * height * 3, '{} != {}'.format(len(rgb_data), width * height * 3)
|
||||||
|
display_bitmap(rgb_data, width, height)
|
||||||
|
finally:
|
||||||
|
set_send_sprite_to_gpu(None)
|
||||||
|
|||||||
@@ -201,18 +201,22 @@ def shape_string(text="abcd", family='monospace', size=11.0, dpi=96.0, path=None
|
|||||||
set_send_sprite_to_gpu(None)
|
set_send_sprite_to_gpu(None)
|
||||||
|
|
||||||
|
|
||||||
def test_render_string(text='Hello, world!', family='monospace', size=144.0, dpi=96.0):
|
def display_bitmap(rgb_data, width, height):
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from kitty.fast_data_types import concat_cells, current_fonts
|
|
||||||
from kitty.icat import detect_support, show
|
from kitty.icat import detect_support, show
|
||||||
if not hasattr(test_render_string, 'detected') and not detect_support():
|
if not hasattr(display_bitmap, 'detected') and not detect_support():
|
||||||
raise SystemExit('Your terminal does not support the graphics protocol')
|
raise SystemExit('Your terminal does not support the graphics protocol')
|
||||||
test_render_string.detected = True
|
display_bitmap.detected = True
|
||||||
|
with NamedTemporaryFile(delete=False) as f:
|
||||||
|
f.write(rgb_data)
|
||||||
|
show(f.name, width, height, 24)
|
||||||
|
|
||||||
|
|
||||||
|
def test_render_string(text='Hello, world!', family='monospace', size=144.0, dpi=96.0):
|
||||||
|
from kitty.fast_data_types import concat_cells, current_fonts
|
||||||
|
|
||||||
cell_width, cell_height, cells = render_string(text, family, size, dpi)
|
cell_width, cell_height, cells = render_string(text, family, size, dpi)
|
||||||
rgb_data = concat_cells(cell_width, cell_height, tuple(cells))
|
rgb_data = concat_cells(cell_width, cell_height, tuple(cells))
|
||||||
with NamedTemporaryFile(delete=False) as f:
|
|
||||||
f.write(rgb_data)
|
|
||||||
cf = current_fonts()
|
cf = current_fonts()
|
||||||
fonts = [cf['medium'].display_name()]
|
fonts = [cf['medium'].display_name()]
|
||||||
fonts.extend(f.display_name() for f in cf['fallback'])
|
fonts.extend(f.display_name() for f in cf['fallback'])
|
||||||
@@ -221,7 +225,7 @@ def test_render_string(text='Hello, world!', family='monospace', size=144.0, dpi
|
|||||||
print(msg)
|
print(msg)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
sys.stdout.buffer.write(msg.encode('utf-8') + b'\n')
|
sys.stdout.buffer.write(msg.encode('utf-8') + b'\n')
|
||||||
show(f.name, cell_width * len(cells), cell_height, 24)
|
display_bitmap(rgb_data, cell_width * len(cells), cell_height)
|
||||||
print('\n')
|
print('\n')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,8 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
|||||||
bool
|
bool
|
||||||
set_size_for_face(PyObject *s, unsigned int desired_height, bool force) {
|
set_size_for_face(PyObject *s, unsigned int desired_height, bool force) {
|
||||||
Face *self = (Face*)s;
|
Face *self = (Face*)s;
|
||||||
FT_UInt w = (FT_UInt)(ceil(global_state.font_sz_in_pts * 64.0)), xdpi = (FT_UInt)global_state.logical_dpi_x, ydpi = (FT_UInt)global_state.logical_dpi_y;
|
FT_F26Dot6 w = (FT_F26Dot6)(ceil(global_state.font_sz_in_pts * 64.0));
|
||||||
|
FT_UInt xdpi = (FT_UInt)global_state.logical_dpi_x, ydpi = (FT_UInt)global_state.logical_dpi_y;
|
||||||
if (!force && (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi)) return true;
|
if (!force && (self->char_width == w && self->char_height == w && self->xdpi == xdpi && self->ydpi == ydpi)) return true;
|
||||||
((Face*)self)->size_in_pts = global_state.font_sz_in_pts;
|
((Face*)self)->size_in_pts = global_state.font_sz_in_pts;
|
||||||
return set_font_size(self, w, w, xdpi, ydpi, desired_height);
|
return set_font_size(self, w, w, xdpi, ydpi, desired_height);
|
||||||
|
|||||||
16
kitty/keys.h
generated
16
kitty/keys.h
generated
@@ -569,9 +569,9 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
case 57: // LEFT
|
case 57: // LEFT
|
||||||
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
||||||
case 58: // DOWN
|
case 58: // DOWN
|
||||||
return "\x03\x1b\x5b\x42";
|
return "\x06\x1b\x5b\x31\x3b\x32\x42";
|
||||||
case 59: // UP
|
case 59: // UP
|
||||||
return "\x03\x1b\x5b\x41";
|
return "\x06\x1b\x5b\x31\x3b\x32\x41";
|
||||||
case 60: // PAGE_UP
|
case 60: // PAGE_UP
|
||||||
return "\x04\x1b\x5b\x35\x7e";
|
return "\x04\x1b\x5b\x35\x7e";
|
||||||
case 61: // PAGE_DOWN
|
case 61: // PAGE_DOWN
|
||||||
@@ -1767,9 +1767,9 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
case 57: // LEFT
|
case 57: // LEFT
|
||||||
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
||||||
case 58: // DOWN
|
case 58: // DOWN
|
||||||
return "\x03\x1b\x5b\x42";
|
return "\x06\x1b\x5b\x31\x3b\x32\x42";
|
||||||
case 59: // UP
|
case 59: // UP
|
||||||
return "\x03\x1b\x5b\x41";
|
return "\x06\x1b\x5b\x31\x3b\x32\x41";
|
||||||
case 60: // PAGE_UP
|
case 60: // PAGE_UP
|
||||||
return "\x04\x1b\x5b\x35\x7e";
|
return "\x04\x1b\x5b\x35\x7e";
|
||||||
case 61: // PAGE_DOWN
|
case 61: // PAGE_DOWN
|
||||||
@@ -2973,9 +2973,9 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
case 57: // LEFT
|
case 57: // LEFT
|
||||||
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
||||||
case 58: // DOWN
|
case 58: // DOWN
|
||||||
return "\x03\x1b\x4f\x42";
|
return "\x06\x1b\x5b\x31\x3b\x32\x42";
|
||||||
case 59: // UP
|
case 59: // UP
|
||||||
return "\x03\x1b\x4f\x41";
|
return "\x06\x1b\x5b\x31\x3b\x32\x41";
|
||||||
case 60: // PAGE_UP
|
case 60: // PAGE_UP
|
||||||
return "\x04\x1b\x5b\x35\x7e";
|
return "\x04\x1b\x5b\x35\x7e";
|
||||||
case 61: // PAGE_DOWN
|
case 61: // PAGE_DOWN
|
||||||
@@ -4171,9 +4171,9 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
|||||||
case 57: // LEFT
|
case 57: // LEFT
|
||||||
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
return "\x06\x1b\x5b\x31\x3b\x32\x44";
|
||||||
case 58: // DOWN
|
case 58: // DOWN
|
||||||
return "\x03\x1b\x4f\x42";
|
return "\x06\x1b\x5b\x31\x3b\x32\x42";
|
||||||
case 59: // UP
|
case 59: // UP
|
||||||
return "\x03\x1b\x4f\x41";
|
return "\x06\x1b\x5b\x31\x3b\x32\x41";
|
||||||
case 60: // PAGE_UP
|
case 60: // PAGE_UP
|
||||||
return "\x04\x1b\x5b\x35\x7e";
|
return "\x04\x1b\x5b\x35\x7e";
|
||||||
case 61: // PAGE_DOWN
|
case 61: // PAGE_DOWN
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ SHIFTED_KEYS = {
|
|||||||
defines.GLFW_KEY_END: key_as_bytes('kEND'),
|
defines.GLFW_KEY_END: key_as_bytes('kEND'),
|
||||||
defines.GLFW_KEY_LEFT: key_as_bytes('kLFT'),
|
defines.GLFW_KEY_LEFT: key_as_bytes('kLFT'),
|
||||||
defines.GLFW_KEY_RIGHT: key_as_bytes('kRIT'),
|
defines.GLFW_KEY_RIGHT: key_as_bytes('kRIT'),
|
||||||
|
defines.GLFW_KEY_UP: key_as_bytes('kri'),
|
||||||
|
defines.GLFW_KEY_DOWN: key_as_bytes('kind'),
|
||||||
}
|
}
|
||||||
|
|
||||||
control_codes.update({
|
control_codes.update({
|
||||||
@@ -225,13 +227,6 @@ def get_shortcut(keymap, mods, 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):
|
|
||||||
m = keyboard_mode_name(window.screen)
|
|
||||||
keymap = send_text_map[m]
|
|
||||||
return keymap.get((mods & 0b1111, key))
|
|
||||||
|
|
||||||
|
|
||||||
def generate_key_table():
|
def generate_key_table():
|
||||||
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
# To run this, use: python3 . -c "from kitty.keys import *; generate_key_table()"
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ font_size_delta 2
|
|||||||
# 100% to reduce line height (but this might cause rendering artifacts).
|
# 100% to reduce line height (but this might cause rendering artifacts).
|
||||||
adjust_line_height 0
|
adjust_line_height 0
|
||||||
|
|
||||||
|
# Change the sizes of the lines used for the box drawing unicode characters
|
||||||
|
# These values are in pts. They will be scaled by the monitor DPI to arrive at
|
||||||
|
# a pixel value. There must be four values corresponding to thin, normal, thick,
|
||||||
|
# and very thick lines;
|
||||||
|
box_drawing_scale 0.001, 1, 1.5, 2
|
||||||
|
|
||||||
# The foreground color
|
# The foreground color
|
||||||
foreground #dddddd
|
foreground #dddddd
|
||||||
|
|
||||||
@@ -298,8 +304,6 @@ map ctrl+shift+f11 toggle_fullscreen
|
|||||||
# to the start of the line (same as pressing the Home key):
|
# to the start of the line (same as pressing the Home key):
|
||||||
# map ctrl+alt+a send_text normal Word\x1b[H
|
# map ctrl+alt+a send_text normal Word\x1b[H
|
||||||
# map ctrl+alt+a send_text application Word\x1bOH
|
# map ctrl+alt+a send_text application Word\x1bOH
|
||||||
# There is also a legacy syntax for send_text, which looks like:
|
|
||||||
# send_text all ctrl+alt+a some text
|
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ linebuf_index(LineBuf* self, index_type top, index_type bottom) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
index(LineBuf *self, PyObject *args) {
|
pyw_index(LineBuf *self, PyObject *args) {
|
||||||
#define index_doc "index(top, bottom) -> Scroll all lines in the range [top, bottom] by one upwards. After scrolling, bottom will be top."
|
#define index_doc "index(top, bottom) -> Scroll all lines in the range [top, bottom] by one upwards. After scrolling, bottom will be top."
|
||||||
unsigned int top, bottom;
|
unsigned int top, bottom;
|
||||||
if (!PyArg_ParseTuple(args, "II", &top, &bottom)) return NULL;
|
if (!PyArg_ParseTuple(args, "II", &top, &bottom)) return NULL;
|
||||||
@@ -435,7 +435,7 @@ static PyMethodDef methods[] = {
|
|||||||
METHOD(set_attribute, METH_VARARGS)
|
METHOD(set_attribute, METH_VARARGS)
|
||||||
METHOD(set_continued, METH_VARARGS)
|
METHOD(set_continued, METH_VARARGS)
|
||||||
METHOD(dirty_lines, METH_NOARGS)
|
METHOD(dirty_lines, METH_NOARGS)
|
||||||
METHOD(index, METH_VARARGS)
|
{"index", (PyCFunction)pyw_index, METH_VARARGS, NULL},
|
||||||
METHOD(reverse_index, METH_VARARGS)
|
METHOD(reverse_index, METH_VARARGS)
|
||||||
METHOD(insert_lines, METH_VARARGS)
|
METHOD(insert_lines, METH_VARARGS)
|
||||||
METHOD(delete_lines, METH_VARARGS)
|
METHOD(delete_lines, METH_VARARGS)
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ url_end_at(Line *self, PyObject *x) {
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
text_at(Line* self, Py_ssize_t xval) {
|
text_at(Line* self, Py_ssize_t xval) {
|
||||||
#define text_at_doc "[x] -> Return the text in the specified cell"
|
#define text_at_doc "[x] -> Return the text in the specified cell"
|
||||||
if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
|
if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
|
||||||
return line_text_at(self->cells[xval].ch, self->cells[xval].cc);
|
return line_text_at(self->cells[xval].ch, self->cells[xval].cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from .fast_data_types import (
|
|||||||
glfw_init, glfw_init_hint_string, glfw_swap_interval, glfw_terminate,
|
glfw_init, glfw_init_hint_string, glfw_swap_interval, glfw_terminate,
|
||||||
glfw_window_hint, install_sigchld_handler, set_logical_dpi, set_options
|
glfw_window_hint, install_sigchld_handler, set_logical_dpi, set_options
|
||||||
)
|
)
|
||||||
|
from .fonts.box_drawing import set_scale
|
||||||
from .layout import all_layouts
|
from .layout import all_layouts
|
||||||
from .utils import (
|
from .utils import (
|
||||||
color_as_int, detach, end_startup_notification, get_logical_dpi,
|
color_as_int, detach, end_startup_notification, get_logical_dpi,
|
||||||
@@ -170,6 +171,7 @@ def initialize_window(window, opts, debug_gl=False):
|
|||||||
def run_app(opts, args):
|
def run_app(opts, args):
|
||||||
set_options(opts)
|
set_options(opts)
|
||||||
setup_opengl(opts)
|
setup_opengl(opts)
|
||||||
|
set_scale(opts.box_drawing_scale)
|
||||||
load_cached_values()
|
load_cached_values()
|
||||||
if 'window-size' in cached_values and opts.remember_window_size:
|
if 'window-size' in cached_values and opts.remember_window_size:
|
||||||
ws = cached_values['window-size']
|
ws = cached_values['window-size']
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
extern PyTypeObject Screen_Type;
|
extern PyTypeObject Screen_Type;
|
||||||
|
|
||||||
// utils {{{
|
// utils {{{
|
||||||
static unsigned long pow10[] = {
|
static uint64_t pow10[] = {
|
||||||
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000
|
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline unsigned long
|
static inline uint64_t
|
||||||
utoi(uint32_t *buf, unsigned int sz) {
|
utoi(uint32_t *buf, unsigned int sz) {
|
||||||
unsigned long ans = 0;
|
uint64_t ans = 0;
|
||||||
uint32_t *p = buf;
|
uint32_t *p = buf;
|
||||||
// Ignore leading zeros
|
// Ignore leading zeros
|
||||||
while(sz > 0) {
|
while(sz > 0) {
|
||||||
@@ -27,7 +27,7 @@ utoi(uint32_t *buf, unsigned int sz) {
|
|||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
if (sz < sizeof(pow10)/sizeof(pow10[0])) {
|
if (sz < sizeof(pow10)/sizeof(pow10[0])) {
|
||||||
for (int i = sz-1, j=0; i >=0; i--, j++) {
|
for (int i = sz-1, j=0; i >= 0; i--, j++) {
|
||||||
ans += (p[i] - '0') * pow10[j];
|
ans += (p[i] - '0') * pow10[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1053,7 +1053,7 @@ extern uint32_t *latin1_charset;
|
|||||||
static inline void
|
static inline void
|
||||||
_parse_bytes(Screen *screen, uint8_t *buf, Py_ssize_t len, PyObject DUMP_UNUSED *dump_callback) {
|
_parse_bytes(Screen *screen, uint8_t *buf, Py_ssize_t len, PyObject DUMP_UNUSED *dump_callback) {
|
||||||
uint32_t prev = screen->utf8_state;
|
uint32_t prev = screen->utf8_state;
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
for (unsigned int i = 0; i < (unsigned int)len; i++) {
|
||||||
if (screen->use_latin1) dispatch_unicode_char(screen, latin1_charset[buf[i]], dump_callback);
|
if (screen->use_latin1) dispatch_unicode_char(screen, latin1_charset[buf[i]], dump_callback);
|
||||||
else {
|
else {
|
||||||
switch (decode_utf8(&screen->utf8_state, &screen->utf8_codepoint, buf[i])) {
|
switch (decode_utf8(&screen->utf8_state, &screen->utf8_codepoint, buf[i])) {
|
||||||
|
|||||||
@@ -436,7 +436,8 @@ screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const u
|
|||||||
if (response != NULL) write_to_child(self, response, strlen(response));
|
if (response != NULL) write_to_child(self, response, strlen(response));
|
||||||
if (x != self->cursor->x || y != self->cursor->y) {
|
if (x != self->cursor->x || y != self->cursor->y) {
|
||||||
if (self->cursor->x >= self->columns) { self->cursor->x = 0; self->cursor->y++; }
|
if (self->cursor->x >= self->columns) { self->cursor->x = 0; self->cursor->y++; }
|
||||||
if (self->cursor->y > self->margin_bottom) { screen_scroll(self, self->cursor->y - self->margin_bottom); }
|
if (self->cursor->y > self->margin_bottom) screen_scroll(self, self->cursor->y - self->margin_bottom);
|
||||||
|
screen_ensure_bounds(self, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
@@ -701,7 +702,6 @@ screen_index(Screen *self) {
|
|||||||
void
|
void
|
||||||
screen_scroll(Screen *self, unsigned int count) {
|
screen_scroll(Screen *self, unsigned int count) {
|
||||||
// Scroll the screen up by count lines, not moving the cursor
|
// Scroll the screen up by count lines, not moving the cursor
|
||||||
count = MIN(self->lines, count);
|
|
||||||
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
unsigned int top = self->margin_top, bottom = self->margin_bottom;
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
count--;
|
count--;
|
||||||
|
|||||||
@@ -170,16 +170,10 @@ PYWRAP1(set_options) {
|
|||||||
OPT(select_by_word_characters)[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i);
|
OPT(select_by_word_characters)[i] = PyUnicode_READ(PyUnicode_KIND(chars), PyUnicode_DATA(chars), i);
|
||||||
}
|
}
|
||||||
OPT(select_by_word_characters_count) = PyUnicode_GET_LENGTH(chars);
|
OPT(select_by_word_characters_count) = PyUnicode_GET_LENGTH(chars);
|
||||||
|
Py_DECREF(chars);
|
||||||
|
|
||||||
GA(keymap); set_special_keys(ret);
|
GA(keymap); set_special_keys(ret);
|
||||||
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
||||||
GA(send_text_map);
|
|
||||||
dict_iter(ret) {
|
|
||||||
set_special_keys(value);
|
|
||||||
}}
|
|
||||||
Py_DECREF(ret); if (PyErr_Occurred()) return NULL;
|
|
||||||
|
|
||||||
Py_DECREF(chars);
|
|
||||||
|
|
||||||
PyObject *al = PyObject_GetAttrString(args, "adjust_line_height");
|
PyObject *al = PyObject_GetAttrString(args, "adjust_line_height");
|
||||||
if (PyFloat_Check(al)) {
|
if (PyFloat_Check(al)) {
|
||||||
|
|||||||
@@ -190,6 +190,8 @@ string_capabilities = {
|
|||||||
'kpp': r'\E[5~',
|
'kpp': r'\E[5~',
|
||||||
# Scroll backwards (reverse index)
|
# Scroll backwards (reverse index)
|
||||||
'kri': r'\E[1;2A',
|
'kri': r'\E[1;2A',
|
||||||
|
# scroll forwards (index)
|
||||||
|
'kind': r'\E[1;2B',
|
||||||
# Restore cursor
|
# Restore cursor
|
||||||
'rc': r'\E8',
|
'rc': r'\E8',
|
||||||
# Reverse video
|
# Reverse video
|
||||||
@@ -276,8 +278,6 @@ string_capabilities = {
|
|||||||
# 'is2': r'\E[!p\E[?3;4l\E[4l\E>',
|
# 'is2': r'\E[!p\E[?3;4l\E[4l\E>',
|
||||||
# # Enter/send key
|
# # Enter/send key
|
||||||
# 'kent': r'\EOM',
|
# 'kent': r'\EOM',
|
||||||
# # scroll forwards
|
|
||||||
# 'kind': r'\E[1;2B',
|
|
||||||
# # reset2
|
# # reset2
|
||||||
# 'rs2': r'\E[!p\E[?3;4l\E[4l\E>',
|
# 'rs2': r'\E[!p\E[?3;4l\E[4l\E>',
|
||||||
}
|
}
|
||||||
@@ -350,6 +350,7 @@ termcap_aliases.update({
|
|||||||
'kN': 'knp',
|
'kN': 'knp',
|
||||||
'kP': 'kpp',
|
'kP': 'kpp',
|
||||||
'kR': 'kri',
|
'kR': 'kri',
|
||||||
|
'kF': 'kind',
|
||||||
'rc': 'rc',
|
'rc': 'rc',
|
||||||
'mr': 'rev',
|
'mr': 'rev',
|
||||||
'sr': 'ri',
|
'sr': 'ri',
|
||||||
@@ -400,7 +401,6 @@ termcap_aliases.update({
|
|||||||
# 'mk': 'invis',
|
# 'mk': 'invis',
|
||||||
# 'is': 'is2',
|
# 'is': 'is2',
|
||||||
# '@8': 'kent',
|
# '@8': 'kent',
|
||||||
# 'kF': 'kind',
|
|
||||||
# 'r2': 'rs2',
|
# 'r2': 'rs2',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -85,6 +85,7 @@ xterm-kitty|KovIdTTY,
|
|||||||
khlp=,
|
khlp=,
|
||||||
khome=\EOH,
|
khome=\EOH,
|
||||||
kich1=\E[2~,
|
kich1=\E[2~,
|
||||||
|
kind=\E[1;2B,
|
||||||
kmous=\E[M,
|
kmous=\E[M,
|
||||||
knp=\E[6~,
|
knp=\E[6~,
|
||||||
kpp=\E[5~,
|
kpp=\E[5~,
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user