Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff52ff7865 | ||
|
|
f6f55aaa9a | ||
|
|
6f1aecab99 | ||
|
|
f33205a490 | ||
|
|
2920638a3d | ||
|
|
1ae32b5742 | ||
|
|
4e9dabfb25 | ||
|
|
ca2c419c9b | ||
|
|
020c1311ca | ||
|
|
8193b2a44b | ||
|
|
324a000777 | ||
|
|
951b1c1e83 | ||
|
|
8e57fd93c6 | ||
|
|
d262f43b5b | ||
|
|
73224e7d95 | ||
|
|
19463c81a3 | ||
|
|
6f2bb31068 | ||
|
|
abf9c4117d | ||
|
|
39f50830d7 | ||
|
|
c2fd7005cb | ||
|
|
52f2ed33f5 | ||
|
|
a51239c6ae | ||
|
|
e6217e1428 |
@@ -4,6 +4,41 @@ Changelog
|
||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||
To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
|
||||
0.14.1 [2019-05-29]
|
||||
---------------------
|
||||
|
||||
- Add an option :opt:`command_on_bell` to run an arbitrary command when
|
||||
a bell occurs (:iss:`1660`)
|
||||
|
||||
- Add a shortcut to toggle maximized window state :sc:`toggle_maximized`
|
||||
|
||||
- Add support for the underscore key found in some keyboard layouts
|
||||
(:iss:`1639`)
|
||||
|
||||
- Fix a missing newline when using the pipe command between the
|
||||
scrollback and screen contents (:iss:`1642`)
|
||||
|
||||
- Fix colors not being preserved when using the pipe command with
|
||||
the pager history buffer (:pull:`1657`)
|
||||
|
||||
- macOS: Fix a regression that could cause rendering of a kitty window
|
||||
to occasionally freeze in certain situations, such as moving it between
|
||||
monitors or transitioning from/to fullscreen (:iss:`1641`)
|
||||
|
||||
- macOS: Fix a regression that caused :kbd:`cmd+v` to double up in the dvorak
|
||||
keyboard layout (:iss:`1652`)
|
||||
|
||||
- When resizing and only a single window is present in the current layout,
|
||||
use that window's background color to fill in the blank areas.
|
||||
|
||||
- Linux: Automatically increase cell height if the font being used is broken
|
||||
and draws the underscore outside the bounding box (:iss:`690`)
|
||||
|
||||
- Wayland: Fix maximizing the window on a compositor that does not provide
|
||||
server side window decorations, such a GNOME or Weston not working
|
||||
(:iss:`1662`)
|
||||
|
||||
|
||||
0.14.0 [2019-05-24]
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
@@ -11,6 +12,7 @@ import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from functools import partial
|
||||
|
||||
from docutils import nodes
|
||||
@@ -32,7 +34,7 @@ str_version = importlib.import_module('kitty.constants').str_version
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'kitty'
|
||||
copyright = '2018, Kovid Goyal'
|
||||
copyright = time.strftime('%Y, Kovid Goyal')
|
||||
author = 'Kovid Goyal'
|
||||
building_man_pages = 'man' in sys.argv
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ You can also use your favorite package manager to install the |kitty| package.
|
||||
|kitty| packages are available for:
|
||||
`macOS with Homebrew (Cask) <https://formulae.brew.sh/cask/kitty>`_,
|
||||
`macOS and Linux with Nix <https://nixos.org/nixos/packages.html#kitty>`_,
|
||||
`Ubuntu <https://launchpad.net/ubuntu/+source/kitty>`_,
|
||||
`Debian <https://packages.debian.org/buster/kitty>`_,
|
||||
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
|
||||
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
|
||||
@@ -186,6 +187,7 @@ Increase font size :sc:`increase_font_size` (also :kbd:`⌘++`
|
||||
Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)
|
||||
Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)
|
||||
Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`^+⌘+f` on macOS)
|
||||
Toggle maximized :sc:`toggle_maximized`
|
||||
Input unicode character :sc:`input_unicode_character`
|
||||
Click URL using the keyboard :sc:`open_url`
|
||||
Reset the terminal :sc:`reset_terminal`
|
||||
|
||||
@@ -313,13 +313,18 @@ static GLFWbool initializeTIS(void)
|
||||
|
||||
static inline bool
|
||||
is_ctrl_tab(NSEvent *event, NSEventModifierFlags modifierFlags) {
|
||||
return event.keyCode == kVK_Tab && (modifierFlags == NSEventModifierFlagControl || modifierFlags == (
|
||||
NSEventModifierFlagControl | NSEventModifierFlagShift));
|
||||
if (modifierFlags == NSEventModifierFlagControl || modifierFlags == (
|
||||
NSEventModifierFlagControl | NSEventModifierFlagShift)) {
|
||||
if ([event.charactersIgnoringModifiers isEqualToString:@"\t"]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_cmd_period(NSEvent *event, NSEventModifierFlags modifierFlags) {
|
||||
return event.keyCode == kVK_ANSI_Period && modifierFlags == NSEventModifierFlagCommand;
|
||||
if (modifierFlags != NSEventModifierFlagCommand) return false;
|
||||
if ([event.charactersIgnoringModifiers isEqualToString:@"."]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
|
||||
@@ -366,6 +366,7 @@ void _glfwPollMonitorsNS(void)
|
||||
|
||||
free(disconnected);
|
||||
free(displays);
|
||||
_glfwRestartDisplayLinks();
|
||||
}
|
||||
|
||||
// Change the current video mode
|
||||
|
||||
1
glfw/cocoa_platform.h
vendored
1
glfw/cocoa_platform.h
vendored
@@ -224,6 +224,7 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||
float _glfwTransformYNS(float y);
|
||||
void _glfwClearDisplayLinks(void);
|
||||
void _glfwRestartDisplayLinks(void);
|
||||
void _glfwCocoaPostEmptyEvent(short subtype, long data1, bool at_start);
|
||||
void _glfwDispatchTickCallback(void);
|
||||
void _glfwDispatchRenderFrame(CGDirectDisplayID);
|
||||
|
||||
@@ -53,6 +53,51 @@ static NSUInteger getStyleMask(_GLFWwindow* window)
|
||||
return styleMask;
|
||||
}
|
||||
|
||||
|
||||
CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
|
||||
NSWindow *nw = w->ns.object;
|
||||
NSDictionary *dict = [nw.screen deviceDescription];
|
||||
NSNumber *displayIDns = [dict objectForKey:@"NSScreenNumber"];
|
||||
if (displayIDns) return [displayIDns unsignedIntValue];
|
||||
return (CGDirectDisplayID)-1;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
|
||||
if (!callback) {
|
||||
w->ns.renderFrameRequested = GLFW_FALSE;
|
||||
w->ns.renderFrameCallback = NULL;
|
||||
return;
|
||||
}
|
||||
w->ns.renderFrameCallback = callback;
|
||||
w->ns.renderFrameRequested = GLFW_TRUE;
|
||||
CGDirectDisplayID displayID = displayIDForWindow(w);
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
|
||||
if (dl->displayID == displayID) {
|
||||
dl->renderFrameRequested = GLFW_TRUE;
|
||||
if (!dl->displayLinkStarted) {
|
||||
CVDisplayLinkStart(dl->displayLink);
|
||||
dl->displayLinkStarted = GLFW_TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
void
|
||||
_glfwRestartDisplayLinks(void) {
|
||||
_GLFWwindow* window;
|
||||
for (window = _glfw.windowListHead; window; window = window->next) {
|
||||
if (window->ns.renderFrameRequested && window->ns.renderFrameCallback) {
|
||||
requestRenderFrame(window, window->ns.renderFrameCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether the cursor is in the content area of the specified window
|
||||
//
|
||||
static GLFWbool cursorInContentArea(_GLFWwindow* window)
|
||||
@@ -272,6 +317,7 @@ static int translateKey(unsigned int key, GLFWbool apply_keymap)
|
||||
K('[', LEFT_BRACKET);
|
||||
K(']', RIGHT_BRACKET);
|
||||
K('+', PLUS);
|
||||
K('_', UNDERSCORE);
|
||||
K('`', GRAVE_ACCENT);
|
||||
K('\\', BACKSLASH);
|
||||
#undef K
|
||||
@@ -452,6 +498,16 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
_glfwInputWindowFocus(window, GLFW_FALSE);
|
||||
}
|
||||
|
||||
- (void)windowDidChangeScreen:(NSNotification *)notification
|
||||
{
|
||||
if (window->ns.renderFrameRequested && window->ns.renderFrameCallback) {
|
||||
// Ensure that if the window changed its monitor, CVDisplayLink
|
||||
// is running for the new monitor
|
||||
requestRenderFrame(window, window->ns.renderFrameCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -591,7 +647,6 @@ static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
|
||||
_glfwInputLiveResize(window, false);
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)wantsUpdateLayer
|
||||
{
|
||||
return YES;
|
||||
@@ -844,11 +899,13 @@ is_ascii_control_char(char x) {
|
||||
&char_count,
|
||||
text
|
||||
) != noErr) {
|
||||
debug_key(@"UCKeyTranslate failed for scancode: 0x%x (%s) %s\n", scancode, safe_name_for_scancode(scancode), format_mods(mods));
|
||||
debug_key(@"UCKeyTranslate failed for scancode: 0x%x (%s) %s\n",
|
||||
scancode, safe_name_for_scancode(scancode), format_mods(mods));
|
||||
window->ns.deadKeyState = 0;
|
||||
return;
|
||||
}
|
||||
debug_key(@"scancode: 0x%x (%s) %schar_count: %lu deadKeyState: %u ", scancode, safe_name_for_scancode(scancode), format_mods(mods), char_count, window->ns.deadKeyState);
|
||||
debug_key(@"scancode: 0x%x (%s) %schar_count: %lu deadKeyState: %u repeat: %d",
|
||||
scancode, safe_name_for_scancode(scancode), format_mods(mods), char_count, window->ns.deadKeyState, event.ARepeat);
|
||||
if (process_text) {
|
||||
// this will call insertText which will fill up _glfw.ns.text
|
||||
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||
@@ -869,9 +926,8 @@ is_ascii_control_char(char x) {
|
||||
}
|
||||
}
|
||||
if (is_ascii_control_char(_glfw.ns.text[0])) _glfw.ns.text[0] = 0; // don't send text for ascii control codes
|
||||
debug_key(@"text: %s glfw_key: %s\n",
|
||||
format_text(_glfw.ns.text), _glfwGetKeyName(key));
|
||||
debug_key(@"marked text: %@", markedText);
|
||||
debug_key(@"text: %s glfw_key: %s marked_text: %@\n",
|
||||
format_text(_glfw.ns.text), _glfwGetKeyName(key), markedText);
|
||||
if (!window->ns.deadKeyState) {
|
||||
if ([self hasMarkedText]) {
|
||||
_glfwInputKeyboard(window, key, scancode, GLFW_PRESS, mods,
|
||||
@@ -1773,14 +1829,6 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
||||
[window->ns.object setAlphaValue:opacity];
|
||||
}
|
||||
|
||||
CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
|
||||
NSWindow *nw = w->ns.object;
|
||||
NSDictionary *dict = [nw.screen deviceDescription];
|
||||
NSNumber *displayIDns = [dict objectForKey:@"NSScreenNumber"];
|
||||
if (displayIDns) return [displayIDns unsignedIntValue];
|
||||
return (CGDirectDisplayID)-1;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
|
||||
_GLFWwindow *w = _glfw.windowListHead;
|
||||
@@ -1793,31 +1841,6 @@ _glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
|
||||
if (!callback) {
|
||||
w->ns.renderFrameRequested = GLFW_FALSE;
|
||||
w->ns.renderFrameCallback = NULL;
|
||||
return;
|
||||
}
|
||||
w->ns.renderFrameCallback = callback;
|
||||
w->ns.renderFrameRequested = GLFW_TRUE;
|
||||
CGDirectDisplayID displayID = displayIDForWindow(w);
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
|
||||
if (dl->displayID == displayID) {
|
||||
dl->renderFrameRequested = GLFW_TRUE;
|
||||
if (!dl->displayLinkStarted) {
|
||||
CVDisplayLinkStart(dl->displayLink);
|
||||
dl->displayLinkStarted = GLFW_TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
void _glfwCocoaPostEmptyEvent(short subtype, long data1, bool at_start)
|
||||
{
|
||||
@autoreleasepool {
|
||||
@@ -2234,6 +2257,7 @@ GLFWAPI void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, unsigned sho
|
||||
K('[', LEFT_BRACKET);
|
||||
K(']', RIGHT_BRACKET);
|
||||
K('+', PLUS);
|
||||
K('_', UNDERSCORE);
|
||||
K('`', GRAVE_ACCENT);
|
||||
K('\\', BACKSLASH);
|
||||
|
||||
|
||||
1
glfw/glfw3.h
vendored
1
glfw/glfw3.h
vendored
@@ -410,6 +410,7 @@ extern "C" {
|
||||
#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
|
||||
#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
|
||||
#define GLFW_KEY_PLUS 163
|
||||
#define GLFW_KEY_UNDERSCORE 164
|
||||
|
||||
/* Function keys */
|
||||
#define GLFW_KEY_ESCAPE 256
|
||||
|
||||
3
glfw/glx_context.c
vendored
3
glfw/glx_context.c
vendored
@@ -226,8 +226,6 @@ static GLFWglproc getProcAddressGLX(const char* procname)
|
||||
return _glfw_dlsym(_glfw.glx.handle, procname);
|
||||
}
|
||||
|
||||
// Destroy the OpenGL context
|
||||
//
|
||||
static void destroyContextGLX(_GLFWwindow* window)
|
||||
{
|
||||
if (window->context.glx.window)
|
||||
@@ -695,4 +693,3 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle)
|
||||
|
||||
return window->context.glx.window;
|
||||
}
|
||||
|
||||
|
||||
1
glfw/input.c
vendored
1
glfw/input.c
vendored
@@ -488,6 +488,7 @@ const char* _glfwGetKeyName(int key)
|
||||
case GLFW_KEY_WORLD_1: return "WORLD 1";
|
||||
case GLFW_KEY_WORLD_2: return "WORLD 2";
|
||||
case GLFW_KEY_PLUS: return "PLUS";
|
||||
case GLFW_KEY_UNDERSCORE: return "UNDERSCORE";
|
||||
|
||||
// Function keys
|
||||
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||
|
||||
@@ -71,8 +71,6 @@ static GLFWglproc getProcAddressNSGL(const char* procname)
|
||||
return symbol;
|
||||
}
|
||||
|
||||
// Destroy the OpenGL context
|
||||
//
|
||||
static void destroyContextNSGL(_GLFWwindow* window)
|
||||
{
|
||||
[window->context.nsgl.pixelFormat release];
|
||||
@@ -134,12 +132,6 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE)
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Context robustness modes (GL_KHR_robustness) are not yet supported by
|
||||
|
||||
6
glfw/wl_window.c
vendored
6
glfw/wl_window.c
vendored
@@ -551,6 +551,12 @@ static void xdgToplevelHandleConfigure(void* data,
|
||||
}
|
||||
}
|
||||
window->wl.fullscreened = fullscreen;
|
||||
if (maximized && !fullscreen) {
|
||||
if (window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.buffer) {
|
||||
width -= _GLFW_DECORATION_HORIZONTAL;
|
||||
height -= _GLFW_DECORATION_VERTICAL;
|
||||
}
|
||||
}
|
||||
dispatchChangesAfterConfigure(window, width, height);
|
||||
_glfwInputWindowFocus(window, activated);
|
||||
}
|
||||
|
||||
1
glfw/xkb_glfw.c
vendored
1
glfw/xkb_glfw.c
vendored
@@ -47,6 +47,7 @@
|
||||
S(backslash, BACKSLASH); \
|
||||
S(bracketright, RIGHT_BRACKET); \
|
||||
S(plus, PLUS); \
|
||||
S(underscore, UNDERSCORE); \
|
||||
S(grave, GRAVE_ACCENT); \
|
||||
S(Escape, ESCAPE); \
|
||||
S(Return, ENTER); \
|
||||
|
||||
@@ -24,7 +24,8 @@ from .fast_data_types import (
|
||||
change_os_window_state, create_os_window, current_os_window,
|
||||
destroy_global_data, get_clipboard_string, global_font_size,
|
||||
mark_os_window_for_close, os_window_font_size, patch_global_colors,
|
||||
set_clipboard_string, set_in_sequence_mode, toggle_fullscreen
|
||||
set_clipboard_string, set_in_sequence_mode, toggle_fullscreen,
|
||||
toggle_maximized
|
||||
)
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
from .layout import set_draw_borders_options
|
||||
@@ -361,6 +362,9 @@ class Boss:
|
||||
def toggle_fullscreen(self):
|
||||
toggle_fullscreen()
|
||||
|
||||
def toggle_maximized(self):
|
||||
toggle_maximized()
|
||||
|
||||
def start(self):
|
||||
if not getattr(self, 'io_thread_started', False):
|
||||
self.child_monitor.start()
|
||||
|
||||
@@ -57,6 +57,13 @@ else:
|
||||
return ans
|
||||
|
||||
|
||||
def checked_terminfo_dir():
|
||||
ans = getattr(checked_terminfo_dir, 'ans', None)
|
||||
if ans is None:
|
||||
ans = checked_terminfo_dir.ans = terminfo_dir if os.path.isdir(terminfo_dir) else None
|
||||
return ans
|
||||
|
||||
|
||||
def processes_in_group(grp):
|
||||
gmap = getattr(process_group_map, 'cached_map', None)
|
||||
if gmap is None:
|
||||
@@ -149,6 +156,23 @@ class Child:
|
||||
self.stdin = stdin
|
||||
self.env = env or {}
|
||||
|
||||
@property
|
||||
def final_env(self):
|
||||
env = getattr(self, '_final_env', None)
|
||||
if env is None:
|
||||
env = self._final_env = default_env().copy()
|
||||
env.update(self.env)
|
||||
env['TERM'] = self.opts.term
|
||||
env['COLORTERM'] = 'truecolor'
|
||||
if self.cwd:
|
||||
# needed incase cwd is a symlink, in which case shells
|
||||
# can use it to display the current directory name rather
|
||||
# than the resolved path
|
||||
env['PWD'] = self.cwd
|
||||
if checked_terminfo_dir():
|
||||
env['TERMINFO'] = checked_terminfo_dir()
|
||||
return env
|
||||
|
||||
def fork(self):
|
||||
if self.forked:
|
||||
return
|
||||
@@ -164,17 +188,7 @@ class Child:
|
||||
remove_cloexec(stdin_read_fd)
|
||||
else:
|
||||
stdin_read_fd = stdin_write_fd = -1
|
||||
env = default_env().copy()
|
||||
env.update(self.env)
|
||||
env['TERM'] = self.opts.term
|
||||
env['COLORTERM'] = 'truecolor'
|
||||
if self.cwd:
|
||||
# needed incase cwd is a symlink, in which case shells
|
||||
# can use it to display the current directory name rather
|
||||
# than the resolved path
|
||||
env['PWD'] = self.cwd
|
||||
if os.path.isdir(terminfo_dir):
|
||||
env['TERMINFO'] = terminfo_dir
|
||||
env = self.final_env
|
||||
env = tuple('{}={}'.format(k, v) for k, v in env.items())
|
||||
argv = list(self.argv)
|
||||
exe = argv[0]
|
||||
|
||||
@@ -24,6 +24,7 @@ named_keys = {
|
||||
"'": 'APOSTROPHE',
|
||||
',': 'COMMA',
|
||||
'-': 'MINUS',
|
||||
'_': 'UNDERSCORE',
|
||||
'.': 'PERIOD',
|
||||
'/': 'SLASH',
|
||||
';': 'SEMICOLON',
|
||||
|
||||
@@ -513,6 +513,9 @@ o('bell_on_tab', True, long_text=_('''
|
||||
Show a bell symbol on the tab if a bell occurs in one of the windows in the
|
||||
tab and the window is not the currently focused window'''))
|
||||
|
||||
o('command_on_bell', 'none', option_type=to_cmdline, long_text=_('''
|
||||
Program to run when a bell occurs.
|
||||
'''))
|
||||
# }}}
|
||||
|
||||
g('window') # {{{
|
||||
@@ -1154,6 +1157,7 @@ Useful with git, which uses sha1 hashes to identify commits'''))
|
||||
|
||||
g('shortcuts.misc') # {{{
|
||||
k('toggle_fullscreen', 'kitty_mod+f11', 'toggle_fullscreen', _('Toggle fullscreen'))
|
||||
k('toggle_maximized', 'kitty_mod+f10', 'toggle_maximized', _('Toggle maximized'))
|
||||
k('input_unicode_character', 'kitty_mod+u', 'kitten unicode_input', _('Unicode input'))
|
||||
k('edit_config_file', 'kitty_mod+f2', 'edit_config_file', _('Edit config file'))
|
||||
k('kitty_shell', 'kitty_mod+escape', 'kitty_shell window', _('Open the kitty command shell'), long_text=_('''
|
||||
|
||||
@@ -8,7 +8,7 @@ import sys
|
||||
from collections import namedtuple
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 14, 0)
|
||||
version = (0, 14, 1)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
|
||||
@@ -280,7 +280,9 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
|
||||
*baseline = (unsigned int)self->ascent;
|
||||
// float line_height = MAX(1, floor(self->ascent + self->descent + MAX(0, self->leading) + 0.5));
|
||||
// Let CoreText's layout engine calculate the line height. Slower, but hopefully more accurate.
|
||||
CFStringRef ts = CFSTR("test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test");
|
||||
#define W "AQWMH_gyl "
|
||||
CFStringRef ts = CFSTR(W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W);
|
||||
#undef W
|
||||
CFMutableAttributedStringRef test_string = CFAttributedStringCreateMutable(kCFAllocatorDefault, CFStringGetLength(ts));
|
||||
CFAttributedStringReplaceString(test_string, CFRangeMake(0, 0), ts);
|
||||
CFAttributedStringSetAttribute(test_string, CFRangeMake(0, CFStringGetLength(ts)), kCTFontAttributeName, self->ct_font);
|
||||
|
||||
@@ -75,8 +75,6 @@ set_freetype_error(const char* prefix, int err_code) {
|
||||
|
||||
static FT_Library library;
|
||||
|
||||
#define CALC_CELL_HEIGHT(self) font_units_to_pixels_y(self, self->height)
|
||||
|
||||
static inline int
|
||||
font_units_to_pixels_y(Face *self, int x) {
|
||||
return ceil((double)FT_MulFix(x, self->face->size->metrics.y_scale) / 64.0);
|
||||
@@ -87,11 +85,56 @@ font_units_to_pixels_x(Face *self, int x) {
|
||||
return ceil((double)FT_MulFix(x, self->face->size->metrics.x_scale) / 64.0);
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
get_load_flags(int hinting, int hintstyle, int base) {
|
||||
int flags = base;
|
||||
if (hinting) {
|
||||
if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;
|
||||
else if (0 < hintstyle && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT;
|
||||
} else flags |= FT_LOAD_NO_HINTING;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
load_glyph(Face *self, int glyph_index, int load_type) {
|
||||
int flags = get_load_flags(self->hinting, self->hintstyle, load_type);
|
||||
int error = FT_Load_Glyph(self->face, glyph_index, flags);
|
||||
if (error) { set_freetype_error("Failed to load glyph, with error:", error); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
get_height_for_char(Face *self, char ch) {
|
||||
unsigned int ans = 0;
|
||||
int glyph_index = FT_Get_Char_Index(self->face, ch);
|
||||
if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) {
|
||||
unsigned int baseline = font_units_to_pixels_y(self, self->ascender);
|
||||
FT_GlyphSlotRec *glyph = self->face->glyph;
|
||||
FT_Bitmap *bm = &glyph->bitmap;
|
||||
if (glyph->bitmap_top <= 0 || (glyph->bitmap_top > 0 && (unsigned int)glyph->bitmap_top < baseline)) {
|
||||
ans = baseline - glyph->bitmap_top + bm->rows;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
calc_cell_height(Face *self, bool for_metrics) {
|
||||
unsigned int ans = font_units_to_pixels_y(self, self->height);
|
||||
unsigned int underscore_height = get_height_for_char(self, '_');
|
||||
if (for_metrics && global_state.debug_font_fallback && underscore_height > ans) {
|
||||
printf("Increasing cell height by %u pixels to work around buggy font that renders underscore outside the bounding box\n", underscore_height - ans);
|
||||
}
|
||||
return MAX(ans, underscore_height);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt xdpi, FT_UInt ydpi, unsigned int desired_height, unsigned int cell_height) {
|
||||
int error = FT_Set_Char_Size(self->face, 0, char_height, xdpi, ydpi);
|
||||
if (!error) {
|
||||
unsigned int ch = CALC_CELL_HEIGHT(self);
|
||||
unsigned int ch = calc_cell_height(self, false);
|
||||
if (desired_height && ch != desired_height) {
|
||||
FT_F26Dot6 h = floor((double)char_height * (double)desired_height / (double) ch);
|
||||
return set_font_size(self, 0, h, xdpi, ydpi, 0, cell_height);
|
||||
@@ -147,17 +190,6 @@ set_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DA
|
||||
return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->cell_height);
|
||||
}
|
||||
|
||||
static inline int
|
||||
get_load_flags(int hinting, int hintstyle, int base) {
|
||||
int flags = base;
|
||||
if (hinting) {
|
||||
if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL;
|
||||
else if (0 < hintstyle && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT;
|
||||
} else flags |= FT_LOAD_NO_HINTING;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_HANDLE fg) {
|
||||
#define CPY(n) self->n = self->face->n;
|
||||
@@ -236,14 +268,6 @@ repr(Face *self) {
|
||||
}
|
||||
|
||||
|
||||
static inline bool
|
||||
load_glyph(Face *self, int glyph_index, int load_type) {
|
||||
int flags = get_load_flags(self->hinting, self->hintstyle, load_type);
|
||||
int error = FT_Load_Glyph(self->face, glyph_index, flags);
|
||||
if (error) { set_freetype_error("Failed to load glyph, with error:", error); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
calc_cell_width(Face *self) {
|
||||
unsigned int ans = 0;
|
||||
@@ -260,7 +284,7 @@ void
|
||||
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
|
||||
Face *self = (Face*)s;
|
||||
*cell_width = calc_cell_width(self);
|
||||
*cell_height = CALC_CELL_HEIGHT(self);
|
||||
*cell_height = calc_cell_height(self, true);
|
||||
*baseline = font_units_to_pixels_y(self, self->ascender);
|
||||
*underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position)));
|
||||
*underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness));
|
||||
@@ -366,9 +390,9 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
||||
// Normalize gray levels to the range [0..255]
|
||||
bitmap.num_grays = 256;
|
||||
unsigned int stride = bitmap.pitch < 0 ? -bitmap.pitch : bitmap.pitch;
|
||||
for (unsigned int i = 0; i < bitmap.rows; ++i) {
|
||||
for (unsigned i = 0; i < (unsigned)bitmap.rows; ++i) {
|
||||
// We only have 2 levels
|
||||
for (unsigned int j = 0; j < bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255;
|
||||
for (unsigned j = 0; j < (unsigned)bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255;
|
||||
}
|
||||
populate_processed_bitmap(self->face->glyph, &bitmap, ans, true);
|
||||
FT_Bitmap_Done(library, &bitmap);
|
||||
|
||||
1
kitty/glfw-wrapper.h
generated
1
kitty/glfw-wrapper.h
generated
@@ -167,6 +167,7 @@
|
||||
#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
|
||||
#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
|
||||
#define GLFW_KEY_PLUS 163
|
||||
#define GLFW_KEY_UNDERSCORE 164
|
||||
|
||||
/* Function keys */
|
||||
#define GLFW_KEY_ESCAPE 256
|
||||
|
||||
39
kitty/glfw.c
39
kitty/glfw.c
@@ -103,7 +103,18 @@ static int min_width = 100, min_height = 100;
|
||||
|
||||
void
|
||||
blank_os_window(OSWindow *w) {
|
||||
blank_canvas(w->is_semi_transparent ? w->background_opacity : 1.0f);
|
||||
color_type color = OPT(background);
|
||||
if (w->num_tabs > 0) {
|
||||
Tab *t = w->tabs + w->active_tab;
|
||||
if (t->num_windows == 1) {
|
||||
Window *w = t->windows + t->active_window;
|
||||
Screen *s = w->render_data.screen;
|
||||
if (s) {
|
||||
color = colorprofile_to_color(s->color_profile, s->color_profile->overridden.default_bg, s->color_profile->configured.default_bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
blank_canvas(w->is_semi_transparent ? w->background_opacity : 1.0f, color);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -387,6 +398,20 @@ toggle_fullscreen_for_os_window(OSWindow *w) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
toggle_maximized_for_os_window(OSWindow *w) {
|
||||
bool maximized = false;
|
||||
if (w && w->handle) {
|
||||
if (glfwGetWindowAttrib(w->handle, GLFW_MAXIMIZED)) {
|
||||
glfwRestoreWindow(w->handle);
|
||||
} else {
|
||||
glfwMaximizeWindow(w->handle);
|
||||
maximized = true;
|
||||
}
|
||||
}
|
||||
return maximized;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
static int
|
||||
@@ -513,7 +538,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
bool is_semi_transparent = glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER);
|
||||
// blank the window once so that there is no initial flash of color
|
||||
// changing, in case the background color is not black
|
||||
blank_canvas(is_semi_transparent ? OPT(background_opacity) : 1.0f);
|
||||
blank_canvas(is_semi_transparent ? OPT(background_opacity) : 1.0f, OPT(background));
|
||||
#ifndef __APPLE__
|
||||
if (is_first_window) glfwSwapInterval(OPT(sync_to_monitor) && !global_state.is_wayland ? 1 : 0);
|
||||
#endif
|
||||
@@ -832,6 +857,14 @@ toggle_fullscreen(PYNOARG) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
toggle_maximized(PYNOARG) {
|
||||
OSWindow *w = current_os_window();
|
||||
if (!w) Py_RETURN_NONE;
|
||||
if (toggle_maximized_for_os_window(w)) { Py_RETURN_TRUE; }
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
change_os_window_state(PyObject *self UNUSED, PyObject *args) {
|
||||
char *state;
|
||||
@@ -1101,6 +1134,7 @@ static PyMethodDef module_methods[] = {
|
||||
METHODB(ring_bell, METH_NOARGS),
|
||||
METHODB(set_clipboard_string, METH_VARARGS),
|
||||
METHODB(toggle_fullscreen, METH_NOARGS),
|
||||
METHODB(toggle_maximized, METH_NOARGS),
|
||||
METHODB(change_os_window_state, METH_VARARGS),
|
||||
METHODB(glfw_window_hint, METH_VARARGS),
|
||||
METHODB(get_primary_selection, METH_NOARGS),
|
||||
@@ -1196,6 +1230,7 @@ init_glfw(PyObject *m) {
|
||||
ADDC(GLFW_KEY_WORLD_1);
|
||||
ADDC(GLFW_KEY_WORLD_2);
|
||||
ADDC(GLFW_KEY_PLUS);
|
||||
ADDC(GLFW_KEY_UNDERSCORE);
|
||||
|
||||
// --- Function keys -----------------------------------------------------------
|
||||
ADDC(GLFW_KEY_ESCAPE);
|
||||
|
||||
4
kitty/key_encoding.py
generated
4
kitty/key_encoding.py
generated
@@ -128,7 +128,8 @@ ENCODING = {
|
||||
'X': 'p',
|
||||
'Y': 'q',
|
||||
'Z': 'r',
|
||||
'PLUS': 'Bi'
|
||||
'PLUS': 'Bi',
|
||||
'UNDERSCORE': 'Bj'
|
||||
}
|
||||
KEY_MAP = {
|
||||
32: 'A',
|
||||
@@ -182,6 +183,7 @@ KEY_MAP = {
|
||||
161: 'w',
|
||||
162: 'x',
|
||||
163: 'Bi',
|
||||
164: 'Bj',
|
||||
256: 'y',
|
||||
257: 'z',
|
||||
258: '0',
|
||||
|
||||
11715
kitty/keys.h
generated
11715
kitty/keys.h
generated
File diff suppressed because it is too large
Load Diff
@@ -121,6 +121,7 @@ control_codes.update({
|
||||
enumerate(range(defines.GLFW_KEY_A, defines.GLFW_KEY_RIGHT_BRACKET + 1))
|
||||
})
|
||||
control_codes[defines.GLFW_KEY_GRAVE_ACCENT] = (0,)
|
||||
control_codes[defines.GLFW_KEY_UNDERSCORE] = (0,)
|
||||
control_codes[defines.GLFW_KEY_SPACE] = (0,)
|
||||
control_codes[defines.GLFW_KEY_2] = (0,)
|
||||
control_codes[defines.GLFW_KEY_3] = (27,)
|
||||
@@ -206,8 +207,8 @@ UN_SHIFTED_PRINTABLE.update(pmap(
|
||||
" ',-./;="
|
||||
))
|
||||
UN_SHIFTED_PRINTABLE.update(pmap(
|
||||
'LEFT_BRACKET BACKSLASH RIGHT_BRACKET GRAVE_ACCENT',
|
||||
"[\\]`"
|
||||
'LEFT_BRACKET BACKSLASH RIGHT_BRACKET GRAVE_ACCENT UNDERSCORE',
|
||||
"[\\]`_"
|
||||
))
|
||||
SHIFTED_PRINTABLE = UN_SHIFTED_PRINTABLE.copy()
|
||||
SHIFTED_PRINTABLE.update({
|
||||
|
||||
@@ -2310,6 +2310,7 @@ static PyMemberDef members[] = {
|
||||
{"grman", T_OBJECT_EX, offsetof(Screen, grman), READONLY, "grman"},
|
||||
{"color_profile", T_OBJECT_EX, offsetof(Screen, color_profile), READONLY, "color_profile"},
|
||||
{"linebuf", T_OBJECT_EX, offsetof(Screen, linebuf), READONLY, "linebuf"},
|
||||
{"main_linebuf", T_OBJECT_EX, offsetof(Screen, main_linebuf), READONLY, "main_linebuf"},
|
||||
{"historybuf", T_OBJECT_EX, offsetof(Screen, historybuf), READONLY, "historybuf"},
|
||||
{"scrolled_by", T_UINT, offsetof(Screen, scrolled_by), READONLY, "scrolled_by"},
|
||||
{"lines", T_UINT, offsetof(Screen, lines), READONLY, "lines"},
|
||||
|
||||
@@ -476,8 +476,8 @@ set_cell_uniforms(float current_inactive_text_alpha) {
|
||||
}
|
||||
|
||||
void
|
||||
blank_canvas(float background_opacity) {
|
||||
#define C(shift) (((GLfloat)((OPT(background) >> shift) & 0xFF)) / 255.0f)
|
||||
blank_canvas(float background_opacity, color_type color) {
|
||||
#define C(shift) (((GLfloat)((color >> shift) & 0xFF)) / 255.0f)
|
||||
glClearColor(C(16), C(8), C(0), background_opacity);
|
||||
#undef C
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
@@ -214,7 +214,7 @@ void update_surface_size(int, int, uint32_t);
|
||||
void free_texture(uint32_t*);
|
||||
void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool);
|
||||
void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*);
|
||||
void blank_canvas(float);
|
||||
void blank_canvas(float, color_type);
|
||||
void blank_os_window(OSWindow *);
|
||||
void set_titlebar_color(OSWindow *w, color_type color);
|
||||
FONTS_DATA_HANDLE load_fonts_data(double, double, double);
|
||||
|
||||
@@ -111,8 +111,12 @@ def setup_colors(screen, opts):
|
||||
|
||||
|
||||
def text_sanitizer(as_ansi, add_wrap_markers):
|
||||
import re
|
||||
pat = re.compile(r'\033\[.+?m')
|
||||
pat = getattr(text_sanitizer, 'pat', None)
|
||||
if pat is None:
|
||||
import re
|
||||
pat = text_sanitizer.pat = re.compile(r'\033\[.+?m')
|
||||
|
||||
ansi, wrap_markers = not as_ansi, not add_wrap_markers
|
||||
|
||||
def remove_wrap_markers(line):
|
||||
return line.replace('\r', '')
|
||||
@@ -123,8 +127,8 @@ def text_sanitizer(as_ansi, add_wrap_markers):
|
||||
def remove_both(line):
|
||||
return pat.sub('', line.replace('\r', ''))
|
||||
|
||||
if as_ansi:
|
||||
return remove_both if add_wrap_markers else remove_sgr
|
||||
if ansi:
|
||||
return remove_both if wrap_markers else remove_sgr
|
||||
return remove_wrap_markers
|
||||
|
||||
|
||||
@@ -302,6 +306,12 @@ class Window:
|
||||
return get_boss().active_window is self
|
||||
|
||||
def on_bell(self):
|
||||
if self.opts.command_on_bell and self.opts.command_on_bell != ['none']:
|
||||
import subprocess
|
||||
import shlex
|
||||
env = self.child.final_env
|
||||
env['KITTY_CHILD_CMDLINE'] = ' '.join(map(shlex.quote, self.child.cmdline))
|
||||
subprocess.Popen(self.opts.command_on_bell, env=env, cwd=self.child.foreground_cwd)
|
||||
if not self.is_active:
|
||||
self.needs_attention = True
|
||||
tab = self.tabref()
|
||||
@@ -483,10 +493,12 @@ class Window:
|
||||
if add_history:
|
||||
h = []
|
||||
self.screen.historybuf.pagerhist_as_text(h.append)
|
||||
if not as_ansi or not add_wrap_markers:
|
||||
if h and (not as_ansi or not add_wrap_markers):
|
||||
sanitizer = text_sanitizer(as_ansi, add_wrap_markers)
|
||||
h = list(map(sanitizer, h))
|
||||
self.screen.historybuf.as_text(h.append, as_ansi, add_wrap_markers)
|
||||
if not self.screen.linebuf.is_continued(0) and h:
|
||||
h[-1] += '\n'
|
||||
lines = chain(h, lines)
|
||||
return ''.join(lines)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user