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

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

View File

@@ -1133,7 +1133,7 @@ is_ascii_control_char(char x) {
break;
}
_glfwInputScroll(window, deltaX, deltaY, flags);
_glfwInputScroll(window, deltaX, deltaY, flags, translateFlags([event modifierFlags]));
}
- (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:
ans = env.copy()
ans.cflags.append('-fpic')
ans.cflags.append('-fPIC')
ans.cppflags.append('-D_GLFW_' + module.upper())
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:
ans.sources.append(wayland_protocol_file_name(p))
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.ldpaths.extend(pkg_config(dep, '--libs'))
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
* in progress, value 4 for momentum scrolling ended, value 5 for momentum
* scrolling cancelled and value 6 if scrolling may begin soon.
* @param[int] mods The keyboard modifiers
*
* @sa @ref scrolling
* @sa @ref glfwSetScrollCallback
@@ -1559,7 +1560,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
*
* @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.
*

4
glfw/input.c vendored
View File

@@ -300,10 +300,10 @@ void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
// 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)
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

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 _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 _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
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)
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 = {

4
glfw/x11_init.c vendored
View File

@@ -502,8 +502,10 @@ static Window createHelperWindow(void)
// 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;
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
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)
_glfwInputScroll(window, 0.0, -1.0, 0);
_glfwInputScroll(window, 0.0, -1.0, 0, mods);
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)
_glfwInputScroll(window, -1.0, 0.0, 0);
_glfwInputScroll(window, -1.0, 0.0, 0, mods);
else
{
@@ -1408,12 +1408,18 @@ static void processEvent(XEvent *event)
if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
{
Window dummy;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display,
window->x11.parent,
_glfw.x11.root,
xpos, ypos,
&xpos, &ypos,
&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)
@@ -1550,17 +1556,21 @@ static void processEvent(XEvent *event)
const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
const int yabs = (event->xclient.data.l[2]) & 0xffff;
Window dummy;
int xpos, ypos;
int xpos = 0, ypos = 0;
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
return;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display,
_glfw.x11.root,
window->x11.handle,
xabs, yabs,
&xpos, &ypos,
&dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success)
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get DND event position");
_glfwInputCursorPos(window, xpos, ypos);
@@ -1988,10 +1998,14 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{
Window dummy;
int x, y;
int x = 0, y = 0;
_glfwGrabErrorHandlerX11();
XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
0, 0, &x, &y, &dummy);
_glfwReleaseErrorHandlerX11();
if (_glfw.x11.errorCode != Success)
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get window position");
if (xpos)
*xpos = x;

View File

@@ -336,7 +336,13 @@ class Boss:
if not getattr(err, 'hide_traceback', False):
response['tb'] = traceback.format_exc()
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
def remote_control(self, *args: str) -> None:
@@ -355,15 +361,16 @@ class Boss:
self.show_error(_('remote_control mapping failed'), str(e))
def peer_message_received(self, msg_bytes: bytes) -> Optional[bytes]:
msg = msg_bytes.decode('utf-8')
cmd_prefix = '\x1bP@kitty-cmd'
if msg.startswith(cmd_prefix):
cmd = msg[len(cmd_prefix):-2]
cmd_prefix = b'\x1bP@kitty-cmd'
terminator = b'\x1b\\'
if msg_bytes.startswith(cmd_prefix) and msg_bytes.endswith(terminator):
cmd = msg_bytes[len(cmd_prefix):-len(terminator)].decode('utf-8')
response = self._handle_remote_command(cmd, from_peer=True)
if response is None:
return None
return (cmd_prefix + json.dumps(response) + '\x1b\\').encode('utf-8')
data = json.loads(msg)
return cmd_prefix + json.dumps(response).encode('utf-8') + terminator
data = json.loads(msg_bytes.decode('utf-8'))
if isinstance(data, dict) and data.get('cmd') == 'new_instance':
from .cli_stub import CLIOptions
startup_id = data.get('startup_id')
@@ -717,6 +724,9 @@ class Boss:
text = '\n'.join(parse_uri_list(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:
self.cached_values['window-size'] = viewport_width, viewport_height
tm = self.os_window_map.pop(os_window_id, None)

View File

@@ -523,17 +523,6 @@ change_menubar_title(PyObject *title UNUSED) {
#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
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
@@ -577,7 +566,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
*active_window_id = w->id;
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;
update_window_title(w, os_window);
set_os_window_title_from_window(w, os_window);
*active_window_bg = window_bg;
} 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;
@@ -625,15 +614,6 @@ render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_windo
#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
draw_resizing_text(OSWindow *w) {
char text[32] = {0};
@@ -865,25 +845,42 @@ process_pending_resizes(monotonic_t now) {
static inline 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
process_pending_closes(ChildMonitor *self) {
global_state.has_pending_closes = false;
bool has_open_windows = false;
for (size_t w = global_state.num_os_windows; w > 0; w--) {
OSWindow *os_window = global_state.os_windows + w - 1;
if (should_os_window_close(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);
} else has_open_windows = true;
switch(os_window->close_request) {
case NO_CLOSE_REQUESTED:
has_open_windows = true;
break;
case CONFIRMABLE_CLOSE_REQUESTED:
os_window->close_request = NO_CLOSE_REQUESTED;
call_boss(confirm_os_window_close, "K", os_window->id);
if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) {
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__
if (!OPT(macos_quit_when_last_window_closed)) {
if (!has_open_windows && !application_quit_requested()) has_open_windows = true;
@@ -1279,7 +1276,7 @@ typedef struct {
char *data;
size_t capacity, used;
int fd;
bool finished, close_socket;
bool finished, close_socket, is_peer_command;
} PeerReadData;
static PeerReadData empty_prd = {.fd = -1, 0};
@@ -1329,6 +1326,41 @@ accept_peer(int listen_fd, bool shutting_down) {
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
read_from_peer(ChildMonitor *self, int s) {
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; }
if (rd->fd == s) {
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->data = realloc(rd->data, rd->capacity);
if (!rd->data) failed("Out of memory");
}
ssize_t n = recv(s, rd->data + rd->used, rd->capacity - rd->used, 0);
if (n == 0) {
while (rd->is_peer_command) dispatch_peer_command(self, rd, s);
read_finished = true; rd->finished = true;
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 = rd->data; rd->data = NULL; m->sz = rd->used; m->fd = s;
children_mutex(unlock);
wakeup_main_loop();
if (rd->used) queue_peer_message(self, rd->data, rd->used, s);
else free(rd->data);
rd->data = NULL;
} else if (n < 0) {
if (errno != EINTR) {
perror("Error reading from talk peer");
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;
}
}

View File

@@ -409,7 +409,10 @@ def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str, e
elif otype == 'list':
t = 'typing.Sequence[str]'
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-'):
t = 'bool'
else:

View File

@@ -9,7 +9,7 @@ from typing import (
Set, Tuple, Union, get_type_hints
)
from .utils import to_bool
from .utils import Choice, to_bool
def to_string(x: str) -> str:
@@ -56,6 +56,8 @@ class Option:
if type(self.option_type) is 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)
try:
rettype = th['return']

View File

@@ -68,17 +68,21 @@ def python_string(text: str) -> str:
return ans
def choices(*choices: str) -> Callable[[str], str]:
defval = choices[0]
uc = frozenset(choices)
class Choice:
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()
if x not in uc:
x = defval
if x not in self.all_choices:
x = self.defval
return x
return choice
def choices(*choices: str) -> Choice:
return Choice(choices)
def parse_line(

View File

@@ -9,8 +9,8 @@ import sys
from contextlib import contextmanager, suppress
from functools import partial
from typing import (
Any, Callable, Dict, Generator, Iterable, List, Match, NamedTuple,
Optional, Sequence, Set, Tuple, Type, Union
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
Sequence, Set, Tuple, Type, Union
)
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 .options_stub import Options as OptionsStub
from .typing import EdgeLiteral, TypedDict
from .utils import log_error
from .utils import expandvars, log_error
KeySpec = Tuple[int, bool, int]
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
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
def handle_env(key: str, val: str, ans: Dict[str, Any]) -> None:
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=_('''
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
any character that is marked as an alphanumeric character in the unicode
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,
use something like: :code:`{index}: {title}`. Useful
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=_('''
Template to use for active tabs, if not specified falls back

View File

@@ -20,7 +20,7 @@ class Version(NamedTuple):
appname: str = 'kitty'
version: Version = Version(0, 17, 3)
version: Version = Version(0, 17, 4)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
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);
}
if (num_cells && (center_glyph || (num_cells == 2 && *was_colored))) {
// center glyphs (two cell emoji and PUA glyphs)
CGFloat delta = (((CGFloat)canvas_width - br.size.width) / 2.f) - br.origin.x;
// center glyphs (two cell emoji, PUA glyphs, ligatures, etc)
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));
}
return true;

View File

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

View File

@@ -30,18 +30,22 @@ static void* libsn_handle = NULL;
static PyObject*
init_x11_startup_notification(PyObject UNUSED *self, PyObject *args) {
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) {
done = true;
libsn_handle = dlopen(libname, RTLD_LAZY);
if (libsn_handle == NULL) libsn_handle = dlopen(libname2, RTLD_LAZY);
if (libsn_handle == NULL) libsn_handle = dlopen(libname3, RTLD_LAZY);
const char* libnames[] = {
"libstartup-notification-1.so",
// 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) {
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;
}
dlerror(); /* Clear any existing error */
@@ -105,18 +109,22 @@ load_libcanberra_functions(void) {
static 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;
if (done) return;
done = true;
libcanberra_handle = dlopen(libname, RTLD_LAZY);
if (libcanberra_handle == NULL) libcanberra_handle = dlopen(libname2, RTLD_LAZY);
if (libcanberra_handle == NULL) libcanberra_handle = dlopen(libname3, RTLD_LAZY);
const char* libnames[] = {
"libcanberra.so",
// 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) {
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;
}
load_libcanberra_functions();

View File

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

View File

@@ -1004,14 +1004,20 @@ merge_groups_for_pua_space_ligature(void) {
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
split_run_at_offset(index_type cursor_offset, index_type *left, index_type *right) {
*left = 0; *right = 0;
for (unsigned idx = 0; idx < G(group_idx) + 1; idx++) {
Group *group = G(groups) + idx;
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 (group->num_cells > 1 && group->has_special_glyph && (first_cell->attrs & WIDTH_MASK) == 1) {
if (is_group_calt_ligature(group)) {
// likely a calt ligature
*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,
// so special case it: https://github.com/kovidgoyal/kitty/issues/1225
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);
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
* in progress, value 4 for momentum scrolling ended, value 5 for momentum
* scrolling cancelled and value 6 if scrolling may begin soon.
* @param[int] mods The keyboard modifiers
*
* @sa @ref scrolling
* @sa @ref glfwSetScrollCallback
@@ -1322,7 +1323,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
*
* @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.
*

View File

@@ -153,6 +153,10 @@ static void
window_close_callback(GLFWwindow* window) {
if (!set_callback_window(window)) return;
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();
global_state.callback_os_window = NULL;
}
@@ -287,12 +291,12 @@ cursor_pos_callback(GLFWwindow *w, double x, double y) {
}
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;
show_mouse_cursor(w);
monotonic_t now = monotonic();
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();
global_state.callback_os_window = NULL;
}
@@ -323,23 +327,17 @@ window_focus_callback(GLFWwindow *w, int focused) {
static int
drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) {
if (!set_callback_window(w)) return 0;
#define RETURN(x) { global_state.callback_os_window = NULL; return x; }
if (!data) {
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") == 0) return 1;
return 0;
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") == 0) RETURN(1);
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();
/* PyObject *s = PyTuple_New(count); */
/* if (s) { */
/* 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;
RETURN(0);
#undef RETURN
}
// }}}
@@ -979,12 +977,6 @@ wakeup_main_loop() {
glfwPostEmptyEvent();
}
void
mark_os_window_for_close(OSWindow* w, bool yes) {
global_state.has_pending_closes = true;
glfwSetWindowShouldClose(w->handle, yes);
}
bool
should_os_window_be_rendered(OSWindow* w) {
return (
@@ -994,11 +986,6 @@ should_os_window_be_rendered(OSWindow* w) {
) ? false : true;
}
bool
should_os_window_close(OSWindow* w) {
return glfwWindowShouldClose(w->handle) ? true : false;
}
static PyObject*
primary_monitor_size(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();

View File

@@ -939,7 +939,7 @@ static PyMethodDef methods[] = {
};
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},
};

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,
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
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
to work with :option:`launch --stdin-source` to copy data to the system
clipboard or primary selection.
@@ -275,8 +275,8 @@ def launch(boss: Boss, opts: LaunchCLIOptions, args: List[str], target_tab: Opti
kw['cmd'] = final_cmd
if opts.type == 'overlay' and active and not active.overlay_window_id:
kw['overlay_for'] = active.id
if opts.stdin_source and opts.stdin_source != 'none':
q = opts.stdin_source
if opts.stdin_source != 'none':
q = str(opts.stdin_source)
if opts.stdin_add_formatting:
if q in ('@screen', '@screen_scrollback', '@alternate', '@alternate_scrollback'):
q = '@ansi_' + q[1:]

View File

@@ -7,7 +7,7 @@ import os
import shutil
import sys
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 .boss import Boss
@@ -15,7 +15,7 @@ from .child import set_default_env
from .cli import create_opts, parse_args
from .cli_stub import CLIOptions
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 (
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
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 .options_stub import Options as OptionsStub
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
)
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;
for (unsigned int i = 0; i < t->num_windows; i++) {
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;
for (unsigned int i = 0; i < t->num_windows; i++) {
Window *w = t->windows + i;
double d = distance_to_window(w);
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }
if (w->visible) {
double d = distance_to_window(w);
if (d < closest_distance) { ans = w; closest_distance = d; *window_idx = i; }
}
}
}
return ans;
@@ -610,7 +613,7 @@ mouse_event(int button, int modifiers, int action) {
}
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;
static id_type window_for_momentum_scroll = 0;
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);
} else {
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) {
mouse_event_buf[sz] = 0;
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);
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
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) draw_graphics(

View File

@@ -43,8 +43,9 @@ GlobalState global_state = {{0}};
if (osw->tabs[t].id == tab_id) { \
Tab *tab = osw->tabs + t; \
for (size_t w = 0; w < tab->num_windows; w++) { \
Window *window = tab->windows + w;
#define END_WITH_WINDOW break; }}}}}
if (tab->windows[w].id == window_id) { \
Window *window = tab->windows + w;
#define END_WITH_WINDOW window_found = 1; break; }}}}}}
#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;
}
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
destroy_window(Window *w) {
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 {{{
#define PYWRAP0(name) static PyObject* py##name(PYNOARG)
@@ -783,10 +813,10 @@ PYWRAP1(os_window_has_background_image) {
PYWRAP1(mark_os_window_for_close) {
id_type os_window_id;
int yes = 1;
PA("K|p", &os_window_id, &yes);
CloseRequest cr = IMPERATIVE_CLOSE_REQUESTED;
PA("K|i", &os_window_id, &cr);
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;
END_WITH_OS_WINDOW
Py_RETURN_FALSE;
@@ -899,6 +929,17 @@ PYWRAP1(update_window_visibility) {
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
dpi_for_os_window_id(id_type os_window_id) {
double dpi = 0;
@@ -1112,6 +1153,7 @@ static PyMethodDef module_methods[] = {
MW(change_background_opacity, METH_VARARGS),
MW(background_opacity_of, METH_O),
MW(update_window_visibility, METH_VARARGS),
MW(sync_os_window_title, METH_VARARGS),
MW(global_font_size, METH_VARARGS),
MW(set_background_image, METH_VARARGS),
MW(os_window_font_size, METH_VARARGS),

View File

@@ -139,6 +139,7 @@ typedef struct {
} OSWindowGeometry;
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 {
monotonic_t last_resize_event_at;
@@ -183,6 +184,7 @@ typedef struct {
uint64_t render_calls;
id_type last_focused_counter;
ssize_t gvao_idx;
CloseRequest close_request;
} OSWindow;
@@ -220,9 +222,8 @@ void remove_vao(ssize_t vao_idx);
bool remove_os_window(id_type os_window_id);
void make_os_window_context_current(OSWindow *w);
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);
bool should_os_window_close(OSWindow* w);
bool should_os_window_be_rendered(OSWindow* w);
void wakeup_main_loop(void);
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 stop_main_loop(void);
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
is_active: bool
needs_attention: bool
num_windows: int
layout_name: str
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:
template = draw_data.active_title_template
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:
if template not in template_failures:
template_failures.add(template)
@@ -84,7 +86,7 @@ def draw_tab_with_separator(draw_data: DrawData, screen: Screen, tab: TabBarData
screen.cursor.fg = 0
if not is_last:
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
return end

View File

@@ -18,7 +18,7 @@ from .constants import appname, is_macos, is_wayland
from .fast_data_types import (
add_tab, attach_window, detach_window, get_boss, mark_tab_bar_dirty,
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 (
Layout, Rect, create_layout_object_for, evict_cached_layouts
@@ -138,6 +138,7 @@ class Tab: # {{{
self._last_used_layout = self._current_layout_name
self.current_layout = self.create_layout_object(layout_name)
self._current_layout_name = layout_name
self.mark_tab_bar_dirty()
def startup(self, session_tab: 'SessionTab') -> None:
for cmd in session_tab.windows:
@@ -183,10 +184,13 @@ class Tab: # {{{
old_active_window.focus_changed(False)
if new_active_window is not None:
new_active_window.focus_changed(True)
tm = self.tab_manager_ref()
if tm is not None:
self.relayout_borders()
tm.mark_tab_bar_dirty()
self.relayout_borders()
self.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
def active_window(self) -> Optional[Window]:
@@ -206,7 +210,7 @@ class Tab: # {{{
if window is self.active_window:
tm = self.tab_manager_ref()
if tm is not None:
tm.mark_tab_bar_dirty()
tm.title_changed(self)
def on_bell(self, window: Window) -> None:
tm = self.tab_manager_ref()
@@ -330,6 +334,7 @@ class Tab: # {{{
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.relayout_borders()
self.mark_tab_bar_dirty()
def new_window(
self,
@@ -431,6 +436,7 @@ class Tab: # {{{
else:
self.active_window_idx = active_window_idx
self.relayout_borders()
self.mark_tab_bar_dirty()
active_window = self.active_window
if active_window:
self.title_changed(active_window)
@@ -639,6 +645,11 @@ class TabManager: # {{{
def update_tab_bar_data(self) -> None:
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:
if not only_tabs:
if not self.tab_bar_hidden:
@@ -799,7 +810,10 @@ class TabManager: # {{{
if w.needs_attention:
needs_attention = True
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
def activate_tab_at(self, x: int) -> None:

View File

@@ -14,8 +14,8 @@ from contextlib import suppress
from functools import lru_cache
from time import monotonic
from typing import (
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
Tuple, Union, cast
Any, Callable, Dict, Generator, Iterable, List, Match, NamedTuple,
Optional, Tuple, Union, cast
)
from .constants import (
@@ -28,6 +28,20 @@ from .typing import AddressFamily, PopenType, Socket, StartupCtx
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]:
from .fast_data_types import GLSL_VERSION
with open(os.path.join(BASE, '{}_vertex.glsl'.format(name))) as f:

View File

@@ -1,14 +1,19 @@
#!/usr/bin/env python
import os
import subprocess
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:
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
q = files[0] + ':'
rc = 0
for line in output.decode('utf-8').splitlines():
if line.startswith(q):
print(line)
rc = 1
raise SystemExit(rc)

View File

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

View File

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