more typing work

This commit is contained in:
Kovid Goyal 2020-03-10 12:58:53 +05:30
parent 25be705bcf
commit 8803eeb890
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 140 additions and 77 deletions

View File

@ -7,13 +7,21 @@ import os
import sys import sys
from collections import defaultdict from collections import defaultdict
from contextlib import contextmanager, suppress from contextlib import contextmanager, suppress
from typing import DefaultDict, Dict, Generator, Iterable, List, Optional from typing import (
DefaultDict, Dict, Generator, Iterable, List, Optional, Sequence, Tuple
)
import kitty.fast_data_types as fast_data_types import kitty.fast_data_types as fast_data_types
from .constants import is_macos, shell_path, terminfo_dir from .constants import is_macos, shell_path, terminfo_dir
from .options_stub import Options from .options_stub import Options
try:
from typing import TypedDict
except ImportError:
TypedDict = dict
if is_macos: if is_macos:
from kitty.fast_data_types import ( from kitty.fast_data_types import (
cmdline_of_process, cwd_of_process as _cwd, environ_of_process as _environ_of_process, cmdline_of_process, cwd_of_process as _cwd, environ_of_process as _environ_of_process,
@ -153,13 +161,19 @@ def set_default_env(val: Optional[Dict[str, str]] = None) -> None:
setattr(default_env, 'env', env) setattr(default_env, 'env', env)
def openpty(): def openpty() -> Tuple[int, int]:
master, slave = os.openpty() # Note that master and slave are in blocking mode master, slave = os.openpty() # Note that master and slave are in blocking mode
remove_cloexec(slave) remove_cloexec(slave)
fast_data_types.set_iutf8_fd(master, True) fast_data_types.set_iutf8_fd(master, True)
return master, slave return master, slave
class ProcessDesc(TypedDict):
cwd: Optional[str]
pid: int
cmdline: Optional[Sequence[str]]
class Child: class Child:
child_fd: Optional[int] = None child_fd: Optional[int] = None
@ -174,7 +188,7 @@ class Child:
stdin: Optional[bytes] = None, stdin: Optional[bytes] = None,
env: Optional[Dict[str, str]] = None, env: Optional[Dict[str, str]] = None,
cwd_from: Optional[int] = None, cwd_from: Optional[int] = None,
allow_remote_control=False allow_remote_control: bool = False
): ):
self.allow_remote_control = allow_remote_control self.allow_remote_control = allow_remote_control
self.argv = argv self.argv = argv
@ -254,27 +268,27 @@ class Child:
remove_blocking(self.child_fd) remove_blocking(self.child_fd)
return pid return pid
def mark_terminal_ready(self): def mark_terminal_ready(self) -> None:
os.close(self.terminal_ready_fd) os.close(self.terminal_ready_fd)
self.terminal_ready_fd = -1 self.terminal_ready_fd = -1
@property @property
def foreground_processes(self) -> List[int]: def foreground_processes(self) -> List[ProcessDesc]:
if self.child_fd is None: if self.child_fd is None:
return [] return []
try: try:
pgrp = os.tcgetpgrp(self.child_fd) pgrp = os.tcgetpgrp(self.child_fd)
foreground_processes = processes_in_group(pgrp) if pgrp >= 0 else [] foreground_processes = processes_in_group(pgrp) if pgrp >= 0 else []
def process_desc(pid): def process_desc(pid: int) -> ProcessDesc:
ans = {'pid': pid} ans: ProcessDesc = {'pid': pid, 'cmdline': None, 'cwd': None}
with suppress(Exception): with suppress(Exception):
ans['cmdline'] = cmdline_of_process(pid) ans['cmdline'] = cmdline_of_process(pid)
with suppress(Exception): with suppress(Exception):
ans['cwd'] = cwd_of_process(pid) or None ans['cwd'] = cwd_of_process(pid) or None
return ans return ans
return list(map(process_desc, foreground_processes)) return [process_desc(x) for x in foreground_processes]
except Exception: except Exception:
return [] return []

View File

@ -1,3 +1,4 @@
from ctypes import Array
from typing import ( from typing import (
Any, AnyStr, Callable, Dict, List, NewType, Optional, Tuple, TypedDict, Any, AnyStr, Callable, Dict, List, NewType, Optional, Tuple, TypedDict,
Union Union
@ -393,6 +394,8 @@ def default_color_table() -> Tuple[int, ...]:
class FontConfigPattern(TypedDict): class FontConfigPattern(TypedDict):
path: str
index: int
family: str family: str
full_name: str full_name: str
postscript_name: str postscript_name: str
@ -400,6 +403,13 @@ class FontConfigPattern(TypedDict):
spacing: str spacing: str
weight: int weight: int
slant: int slant: int
hint_style: int
subpixel: int
lcdfilter: int
hinting: bool
scalable: bool
outline: bool
color: bool
def fc_list( def fc_list(
@ -885,10 +895,10 @@ def set_send_sprite_to_gpu(
def set_font_data( def set_font_data(
box_drawing_func: Callable[[int, int, int, float], box_drawing_func: Callable[[int, int, int, float],
Tuple[int, Union[bytearray, bytes]]], Tuple[int, Union[bytearray, bytes, Array]]],
prerender_func: Callable[ prerender_func: Callable[
[int, int, int, int, int, float, float, float, float], [int, int, int, int, int, float, float, float, float],
Tuple[int, ...]], Tuple[Union[Array, int], ...]],
descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]], descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]],
bold: int, italic: int, bold_italic: int, num_symbol_fonts: int, bold: int, italic: int, bold_italic: int, num_symbol_fonts: int,
symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float, symbol_maps: Tuple[Tuple[int, int, int], ...], font_sz_in_pts: float,

View File

@ -49,12 +49,12 @@ def draw_vline(buf: BufType, width: int, y1: int, y2: int, x: int, level: int) -
buf[x + y * width] = 255 buf[x + y * width] = 255
def half_hline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'left', extend_by: int = 0): def half_hline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'left', extend_by: int = 0) -> None:
x1, x2 = (0, extend_by + width // 2) if which == 'left' else (width // 2 - extend_by, width) x1, x2 = (0, extend_by + width // 2) if which == 'left' else (width // 2 - extend_by, width)
draw_hline(buf, width, x1, x2, height // 2, level) draw_hline(buf, width, x1, x2, height // 2, level)
def half_vline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'top', extend_by: int = 0): def half_vline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'top', extend_by: int = 0) -> None:
y1, y2 = (0, height // 2 + extend_by) if which == 'top' else (height // 2 - extend_by, height) y1, y2 = (0, height // 2 + extend_by) if which == 'top' else (height // 2 - extend_by, height)
draw_vline(buf, width, y1, y2, width // 2, level) draw_vline(buf, width, y1, y2, width // 2, level)
@ -105,41 +105,41 @@ def hline(buf: BufType, width: int, height: int, level: int = 1) -> None:
half_hline(buf, width, height, level=level, which='right') half_hline(buf, width, height, level=level, which='right')
def vline(buf: BufType, width: int, height: int, level=1) -> None: def vline(buf: BufType, width: int, height: int, level: int = 1) -> None:
half_vline(buf, width, height, level=level) half_vline(buf, width, height, level=level)
half_vline(buf, width, height, level=level, which='bottom') half_vline(buf, width, height, level=level, which='bottom')
def hholes(buf: BufType, width: int, height: int, level=1, num=1) -> None: def hholes(buf: BufType, width: int, height: int, level: int = 1, num: int = 1) -> None:
hline(buf, width, height, level=level) hline(buf, width, height, level=level)
add_hholes(buf, width, height, level=level, num=num) add_hholes(buf, width, height, level=level, num=num)
def vholes(buf: BufType, width: int, height: int, level=1, num=1) -> None: def vholes(buf: BufType, width: int, height: int, level: int = 1, num: int = 1) -> None:
vline(buf, width, height, level=level) vline(buf, width, height, level=level)
add_vholes(buf, width, height, level=level, num=num) add_vholes(buf, width, height, level=level, num=num)
def corner(buf: BufType, width: int, height: int, hlevel=1, vlevel=1, which=None) -> None: def corner(buf: BufType, width: int, height: int, hlevel: int = 1, vlevel: int = 1, which: Optional[str] = None) -> None:
wh = 'right' if which in '┌└' else 'left' wh = 'right' if which is not None and which in '┌└' else 'left'
half_hline(buf, width, height, level=hlevel, which=wh, extend_by=thickness(vlevel, horizontal=True) // 2) half_hline(buf, width, height, level=hlevel, which=wh, extend_by=thickness(vlevel, horizontal=True) // 2)
wv = 'top' if which in '└┘' else 'bottom' wv = 'top' if which is not None and which in '└┘' else 'bottom'
half_vline(buf, width, height, level=vlevel, which=wv) half_vline(buf, width, height, level=vlevel, which=wv)
def vert_t(buf: BufType, width: int, height: int, a=1, b=1, c=1, which=None) -> None: def vert_t(buf: BufType, width: int, height: int, a: int = 1, b: int = 1, c: int = 1, which: Optional[str] = None) -> None:
half_vline(buf, width, height, level=a, which='top') half_vline(buf, width, height, level=a, which='top')
half_hline(buf, width, height, level=b, which='left' if which == '' else 'right') half_hline(buf, width, height, level=b, which='left' if which == '' else 'right')
half_vline(buf, width, height, level=c, which='bottom') half_vline(buf, width, height, level=c, which='bottom')
def horz_t(buf: BufType, width: int, height: int, a=1, b=1, c=1, which=None) -> None: def horz_t(buf: BufType, width: int, height: int, a: int = 1, b: int = 1, c: int = 1, which: Optional[str] = None) -> None:
half_hline(buf, width, height, level=a, which='left') half_hline(buf, width, height, level=a, which='left')
half_hline(buf, width, height, level=b, which='right') half_hline(buf, width, height, level=b, which='right')
half_vline(buf, width, height, level=c, which='top' if which == '' else 'bottom') half_vline(buf, width, height, level=c, which='top' if which == '' else 'bottom')
def cross(buf: BufType, width: int, height: int, a=1, b=1, c=1, d=1) -> None: def cross(buf: BufType, width: int, height: int, a: int = 1, b: int = 1, c: int = 1, d: int = 1) -> None:
half_hline(buf, width, height, level=a) half_hline(buf, width, height, level=a)
half_hline(buf, width, height, level=b, which='right') half_hline(buf, width, height, level=b, which='right')
half_vline(buf, width, height, level=c) half_vline(buf, width, height, level=c)
@ -400,17 +400,17 @@ def half_dvline(buf: BufType, width: int, height: int, level: int = 1, which: st
return width // 2 - gap, width // 2 + gap return width // 2 - gap, width // 2 + gap
def dvline(buf: BufType, width: int, height: int, only=None, level=1): def dvline(buf: BufType, width: int, height: int, only: Optional[str] = None, level: int = 1) -> Tuple[int, int]:
half_dvline(buf, width, height, only=only, level=level) half_dvline(buf, width, height, only=only, level=level)
return half_dvline(buf, width, height, only=only, which='bottom', level=level) return half_dvline(buf, width, height, only=only, which='bottom', level=level)
def dhline(buf: BufType, width: int, height: int, only=None, level=1): def dhline(buf: BufType, width: int, height: int, only: Optional[str] = None, level: int = 1) -> Tuple[int, int]:
half_dhline(buf, width, height, only=only, level=level) half_dhline(buf, width, height, only=only, level=level)
return half_dhline(buf, width, height, only=only, which='bottom', level=level) return half_dhline(buf, width, height, only=only, which='bottom', level=level)
def dvcorner(buf: BufType, width: int, height: int, level=1, which=''): def dvcorner(buf: BufType, width: int, height: int, level: int = 1, which: str = '') -> None:
hw = 'right' if which in '╒╘' else 'left' hw = 'right' if which in '╒╘' else 'left'
half_dhline(buf, width, height, which=hw) half_dhline(buf, width, height, which=hw)
vw = 'top' if which in '╘╛' else 'bottom' vw = 'top' if which in '╘╛' else 'bottom'
@ -418,7 +418,7 @@ def dvcorner(buf: BufType, width: int, height: int, level=1, which='╒'):
half_vline(buf, width, height, which=vw, extend_by=gap // 2 + thickness(level, horizontal=False)) half_vline(buf, width, height, which=vw, extend_by=gap // 2 + thickness(level, horizontal=False))
def dhcorner(buf: BufType, width: int, height: int, level=1, which=''): def dhcorner(buf: BufType, width: int, height: int, level: int = 1, which: str = '') -> None:
vw = 'top' if which in '╙╜' else 'bottom' vw = 'top' if which in '╙╜' else 'bottom'
half_dvline(buf, width, height, which=vw) half_dvline(buf, width, height, which=vw)
hw = 'right' if which in '╓╙' else 'left' hw = 'right' if which in '╓╙' else 'left'
@ -537,7 +537,7 @@ def shade(buf: BufType, width: int, height: int, light: bool = False, invert: bo
buf[q] = 255 - dest[q] buf[q] = 255 - dest[q]
def quad(buf, width, height, x=0, y=0): def quad(buf: BufType, width: int, height: int, x: int = 0, y: int = 0) -> None:
num_cols = width // 2 num_cols = width // 2
left = x * num_cols left = x * num_cols
right = width if x else num_cols right = width if x else num_cols
@ -692,7 +692,7 @@ def test_char(ch: str, sz: int = 48) -> None:
try: try:
render_box_char(ch, buf, width, height) render_box_char(ch, buf, width, height)
def join_cells(*cells): def join_cells(*cells: bytes) -> bytes:
cells = tuple(bytes(x) for x in cells) cells = tuple(bytes(x) for x in cells)
return concat_cells(width, height, False, cells) return concat_cells(width, height, False, cells)
@ -710,11 +710,11 @@ def test_drawing(sz: int = 48, family: str = 'monospace') -> None:
with setup_for_testing(family, sz) as (_, width, height): with setup_for_testing(family, sz) as (_, width, height):
space = bytearray(width * height) space = bytearray(width * height)
def join_cells(cells): def join_cells(cells: Iterable[bytes]) -> bytes:
cells = tuple(bytes(x) for x in cells) cells = tuple(bytes(x) for x in cells)
return concat_cells(width, height, False, cells) return concat_cells(width, height, False, cells)
def render_chr(ch): def render_chr(ch: str) -> bytearray:
if ch in box_chars: if ch in box_chars:
cell = bytearray(len(space)) cell = bytearray(len(space))
render_box_char(ch, cell, width, height) render_box_char(ch, cell, width, height)

View File

@ -60,7 +60,7 @@ def find_best_match(family: str, bold: bool = False, italic: bool = False) -> 'C
q = re.sub(r'\s+', ' ', family.lower()) q = re.sub(r'\s+', ' ', family.lower())
font_map = all_fonts_map() font_map = all_fonts_map()
def score(candidate): def score(candidate: 'CoreTextFont') -> Tuple[int, int]:
style_match = 1 if candidate['bold'] == bold and candidate[ style_match = 1 if candidate['bold'] == bold and candidate[
'italic' 'italic'
] == italic else 0 ] == italic else 0

View File

@ -43,7 +43,7 @@ def create_font_map(all_fonts: Tuple['FontConfigPattern', ...]) -> FontMap:
@lru_cache() @lru_cache()
def all_fonts_map(monospaced=True) -> FontMap: def all_fonts_map(monospaced: bool = True) -> FontMap:
if monospaced: if monospaced:
ans = fc_list(FC_DUAL) + fc_list(FC_MONO) ans = fc_list(FC_DUAL) + fc_list(FC_MONO)
else: else:
@ -69,15 +69,15 @@ def family_name_to_key(family: str) -> str:
@lru_cache() @lru_cache()
def fc_match(family, bold, italic, spacing=FC_MONO) -> 'FontConfigPattern': def fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> 'FontConfigPattern':
return fc_match_impl(family, bold, italic, spacing) return fc_match_impl(family, bold, italic, spacing)
def find_best_match(family: str, bold=False, italic=False, monospaced=True) -> 'FontConfigPattern': def find_best_match(family: str, bold: bool = False, italic: bool = False, monospaced: bool = True) -> 'FontConfigPattern':
q = family_name_to_key(family) q = family_name_to_key(family)
font_map = all_fonts_map(monospaced) font_map = all_fonts_map(monospaced)
def score(candidate): def score(candidate: 'FontConfigPattern') -> Tuple[int, int]:
bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate.get('weight', 0)) bold_score = abs((FC_WEIGHT_BOLD if bold else FC_WEIGHT_REGULAR) - candidate.get('weight', 0))
italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate.get('slant', 0)) italic_score = abs((FC_SLANT_ITALIC if italic else FC_SLANT_ROMAN) - candidate.get('slant', 0))
monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1 monospace_match = 0 if candidate.get('spacing') == 'MONO' else 1

View File

@ -6,7 +6,9 @@ import ctypes
import sys import sys
from functools import partial from functools import partial
from math import ceil, cos, floor, pi from math import ceil, cos, floor, pi
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast from typing import (
TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union, cast
)
from kitty.config import defaults from kitty.config import defaults
from kitty.constants import is_macos from kitty.constants import is_macos
@ -54,7 +56,7 @@ def coalesce_symbol_maps(maps: Dict[Tuple[int, int], str]) -> Dict[Tuple[int, in
items = tuple((k, maps[k]) for k in sorted(maps)) items = tuple((k, maps[k]) for k in sorted(maps))
ans = [items[0]] ans = [items[0]]
def merge(prev_item, item): def merge(prev_item: Tuple[Tuple[int, int], str], item: Tuple[Tuple[int, int], str]) -> None:
s, e = item[0] s, e = item[0]
pe = prev_item[0][1] pe = prev_item[0][1]
ans[-1] = ((prev_item[0][0], max(pe, e)), prev_item[1]) ans[-1] = ((prev_item[0][0], max(pe, e)), prev_item[1])
@ -88,11 +90,12 @@ def descriptor_for_idx(idx: int) -> Tuple[FontObject, bool, bool]:
def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None: def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None:
def face_str(f): def face_str(f: Tuple[FontObject, bool, bool]) -> str:
f = f[0] fo = f[0]
if is_macos: if 'index' in fo:
return f return '{}:{}'.format(fo['path'], cast('FontConfigPattern', fo)['index'])
return '{}:{}'.format(f['path'], f['index']) fo = cast('CoreTextFont', fo)
return fo['path']
log_error('Preloaded font faces:') log_error('Preloaded font faces:')
log_error('normal face:', face_str(current_faces[0])) log_error('normal face:', face_str(current_faces[0]))
@ -106,7 +109,7 @@ def dump_faces(ftypes: List[str], indices: Dict[str, int]) -> None:
log_error(face_str(face)) log_error(face_str(face))
def set_font_family(opts: Optional[OptionsStub] = None, override_font_size=None, debug_font_matching=False): def set_font_family(opts: Optional[OptionsStub] = None, override_font_size: Optional[float] = None, debug_font_matching: bool = False) -> None:
global current_faces global current_faces
opts = opts or defaults opts = opts or defaults
sz = override_font_size or opts.font_size sz = override_font_size or opts.font_size
@ -130,7 +133,10 @@ def set_font_family(opts: Optional[OptionsStub] = None, override_font_size=None,
) )
def add_line(buf, cell_width, position, thickness, cell_height): UnderlineCallback = Callable[[ctypes.Array, int, int, int, int], None]
def add_line(buf: ctypes.Array, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
y = position - thickness // 2 y = position - thickness // 2
while thickness > 0 and -1 < y < cell_height: while thickness > 0 and -1 < y < cell_height:
thickness -= 1 thickness -= 1
@ -138,7 +144,7 @@ def add_line(buf, cell_width, position, thickness, cell_height):
y += 1 y += 1
def add_dline(buf, cell_width, position, thickness, cell_height): def add_dline(buf: ctypes.Array, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
a = min(position - thickness, cell_height - 1) a = min(position - thickness, cell_height - 1)
b = min(position, cell_height - 1) b = min(position, cell_height - 1)
top, bottom = min(a, b), max(a, b) top, bottom = min(a, b), max(a, b)
@ -158,12 +164,12 @@ def add_dline(buf, cell_width, position, thickness, cell_height):
ctypes.memset(ctypes.addressof(buf) + (cell_width * y), 255, cell_width) ctypes.memset(ctypes.addressof(buf) + (cell_width * y), 255, cell_width)
def add_curl(buf, cell_width, position, thickness, cell_height): def add_curl(buf: ctypes.Array, cell_width: int, position: int, thickness: int, cell_height: int) -> None:
max_x, max_y = cell_width - 1, cell_height - 1 max_x, max_y = cell_width - 1, cell_height - 1
xfactor = 2.0 * pi / max_x xfactor = 2.0 * pi / max_x
half_height = max(thickness // 2, 1) half_height = max(thickness // 2, 1)
def add_intensity(x, y, val): def add_intensity(x: int, y: int, val: int) -> None:
y += position y += position
y = min(y, max_y) y = min(y, max_y)
idx = cell_width * y + x idx = cell_width * y + x
@ -186,13 +192,25 @@ def add_curl(buf, cell_width, position, thickness, cell_height):
def render_special( def render_special(
underline=0, strikethrough=False, missing=False, underline: int = 0,
cell_width=None, cell_height=None, baseline=None, underline_position=None, underline_thickness=None): strikethrough: bool = False,
missing: bool = False,
cell_width: int = 0, cell_height: int = 0,
baseline: int = 0,
underline_position: int = 0,
underline_thickness: int = 0
) -> ctypes.Array:
underline_position = min(underline_position, cell_height - underline_thickness) underline_position = min(underline_position, cell_height - underline_thickness)
CharTexture = ctypes.c_ubyte * (cell_width * cell_height) CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
ans = CharTexture if missing else CharTexture()
def dl(f, *a): if missing:
buf = bytearray(cell_width * cell_height)
render_missing_glyph(buf, cell_width, cell_height)
return CharTexture.from_buffer(buf)
ans = CharTexture()
def dl(f: UnderlineCallback, *a: Any) -> None:
try: try:
f(ans, cell_width, *a) f(ans, cell_width, *a)
except Exception as e: except Exception as e:
@ -203,23 +221,27 @@ def render_special(
t = underline_thickness t = underline_thickness
if underline > 1: if underline > 1:
t = max(1, min(cell_height - underline_position - 1, t)) t = max(1, min(cell_height - underline_position - 1, t))
dl([None, add_line, add_dline, add_curl][underline], underline_position, t, cell_height) dl([add_line, add_line, add_dline, add_curl][underline], underline_position, t, cell_height)
if strikethrough: if strikethrough:
pos = int(0.65 * baseline) pos = int(0.65 * baseline)
dl(add_line, pos, underline_thickness, cell_height) dl(add_line, pos, underline_thickness, cell_height)
if missing:
buf = bytearray(cell_width * cell_height)
render_missing_glyph(buf, cell_width, cell_height)
ans = CharTexture.from_buffer(buf)
return ans return ans
def render_cursor(which, cursor_beam_thickness, cursor_underline_thickness, cell_width=0, cell_height=0, dpi_x=0, dpi_y=0): def render_cursor(
which: int,
cursor_beam_thickness: float,
cursor_underline_thickness: float,
cell_width: int = 0,
cell_height: int = 0,
dpi_x: float = 0,
dpi_y: float = 0
) -> ctypes.Array:
CharTexture = ctypes.c_ubyte * (cell_width * cell_height) CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
ans = CharTexture() ans = CharTexture()
def vert(edge, width_pt=1): def vert(edge: str, width_pt: float = 1) -> None:
width = max(1, min(int(round(width_pt * dpi_x / 72.0)), cell_width)) width = max(1, min(int(round(width_pt * dpi_x / 72.0)), cell_width))
left = 0 if edge == 'left' else max(0, cell_width - width) left = 0 if edge == 'left' else max(0, cell_width - width)
for y in range(cell_height): for y in range(cell_height):
@ -227,7 +249,7 @@ def render_cursor(which, cursor_beam_thickness, cursor_underline_thickness, cell
for x in range(offset, offset + width): for x in range(offset, offset + width):
ans[x] = 255 ans[x] = 255
def horz(edge, height_pt=1): def horz(edge: str, height_pt: float = 1) -> None:
height = max(1, min(int(round(height_pt * dpi_y / 72.0)), cell_height)) height = max(1, min(int(round(height_pt * dpi_y / 72.0)), cell_height))
top = 0 if edge == 'top' else max(0, cell_height - height) top = 0 if edge == 'top' else max(0, cell_height - height)
for y in range(top, top + height): for y in range(top, top + height):
@ -240,13 +262,24 @@ def render_cursor(which, cursor_beam_thickness, cursor_underline_thickness, cell
elif which == 2: # underline elif which == 2: # underline
horz('bottom', cursor_underline_thickness) horz('bottom', cursor_underline_thickness)
elif which == 3: # hollow elif which == 3: # hollow
vert('left'), vert('right'), horz('top'), horz('bottom') vert('left')
vert('right')
horz('top')
horz('bottom')
return ans return ans
def prerender_function( def prerender_function(
cell_width, cell_height, baseline, underline_position, underline_thickness, cell_width: int,
cursor_beam_thickness, cursor_underline_thickness, dpi_x, dpi_y): cell_height: int,
baseline: int,
underline_position: int,
underline_thickness: int,
cursor_beam_thickness: float,
cursor_underline_thickness: float,
dpi_x: float,
dpi_y: float
) -> Tuple[Union[int, ctypes.Array], ...]:
# Pre-render the special underline, strikethrough and missing and cursor cells # Pre-render the special underline, strikethrough and missing and cursor cells
f = partial( f = partial(
render_special, cell_width=cell_width, cell_height=cell_height, baseline=baseline, render_special, cell_width=cell_width, cell_height=cell_height, baseline=baseline,
@ -259,7 +292,7 @@ def prerender_function(
return tuple(map(ctypes.addressof, cells)) + (cells,) return tuple(map(ctypes.addressof, cells)) + (cells,)
def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: float): def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: float) -> Tuple[int, ctypes.Array]:
CharTexture = ctypes.c_ubyte * (cell_width * cell_height) CharTexture = ctypes.c_ubyte * (cell_width * cell_height)
buf = CharTexture() buf = CharTexture()
render_box_char( render_box_char(
@ -270,16 +303,15 @@ def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: f
class setup_for_testing: class setup_for_testing:
def __init__(self, family='monospace', size=11.0, dpi=96.0): def __init__(self, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0):
self.family, self.size, self.dpi = family, size, dpi self.family, self.size, self.dpi = family, size, dpi
def __enter__(self): def __enter__(self) -> Tuple[Dict[Tuple[int, int, int], bytes], int, int]:
from collections import OrderedDict
opts = defaults._replace(font_family=self.family, font_size=self.size) opts = defaults._replace(font_family=self.family, font_size=self.size)
set_options(opts) set_options(opts)
sprites = OrderedDict() sprites = {}
def send_to_gpu(x, y, z, data): def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None:
sprites[(x, y, z)] = data sprites[(x, y, z)] = data
sprite_map_set_limits(100000, 100) sprite_map_set_limits(100000, 100)
@ -292,11 +324,11 @@ class setup_for_testing:
set_send_sprite_to_gpu(None) set_send_sprite_to_gpu(None)
raise raise
def __exit__(self, *args): def __exit__(self, *args: Any) -> None:
set_send_sprite_to_gpu(None) set_send_sprite_to_gpu(None)
def render_string(text, family='monospace', size=11.0, dpi=96.0): def render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0) -> Tuple[int, int, List[bytes]]:
with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height): with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):
s = Screen(None, 1, len(text)*2) s = Screen(None, 1, len(text)*2)
line = s.line(0) line = s.line(0)
@ -307,7 +339,7 @@ def render_string(text, family='monospace', size=11.0, dpi=96.0):
for i in reversed(range(s.columns)): for i in reversed(range(s.columns)):
sp = list(line.sprite_at(i)) sp = list(line.sprite_at(i))
sp[2] &= 0xfff sp[2] &= 0xfff
tsp = tuple(sp) tsp = sp[0], sp[1], sp[2]
if tsp == (0, 0, 0) and not found_content: if tsp == (0, 0, 0) and not found_content:
continue continue
found_content = True found_content = True
@ -315,7 +347,9 @@ def render_string(text, family='monospace', size=11.0, dpi=96.0):
return cell_width, cell_height, list(reversed(cells)) return cell_width, cell_height, list(reversed(cells))
def shape_string(text="abcd", family='monospace', size=11.0, dpi=96.0, path=None): def shape_string(
text: str = "abcd", family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, path: Optional[str] = None
) -> List[Tuple[int, int, int, Tuple[int, ...]]]:
with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height): with setup_for_testing(family, size, dpi) as (sprites, cell_width, cell_height):
s = Screen(None, 1, len(text)*2) s = Screen(None, 1, len(text)*2)
line = s.line(0) line = s.line(0)
@ -323,7 +357,7 @@ def shape_string(text="abcd", family='monospace', size=11.0, dpi=96.0, path=None
return test_shape(line, path) return test_shape(line, path)
def display_bitmap(rgb_data, width, height): def display_bitmap(rgb_data: bytes, width: int, height: int) -> None:
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from kittens.icat.main import detect_support, show from kittens.icat.main import detect_support, show
if not hasattr(display_bitmap, 'detected') and not detect_support(): if not hasattr(display_bitmap, 'detected') and not detect_support():
@ -335,7 +369,12 @@ def display_bitmap(rgb_data, width, height):
show(f.name, width, height, 0, 32, align='left') show(f.name, width, height, 0, 32, align='left')
def test_render_string(text='Hello, world!', family='monospace', size=64.0, dpi=96.0): def test_render_string(
text: str = 'Hello, world!',
family: str = 'monospace',
size: float = 64.0,
dpi: float = 96.0
) -> None:
from kitty.fast_data_types import concat_cells, current_fonts 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)
@ -352,7 +391,7 @@ def test_render_string(text='Hello, world!', family='monospace', size=64.0, dpi=
print('\n') print('\n')
def test_fallback_font(qtext: Optional[str] = None, bold=False, italic=False): def test_fallback_font(qtext: Optional[str] = None, bold: bool = False, italic: bool = False) -> None:
with setup_for_testing(): with setup_for_testing():
if qtext: if qtext:
trials = [qtext] trials = [qtext]
@ -366,7 +405,7 @@ def test_fallback_font(qtext: Optional[str] = None, bold=False, italic=False):
sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8')) sys.stdout.buffer.write((text + ' %s\n' % f).encode('utf-8'))
def showcase(): def showcase() -> None:
f = 'monospace' if is_macos else 'Liberation Mono' f = 'monospace' if is_macos else 'Liberation Mono'
test_render_string('He\u0347\u0305llo\u0337, w\u0302or\u0306l\u0354d!', family=f) test_render_string('He\u0347\u0305llo\u0337, w\u0302or\u0306l\u0354d!', family=f)
test_render_string('你好,世界', family=f) test_render_string('你好,世界', family=f)

View File

@ -22,7 +22,7 @@ except ImportError:
@lru_cache(maxsize=2) @lru_cache(maxsize=2)
def options_spec(): def options_spec() -> str:
return ''' return '''
--window-title --title --window-title --title
The title to set for the new window. By default, title is controlled by the The title to set for the new window. By default, title is controlled by the

View File

@ -27,7 +27,7 @@ warn_unused_configs = True
check_untyped_defs = True check_untyped_defs = True
# disallow_untyped_defs = True # disallow_untyped_defs = True
[mypy-kitty.rc.*,kitty.conf.*] [mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kitty.launch,kitty.child]
disallow_untyped_defs = True disallow_untyped_defs = True
[mypy-conf] [mypy-conf]