kitty/kitty/main.py
2017-01-11 21:07:31 +05:30

165 lines
5.8 KiB
Python

#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import argparse
import tempfile
import os
import sys
from queue import Empty
from gettext import gettext as _
from .config import load_config, load_cached_values, cached_values, save_cached_values
from .constants import appname, str_version, config_dir, viewport_size, isosx
from .layout import all_layouts
from .boss import Boss
from .shaders import GL_VERSION
from .fast_data_types import (
glewInit, enable_automatic_opengl_error_checking, glClear, glClearColor,
GL_COLOR_BUFFER_BIT, GLFW_CONTEXT_VERSION_MAJOR, GLFW_STENCIL_BITS,
GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_PROFILE,
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_CORE_PROFILE, GLFW_SAMPLES,
glfw_set_error_callback, glfw_init, glfw_terminate, glfw_window_hint,
glfw_swap_interval, glfw_wait_events, Window
)
from .utils import safe_print
def option_parser():
parser = argparse.ArgumentParser(prog=appname, description=_('The {} terminal emulator').format(appname))
defconf = os.path.join(config_dir, 'kitty.conf')
a = parser.add_argument
a('--class', default=appname, dest='cls', help=_('Set the WM_CLASS property'))
a('--config', default=defconf, help=_('Specify a path to the config file to use. Default: {}').format(defconf))
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', action='version', version='{} {} by Kovid Goyal'.format(appname, '.'.join(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('--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:
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)
window = Window(
viewport_size.width, viewport_size.height, args.cls)
window.set_title(appname)
window.make_context_current()
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():
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
opts = load_config(args.config)
glfw_set_error_callback(on_glfw_error)
enable_automatic_opengl_error_checking(False)
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()