mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-26 10:51:55 +00:00
137 lines
3.7 KiB
Python
137 lines
3.7 KiB
Python
#!/usr/bin/env python
|
|
# vim:fileencoding=utf-8
|
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import termios
|
|
import struct
|
|
import fcntl
|
|
import signal
|
|
import ctypes
|
|
from contextlib import contextmanager
|
|
from functools import lru_cache
|
|
from time import monotonic
|
|
|
|
import glfw
|
|
|
|
from .constants import terminfo_dir
|
|
|
|
libc = ctypes.CDLL(None)
|
|
wcwidth_native = libc.wcwidth
|
|
del libc
|
|
wcwidth_native.argtypes = [ctypes.c_wchar]
|
|
wcwidth_native.restype = ctypes.c_int
|
|
|
|
|
|
@lru_cache(maxsize=2**13)
|
|
def wcwidth(c: str) -> int:
|
|
ans = min(2, wcwidth_native(c))
|
|
if ans == -1:
|
|
ans = 1
|
|
return ans
|
|
|
|
|
|
def create_pty():
|
|
if not hasattr(create_pty, 'master'):
|
|
create_pty.master, create_pty.slave = os.openpty()
|
|
fcntl.fcntl(create_pty.slave, fcntl.F_SETFD, fcntl.fcntl(create_pty.slave, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC)
|
|
# Note that master and slave are in blocking mode
|
|
return create_pty.master, create_pty.slave
|
|
|
|
|
|
def fork_child(argv, cwd, opts):
|
|
master, slave = create_pty()
|
|
pid = os.fork()
|
|
if pid == 0:
|
|
try:
|
|
os.chdir(cwd)
|
|
except EnvironmentError:
|
|
os.chdir('/')
|
|
os.setsid()
|
|
for i in range(3):
|
|
os.dup2(slave, i)
|
|
os.close(slave), os.close(master)
|
|
os.closerange(3, 200)
|
|
# Establish the controlling terminal (see man 7 credentials)
|
|
os.close(os.open(os.ttyname(1), os.O_RDWR))
|
|
os.environ['TERM'] = opts.term
|
|
os.environ['COLORTERM'] = 'truecolor'
|
|
if os.path.isdir(terminfo_dir):
|
|
os.environ['TERMINFO'] = terminfo_dir
|
|
try:
|
|
os.execvp(argv[0], argv)
|
|
except Exception as err:
|
|
print('Could not launch:', argv[0])
|
|
print('\t', err)
|
|
input('\nPress Enter to exit:')
|
|
else:
|
|
os.close(slave)
|
|
fork_child.pid = pid
|
|
return pid
|
|
|
|
|
|
def resize_pty(w, h):
|
|
master = create_pty()[0]
|
|
fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack('4H', h, w, 0, 0))
|
|
|
|
|
|
def hangup():
|
|
if hasattr(fork_child, 'pid'):
|
|
pid = fork_child.pid
|
|
del fork_child.pid
|
|
try:
|
|
pgrp = os.getpgid(pid)
|
|
except ProcessLookupError:
|
|
return
|
|
os.killpg(pgrp, signal.SIGHUP)
|
|
os.close(create_pty()[0])
|
|
|
|
|
|
def get_child_status():
|
|
if hasattr(fork_child, 'pid'):
|
|
try:
|
|
return os.waitid(os.P_PID, fork_child.pid, os.WEXITED | os.WNOHANG)
|
|
except ChildProcessError:
|
|
del fork_child.pid
|
|
|
|
base_size = sys.getsizeof('')
|
|
|
|
|
|
def is_simple_string(x):
|
|
' We use the fact that python stores unicode strings with a 1-byte representation when possible '
|
|
return sys.getsizeof(x) == base_size + len(x)
|
|
|
|
|
|
@contextmanager
|
|
def timeit(name, do_timing=False):
|
|
if do_timing:
|
|
st = monotonic()
|
|
yield
|
|
if do_timing:
|
|
print('Time for {}: {}'.format(name, monotonic() - st))
|
|
|
|
|
|
def sanitize_title(x):
|
|
return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19]', '', x))
|
|
|
|
|
|
def get_logical_dpi():
|
|
if not hasattr(get_logical_dpi, 'ans'):
|
|
raw = subprocess.check_output(['xdpyinfo']).decode('utf-8')
|
|
m = re.search(r'^\s*resolution:\s*(\d+)+x(\d+)', raw, flags=re.MULTILINE)
|
|
get_logical_dpi.ans = int(m.group(1)), int(m.group(2))
|
|
return get_logical_dpi.ans
|
|
|
|
|
|
def get_dpi():
|
|
if not hasattr(get_dpi, 'ans'):
|
|
m = glfw.glfwGetPrimaryMonitor()
|
|
width, height = glfw.glfwGetMonitorPhysicalSize(m)
|
|
vmode = glfw.glfwGetVideoMode(m)
|
|
dpix = vmode.width / (width / 25.4)
|
|
dpiy = vmode.height / (height / 25.4)
|
|
get_dpi.ans = {'physical': (dpix, dpiy), 'logical': get_logical_dpi()}
|
|
return get_dpi.ans
|