Compare commits

...

43 Commits

Author SHA1 Message Date
Kovid Goyal
3d32202b3a version 0.17.4 2020-05-09 10:48:21 +05:30
Kovid Goyal
4f4d562670 Fix background_tint making the window semi-transparent
Fixes #2618
2020-05-09 09:06:18 +05:30
Kovid Goyal
28fc9c69da Allow showing the name of the current layout and the number of windows in tab titles
Fixes #2634
2020-05-09 08:00:17 +05:30
Kovid Goyal
6edf00e699 Wayland: Fix OS window title not updating for hidden windows
Fixes #2629
2020-05-05 17:51:36 +05:30
Kovid Goyal
a0d6445dda Move expandvars to utils 2020-05-05 08:22:08 +05:30
Kovid Goyal
e74797037c ... 2020-05-04 13:30:35 +05:30
Kovid Goyal
3fb125dba7 Fix mypy editor integration for changes in syntastic 2020-05-04 13:19:03 +05:30
Kovid Goyal
3c8640f9ad Infrastructure for close confirmation 2020-05-02 18:19:08 +05:30
Kovid Goyal
2e63a4c83f Wayland: Remove unused link-time dependency
6aca3e99f0
2020-05-01 15:00:38 +05:30
Kovid Goyal
9da5bee178 X11: Filter out Xlib errors from other connections
2c8e0512dd
2020-05-01 14:59:16 +05:30
Kovid Goyal
2fc6f3b542 Merge changes from upstream
e65de2941c
2020-05-01 14:57:55 +05:30
Kovid Goyal
4754f42e51 Merge branch 'omit-final-tab-separator' of https://github.com/trygveaa/kitty 2020-05-01 14:05:26 +05:30
Trygve Aaberge
941fe918fb Omit final tab separator
This makes tab separators only drawn between the tabs.

Relates to #2616
2020-05-01 10:29:18 +02:00
Kovid Goyal
f4e82e972e ... 2020-05-01 08:10:11 +05:30
Kovid Goyal
f66dbf111d Pass env to get_vcs_revs() 2020-05-01 08:03:31 +05:30
Kovid Goyal
1a2da5c49d Use python3 in session.vim 2020-04-30 23:12:37 +05:30
Kovid Goyal
8327dabb29 Allow @ send-text over a socket to work with more than 1MB of text 2020-04-30 08:58:46 +05:30
Kovid Goyal
195ea6c140 Dont send back an rc disallowed response for commands that dont expect responses 2020-04-29 21:53:20 +05:30
Kovid Goyal
fcc0707174 Fix kitty @ send-text not working with text larger than 1024 bytes when using --listen-on
Fixes #2607
2020-04-29 21:48:09 +05:30
Kovid Goyal
aa9c3cd634 Report modifier key state when sending wheel events to the terminal program 2020-04-29 20:02:55 +05:30
Kovid Goyal
b541341681 Avoid use of goto 2020-04-29 14:37:26 +05:30
Kovid Goyal
e5d50aca6d ... 2020-04-29 09:21:36 +05:30
Kovid Goyal
b738f4d4ff Fix a regression that broke drag and drop on macOS
Fixes #2605
2020-04-29 09:12:04 +05:30
Kovid Goyal
5c38aabf0e ... 2020-04-29 07:50:01 +05:30
Kovid Goyal
03e9ed6947 Fix set_window_padding() only working on first window 2020-04-28 22:34:32 +05:30
Kovid Goyal
63493fad22 Fix a regression that caused clicking in the padding/margins of windows in the stack layout to switch the window to the first window
Fixes #2604
2020-04-28 22:23:38 +05:30
Kovid Goyal
696b857f91 By default, double clicking no longer considers the : as part of words
Fixes #2602
2020-04-28 19:57:40 +05:30
Kovid Goyal
53ae2bbe0d Merge branch 'use_loop' of https://github.com/Luflosi/kitty 2020-04-28 19:40:40 +05:30
Luflosi
01069e919d Use loop instead of calling dlopen() multiple times
Having an array of library names and iterating over it looks nicer than having several variables and calling `dlopen()` multiple times manually.
It should also make it easier to use custom library names instead by using the preprocessor, which I plan to do in the near future.
2020-04-28 15:53:58 +02:00
Kovid Goyal
fc0095f505 Merge branch 'master' of https://github.com/ixydo/kitty 2020-04-27 21:34:52 +05:30
Jinn Koriech
421528e130 Update note about icat + tmux to be more generic 2020-04-27 16:40:24 +01:00
Jinn Koriech
7b70356515 Add note clarifying that icat won't work with tmux
When using icat with tmux we get the following:

```
$ kitty icat ~/Pictures/fc.jpg 
Terminal does not support reporting screen sizes via the TIOCGWINSZ ioctl
```

I found a suggestion[1] this is due to limitations between kitty and tmux, so I'm raising this PR to either confirm this is the case and make it clear in the documentation, or to have it rejected so an issue can be raised to address the feature.

1: https://unix.stackexchange.com/a/484776/41996
2020-04-27 14:11:16 +01:00
Kovid Goyal
e803505aad CoreText: When centering glyphs ignore negative origins
This is needed for FiraCode ligatures to line up correctly. See
https://github.com/kovidgoyal/kitty/issues/2591#issuecomment-619937030
2020-04-27 18:02:53 +05:30
Kovid Goyal
ad1ff455a0 macOS: Fix a regression in the previous release that caused ligatures to be not be centered horizontally
Fixes #2591
2020-04-27 16:17:05 +05:30
Kovid Goyal
7692cb5c3d Merge branch 'graphics-big-endian' of https://github.com/jamessan/kitty 2020-04-26 09:36:53 +05:30
James McCoy
a94e208db3 Use T_PYSSIZET for GraphicsManager.image_count
GraphicsManager.image_count is declared as a size_t, so the Python type
needs to be the same width.  On 64-bit big-endian architectures, this
mismatch was showing up as test failures:

    ======================================================================
    FAIL: test_gr_delete (kitty_tests.graphics.TestGraphics)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/<<PKGBUILDDIR>>/kitty_tests/graphics.py", line 319, in test_gr_delete
        self.ae(len(layers(s)), 0), self.ae(s.grman.image_count, 1)
        ch = 20
        cw = 10
        delete = <function TestGraphics.test_gr_delete.<locals>.delete at 0x3ffb7fc38b0>
        dx = 0.2
        dy = 0.4
        layers = <function put_helpers.<locals>.layers at 0x3ffb77709d0>
        put_image = <function put_helpers.<locals>.put_image at 0x3ffb77708b0>
        put_ref = <function put_helpers.<locals>.put_ref at 0x3ffb7770940>
        rect_eq = <function put_helpers.<locals>.rect_eq at 0x3ffb7770a60>
        s = <fast_data_types.Screen object at 0x1b60df0>
        self = <kitty_tests.graphics.TestGraphics testMethod=test_gr_delete>
    AssertionError: 0 != 1
2020-04-25 23:25:18 -04:00
Kovid Goyal
ba9d5cf3ff Merge branch 'use-fPIC' of https://github.com/jamessan/kitty 2020-04-25 21:11:10 +05:30
James McCoy
0dff0d03f9 Use -fPIC instead of -fpic
With large objects, use of -fpic will fail to link on certain
architectures due to size limitations of the global offset table (GOT).
For example, the link stage for glfw-wayland.so fails on sparc64, as
below.  Using -fPIC avoids these size limitations.

    ./glfw/osmesa_context.c:73:(.text+0xf2c): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `swapIntervalEGL':
    ./glfw/egl_context.c:238:(.text+0xf68): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `destroyContextOSMesa':
    ./glfw/osmesa_context.c:80:(.text+0x10a4): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `data_offer_action':
    ./glfw/wl_window.c:1692:(.text+0x15e4): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `data_offer_source_actions':
    ./glfw/wl_window.c:1683:(.text+0x1648): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `keyboardHandleRepeatInfo':
    ./glfw/wl_init.c:471:(.text+0x1704): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `checkScaleChange':
    ./glfw/wl_window.c:55:(.text+0x1768): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `primary_selection_copy_callback_done':
    ./glfw/wl_window.c:1833:(.text+0x18bc): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `clipboard_copy_callback_done':
    ./glfw/wl_window.c:1825:(.text+0x1910): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `primary_selection_source_canceled':
    ./glfw/wl_window.c:1582:(.text+0x1968): relocation truncated to fit: R_SPARC_GOT13 against symbol `_glfw' defined in .bss section in /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o
    /tmp/glfw-wayland.so.SYvdxr.ltrans0.ltrans.o: in function `data_source_canceled':
    ./glfw/wl_window.c:1576:(.text+0x19c0): additional relocation overflows omitted from the output
    collect2: error: ld returned 1 exit status
2020-04-25 10:08:28 -04:00
Kovid Goyal
c92aca5d19 Use a Literal as the type for choice based options 2020-04-24 19:45:51 +05:30
Kovid Goyal
5762baeed7 Typing fixes 2020-04-24 19:27:22 +05:30
Kovid Goyal
601cf01222 Fix #2581 2020-04-24 19:24:11 +05:30
Kovid Goyal
0eef48b516 Use literals as the type for cli opts that take choices 2020-04-24 15:28:04 +05:30
Kovid Goyal
3c1954f329 ... 2020-04-24 15:07:44 +05:30
38 changed files with 371 additions and 176 deletions

View File

@@ -4,6 +4,34 @@ Changelog
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator. |kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
To update |kitty|, :doc:`follow the instructions <binary>`. To update |kitty|, :doc:`follow the instructions <binary>`.
0.17.4 [2020-05-09]
--------------------
- Allow showing the name of the current layout and the number of windows
in tab titles (:iss:`2634`)
- macOS: Fix a regression in the previous release that caused ligatures to be
not be centered horizontally (:iss:`2591`)
- By default, double clicking no longer considers the : as part of words, see
:opt:`select_by_word_characters` (:iss:`2602`)
- Fix a regression that caused clicking in the padding/margins of windows in
the stack layout to switch the window to the first window (:iss:`2604`)
- macOS: Fix a regression that broke drag and drop (:iss:`2605`)
- Report modifier key state when sending wheel events to the terminal program
- Fix kitty @ send-text not working with text larger than 1024 bytes when using
:option:`kitty --listen-on` (:iss:`2607`)
- Wayland: Fix OS window title not updating for hidden windows (:iss:`2629`)
- Fix :opt:`background_tint` making the window semi-transparent (:iss:`2618`)
0.17.3 [2020-04-23] 0.17.3 [2020-04-23]
-------------------- --------------------

View File

@@ -21,6 +21,12 @@ Then you can simply use ``icat image.png`` to view images.
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to `ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
work. work.
.. note::
kitty's image display protocol may not work when used within a terminal
multiplexer such as ``screen`` or ``tmux``, depending on whether the
multiplexer has added support for it or not.
.. program:: kitty +kitten icat .. program:: kitty +kitten icat

View File

@@ -1133,7 +1133,7 @@ is_ascii_control_char(char x) {
break; break;
} }
_glfwInputScroll(window, deltaX, deltaY, flags); _glfwInputScroll(window, deltaX, deltaY, flags, translateFlags([event modifierFlags]));
} }
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender

View File

@@ -55,7 +55,7 @@ def wayland_protocol_file_name(base: str, ext: str = 'c') -> str:
def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_compile: Callable, module: str = 'x11') -> Env: def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_compile: Callable, module: str = 'x11') -> Env:
ans = env.copy() ans = env.copy()
ans.cflags.append('-fpic') ans.cflags.append('-fPIC')
ans.cppflags.append('-D_GLFW_' + module.upper()) ans.cppflags.append('-D_GLFW_' + module.upper())
ans.cppflags.append('-D_GLFW_BUILD_DLL') ans.cppflags.append('-D_GLFW_BUILD_DLL')
@@ -95,7 +95,7 @@ def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_co
for p in ans.wayland_protocols: for p in ans.wayland_protocols:
ans.sources.append(wayland_protocol_file_name(p)) ans.sources.append(wayland_protocol_file_name(p))
ans.all_headers.append(wayland_protocol_file_name(p, 'h')) ans.all_headers.append(wayland_protocol_file_name(p, 'h'))
for dep in 'wayland-egl wayland-client wayland-cursor xkbcommon dbus-1'.split(): for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split():
ans.cflags.extend(pkg_config(dep, '--cflags-only-I')) ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
ans.ldpaths.extend(pkg_config(dep, '--libs')) ans.ldpaths.extend(pkg_config(dep, '--libs'))
has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE

3
glfw/glfw3.h vendored
View File

@@ -1550,6 +1550,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
* value 2 for stationary momentum scrolling, value 3 for momentum scrolling * value 2 for stationary momentum scrolling, value 3 for momentum scrolling
* in progress, value 4 for momentum scrolling ended, value 5 for momentum * in progress, value 4 for momentum scrolling ended, value 5 for momentum
* scrolling cancelled and value 6 if scrolling may begin soon. * scrolling cancelled and value 6 if scrolling may begin soon.
* @param[int] mods The keyboard modifiers
* *
* @sa @ref scrolling * @sa @ref scrolling
* @sa @ref glfwSetScrollCallback * @sa @ref glfwSetScrollCallback
@@ -1559,7 +1560,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
* *
* @ingroup input * @ingroup input
*/ */
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int); typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int,int);
/*! @brief The function pointer type for key callbacks. /*! @brief The function pointer type for key callbacks.
* *

4
glfw/input.c vendored
View File

@@ -300,10 +300,10 @@ void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
// Notifies shared code of a scroll event // Notifies shared code of a scroll event
// //
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags) void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods)
{ {
if (window->callbacks.scroll) if (window->callbacks.scroll)
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags); window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags, mods);
} }
// Notifies shared code of a mouse button click event // Notifies shared code of a mouse button click event

2
glfw/internal.h vendored
View File

@@ -755,7 +755,7 @@ void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods); void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods);
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev); void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags); void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods);
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered); void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);

2
glfw/wl_init.c vendored
View File

@@ -334,7 +334,7 @@ static void pointerHandleAxis(void* data UNUSED,
else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
y = wl_fixed_to_double(value) * -1; y = wl_fixed_to_double(value) * -1;
_glfwInputScroll(window, x, y, 1); _glfwInputScroll(window, x, y, 1, _glfw.wl.xkb.states.modifiers);
} }
static const struct wl_pointer_listener pointerListener = { static const struct wl_pointer_listener pointerListener = {

4
glfw/x11_init.c vendored
View File

@@ -502,8 +502,10 @@ static Window createHelperWindow(void)
// X error handler // X error handler
// //
static int errorHandler(Display *display UNUSED, XErrorEvent* event) static int errorHandler(Display *display, XErrorEvent* event)
{ {
if (_glfw.x11.display != display)
return 0;
_glfw.x11.errorCode = event->error_code; _glfw.x11.errorCode = event->error_code;
return 0; return 0;
} }

26
glfw/x11_window.c vendored
View File

@@ -1265,13 +1265,13 @@ static void processEvent(XEvent *event)
// Modern X provides scroll events as mouse button presses // Modern X provides scroll events as mouse button presses
else if (event->xbutton.button == Button4) else if (event->xbutton.button == Button4)
_glfwInputScroll(window, 0.0, 1.0, 0); _glfwInputScroll(window, 0.0, 1.0, 0, mods);
else if (event->xbutton.button == Button5) else if (event->xbutton.button == Button5)
_glfwInputScroll(window, 0.0, -1.0, 0); _glfwInputScroll(window, 0.0, -1.0, 0, mods);
else if (event->xbutton.button == Button6) else if (event->xbutton.button == Button6)
_glfwInputScroll(window, 1.0, 0.0, 0); _glfwInputScroll(window, 1.0, 0.0, 0, mods);
else if (event->xbutton.button == Button7) else if (event->xbutton.button == Button7)
_glfwInputScroll(window, -1.0, 0.0, 0); _glfwInputScroll(window, -1.0, 0.0, 0, mods);
else else
{ {
@@ -1408,12 +1408,18 @@ static void processEvent(XEvent *event)
if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
{ {
Window dummy; Window dummy;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display, XTranslateCoordinates(_glfw.x11.display,
window->x11.parent, window->x11.parent,
_glfw.x11.root, _glfw.x11.root,
xpos, ypos, xpos, ypos,
&xpos, &ypos, &xpos, &ypos,
&dummy); &dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success) {
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to translate ConfigureNotiy co-ords for reparented window");
return;
}
} }
if (xpos != window->x11.xpos || ypos != window->x11.ypos) if (xpos != window->x11.xpos || ypos != window->x11.ypos)
@@ -1550,17 +1556,21 @@ static void processEvent(XEvent *event)
const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
const int yabs = (event->xclient.data.l[2]) & 0xffff; const int yabs = (event->xclient.data.l[2]) & 0xffff;
Window dummy; Window dummy;
int xpos, ypos; int xpos = 0, ypos = 0;
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
return; return;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display, XTranslateCoordinates(_glfw.x11.display,
_glfw.x11.root, _glfw.x11.root,
window->x11.handle, window->x11.handle,
xabs, yabs, xabs, yabs,
&xpos, &ypos, &xpos, &ypos,
&dummy); &dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success)
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get DND event position");
_glfwInputCursorPos(window, xpos, ypos); _glfwInputCursorPos(window, xpos, ypos);
@@ -1988,10 +1998,14 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{ {
Window dummy; Window dummy;
int x, y; int x = 0, y = 0;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
0, 0, &x, &y, &dummy); 0, 0, &x, &y, &dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success)
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get window position");
if (xpos) if (xpos)
*xpos = x; *xpos = x;

View File

@@ -336,7 +336,13 @@ class Boss:
if not getattr(err, 'hide_traceback', False): if not getattr(err, 'hide_traceback', False):
response['tb'] = traceback.format_exc() response['tb'] = traceback.format_exc()
else: else:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'} no_response = False
try:
no_response = json.loads(cmd).get('no_response')
except Exception:
pass
if not no_response:
response = {'ok': False, 'error': 'Remote control is disabled. Add allow_remote_control to your kitty.conf'}
return response return response
def remote_control(self, *args: str) -> None: def remote_control(self, *args: str) -> None:
@@ -355,15 +361,16 @@ class Boss:
self.show_error(_('remote_control mapping failed'), str(e)) self.show_error(_('remote_control mapping failed'), str(e))
def peer_message_received(self, msg_bytes: bytes) -> Optional[bytes]: def peer_message_received(self, msg_bytes: bytes) -> Optional[bytes]:
msg = msg_bytes.decode('utf-8') cmd_prefix = b'\x1bP@kitty-cmd'
cmd_prefix = '\x1bP@kitty-cmd' terminator = b'\x1b\\'
if msg.startswith(cmd_prefix): if msg_bytes.startswith(cmd_prefix) and msg_bytes.endswith(terminator):
cmd = msg[len(cmd_prefix):-2] cmd = msg_bytes[len(cmd_prefix):-len(terminator)].decode('utf-8')
response = self._handle_remote_command(cmd, from_peer=True) response = self._handle_remote_command(cmd, from_peer=True)
if response is None: if response is None:
return None return None
return (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8') return cmd_prefix + json.dumps(response).encode('utf-8') + terminator
data = json.loads(msg)
data = json.loads(msg_bytes.decode('utf-8'))
if isinstance(data, dict) and data.get('cmd') == 'new_instance': if isinstance(data, dict) and data.get('cmd') == 'new_instance':
from .cli_stub import CLIOptions from .cli_stub import CLIOptions
startup_id = data.get('startup_id') startup_id = data.get('startup_id')
@@ -717,6 +724,9 @@ class Boss:
text = '\n'.join(parse_uri_list(text)) text = '\n'.join(parse_uri_list(text))
w.paste(text) w.paste(text)
def confirm_os_window_close(self, os_window_id: int) -> None:
mark_os_window_for_close(os_window_id)
def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None: def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None:
self.cached_values['window-size'] = viewport_width, viewport_height self.cached_values['window-size'] = viewport_width, viewport_height
tm = self.os_window_map.pop(os_window_id, None) tm = self.os_window_map.pop(os_window_id, None)

View File

@@ -523,17 +523,6 @@ change_menubar_title(PyObject *title UNUSED) {
#endif #endif
} }
static inline bool
update_window_title(Window *w, OSWindow *os_window) {
if (w->title && w->title != os_window->window_title) {
os_window->window_title = w->title;
Py_INCREF(os_window->window_title);
set_os_window_title(os_window, PyUnicode_AsUTF8(w->title));
return true;
}
return false;
}
static inline bool static inline bool
prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) { prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) {
#define TD os_window->tab_bar_render_data #define TD os_window->tab_bar_render_data
@@ -577,7 +566,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
*active_window_id = w->id; *active_window_id = w->id;
collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window); collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window);
if (w->cursor_visible_at_last_render != WD.screen->cursor_render_info.is_visible || w->last_cursor_x != WD.screen->cursor_render_info.x || w->last_cursor_y != WD.screen->cursor_render_info.y || w->last_cursor_shape != WD.screen->cursor_render_info.shape) needs_render = true; if (w->cursor_visible_at_last_render != WD.screen->cursor_render_info.is_visible || w->last_cursor_x != WD.screen->cursor_render_info.x || w->last_cursor_y != WD.screen->cursor_render_info.y || w->last_cursor_shape != WD.screen->cursor_render_info.shape) needs_render = true;
update_window_title(w, os_window); set_os_window_title_from_window(w, os_window);
*active_window_bg = window_bg; *active_window_bg = window_bg;
} else WD.screen->cursor_render_info.is_visible = false; } else WD.screen->cursor_render_info.is_visible = false;
if (send_cell_data_to_gpu(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true; if (send_cell_data_to_gpu(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true;
@@ -625,15 +614,6 @@ render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_windo
#undef TD #undef TD
} }
static inline void
update_os_window_title(OSWindow *os_window) {
Tab *tab = os_window->tabs + os_window->active_tab;
if (tab->num_windows) {
Window *w = tab->windows + tab->active_window;
update_window_title(w, os_window);
}
}
static void static void
draw_resizing_text(OSWindow *w) { draw_resizing_text(OSWindow *w) {
char text[32] = {0}; char text[32] = {0};
@@ -865,25 +845,42 @@ process_pending_resizes(monotonic_t now) {
static inline void static inline void
close_all_windows(void) { close_all_windows(void) {
for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true); for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], IMPERATIVE_CLOSE_REQUESTED);
}
static inline void
close_os_window(ChildMonitor *self, OSWindow *os_window) {
destroy_os_window(os_window);
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height);
for (size_t t=0; t < os_window->num_tabs; t++) {
Tab *tab = os_window->tabs + t;
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);
}
remove_os_window(os_window->id);
} }
static inline bool static inline bool
process_pending_closes(ChildMonitor *self) { process_pending_closes(ChildMonitor *self) {
global_state.has_pending_closes = false;
bool has_open_windows = false; bool has_open_windows = false;
for (size_t w = global_state.num_os_windows; w > 0; w--) { for (size_t w = global_state.num_os_windows; w > 0; w--) {
OSWindow *os_window = global_state.os_windows + w - 1; OSWindow *os_window = global_state.os_windows + w - 1;
if (should_os_window_close(os_window)) { switch(os_window->close_request) {
destroy_os_window(os_window); case NO_CLOSE_REQUESTED:
call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height); has_open_windows = true;
for (size_t t=0; t < os_window->num_tabs; t++) { break;
Tab *tab = os_window->tabs + t; case CONFIRMABLE_CLOSE_REQUESTED:
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id); os_window->close_request = NO_CLOSE_REQUESTED;
} call_boss(confirm_os_window_close, "K", os_window->id);
remove_os_window(os_window->id); if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) {
} else has_open_windows = true; close_os_window(self, os_window);
} else has_open_windows = true;
break;
case IMPERATIVE_CLOSE_REQUESTED:
close_os_window(self, os_window);
break;
}
} }
global_state.has_pending_closes = false;
#ifdef __APPLE__ #ifdef __APPLE__
if (!OPT(macos_quit_when_last_window_closed)) { if (!OPT(macos_quit_when_last_window_closed)) {
if (!has_open_windows && !application_quit_requested()) has_open_windows = true; if (!has_open_windows && !application_quit_requested()) has_open_windows = true;
@@ -1279,7 +1276,7 @@ typedef struct {
char *data; char *data;
size_t capacity, used; size_t capacity, used;
int fd; int fd;
bool finished, close_socket; bool finished, close_socket, is_peer_command;
} PeerReadData; } PeerReadData;
static PeerReadData empty_prd = {.fd = -1, 0}; static PeerReadData empty_prd = {.fd = -1, 0};
@@ -1329,6 +1326,41 @@ accept_peer(int listen_fd, bool shutting_down) {
return true; return true;
} }
#define KITTY_CMD_PREFIX "\x1bP@kitty-cmd{"
static inline void
queue_peer_message(ChildMonitor *self, char *buf, size_t sz, int fd) {
children_mutex(lock);
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true);
Message *m = self->messages + self->messages_count++;
m->data = buf; m->sz = sz; m->fd = fd;
children_mutex(unlock);
wakeup_main_loop();
}
static inline void
dispatch_peer_command(ChildMonitor *self, PeerReadData *rd, int fd) {
size_t end = 0;
for (size_t i = 0; i < rd->used - 1; i++) {
if (rd->data[i] == 0x1b && rd->data[i+1] == '\\') {
end = i + 2;
break;
}
}
if (!end) return;
char *buf = malloc(end + 8);
if (buf) {
memcpy(buf, rd->data, end);
queue_peer_message(self, buf, end, fd);
}
rd->is_peer_command = false;
if (rd->used > end) {
rd->used -= end;
memmove(rd->data, rd->data + end, rd->used);
if (rd->used >= sizeof(KITTY_CMD_PREFIX) - 1 && memcmp(rd->data, KITTY_CMD_PREFIX, sizeof(KITTY_CMD_PREFIX)-1) == 0) rd->is_peer_command = true;
} else rd->used = 0;
}
static inline bool static inline bool
read_from_peer(ChildMonitor *self, int s) { read_from_peer(ChildMonitor *self, int s) {
bool read_finished = false; bool read_finished = false;
@@ -1337,26 +1369,28 @@ read_from_peer(ChildMonitor *self, int s) {
#define failed(msg) { read_finished = true; log_error("%s", msg); rd->finished = true; rd->close_socket = true; break; } #define failed(msg) { read_finished = true; log_error("%s", msg); rd->finished = true; rd->close_socket = true; break; }
if (rd->fd == s) { if (rd->fd == s) {
if (rd->used >= rd->capacity) { if (rd->used >= rd->capacity) {
if (rd->capacity >= 1024 * 1024) failed("Ignoring too large message from peer"); if (rd->capacity >= 64 * 1024) failed("Ignoring too large message from peer");
rd->capacity = MAX(8192u, rd->capacity * 2); rd->capacity = MAX(8192u, rd->capacity * 2);
rd->data = realloc(rd->data, rd->capacity); rd->data = realloc(rd->data, rd->capacity);
if (!rd->data) failed("Out of memory"); if (!rd->data) failed("Out of memory");
} }
ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0); ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0);
if (n == 0) { if (n == 0) {
while (rd->is_peer_command) dispatch_peer_command(self, rd, s);
read_finished = true; rd->finished = true; read_finished = true; rd->finished = true;
children_mutex(lock); if (rd->used) queue_peer_message(self, rd->data, rd->used, s);
ensure_space_for(self, messages, Message, self->messages_count + 1, messages_capacity, 16, true); else free(rd->data);
Message *m = self->messages + self->messages_count++; rd->data = NULL;
m->data = rd->data; rd->data = NULL; m->sz = rd->used; m->fd = s;
children_mutex(unlock);
wakeup_main_loop();
} else if (n < 0) { } else if (n < 0) {
if (errno != EINTR) { if (errno != EINTR) {
perror("Error reading from talk peer"); perror("Error reading from talk peer");
failed(""); failed("");
} }
} else rd->used += n; } else {
if (!rd->used && memcmp(rd->data, KITTY_CMD_PREFIX, sizeof(KITTY_CMD_PREFIX)-1) == 0) rd->is_peer_command = true;
rd->used += n;
while (rd->is_peer_command) dispatch_peer_command(self, rd, s);
}
break; break;
} }
} }

View File

@@ -409,7 +409,10 @@ def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str, e
elif otype == 'list': elif otype == 'list':
t = 'typing.Sequence[str]' t = 'typing.Sequence[str]'
elif otype in ('choice', 'choices'): elif otype in ('choice', 'choices'):
t = 'str' if opt['choices']:
t = 'typing.Literal[{}]'.format(','.join(f'{x!r}' for x in opt['choices']))
else:
t = 'str'
elif otype.startswith('bool-'): elif otype.startswith('bool-'):
t = 'bool' t = 'bool'
else: else:

View File

@@ -9,7 +9,7 @@ from typing import (
Set, Tuple, Union, get_type_hints Set, Tuple, Union, get_type_hints
) )
from .utils import to_bool from .utils import Choice, to_bool
def to_string(x: str) -> str: def to_string(x: str) -> str:
@@ -56,6 +56,8 @@ class Option:
if type(self.option_type) is type: if type(self.option_type) is type:
return type_name(self.option_type) return type_name(self.option_type)
if isinstance(self.option_type, Choice):
return 'typing.Literal[{}]'.format(','.join(f'{x!r}' for x in self.option_type.all_choices))
th = get_type_hints(self.option_type) th = get_type_hints(self.option_type)
try: try:
rettype = th['return'] rettype = th['return']

View File

@@ -68,17 +68,21 @@ def python_string(text: str) -> str:
return ans return ans
def choices(*choices: str) -> Callable[[str], str]: class Choice:
defval = choices[0]
uc = frozenset(choices)
def choice(x: str) -> str: def __init__(self, choices: Sequence[str]):
self.defval = choices[0]
self.all_choices = frozenset(choices)
def __call__(self, x: str) -> str:
x = x.lower() x = x.lower()
if x not in uc: if x not in self.all_choices:
x = defval x = self.defval
return x return x
return choice
def choices(*choices: str) -> Choice:
return Choice(choices)
def parse_line( def parse_line(

View File

@@ -9,8 +9,8 @@ import sys
from contextlib import contextmanager, suppress from contextlib import contextmanager, suppress
from functools import partial from functools import partial
from typing import ( from typing import (
Any, Callable, Dict, Generator, Iterable, List, Match, NamedTuple, Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
Optional, Sequence, Set, Tuple, Type, Union Sequence, Set, Tuple, Type, Union
) )
from . import fast_data_types as defines from . import fast_data_types as defines
@@ -24,7 +24,7 @@ from .constants import cache_dir, defconf, is_macos
from .key_names import get_key_name_lookup, key_name_aliases from .key_names import get_key_name_lookup, key_name_aliases
from .options_stub import Options as OptionsStub from .options_stub import Options as OptionsStub
from .typing import EdgeLiteral, TypedDict from .typing import EdgeLiteral, TypedDict
from .utils import log_error from .utils import expandvars, log_error
KeySpec = Tuple[int, bool, int] KeySpec = Tuple[int, bool, int]
KeyMap = Dict[KeySpec, 'KeyAction'] KeyMap = Dict[KeySpec, 'KeyAction']
@@ -585,20 +585,6 @@ def handle_deprecated_macos_show_window_title_in_menubar_alias(key: str, val: st
ans['macos_show_window_title_in'] = macos_show_window_title_in ans['macos_show_window_title_in'] = macos_show_window_title_in
def expandvars(val: str, env: Dict[str, str] = {}) -> str:
def sub(m: Match) -> str:
key = m.group(1)
result = env.get(key)
if result is None:
result = os.environ.get(key)
if result is None:
result = m.group()
return result
return re.sub(r'\$\{(\S+?)\}', sub, val)
@special_handler @special_handler
def handle_env(key: str, val: str, ans: Dict[str, Any]) -> None: def handle_env(key: str, val: str, ans: Dict[str, Any]) -> None:
key, val = val.partition('=')[::2] key, val = val.partition('=')[::2]

View File

@@ -548,7 +548,7 @@ rectangular block with the mouse)'''))
o('terminal_select_modifiers', 'shift', option_type=to_modifiers, long_text=_(''' o('terminal_select_modifiers', 'shift', option_type=to_modifiers, long_text=_('''
The modifiers to override mouse selection even when a terminal application has grabbed the mouse''')) The modifiers to override mouse selection even when a terminal application has grabbed the mouse'''))
o('select_by_word_characters', ':@-./_~?&=%+#', long_text=_(''' o('select_by_word_characters', '@-./_~?&=%+#', long_text=_('''
Characters considered part of a word when double clicking. In addition to these characters Characters considered part of a word when double clicking. In addition to these characters
any character that is marked as an alphanumeric character in the unicode any character that is marked as an alphanumeric character in the unicode
database will be matched.''')) database will be matched.'''))
@@ -870,6 +870,11 @@ A template to render the tab title. The default just renders
the title. If you wish to include the tab-index as well, the title. If you wish to include the tab-index as well,
use something like: :code:`{index}: {title}`. Useful use something like: :code:`{index}: {title}`. Useful
if you have shortcuts mapped for :code:`goto_tab N`. if you have shortcuts mapped for :code:`goto_tab N`.
In addition you can use :code:`{layout_name}` for the current
layout name and :code:`{num_windows}` for the number of windows
in the tab. Note that formatting is done by Python's string formatting
machinery, so you can use, for instance, :code:`{layout_name[:2].upper()}` to
show only the first two letters of the layout name, upper-cased.
''')) '''))
o('active_tab_title_template', 'none', option_type=active_tab_title_template, long_text=_(''' o('active_tab_title_template', 'none', option_type=active_tab_title_template, long_text=_('''
Template to use for active tabs, if not specified falls back Template to use for active tabs, if not specified falls back

View File

@@ -20,7 +20,7 @@ class Version(NamedTuple):
appname: str = 'kitty' appname: str = 'kitty'
version: Version = Version(0, 17, 3) version: Version = Version(0, 17, 4)
str_version: str = '.'.join(map(str, version)) str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower() _plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat is_macos: bool = 'darwin' in _plat

View File

@@ -506,8 +506,10 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
render_alpha_mask(render_buf, canvas, &src, &dest, canvas_width, canvas_width); render_alpha_mask(render_buf, canvas, &src, &dest, canvas_width, canvas_width);
} }
if (num_cells && (center_glyph || (num_cells == 2 && *was_colored))) { if (num_cells && (center_glyph || (num_cells == 2 && *was_colored))) {
// center glyphs (two cell emoji and PUA glyphs) // center glyphs (two cell emoji, PUA glyphs, ligatures, etc)
CGFloat delta = (((CGFloat)canvas_width - br.size.width) / 2.f) - br.origin.x; CGFloat delta = (((CGFloat)canvas_width - br.size.width) / 2.f);
// FiraCode ligatures result in negative origins
if (br.origin.x > 0) delta -= br.origin.x;
if (delta >= 1.f) right_shift_canvas(canvas, canvas_width, cell_height, (unsigned)(delta)); if (delta >= 1.f) right_shift_canvas(canvas, canvas_width, cell_height, (unsigned)(delta));
} }
return true; return true;

View File

@@ -313,7 +313,7 @@ void set_mouse_cursor(MouseShape);
void enter_event(void); void enter_event(void);
void mouse_event(int, int, int); void mouse_event(int, int, int);
void focus_in_event(void); void focus_in_event(void);
void scroll_event(double, double, int); void scroll_event(double, double, int, int);
void fake_scroll(int, bool); void fake_scroll(int, bool);
void set_special_key_combo(int glfw_key, int mods, bool is_native); void set_special_key_combo(int glfw_key, int mods, bool is_native);
void on_key_input(GLFWkeyevent *ev); void on_key_input(GLFWkeyevent *ev);

View File

@@ -30,18 +30,22 @@ static void* libsn_handle = NULL;
static PyObject* static PyObject*
init_x11_startup_notification(PyObject UNUSED *self, PyObject *args) { init_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {
static bool done = false; static bool done = false;
static const char* libname = "libstartup-notification-1.so";
// some installs are missing the .so symlink, so try the full name
static const char* libname2 = "libstartup-notification-1.so.0";
static const char* libname3 = "libstartup-notification-1.so.0.0.0";
if (!done) { if (!done) {
done = true; done = true;
libsn_handle = dlopen(libname, RTLD_LAZY); const char* libnames[] = {
if (libsn_handle == NULL) libsn_handle = dlopen(libname2, RTLD_LAZY); "libstartup-notification-1.so",
if (libsn_handle == NULL) libsn_handle = dlopen(libname3, RTLD_LAZY); // some installs are missing the .so symlink, so try the full name
"libstartup-notification-1.so.0",
"libstartup-notification-1.so.0.0.0",
NULL
};
for (int i = 0; libnames[i]; i++) {
libsn_handle = dlopen(libnames[i], RTLD_LAZY);
if (libsn_handle) break;
}
if (libsn_handle == NULL) { if (libsn_handle == NULL) {
PyErr_Format(PyExc_OSError, "Failed to load %s with error: %s", libname, dlerror()); PyErr_Format(PyExc_OSError, "Failed to load %s with error: %s", libnames[0], dlerror());
return NULL; return NULL;
} }
dlerror(); /* Clear any existing error */ dlerror(); /* Clear any existing error */
@@ -105,18 +109,22 @@ load_libcanberra_functions(void) {
static void static void
load_libcanberra(void) { load_libcanberra(void) {
static const char* libname = "libcanberra.so";
// some installs are missing the .so symlink, so try the full name
static const char* libname2 = "libcanberra.so.0";
static const char* libname3 = "libcanberra.so.0.2.5";
static bool done = false; static bool done = false;
if (done) return; if (done) return;
done = true; done = true;
libcanberra_handle = dlopen(libname, RTLD_LAZY); const char* libnames[] = {
if (libcanberra_handle == NULL) libcanberra_handle = dlopen(libname2, RTLD_LAZY); "libcanberra.so",
if (libcanberra_handle == NULL) libcanberra_handle = dlopen(libname3, RTLD_LAZY); // some installs are missing the .so symlink, so try the full name
"libcanberra.so.0",
"libcanberra.so.0.2.5",
NULL
};
for (int i = 0; libnames[i]; i++) {
libcanberra_handle = dlopen(libnames[i], RTLD_LAZY);
if (libcanberra_handle) break;
}
if (libcanberra_handle == NULL) { if (libcanberra_handle == NULL) {
fprintf(stderr, "Failed to load %s, cannot play beep sound, with error: %s\n", libname, dlerror()); fprintf(stderr, "Failed to load %s, cannot play beep sound, with error: %s\n", libnames[0], dlerror());
return; return;
} }
load_libcanberra_functions(); load_libcanberra_functions();

View File

@@ -550,6 +550,10 @@ def update_window_visibility(
pass pass
def sync_os_window_title(os_window_id: int) -> None:
pass
def set_options( def set_options(
opts: Options, opts: Options,
is_wayland: bool = False, is_wayland: bool = False,

View File

@@ -1004,14 +1004,20 @@ merge_groups_for_pua_space_ligature(void) {
G(groups)->is_space_ligature = true; G(groups)->is_space_ligature = true;
} }
static inline bool
is_group_calt_ligature(const Group *group) {
GPUCell *first_cell = G(first_gpu_cell) + group->first_cell_idx;
return group->num_cells > 1 && group->has_special_glyph && (first_cell->attrs & WIDTH_MASK) == 1;
}
static inline void static inline void
split_run_at_offset(index_type cursor_offset, index_type *left, index_type *right) { split_run_at_offset(index_type cursor_offset, index_type *left, index_type *right) {
*left = 0; *right = 0; *left = 0; *right = 0;
for (unsigned idx = 0; idx < G(group_idx) + 1; idx++) { for (unsigned idx = 0; idx < G(group_idx) + 1; idx++) {
Group *group = G(groups) + idx; Group *group = G(groups) + idx;
if (group->first_cell_idx <= cursor_offset && cursor_offset < group->first_cell_idx + group->num_cells) { if (group->first_cell_idx <= cursor_offset && cursor_offset < group->first_cell_idx + group->num_cells) {
GPUCell *first_cell = G(first_gpu_cell) + group->first_cell_idx; if (is_group_calt_ligature(group)) {
if (group->num_cells > 1 && group->has_special_glyph && (first_cell->attrs & WIDTH_MASK) == 1) {
// likely a calt ligature // likely a calt ligature
*left = group->first_cell_idx; *right = group->first_cell_idx + group->num_cells; *left = group->first_cell_idx; *right = group->first_cell_idx + group->num_cells;
} }
@@ -1039,6 +1045,11 @@ render_groups(FontGroup *fg, Font *font, bool center_glyph) {
// there exist stupid fonts like Powerline that have no space glyph, // there exist stupid fonts like Powerline that have no space glyph,
// so special case it: https://github.com/kovidgoyal/kitty/issues/1225 // so special case it: https://github.com/kovidgoyal/kitty/issues/1225
unsigned int num_glyphs = group->is_space_ligature ? 1 : group->num_glyphs; unsigned int num_glyphs = group->is_space_ligature ? 1 : group->num_glyphs;
#ifdef __APPLE__
// FiraCode ligatures need to be centered on macOS
// see https://github.com/kovidgoyal/kitty/issues/2591
if (is_group_calt_ligature(group)) center_glyph = true;
#endif
render_group(fg, group->num_cells, num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed, center_glyph); render_group(fg, group->num_cells, num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed, center_glyph);
idx++; idx++;
} }

3
kitty/glfw-wrapper.h generated
View File

@@ -1313,6 +1313,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
* value 2 for stationary momentum scrolling, value 3 for momentum scrolling * value 2 for stationary momentum scrolling, value 3 for momentum scrolling
* in progress, value 4 for momentum scrolling ended, value 5 for momentum * in progress, value 4 for momentum scrolling ended, value 5 for momentum
* scrolling cancelled and value 6 if scrolling may begin soon. * scrolling cancelled and value 6 if scrolling may begin soon.
* @param[int] mods The keyboard modifiers
* *
* @sa @ref scrolling * @sa @ref scrolling
* @sa @ref glfwSetScrollCallback * @sa @ref glfwSetScrollCallback
@@ -1322,7 +1323,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
* *
* @ingroup input * @ingroup input
*/ */
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int); typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int,int);
/*! @brief The function pointer type for key callbacks. /*! @brief The function pointer type for key callbacks.
* *

View File

@@ -153,6 +153,10 @@ static void
window_close_callback(GLFWwindow* window) { window_close_callback(GLFWwindow* window) {
if (!set_callback_window(window)) return; if (!set_callback_window(window)) return;
global_state.has_pending_closes = true; global_state.has_pending_closes = true;
if (global_state.callback_os_window->close_request < CONFIRMABLE_CLOSE_REQUESTED) {
global_state.callback_os_window->close_request = CONFIRMABLE_CLOSE_REQUESTED;
}
glfwSetWindowShouldClose(window, false);
request_tick_callback(); request_tick_callback();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@@ -287,12 +291,12 @@ cursor_pos_callback(GLFWwindow *w, double x, double y) {
} }
static void static void
scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags) { scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags, int mods) {
if (!set_callback_window(w)) return; if (!set_callback_window(w)) return;
show_mouse_cursor(w); show_mouse_cursor(w);
monotonic_t now = monotonic(); monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now; global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags); if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags, mods);
request_tick_callback(); request_tick_callback();
global_state.callback_os_window = NULL; global_state.callback_os_window = NULL;
} }
@@ -323,23 +327,17 @@ window_focus_callback(GLFWwindow *w, int focused) {
static int static int
drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) { drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) {
if (!set_callback_window(w)) return 0; if (!set_callback_window(w)) return 0;
#define RETURN(x) { global_state.callback_os_window = NULL; return x; }
if (!data) { if (!data) {
if (strcmp(mime, "text/uri-list") == 0) return 3; if (strcmp(mime, "text/uri-list") == 0) RETURN(3);
if (strcmp(mime, "text/plain;charset=utf-8") == 0) return 2; if (strcmp(mime, "text/plain;charset=utf-8") == 0) RETURN(2);
if (strcmp(mime, "text/plain") == 0) return 1; if (strcmp(mime, "text/plain") == 0) RETURN(1);
return 0; RETURN(0);
} }
WINDOW_CALLBACK(on_drop, "sy#", mime, data, (int)sz); WINDOW_CALLBACK(on_drop, "sy#", mime, data, (Py_ssize_t)sz);
request_tick_callback(); request_tick_callback();
/* PyObject *s = PyTuple_New(count); */ RETURN(0);
/* if (s) { */ #undef RETURN
/* for (int i = 0; i < count; i++) PyTuple_SET_ITEM(s, i, PyUnicode_FromString(strings[i])); */
/* WINDOW_CALLBACK(on_drop, "O", s); */
/* Py_CLEAR(s); */
/* request_tick_callback(); */
/* } */
global_state.callback_os_window = NULL;
return 0;
} }
// }}} // }}}
@@ -979,12 +977,6 @@ wakeup_main_loop() {
glfwPostEmptyEvent(); glfwPostEmptyEvent();
} }
void
mark_os_window_for_close(OSWindow* w, bool yes) {
global_state.has_pending_closes = true;
glfwSetWindowShouldClose(w->handle, yes);
}
bool bool
should_os_window_be_rendered(OSWindow* w) { should_os_window_be_rendered(OSWindow* w) {
return ( return (
@@ -994,11 +986,6 @@ should_os_window_be_rendered(OSWindow* w) {
) ? false : true; ) ? false : true;
} }
bool
should_os_window_close(OSWindow* w) {
return glfwWindowShouldClose(w->handle) ? true : false;
}
static PyObject* static PyObject*
primary_monitor_size(PYNOARG) { primary_monitor_size(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor(); GLFWmonitor* monitor = glfwGetPrimaryMonitor();

View File

@@ -939,7 +939,7 @@ static PyMethodDef methods[] = {
}; };
static PyMemberDef members[] = { static PyMemberDef members[] = {
{"image_count", T_UINT, offsetof(GraphicsManager, image_count), 0, "image_count"}, {"image_count", T_PYSSIZET, offsetof(GraphicsManager, image_count), 0, "image_count"},
{NULL}, {NULL},
}; };

View File

@@ -42,7 +42,7 @@ choices=window,tab,os-window,overlay,background,clipboard,primary
Where to launch the child process, in a new kitty window in the current tab, Where to launch the child process, in a new kitty window in the current tab,
a new tab, or a new OS window or an overlay over the current window. a new tab, or a new OS window or an overlay over the current window.
Note that if the current window already has an overlay, then it will Note that if the current window already has an overlay, then it will
open a new window. The value of none means the process will be open a new window. The value of background means the process will be
run in the background. The values clipboard and primary are meant run in the background. The values clipboard and primary are meant
to work with :option:`launch --stdin-source` to copy data to the system to work with :option:`launch --stdin-source` to copy data to the system
clipboard or primary selection. clipboard or primary selection.
@@ -275,8 +275,8 @@ def launch(boss: Boss, opts: LaunchCLIOptions, args: List[str], target_tab: Opti
kw['cmd'] = final_cmd kw['cmd'] = final_cmd
if opts.type == 'overlay' and active and not active.overlay_window_id: if opts.type == 'overlay' and active and not active.overlay_window_id:
kw['overlay_for'] = active.id kw['overlay_for'] = active.id
if opts.stdin_source and opts.stdin_source != 'none': if opts.stdin_source != 'none':
q = opts.stdin_source q = str(opts.stdin_source)
if opts.stdin_add_formatting: if opts.stdin_add_formatting:
if q in ('@screen', '@screen_scrollback', '@alternate', '@alternate_scrollback'): if q in ('@screen', '@screen_scrollback', '@alternate', '@alternate_scrollback'):
q = '@ansi_' + q[1:] q = '@ansi_' + q[1:]

View File

@@ -7,7 +7,7 @@ import os
import shutil import shutil
import sys import sys
from contextlib import contextmanager, suppress from contextlib import contextmanager, suppress
from typing import Generator, List, Mapping, Optional, Tuple, Sequence from typing import Generator, List, Mapping, Optional, Sequence, Tuple
from .borders import load_borders_program from .borders import load_borders_program
from .boss import Boss from .boss import Boss
@@ -15,7 +15,7 @@ from .child import set_default_env
from .cli import create_opts, parse_args from .cli import create_opts, parse_args
from .cli_stub import CLIOptions from .cli_stub import CLIOptions
from .conf.utils import BadLine from .conf.utils import BadLine
from .config import cached_values_for, initial_window_size_func, expandvars from .config import cached_values_for, initial_window_size_func
from .constants import ( from .constants import (
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos, appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
is_wayland, kitty_exe, logo_data_file, running_in_kitty is_wayland, kitty_exe, logo_data_file, running_in_kitty
@@ -29,7 +29,7 @@ from .fonts.box_drawing import set_scale
from .fonts.render import set_font_family from .fonts.render import set_font_family
from .options_stub import Options as OptionsStub from .options_stub import Options as OptionsStub
from .utils import ( from .utils import (
detach, log_error, read_shell_environment, single_instance, detach, expandvars, log_error, read_shell_environment, single_instance,
startup_notification_handler, unix_socket_paths startup_notification_handler, unix_socket_paths
) )
from .window import load_shader_programs from .window import load_shader_programs

View File

@@ -514,7 +514,8 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar) {
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab; Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
for (unsigned int i = 0; i < t->num_windows; i++) { for (unsigned int i = 0; i < t->num_windows; i++) {
if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) { if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) {
*window_idx = i; return t->windows + i; *window_idx = i;
return t->windows + i;
} }
} }
} }
@@ -529,8 +530,10 @@ closest_window_for_event(unsigned int *window_idx) {
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab; Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
for (unsigned int i = 0; i < t->num_windows; i++) { for (unsigned int i = 0; i < t->num_windows; i++) {
Window *w = t->windows + i; Window *w = t->windows + i;
double d = distance_to_window(w); if (w->visible) {
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; } double d = distance_to_window(w);
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }
}
} }
} }
return ans; return ans;
@@ -610,7 +613,7 @@ mouse_event(int button, int modifiers, int action) {
} }
void void
scroll_event(double UNUSED xoffset, double yoffset, int flags) { scroll_event(double UNUSED xoffset, double yoffset, int flags, int modifiers) {
bool in_tab_bar; bool in_tab_bar;
static id_type window_for_momentum_scroll = 0; static id_type window_for_momentum_scroll = 0;
static bool main_screen_for_momentum_scroll = false; static bool main_screen_for_momentum_scroll = false;
@@ -687,7 +690,7 @@ scroll_event(double UNUSED xoffset, double yoffset, int flags) {
screen_history_scroll(screen, abs(s), upwards); screen_history_scroll(screen, abs(s), upwards);
} else { } else {
if (screen->modes.mouse_tracking_mode) { if (screen->modes.mouse_tracking_mode) {
int sz = encode_mouse_event(w, upwards ? GLFW_MOUSE_BUTTON_4 : GLFW_MOUSE_BUTTON_5, PRESS, 0); int sz = encode_mouse_event(w, upwards ? GLFW_MOUSE_BUTTON_4 : GLFW_MOUSE_BUTTON_5, PRESS, modifiers);
if (sz > 0) { if (sz > 0) {
mouse_event_buf[sz] = 0; mouse_event_buf[sz] = 0;
for (s = abs(s); s > 0; s--) { for (s = abs(s); s > 0; s--) {

View File

@@ -465,7 +465,11 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind
bind_program(CELL_BG_PROGRAM); bind_program(CELL_BG_PROGRAM);
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
} else if (OPT(background_tint) > 0) draw_tint(false, screen, xstart, ystart, width, height); } else if (OPT(background_tint) > 0) {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
draw_tint(false, screen, xstart, ystart, width, height);
BLEND_ONTO_OPAQUE;
}
if (screen->grman->num_of_below_refs || has_bgimage(w)) { if (screen->grman->num_of_below_refs || has_bgimage(w)) {
if (screen->grman->num_of_below_refs) draw_graphics( if (screen->grman->num_of_below_refs) draw_graphics(

View File

@@ -43,8 +43,9 @@ GlobalState global_state = {{0}};
if (osw->tabs[t].id == tab_id) { \ if (osw->tabs[t].id == tab_id) { \
Tab *tab = osw->tabs + t; \ Tab *tab = osw->tabs + t; \
for (size_t w = 0; w < tab->num_windows; w++) { \ for (size_t w = 0; w < tab->num_windows; w++) { \
Window *window = tab->windows + w; if (tab->windows[w].id == window_id) { \
#define END_WITH_WINDOW break; }}}}} Window *window = tab->windows + w;
#define END_WITH_WINDOW window_found = 1; break; }}}}}}
#define WITH_OS_WINDOW_REFS \ #define WITH_OS_WINDOW_REFS \
@@ -213,6 +214,27 @@ update_window_title(id_type os_window_id, id_type tab_id, id_type window_id, PyO
END_WITH_TAB; END_WITH_TAB;
} }
void
set_os_window_title_from_window(Window *w, OSWindow *os_window) {
if (w->title && w->title != os_window->window_title) {
Py_XDECREF(os_window->window_title);
os_window->window_title = w->title;
Py_INCREF(os_window->window_title);
set_os_window_title(os_window, PyUnicode_AsUTF8(w->title));
}
}
void
update_os_window_title(OSWindow *os_window) {
if (os_window->num_tabs) {
Tab *tab = os_window->tabs + os_window->active_tab;
if (tab->num_windows) {
Window *w = tab->windows + tab->active_window;
set_os_window_title_from_window(w, os_window);
}
}
}
static inline void static inline void
destroy_window(Window *w) { destroy_window(Window *w) {
Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title); Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);
@@ -422,6 +444,14 @@ os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) {
} }
} }
void
mark_os_window_for_close(OSWindow* w, CloseRequest cr) {
global_state.has_pending_closes = true;
w->close_request = cr;
}
// Python API {{{ // Python API {{{
#define PYWRAP0(name) static PyObject* py##name(PYNOARG) #define PYWRAP0(name) static PyObject* py##name(PYNOARG)
@@ -783,10 +813,10 @@ PYWRAP1(os_window_has_background_image) {
PYWRAP1(mark_os_window_for_close) { PYWRAP1(mark_os_window_for_close) {
id_type os_window_id; id_type os_window_id;
int yes = 1; CloseRequest cr = IMPERATIVE_CLOSE_REQUESTED;
PA("K|p", &os_window_id, &yes); PA("K|i", &os_window_id, &cr);
WITH_OS_WINDOW(os_window_id) WITH_OS_WINDOW(os_window_id)
mark_os_window_for_close(os_window, yes ? true : false); mark_os_window_for_close(os_window, cr);
Py_RETURN_TRUE; Py_RETURN_TRUE;
END_WITH_OS_WINDOW END_WITH_OS_WINDOW
Py_RETURN_FALSE; Py_RETURN_FALSE;
@@ -899,6 +929,17 @@ PYWRAP1(update_window_visibility) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
PYWRAP1(sync_os_window_title) {
id_type os_window_id;
PA("K", &os_window_id);
WITH_OS_WINDOW(os_window_id)
update_os_window_title(os_window);
END_WITH_OS_WINDOW
Py_RETURN_NONE;
}
static inline double static inline double
dpi_for_os_window_id(id_type os_window_id) { dpi_for_os_window_id(id_type os_window_id) {
double dpi = 0; double dpi = 0;
@@ -1112,6 +1153,7 @@ static PyMethodDef module_methods[] = {
MW(change_background_opacity, METH_VARARGS), MW(change_background_opacity, METH_VARARGS),
MW(background_opacity_of, METH_O), MW(background_opacity_of, METH_O),
MW(update_window_visibility, METH_VARARGS), MW(update_window_visibility, METH_VARARGS),
MW(sync_os_window_title, METH_VARARGS),
MW(global_font_size, METH_VARARGS), MW(global_font_size, METH_VARARGS),
MW(set_background_image, METH_VARARGS), MW(set_background_image, METH_VARARGS),
MW(os_window_font_size, METH_VARARGS), MW(os_window_font_size, METH_VARARGS),

View File

@@ -139,6 +139,7 @@ typedef struct {
} OSWindowGeometry; } OSWindowGeometry;
enum RENDER_STATE { RENDER_FRAME_NOT_REQUESTED, RENDER_FRAME_REQUESTED, RENDER_FRAME_READY }; enum RENDER_STATE { RENDER_FRAME_NOT_REQUESTED, RENDER_FRAME_REQUESTED, RENDER_FRAME_READY };
typedef enum { NO_CLOSE_REQUESTED, CONFIRMABLE_CLOSE_REQUESTED, IMPERATIVE_CLOSE_REQUESTED } CloseRequest;
typedef struct { typedef struct {
monotonic_t last_resize_event_at; monotonic_t last_resize_event_at;
@@ -183,6 +184,7 @@ typedef struct {
uint64_t render_calls; uint64_t render_calls;
id_type last_focused_counter; id_type last_focused_counter;
ssize_t gvao_idx; ssize_t gvao_idx;
CloseRequest close_request;
} OSWindow; } OSWindow;
@@ -220,9 +222,8 @@ void remove_vao(ssize_t vao_idx);
bool remove_os_window(id_type os_window_id); bool remove_os_window(id_type os_window_id);
void make_os_window_context_current(OSWindow *w); void make_os_window_context_current(OSWindow *w);
void update_os_window_references(void); void update_os_window_references(void);
void mark_os_window_for_close(OSWindow* w, bool yes); void mark_os_window_for_close(OSWindow* w, CloseRequest cr);
void update_os_window_viewport(OSWindow *window, bool); void update_os_window_viewport(OSWindow *window, bool);
bool should_os_window_close(OSWindow* w);
bool should_os_window_be_rendered(OSWindow* w); bool should_os_window_be_rendered(OSWindow* w);
void wakeup_main_loop(void); void wakeup_main_loop(void);
void swap_window_buffers(OSWindow *w); void swap_window_buffers(OSWindow *w);
@@ -276,3 +277,5 @@ void update_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled
void run_main_loop(tick_callback_fun, void*); void run_main_loop(tick_callback_fun, void*);
void stop_main_loop(void); void stop_main_loop(void);
void os_window_update_size_increments(OSWindow *window); void os_window_update_size_increments(OSWindow *window);
void set_os_window_title_from_window(Window *w, OSWindow *os_window);
void update_os_window_title(OSWindow *os_window);

View File

@@ -21,6 +21,8 @@ class TabBarData(NamedTuple):
title: str title: str
is_active: bool is_active: bool
needs_attention: bool needs_attention: bool
num_windows: int
layout_name: str
class DrawData(NamedTuple): class DrawData(NamedTuple):
@@ -56,7 +58,7 @@ def draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int)
if tab.is_active and draw_data.active_title_template is not None: if tab.is_active and draw_data.active_title_template is not None:
template = draw_data.active_title_template template = draw_data.active_title_template
try: try:
title = template.format(title=tab.title, index=index) title = template.format(title=tab.title, index=index, layout_name=tab.layout_name, num_windows=tab.num_windows)
except Exception as e: except Exception as e:
if template not in template_failures: if template not in template_failures:
template_failures.add(template) template_failures.add(template)
@@ -84,7 +86,7 @@ def draw_tab_with_separator(draw_data: DrawData, screen: Screen, tab: TabBarData
screen.cursor.fg = 0 screen.cursor.fg = 0
if not is_last: if not is_last:
screen.cursor.bg = as_rgb(color_as_int(draw_data.inactive_bg)) screen.cursor.bg = as_rgb(color_as_int(draw_data.inactive_bg))
screen.draw(draw_data.sep) screen.draw(draw_data.sep)
screen.cursor.bg = 0 screen.cursor.bg = 0
return end return end

View File

@@ -18,7 +18,7 @@ from .constants import appname, is_macos, is_wayland
from .fast_data_types import ( from .fast_data_types import (
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty, add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
next_window_id, remove_tab, remove_window, ring_bell, set_active_tab, next_window_id, remove_tab, remove_window, ring_bell, set_active_tab,
swap_tabs, x11_window_id swap_tabs, sync_os_window_title, x11_window_id
) )
from .layout import ( from .layout import (
Layout, Rect, create_layout_object_for, evict_cached_layouts Layout, Rect, create_layout_object_for, evict_cached_layouts
@@ -138,6 +138,7 @@ class Tab: # {{{
self._last_used_layout = self._current_layout_name self._last_used_layout = self._current_layout_name
self.current_layout = self.create_layout_object(layout_name) self.current_layout = self.create_layout_object(layout_name)
self._current_layout_name = layout_name self._current_layout_name = layout_name
self.mark_tab_bar_dirty()
def startup(self, session_tab: 'SessionTab') -> None: def startup(self, session_tab: 'SessionTab') -> None:
for cmd in session_tab.windows: for cmd in session_tab.windows:
@@ -183,10 +184,13 @@ class Tab: # {{{
old_active_window.focus_changed(False) old_active_window.focus_changed(False)
if new_active_window is not None: if new_active_window is not None:
new_active_window.focus_changed(True) new_active_window.focus_changed(True)
tm = self.tab_manager_ref() self.relayout_borders()
if tm is not None: self.mark_tab_bar_dirty()
self.relayout_borders()
tm.mark_tab_bar_dirty() def mark_tab_bar_dirty(self) -> None:
tm = self.tab_manager_ref()
if tm is not None:
tm.mark_tab_bar_dirty()
@property @property
def active_window(self) -> Optional[Window]: def active_window(self) -> Optional[Window]:
@@ -206,7 +210,7 @@ class Tab: # {{{
if window is self.active_window: if window is self.active_window:
tm = self.tab_manager_ref() tm = self.tab_manager_ref()
if tm is not None: if tm is not None:
tm.mark_tab_bar_dirty() tm.title_changed(self)
def on_bell(self, window: Window) -> None: def on_bell(self, window: Window) -> None:
tm = self.tab_manager_ref() tm = self.tab_manager_ref()
@@ -330,6 +334,7 @@ class Tab: # {{{
def _add_window(self, window: Window, location: Optional[str] = None) -> None: def _add_window(self, window: Window, location: Optional[str] = None) -> None:
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx, location) self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx, location)
self.relayout_borders() self.relayout_borders()
self.mark_tab_bar_dirty()
def new_window( def new_window(
self, self,
@@ -431,6 +436,7 @@ class Tab: # {{{
else: else:
self.active_window_idx = active_window_idx self.active_window_idx = active_window_idx
self.relayout_borders() self.relayout_borders()
self.mark_tab_bar_dirty()
active_window = self.active_window active_window = self.active_window
if active_window: if active_window:
self.title_changed(active_window) self.title_changed(active_window)
@@ -639,6 +645,11 @@ class TabManager: # {{{
def update_tab_bar_data(self) -> None: def update_tab_bar_data(self) -> None:
self.tab_bar.update(self.tab_bar_data) self.tab_bar.update(self.tab_bar_data)
def title_changed(self, tab: Tab) -> None:
self.mark_tab_bar_dirty()
if tab is self.active_tab:
sync_os_window_title(self.os_window_id)
def resize(self, only_tabs: bool = False) -> None: def resize(self, only_tabs: bool = False) -> None:
if not only_tabs: if not only_tabs:
if not self.tab_bar_hidden: if not self.tab_bar_hidden:
@@ -799,7 +810,10 @@ class TabManager: # {{{
if w.needs_attention: if w.needs_attention:
needs_attention = True needs_attention = True
break break
ans.append(TabBarData(title, t is at, needs_attention)) ans.append(TabBarData(
title, t is at, needs_attention,
len(t), t.current_layout.name or ''
))
return ans return ans
def activate_tab_at(self, x: int) -> None: def activate_tab_at(self, x: int) -> None:

View File

@@ -14,8 +14,8 @@ from contextlib import suppress
from functools import lru_cache from functools import lru_cache
from time import monotonic from time import monotonic
from typing import ( from typing import (
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional, Any, Callable, Dict, Generator, Iterable, List, Match, NamedTuple,
Tuple, Union, cast Optional, Tuple, Union, cast
) )
from .constants import ( from .constants import (
@@ -28,6 +28,20 @@ from .typing import AddressFamily, PopenType, Socket, StartupCtx
BASE = os.path.dirname(os.path.abspath(__file__)) BASE = os.path.dirname(os.path.abspath(__file__))
def expandvars(val: str, env: Dict[str, str] = {}) -> str:
def sub(m: Match) -> str:
key = m.group(1)
result = env.get(key)
if result is None:
result = os.environ.get(key)
if result is None:
result = m.group()
return result
return re.sub(r'\$\{(\S+?)\}', sub, val)
def load_shaders(name: str) -> Tuple[str, str]: def load_shaders(name: str) -> Tuple[str, str]:
from .fast_data_types import GLSL_VERSION from .fast_data_types import GLSL_VERSION
with open(os.path.join(BASE, '{}_vertex.glsl'.format(name))) as f: with open(os.path.join(BASE, '{}_vertex.glsl'.format(name))) as f:

View File

@@ -1,14 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
import os
import subprocess import subprocess
import sys import sys
files = [x for x in sys.argv[1:] if not x.startswith('-')] base = os.path.dirname(os.path.abspath(__file__))
files = [os.path.relpath(x, base) for x in sys.argv[1:] if not x.startswith('-')]
if not files: if not files:
raise SystemExit(subprocess.Popen(['mypy'] + sys.argv[1:]).wait()) raise SystemExit(subprocess.Popen(['mypy'] + sys.argv[1:]).wait())
output = subprocess.run('dmypy run -- --follow-imports=error --show-column-numbers --no-color-output'.split(), stdout=subprocess.PIPE).stdout output = subprocess.run('dmypy run -- --follow-imports=error --show-column-numbers --no-color-output'.split(), stdout=subprocess.PIPE).stdout
q = files[0] + ':' q = files[0] + ':'
rc = 0
for line in output.decode('utf-8').splitlines(): for line in output.decode('utf-8').splitlines():
if line.startswith(q): if line.startswith(q):
print(line) print(line)
rc = 1
raise SystemExit(rc)

View File

@@ -10,7 +10,7 @@ set tabstop=4
set shiftwidth=4 set shiftwidth=4
set softtabstop=0 set softtabstop=0
set smarttab set smarttab
python <<endpython python3 <<endpython
import sys import sys
sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('.'))
import kitty import kitty

View File

@@ -349,7 +349,7 @@ def run_tool(cmd: Union[str, List[str]], desc: Optional[str] = None) -> None:
raise SystemExit(ret) raise SystemExit(ret)
def get_vcs_rev_defines() -> List[str]: def get_vcs_rev_defines(env: Env) -> List[str]:
ans = [] ans = []
if os.path.exists('.git'): if os.path.exists('.git'):
try: try:
@@ -368,7 +368,7 @@ def get_vcs_rev_defines() -> List[str]:
return ans return ans
SPECIAL_SOURCES: Dict[str, Tuple[str, Union[List[str], Callable[[], Union[List[str], Iterator[str]]]]]] = { SPECIAL_SOURCES: Dict[str, Tuple[str, Union[List[str], Callable[[Env], Union[List[str], Iterator[str]]]]]] = {
'kitty/parser_dump.c': ('kitty/parser.c', ['DUMP_COMMANDS']), 'kitty/parser_dump.c': ('kitty/parser.c', ['DUMP_COMMANDS']),
'kitty/data-types.c': ('kitty/data-types.c', get_vcs_rev_defines), 'kitty/data-types.c': ('kitty/data-types.c', get_vcs_rev_defines),
} }
@@ -557,7 +557,7 @@ def compile_c_extension(
if is_special: if is_special:
src, defines_ = SPECIAL_SOURCES[src] src, defines_ = SPECIAL_SOURCES[src]
if callable(defines_): if callable(defines_):
defines = defines_() defines = defines_(kenv)
cppflags.extend(map(define, defines)) cppflags.extend(map(define, defines))
else: else:
cppflags.extend(map(define, defines_)) cppflags.extend(map(define, defines_))