Compare commits

..

23 Commits

Author SHA1 Message Date
Kovid Goyal
ff52ff7865 version 0.14.1 2019-05-29 19:57:00 +05:30
Kovid Goyal
f6f55aaa9a NSGL: Remove enforcement of forward-compatible flag
Upstream commit:
0b01d850ed
2019-05-29 19:52:15 +05:30
Kovid Goyal
6f1aecab99 Remove pointless comment
Upstream commit
84ec99bb01
2019-05-29 19:50:22 +05:30
Kovid Goyal
f33205a490 Wayland: Fix maximizing the window on a compositor that does not provide server side window decorations, such a GNOME or Weston not working
Fixes #1662
2019-05-29 17:07:42 +05:30
Kovid Goyal
2920638a3d Add a shortcut to toggle maximized window state 2019-05-29 16:41:34 +05:30
Kovid Goyal
1ae32b5742 Linux: Automatically increase cell height if the font being used is broken and draws the underscore outside the bounding box
See #690
2019-05-29 16:04:50 +05:30
Kovid Goyal
4e9dabfb25 Add an option :opt:command_on_bell to run an arbitrary command when a bell occurs
Fixes #1660
2019-05-29 09:42:52 +05:30
Kovid Goyal
ca2c419c9b Fix colors not being preserved when using the pipe command with the pager history buffer
Fixes #1657
2019-05-29 09:13:55 +05:30
Kovid Goyal
020c1311ca 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. 2019-05-29 09:01:57 +05:30
Kovid Goyal
8193b2a44b Use layout depenedent key check for strl+tab as well 2019-05-29 08:43:13 +05:30
Kovid Goyal
324a000777 Merge branch 'docs_conf.py_shebang' of https://github.com/Luflosi/kitty 2019-05-28 20:53:30 +05:30
Luflosi
951b1c1e83 Add shebang for docs/conf.py 2019-05-28 17:17:18 +02:00
Kovid Goyal
8e57fd93c6 macOS: Fix a regression that caused cmd+v to double up in the dvorak keyboard layout
Fixes #1652
2019-05-28 20:35:13 +05:30
Kovid Goyal
d262f43b5b Log ARepeat property 2019-05-28 20:18:50 +05:30
Kovid Goyal
73224e7d95 One less line in the debug log 2019-05-28 20:04:21 +05:30
Kovid Goyal
19463c81a3 Merge branch 'docs_copyright_year' of https://github.com/Luflosi/kitty 2019-05-28 19:12:24 +05:30
Luflosi
6f2bb31068 Make the copyright year dynamic for the documentation 2019-05-28 14:58:09 +02:00
Kovid Goyal
abf9c4117d 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
Fixes #1641
2019-05-28 17:54:34 +05:30
Kovid Goyal
39f50830d7 Fix a missing newline when using the pipe command between the scrollback and screen contents
See #1642
2019-05-28 15:30:44 +05:30
Kovid Goyal
c2fd7005cb Fix Ctrl+underscore not being passed to child programs in normal and application keyboard modes 2019-05-24 19:21:48 +05:30
Kovid Goyal
52f2ed33f5 Add a link to the Ubuntu kitty package 2019-05-24 18:08:29 +05:30
Kovid Goyal
a51239c6ae Add support for the underscore key found in some keyboard layouts
Fixes #1639
2019-05-24 17:59:50 +05:30
Jerome Reybert
e6217e1428 Fix warning with old freetype headers
On Centos 7.6, build produces the following warnings:

    kitty/freetype.c: In function ‘render_bitmap’:
    kitty/freetype.c:369:36: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
             for (unsigned int i = 0; i < bitmap.rows; ++i) {
                                        ^
    kitty/freetype.c:371:40: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
                 for (unsigned int j = 0; j < bitmap.width; ++j) bitmap.buffer[i * stride + j] *= 255;
2019-05-24 11:57:08 +02:00
29 changed files with 6193 additions and 5903 deletions

View File

@@ -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]
---------------------

View File

@@ -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

View File

@@ -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`

View File

@@ -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)

View File

@@ -366,6 +366,7 @@ void _glfwPollMonitorsNS(void)
free(disconnected);
free(displays);
_glfwRestartDisplayLinks();
}
// Change the current video mode

View File

@@ -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);

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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";

View File

@@ -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
View File

@@ -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
View File

@@ -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); \

View File

@@ -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()

View File

@@ -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]

View File

@@ -24,6 +24,7 @@ named_keys = {
"'": 'APOSTROPHE',
',': 'COMMA',
'-': 'MINUS',
'_': 'UNDERSCORE',
'.': 'PERIOD',
'/': 'SLASH',
';': 'SEMICOLON',

View File

@@ -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=_('''

View File

@@ -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

View File

@@ -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);

View File

@@ -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
View File

@@ -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

View File

@@ -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
View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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({

View File

@@ -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"},

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)