Compare commits

...

11 Commits

Author SHA1 Message Date
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
7 changed files with 60 additions and 112 deletions

View File

@@ -3,6 +3,16 @@ Changelog
kitty is a feature full, cross-platform, *fast*, OpenGL based terminal emulator. kitty is a feature full, cross-platform, *fast*, OpenGL based terminal emulator.
version 0.4.1 [2017-10-23]
---------------------------
- Fix a regression in 0.4.0 that broke custom key mappings
- 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] 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 static inline void
cleanup_child(ssize_t i) { cleanup_child(ssize_t i) {
close(children[i].fd); close(children[i].fd);
hangup(children[i].pid); 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) defaults = parse_config(f.readlines(), check_keys=False)
Options = namedtuple('Defaults', ','.join(defaults.keys())) Options = namedtuple('Defaults', ','.join(defaults.keys()))
defaults = Options(**defaults) 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): def merge_keymaps(defaults, newvals):
ans = defaults.copy() ans = defaults.copy()
for k, v in newvals.items(): 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) ans.pop(k, None)
continue continue
if v in actions: if f in actions:
ans[k] = v ans[k] = v
return ans return ans

View File

@@ -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, 4, 0) version = (0, 4, 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
@@ -124,4 +124,12 @@ def x11_window_id(window):
return lib.glfwGetX11Window(window.window_id()) 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') iswayland = not isosx and hasattr(glfw_lib(), 'glfwGetWaylandDisplay')

View File

@@ -4,7 +4,6 @@
import os import os
import re import re
import subprocess
from collections import namedtuple from collections import namedtuple
from kitty.fast_data_types import Face, get_fontconfig_font from kitty.fast_data_types import Face, get_fontconfig_font
@@ -35,51 +34,7 @@ def font_not_found(err, char):
return FontNotFound(msg) return FontNotFound(msg)
def get_font_subprocess( def get_font(
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(
family='monospace', family='monospace',
bold=False, bold=False,
italic=False, italic=False,
@@ -102,9 +57,6 @@ def get_font_lib(
) )
get_font = get_font_lib
def find_font_for_character( def find_font_for_character(
family, family,
char, char,

View File

@@ -3,8 +3,10 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import argparse import argparse
import errno
import locale import locale
import os import os
import signal
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from gettext import gettext as _ from gettext import gettext as _
@@ -235,14 +237,27 @@ def setup_profiling(args):
exe = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'kitty-profile') exe = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'kitty-profile')
cg = '/tmp/kitty-profile.callgrind' cg = '/tmp/kitty-profile.callgrind'
print('Post processing profile data for', exe, '...') 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: try:
subprocess.Popen(['kcachegrind', cg]) subprocess.Popen(['kcachegrind', cg])
except FileNotFoundError: 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) print('To view the graphical call data, use: kcachegrind', cg)
def reap_zombies(*a):
while True:
try:
pid, status = os.waitpid(-1, os.WNOHANG)
if pid == 0:
break
except OSError as err:
if err.errno != errno.EINTR:
break
except Exception:
break
def main(): def main():
try: try:
sys.setswitchinterval(1000.0) # we have only a single python thread sys.setswitchinterval(1000.0) # we have only a single python thread
@@ -286,6 +301,9 @@ def main():
raise SystemExit('GLFW initialization failed') raise SystemExit('GLFW initialization failed')
try: try:
with setup_profiling(args): with setup_profiling(args):
# Avoid needing to launch threads to reap zombies
signal.signal(signal.SIGCHLD, reap_zombies)
run_app(opts, args) run_app(opts, args)
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
finally: finally:
glfw_terminate() glfw_terminate()

View File

@@ -12,7 +12,7 @@ from contextlib import contextmanager
from functools import lru_cache from functools import lru_cache
from time import monotonic 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 ( from .fast_data_types import (
GLSL_VERSION, glfw_get_physical_dpi, redirect_std_streams, GLSL_VERSION, glfw_get_physical_dpi, redirect_std_streams,
wcwidth as wcwidth_impl wcwidth as wcwidth_impl
@@ -72,7 +72,6 @@ def load_libx11():
import ctypes import ctypes
from ctypes.util import find_library from ctypes.util import find_library
libx11 = ctypes.CDLL(find_library('X11')) libx11 = ctypes.CDLL(find_library('X11'))
ans = []
def cdef(name, restype, *argtypes): def cdef(name, restype, *argtypes):
f = getattr(libx11, name) f = getattr(libx11, name)
@@ -80,43 +79,27 @@ def load_libx11():
f.restype = restype f.restype = restype
if argtypes: if argtypes:
f.argtypes = argtypes f.argtypes = argtypes
ans.append(f) return f
cdef('XOpenDisplay', ctypes.c_void_p, ctypes.c_char_p) return cdef('XResourceManagerString', ctypes.c_char_p, ctypes.c_void_p)
cdef('XCloseDisplay', ctypes.c_int, ctypes.c_void_p)
cdef('XResourceManagerString', ctypes.c_char_p, ctypes.c_void_p)
return ans
def parse_xrdb(raw): def parse_xrdb(raw):
q = 'Xft.dpi:\t' 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): if line.startswith(q):
return float(line[len(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(): def x11_dpi():
try: XResourceManagerString = load_libx11()
return x11_dpi_native() display = x11_display()
except Exception: if display:
pass try:
try: raw = XResourceManagerString(display)
raw = subprocess.check_output(['xrdb', '-query'], stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) return parse_xrdb(raw)
return parse_xrdb(raw) except Exception:
except Exception: pass
pass
def get_logical_dpi(): def get_logical_dpi():
@@ -182,7 +165,8 @@ def get_primary_selection():
return '' # There is no primary selection on OS X return '' # There is no primary selection on OS X
g = selection_clipboard_funcs()[0] g = selection_clipboard_funcs()[0]
if g is None: 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: if ans:
# Without this for some reason repeated pastes dont work # Without this for some reason repeated pastes dont work
set_primary_selection(ans) set_primary_selection(ans)
@@ -209,7 +193,7 @@ def open_cmd(cmd, arg=None):
if arg is not None: if arg is not None:
cmd = list(cmd) cmd = list(cmd)
cmd.append(arg) 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'): def open_url(url, program='default'):