Compare commits

..

16 Commits

Author SHA1 Message Date
Kovid Goyal
4e7b957cc7 version 0.4.2 2017-10-23 17:11:04 +05:30
Kovid Goyal
b1424cc4c7 ... 2017-10-23 17:09:52 +05:30
Kovid Goyal
be7d0e2016 forgot to remove debugging printf 2017-10-23 16:46:57 +05:30
Kovid Goyal
7e8d509fdd Fix #153 2017-10-23 16:45:17 +05:30
Kovid Goyal
8672a29503 Use a C based SIGCHLD handler
Running python code in signal handlers makes me nervous
2017-10-23 12:42:03 +05:30
Kovid Goyal
32a1886b31 version 0.4.1 2017-10-23 11:19:30 +05:30
Kovid Goyal
70bfe8589f Better fix for #152 2017-10-23 11:12:31 +05:30
Kovid Goyal
4f3d71d4ee Fix a regression that broke setting custom key mappings
Fixes #152
2017-10-23 11:04:02 +05:30
Kovid Goyal
0df0fd6a0b Handle EINTR in reap_zombies 2017-10-22 19:29:13 +05:30
Kovid Goyal
2b9198866a ... 2017-10-22 19:25:51 +05:30
Kovid Goyal
6350652009 Reap zombies in a signal handler
Avoids relying on jernel behavior, which may not be portable across all
unices
2017-10-22 19:13:13 +05:30
Kovid Goyal
d239db8492 Simply reaping of dead processes
Let the kernel do the reaping, since we do not care about the exit code
anyway. This should also fix #151.
2017-10-22 17:42:49 +05:30
Kovid Goyal
bdb6723564 Remove unused code 2017-10-22 17:13:38 +05:30
Kovid Goyal
d6bf5aa011 Remove unused code 2017-10-22 17:13:11 +05:30
Kovid Goyal
b5c4fbb42b ... 2017-10-22 17:11:58 +05:30
Kovid Goyal
1276bd6f5d Simply code to query xrdb for Xft.dpi 2017-10-22 17:09:30 +05:30
9 changed files with 101 additions and 139 deletions

View File

@@ -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]
---------------------------

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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')

View File

@@ -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, ""},

View File

@@ -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,

View File

@@ -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);

View File

@@ -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()

View File

@@ -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'):