Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32a1886b31 | ||
|
|
70bfe8589f | ||
|
|
4f3d71d4ee | ||
|
|
0df0fd6a0b | ||
|
|
2b9198866a | ||
|
|
6350652009 | ||
|
|
d239db8492 | ||
|
|
bdb6723564 | ||
|
|
d6bf5aa011 | ||
|
|
b5c4fbb42b | ||
|
|
1276bd6f5d |
@@ -3,6 +3,16 @@ Changelog
|
||||
|
||||
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]
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -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, 1)
|
||||
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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import locale
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
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')
|
||||
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)
|
||||
|
||||
|
||||
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():
|
||||
try:
|
||||
sys.setswitchinterval(1000.0) # we have only a single python thread
|
||||
@@ -286,6 +301,9 @@ def main():
|
||||
raise SystemExit('GLFW initialization failed')
|
||||
try:
|
||||
with setup_profiling(args):
|
||||
# Avoid needing to launch threads to reap zombies
|
||||
signal.signal(signal.SIGCHLD, reap_zombies)
|
||||
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