mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-14 20:47:58 +02:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e7b957cc7 | ||
|
|
b1424cc4c7 | ||
|
|
be7d0e2016 | ||
|
|
7e8d509fdd | ||
|
|
8672a29503 | ||
|
|
32a1886b31 | ||
|
|
70bfe8589f | ||
|
|
4f3d71d4ee | ||
|
|
0df0fd6a0b | ||
|
|
2b9198866a | ||
|
|
6350652009 | ||
|
|
d239db8492 | ||
|
|
bdb6723564 | ||
|
|
d6bf5aa011 | ||
|
|
b5c4fbb42b | ||
|
|
1276bd6f5d |
@@ -3,6 +3,18 @@ Changelog
|
||||
|
||||
kitty is a feature full, cross-platform, *fast*, OpenGL based terminal emulator.
|
||||
|
||||
|
||||
version 0.4.2 [2017-10-23]
|
||||
---------------------------
|
||||
|
||||
- Fix a regression in 0.4.0 that broke custom key mappings
|
||||
|
||||
- Fix a regression in 0.4.0 that broke support for non-QWERTY keyboard layouts
|
||||
|
||||
- Avoid using threads to reap zombie child processes. Also prevent kitty from
|
||||
hanging if the open program hangs when clicking on a URL.
|
||||
|
||||
|
||||
version 0.4.0 [2017-10-22]
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -662,37 +662,11 @@ hangup(pid_t pid) {
|
||||
}
|
||||
}
|
||||
|
||||
static pid_t pid_buf[MAX_CHILDREN] = {0};
|
||||
static size_t pid_buf_pos = 0;
|
||||
static pthread_t reap_thread;
|
||||
|
||||
|
||||
static void*
|
||||
reap(void *pid_p) {
|
||||
set_thread_name("KittyReapChild");
|
||||
pid_t pid = *((pid_t*)pid_p);
|
||||
while(true) {
|
||||
pid_t ret = waitpid(pid, NULL, 0);
|
||||
if (ret != pid) {
|
||||
if (errno == EINTR) continue;
|
||||
fprintf(stderr, "Failed to reap child process with pid: %d with error: %s\n", pid, strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
cleanup_child(ssize_t i) {
|
||||
close(children[i].fd);
|
||||
hangup(children[i].pid);
|
||||
pid_buf[pid_buf_pos] = children[i].pid;
|
||||
if (waitpid(pid_buf[pid_buf_pos], NULL, WNOHANG) != pid_buf[pid_buf_pos]) {
|
||||
errno = 0;
|
||||
int ret = pthread_create(&reap_thread, NULL, reap, pid_buf + pid_buf_pos);
|
||||
if (ret != 0) perror("Failed to create thread to reap child");
|
||||
}
|
||||
pid_buf_pos = (pid_buf_pos + 1) % MAX_CHILDREN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -324,16 +324,18 @@ with open(
|
||||
defaults = parse_config(f.readlines(), check_keys=False)
|
||||
Options = namedtuple('Defaults', ','.join(defaults.keys()))
|
||||
defaults = Options(**defaults)
|
||||
actions = frozenset(a.func for a in defaults.keymap.values())
|
||||
actions = frozenset(a.func for a in defaults.keymap.values()) | frozenset({'combine'})
|
||||
no_op_actions = frozenset({'noop', 'no-op', 'no_op'})
|
||||
|
||||
|
||||
def merge_keymaps(defaults, newvals):
|
||||
ans = defaults.copy()
|
||||
for k, v in newvals.items():
|
||||
if v in {'noop', 'no-op', 'no_op'}:
|
||||
f = v.func
|
||||
if f in no_op_actions:
|
||||
ans.pop(k, None)
|
||||
continue
|
||||
if v in actions:
|
||||
if f in actions:
|
||||
ans[k] = v
|
||||
return ans
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from collections import namedtuple
|
||||
from .fast_data_types import set_boss as set_c_boss
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 4, 0)
|
||||
version = (0, 4, 2)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
isosx = 'darwin' in _plat
|
||||
@@ -124,4 +124,12 @@ def x11_window_id(window):
|
||||
return lib.glfwGetX11Window(window.window_id())
|
||||
|
||||
|
||||
def x11_display():
|
||||
lib = glfw_lib()
|
||||
ans = lib.glfwGetX11Display
|
||||
ans.restype = ctypes.c_void_p
|
||||
ans.argtypes = []
|
||||
return ans()
|
||||
|
||||
|
||||
iswayland = not isosx and hasattr(glfw_lib(), 'glfwGetWaylandDisplay')
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "modes.h"
|
||||
#include <stddef.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef WITH_PROFILER
|
||||
#include <gperftools/profiler.h>
|
||||
#endif
|
||||
@@ -81,6 +83,28 @@ pyset_iutf8(PyObject UNUSED *self, PyObject *args) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_sigchld(int UNUSED signum, siginfo_t *sinfo, void UNUSED *unused) {
|
||||
if (sinfo->si_code != CLD_EXITED) return;
|
||||
int sav_errno = errno, status;
|
||||
while(true) {
|
||||
if (waitpid(sinfo->si_pid, &status, WNOHANG) == -1) {
|
||||
if (errno != EINTR) break;
|
||||
} else break;
|
||||
}
|
||||
errno = sav_errno;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
install_sigchld_handler(PyObject UNUSED *self) {
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = handle_sigchld;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGCHLD, &sa, NULL) == -1) return PyErr_SetFromErrno(PyExc_OSError);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef WITH_PROFILER
|
||||
static PyObject*
|
||||
start_profiler(PyObject UNUSED *self, PyObject *args) {
|
||||
@@ -105,6 +129,7 @@ static PyMethodDef module_methods[] = {
|
||||
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
|
||||
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
|
||||
{"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""},
|
||||
{"install_sigchld_handler", (PyCFunction)install_sigchld_handler, METH_NOARGS, ""},
|
||||
#ifdef WITH_PROFILER
|
||||
{"start_profiler", (PyCFunction)start_profiler, METH_VARARGS, ""},
|
||||
{"stop_profiler", (PyCFunction)stop_profiler, METH_NOARGS, ""},
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from collections import namedtuple
|
||||
|
||||
from kitty.fast_data_types import Face, get_fontconfig_font
|
||||
@@ -35,51 +34,7 @@ def font_not_found(err, char):
|
||||
return FontNotFound(msg)
|
||||
|
||||
|
||||
def get_font_subprocess(
|
||||
family='monospace',
|
||||
bold=False,
|
||||
italic=False,
|
||||
allow_bitmaped_fonts=False,
|
||||
size_in_pts=None,
|
||||
character=None,
|
||||
dpi=None
|
||||
):
|
||||
query = escape_family_name(family)
|
||||
if character is not None:
|
||||
query += ':charset={:x}'.format(ord(character[0]))
|
||||
if not allow_bitmaped_fonts:
|
||||
query += ':scalable=true:outline=true'
|
||||
if size_in_pts is not None:
|
||||
query += ':size={:.1f}'.format(size_in_pts)
|
||||
if dpi is not None:
|
||||
query += ':dpi={:.1f}'.format(dpi)
|
||||
if bold:
|
||||
query += ':weight=200'
|
||||
if italic:
|
||||
query += ':slant=100'
|
||||
try:
|
||||
raw = subprocess.check_output([
|
||||
'fc-match', query, '-f',
|
||||
'%{file}\x1e%{hinting}\x1e%{hintstyle}\x1e%{scalable}\x1e%{outline}\x1e%{weight}\x1e%{slant}\x1e%{index}'
|
||||
]).decode('utf-8')
|
||||
except subprocess.CalledProcessError as err:
|
||||
raise font_not_found(err, character)
|
||||
parts = raw.split('\x1e')
|
||||
try:
|
||||
path, hinting, hintstyle, scalable, outline, weight, slant, index = parts
|
||||
except ValueError:
|
||||
path = parts[0]
|
||||
hintstyle, hinting, scalable, outline, weight, slant, index = 1, 'True', 'True', 'True', 100, 0, 0
|
||||
return Font(
|
||||
path,
|
||||
to_bool(hinting),
|
||||
int(hintstyle), bold, italic,
|
||||
to_bool(scalable),
|
||||
to_bool(outline), int(weight), int(slant), int(index)
|
||||
)
|
||||
|
||||
|
||||
def get_font_lib(
|
||||
def get_font(
|
||||
family='monospace',
|
||||
bold=False,
|
||||
italic=False,
|
||||
@@ -102,9 +57,6 @@ def get_font_lib(
|
||||
)
|
||||
|
||||
|
||||
get_font = get_font_lib
|
||||
|
||||
|
||||
def find_font_for_character(
|
||||
family,
|
||||
char,
|
||||
|
||||
51
kitty/keys.c
51
kitty/keys.c
@@ -89,31 +89,32 @@ get_localized_key(int key, int scancode) {
|
||||
switch(name[0]) {
|
||||
#define K(ch, name) case ch: return GLFW_KEY_##name
|
||||
// key names {{{
|
||||
K('A', A);
|
||||
K('B', B);
|
||||
K('C', C);
|
||||
K('D', D);
|
||||
K('E', E);
|
||||
K('F', F);
|
||||
K('G', G);
|
||||
K('H', H);
|
||||
K('I', I);
|
||||
K('J', J);
|
||||
K('K', K);
|
||||
K('L', L);
|
||||
K('M', M);
|
||||
K('N', N);
|
||||
K('O', O);
|
||||
K('P', P);
|
||||
K('Q', Q);
|
||||
K('S', S);
|
||||
K('T', T);
|
||||
K('U', U);
|
||||
K('V', V);
|
||||
K('W', W);
|
||||
K('X', X);
|
||||
K('Y', Y);
|
||||
K('Z', Z);
|
||||
K('A', A); K('a', A);
|
||||
K('B', B); K('b', B);
|
||||
K('C', C); K('c', C);
|
||||
K('D', D); K('d', D);
|
||||
K('E', E); K('e', E);
|
||||
K('F', F); K('f', F);
|
||||
K('G', G); K('g', G);
|
||||
K('H', H); K('h', H);
|
||||
K('I', I); K('i', I);
|
||||
K('J', J); K('j', J);
|
||||
K('K', K); K('k', K);
|
||||
K('L', L); K('l', L);
|
||||
K('M', M); K('m', M);
|
||||
K('N', N); K('n', N);
|
||||
K('O', O); K('o', O);
|
||||
K('P', P); K('p', P);
|
||||
K('Q', Q); K('q', Q);
|
||||
K('R', R); K('r', R);
|
||||
K('S', S); K('s', S);
|
||||
K('T', T); K('t', T);
|
||||
K('U', U); K('u', U);
|
||||
K('V', V); K('v', V);
|
||||
K('W', W); K('w', W);
|
||||
K('X', X); K('x', X);
|
||||
K('Y', Y); K('y', Y);
|
||||
K('Z', Z); K('z', Z);
|
||||
K('0', 0);
|
||||
K('1', 1);
|
||||
K('2', 2);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import argparse
|
||||
import locale
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from gettext import gettext as _
|
||||
@@ -23,8 +24,8 @@ from .fast_data_types import (
|
||||
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_PROFILE, GLFW_SAMPLES,
|
||||
GLFW_STENCIL_BITS, GLFWWindow, change_wcwidth, check_for_extensions,
|
||||
clear_buffers, glewInit, glfw_init, glfw_init_hint_string,
|
||||
glfw_swap_interval, glfw_terminate, glfw_window_hint, set_logical_dpi,
|
||||
set_options
|
||||
glfw_swap_interval, glfw_terminate, glfw_window_hint,
|
||||
install_sigchld_handler, set_logical_dpi, set_options
|
||||
)
|
||||
from .layout import all_layouts
|
||||
from .utils import color_as_int, detach, get_logical_dpi, safe_print
|
||||
@@ -235,11 +236,11 @@ def setup_profiling(args):
|
||||
exe = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'kitty-profile')
|
||||
cg = '/tmp/kitty-profile.callgrind'
|
||||
print('Post processing profile data for', exe, '...')
|
||||
subprocess.check_call(['pprof', '--callgrind', exe, '/tmp/kitty-profile.log'], stdout=open(cg, 'wb'))
|
||||
subprocess.call(['pprof', '--callgrind', exe, '/tmp/kitty-profile.log'], stdout=open(cg, 'wb'))
|
||||
try:
|
||||
subprocess.Popen(['kcachegrind', cg])
|
||||
except FileNotFoundError:
|
||||
subprocess.check_call(['pprof', '--text', exe, '/tmp/kitty-profile.log'])
|
||||
subprocess.call(['pprof', '--text', exe, '/tmp/kitty-profile.log'])
|
||||
print('To view the graphical call data, use: kcachegrind', cg)
|
||||
|
||||
|
||||
@@ -286,6 +287,9 @@ def main():
|
||||
raise SystemExit('GLFW initialization failed')
|
||||
try:
|
||||
with setup_profiling(args):
|
||||
# Avoid needing to launch threads to reap zombies
|
||||
install_sigchld_handler()
|
||||
run_app(opts, args)
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
finally:
|
||||
glfw_terminate()
|
||||
|
||||
@@ -12,7 +12,7 @@ from contextlib import contextmanager
|
||||
from functools import lru_cache
|
||||
from time import monotonic
|
||||
|
||||
from .constants import isosx, iswayland, selection_clipboard_funcs
|
||||
from .constants import isosx, iswayland, selection_clipboard_funcs, x11_display
|
||||
from .fast_data_types import (
|
||||
GLSL_VERSION, glfw_get_physical_dpi, redirect_std_streams,
|
||||
wcwidth as wcwidth_impl
|
||||
@@ -72,7 +72,6 @@ def load_libx11():
|
||||
import ctypes
|
||||
from ctypes.util import find_library
|
||||
libx11 = ctypes.CDLL(find_library('X11'))
|
||||
ans = []
|
||||
|
||||
def cdef(name, restype, *argtypes):
|
||||
f = getattr(libx11, name)
|
||||
@@ -80,43 +79,27 @@ def load_libx11():
|
||||
f.restype = restype
|
||||
if argtypes:
|
||||
f.argtypes = argtypes
|
||||
ans.append(f)
|
||||
return f
|
||||
|
||||
cdef('XOpenDisplay', ctypes.c_void_p, ctypes.c_char_p)
|
||||
cdef('XCloseDisplay', ctypes.c_int, ctypes.c_void_p)
|
||||
cdef('XResourceManagerString', ctypes.c_char_p, ctypes.c_void_p)
|
||||
return ans
|
||||
return cdef('XResourceManagerString', ctypes.c_char_p, ctypes.c_void_p)
|
||||
|
||||
|
||||
def parse_xrdb(raw):
|
||||
q = 'Xft.dpi:\t'
|
||||
for line in raw.decode('utf-8').splitlines():
|
||||
for line in raw.decode('utf-8', 'replace').splitlines():
|
||||
if line.startswith(q):
|
||||
return float(line[len(q):])
|
||||
|
||||
|
||||
def x11_dpi_native():
|
||||
XOpenDisplay, XCloseDisplay, XResourceManagerString = load_libx11()
|
||||
display = XOpenDisplay(None)
|
||||
if display is None:
|
||||
raise RuntimeError('Could not connect to the X server')
|
||||
try:
|
||||
raw = XResourceManagerString(display)
|
||||
return parse_xrdb(raw)
|
||||
finally:
|
||||
XCloseDisplay(display)
|
||||
|
||||
|
||||
def x11_dpi():
|
||||
try:
|
||||
return x11_dpi_native()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
raw = subprocess.check_output(['xrdb', '-query'], stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)
|
||||
return parse_xrdb(raw)
|
||||
except Exception:
|
||||
pass
|
||||
XResourceManagerString = load_libx11()
|
||||
display = x11_display()
|
||||
if display:
|
||||
try:
|
||||
raw = XResourceManagerString(display)
|
||||
return parse_xrdb(raw)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def get_logical_dpi():
|
||||
@@ -182,7 +165,8 @@ def get_primary_selection():
|
||||
return '' # There is no primary selection on OS X
|
||||
g = selection_clipboard_funcs()[0]
|
||||
if g is None:
|
||||
ans = subprocess.check_output(['xsel', '-p'], stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL).decode('utf-8')
|
||||
# We cannot use check_output as we set a SIGCHLD handler to reap zombies
|
||||
ans = subprocess.Popen(['xsel', '-p'], stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
|
||||
if ans:
|
||||
# Without this for some reason repeated pastes dont work
|
||||
set_primary_selection(ans)
|
||||
@@ -209,7 +193,7 @@ def open_cmd(cmd, arg=None):
|
||||
if arg is not None:
|
||||
cmd = list(cmd)
|
||||
cmd.append(arg)
|
||||
return subprocess.Popen(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).wait()
|
||||
return subprocess.Popen(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def open_url(url, program='default'):
|
||||
|
||||
Reference in New Issue
Block a user