mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
266 lines
8.0 KiB
Python
266 lines
8.0 KiB
Python
#!/usr/bin/env python
|
|
# vim:fileencoding=utf-8
|
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from gettext import gettext as _
|
|
|
|
from queue import Empty
|
|
|
|
from .boss import Boss
|
|
from .config import (
|
|
cached_values, load_cached_values, load_config, save_cached_values
|
|
)
|
|
from .constants import (
|
|
appname, config_dir, isosx, logo_data_file, str_version, viewport_size
|
|
)
|
|
from .fast_data_types import (
|
|
GL_COLOR_BUFFER_BIT, GLFW_CONTEXT_VERSION_MAJOR,
|
|
GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_CORE_PROFILE,
|
|
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_PROFILE, GLFW_SAMPLES,
|
|
GLFW_STENCIL_BITS, Window, change_wcwidth,
|
|
enable_automatic_opengl_error_checking, glClear, glClearColor, glewInit,
|
|
glfw_init, glfw_set_error_callback, glfw_swap_interval, glfw_terminate,
|
|
glfw_wait_events, glfw_window_hint
|
|
)
|
|
from .layout import all_layouts
|
|
from .shaders import GL_VERSION
|
|
from .utils import safe_print
|
|
|
|
defconf = os.path.join(config_dir, 'kitty.conf')
|
|
|
|
|
|
def option_parser():
|
|
parser = argparse.ArgumentParser(
|
|
prog=appname,
|
|
description=_('The {} terminal emulator').format(appname)
|
|
)
|
|
a = parser.add_argument
|
|
a(
|
|
'--class',
|
|
default=appname,
|
|
dest='cls',
|
|
help=_('Set the WM_CLASS property')
|
|
)
|
|
a(
|
|
'--config',
|
|
action='append',
|
|
help=_(
|
|
'Specify a path to the config file(s) to use.'
|
|
' Can be specified multiple times to read multiple'
|
|
' config files in sequence, which are merged. Default: {}'
|
|
).format(defconf)
|
|
)
|
|
a(
|
|
'--override',
|
|
'-o',
|
|
action='append',
|
|
help=_(
|
|
'Override individual configuration options, can be specified'
|
|
' multiple times. Syntax: name=value. For example: {}'
|
|
).format('-o font_size=20')
|
|
)
|
|
a(
|
|
'--cmd',
|
|
'-c',
|
|
default=None,
|
|
help=_('Run python code in the kitty context')
|
|
)
|
|
a(
|
|
'-d',
|
|
'--directory',
|
|
default='.',
|
|
help=_('Change to the specified directory when launching')
|
|
)
|
|
a(
|
|
'--version',
|
|
'-v',
|
|
action='version',
|
|
version='{} {} by Kovid Goyal'.format(appname, str_version)
|
|
)
|
|
a(
|
|
'--profile',
|
|
action='store_true',
|
|
default=False,
|
|
help=_('Show profiling data after exit')
|
|
)
|
|
a(
|
|
'--dump-commands',
|
|
action='store_true',
|
|
default=False,
|
|
help=_('Output commands received from child process to stdout')
|
|
)
|
|
a(
|
|
'--replay-commands',
|
|
default=None,
|
|
help=_('Replay previously dumped commands')
|
|
)
|
|
a(
|
|
'--dump-bytes',
|
|
help=_('Path to file in which to store the raw bytes received from the'
|
|
' child process. Useful for debugging.')
|
|
)
|
|
a(
|
|
'--debug-gl',
|
|
action='store_true',
|
|
default=False,
|
|
help=_('Debug OpenGL commands. This will cause all OpenGL calls'
|
|
' to check for errors instead of ignoring them. Useful'
|
|
' when debugging rendering problems.')
|
|
)
|
|
a(
|
|
'--window-layout',
|
|
default=None,
|
|
choices=frozenset(all_layouts.keys()),
|
|
help=_('The window layout to use on startup')
|
|
)
|
|
a(
|
|
'--session',
|
|
default=None,
|
|
help=_(
|
|
'Path to a file containing the startup session (tabs, windows, layout, programs)'
|
|
)
|
|
)
|
|
a(
|
|
'args',
|
|
nargs=argparse.REMAINDER,
|
|
help=_(
|
|
'The remaining arguments are used to launch a program other than the default shell. Any further options are passed'
|
|
' directly to the program being invoked.'
|
|
)
|
|
)
|
|
return parser
|
|
|
|
|
|
def setup_opengl():
|
|
glfw_window_hint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION[0])
|
|
glfw_window_hint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION[1])
|
|
glfw_window_hint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
|
|
glfw_window_hint(GLFW_OPENGL_FORWARD_COMPAT, True)
|
|
glfw_window_hint(GLFW_SAMPLES, 0)
|
|
if isosx:
|
|
# OS X cannot handle 16bit stencil buffers
|
|
glfw_window_hint(GLFW_STENCIL_BITS, 8)
|
|
|
|
|
|
def clear_buffers(window, opts):
|
|
bg = opts.background
|
|
glClearColor(bg.red / 255, bg.green / 255, bg.blue / 255, 1)
|
|
glfw_swap_interval(0)
|
|
glClear(GL_COLOR_BUFFER_BIT)
|
|
window.swap_buffers()
|
|
glClear(GL_COLOR_BUFFER_BIT)
|
|
# We dont turn this on as it causes rendering performance to be much worse,
|
|
# for example, dragging the mouse to select is laggy
|
|
# glfw_swap_interval(1)
|
|
|
|
|
|
def dispatch_pending_calls(boss):
|
|
while True:
|
|
try:
|
|
func, args = boss.pending_ui_thread_calls.get_nowait()
|
|
except Empty:
|
|
break
|
|
try:
|
|
func(*args)
|
|
except Exception:
|
|
import traceback
|
|
safe_print(traceback.format_exc())
|
|
boss.ui_timers()
|
|
|
|
|
|
def run_app(opts, args):
|
|
setup_opengl()
|
|
load_cached_values()
|
|
if 'window-size' in cached_values and opts.remember_window_size:
|
|
ws = cached_values['window-size']
|
|
try:
|
|
viewport_size.width, viewport_size.height = map(int, ws)
|
|
except Exception:
|
|
safe_print('Invalid cached window size, ignoring', file=sys.stderr)
|
|
viewport_size.width = max(100, viewport_size.width)
|
|
viewport_size.height = max(80, viewport_size.height)
|
|
else:
|
|
viewport_size.width = opts.initial_window_width
|
|
viewport_size.height = opts.initial_window_height
|
|
window = Window(viewport_size.width, viewport_size.height, args.cls)
|
|
window.set_title(appname)
|
|
window.make_context_current()
|
|
if not isosx:
|
|
with open(logo_data_file, 'rb') as f:
|
|
window.set_icon(f.read(), 256, 256)
|
|
viewport_size.width, viewport_size.height = window.get_framebuffer_size()
|
|
w, h = window.get_window_size()
|
|
viewport_size.x_ratio = viewport_size.width / float(w)
|
|
viewport_size.y_ratio = viewport_size.height / float(h)
|
|
glewInit()
|
|
boss = Boss(window, opts, args)
|
|
boss.start()
|
|
clear_buffers(window, opts)
|
|
try:
|
|
while not window.should_close():
|
|
boss.render()
|
|
window.swap_buffers()
|
|
glfw_wait_events(boss.ui_timers.timeout())
|
|
dispatch_pending_calls(boss)
|
|
finally:
|
|
boss.destroy()
|
|
del window
|
|
cached_values['window-size'] = viewport_size.width, viewport_size.height
|
|
save_cached_values()
|
|
|
|
|
|
def on_glfw_error(code, msg):
|
|
if isinstance(msg, bytes):
|
|
try:
|
|
msg = msg.decode('utf-8')
|
|
except Exception:
|
|
msg = repr(msg)
|
|
safe_print('[glfw error] ', msg, file=sys.stderr)
|
|
|
|
|
|
def main():
|
|
if os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES',
|
|
None) == '1' and getattr(sys, 'frozen', True):
|
|
os.chdir(os.path.expanduser('~'))
|
|
args = option_parser().parse_args()
|
|
if args.cmd:
|
|
exec(args.cmd)
|
|
return
|
|
if args.replay_commands:
|
|
from kitty.client import main
|
|
main(args.replay_commands)
|
|
return
|
|
config = args.config or (defconf, )
|
|
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
|
|
opts = load_config(*config, overrides=overrides)
|
|
change_wcwidth(not opts.use_system_wcwidth)
|
|
glfw_set_error_callback(on_glfw_error)
|
|
enable_automatic_opengl_error_checking(args.debug_gl)
|
|
if not glfw_init():
|
|
raise SystemExit('GLFW initialization failed')
|
|
try:
|
|
if args.profile:
|
|
tf = tempfile.NamedTemporaryFile(prefix='kitty-profiling-stats-')
|
|
args.profile = tf.name
|
|
import cProfile
|
|
import pstats
|
|
pr = cProfile.Profile()
|
|
pr.enable()
|
|
run_app(opts, args)
|
|
pr.disable()
|
|
pr.create_stats()
|
|
s = pstats.Stats(pr)
|
|
s.add(args.profile)
|
|
tf.close()
|
|
s.strip_dirs()
|
|
s.sort_stats('time', 'name')
|
|
s.print_stats(30)
|
|
else:
|
|
run_app(opts, args)
|
|
finally:
|
|
glfw_terminate()
|