Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d32202b3a | ||
|
|
4f4d562670 | ||
|
|
28fc9c69da | ||
|
|
6edf00e699 | ||
|
|
a0d6445dda | ||
|
|
e74797037c | ||
|
|
3fb125dba7 | ||
|
|
3c8640f9ad | ||
|
|
2e63a4c83f | ||
|
|
9da5bee178 | ||
|
|
2fc6f3b542 | ||
|
|
4754f42e51 | ||
|
|
941fe918fb | ||
|
|
f4e82e972e | ||
|
|
f66dbf111d | ||
|
|
1a2da5c49d | ||
|
|
8327dabb29 | ||
|
|
195ea6c140 | ||
|
|
fcc0707174 | ||
|
|
aa9c3cd634 | ||
|
|
b541341681 | ||
|
|
e5d50aca6d | ||
|
|
b738f4d4ff | ||
|
|
5c38aabf0e | ||
|
|
03e9ed6947 | ||
|
|
63493fad22 | ||
|
|
696b857f91 | ||
|
|
53ae2bbe0d | ||
|
|
01069e919d | ||
|
|
fc0095f505 | ||
|
|
421528e130 | ||
|
|
7b70356515 | ||
|
|
e803505aad | ||
|
|
ad1ff455a0 | ||
|
|
7692cb5c3d | ||
|
|
a94e208db3 | ||
|
|
ba9d5cf3ff | ||
|
|
0dff0d03f9 | ||
|
|
c92aca5d19 | ||
|
|
5762baeed7 | ||
|
|
601cf01222 | ||
|
|
0eef48b516 | ||
|
|
3c1954f329 |
@@ -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]
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
3
glfw/glfw3.h
vendored
@@ -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
4
glfw/input.c
vendored
@@ -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
2
glfw/internal.h
vendored
@@ -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
2
glfw/wl_init.c
vendored
@@ -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
4
glfw/x11_init.c
vendored
@@ -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
26
glfw/x11_window.c
vendored
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
3
kitty/glfw-wrapper.h
generated
@@ -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.
|
||||
*
|
||||
|
||||
41
kitty/glfw.c
41
kitty/glfw.c
@@ -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();
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
|
||||
@@ -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:]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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--) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
6
setup.py
6
setup.py
@@ -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_))
|
||||
|
||||
Reference in New Issue
Block a user