mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-04 13:42:33 +02:00
Wayland: Remove the 120ms penalty from waiting for window creation
When showing the window we loop in the wayland backend using a temporary buffer of blank pixels to force the compositor to finish setting up the top level surface pronto. TODO: Set the color of the temmporary buffer to the background color
This commit is contained in:
@@ -316,7 +316,6 @@ def generate_wrappers(glfw_header: str) -> None:
|
||||
void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle)
|
||||
void glfwWaylandSetupLayerShellForNextWindow(GLFWLayerShellConfig c)
|
||||
pid_t glfwWaylandCompositorPID(void)
|
||||
bool glfwWaylandWindowFullyCreated(GLFWwindow *handle)
|
||||
unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \
|
||||
const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data)
|
||||
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)
|
||||
|
||||
1
glfw/wl_platform.h
vendored
1
glfw/wl_platform.h
vendored
@@ -172,6 +172,7 @@ typedef struct _GLFWwindowWayland
|
||||
struct {
|
||||
bool surface_configured, fractional_scale_received, preferred_scale_received;
|
||||
} once;
|
||||
struct wl_buffer *temp_buffer_used_during_window_creation;
|
||||
struct {
|
||||
GLFWLayerShellConfig config;
|
||||
struct zwlr_layer_surface_v1* zwlr_layer_surface_v1;
|
||||
|
||||
69
glfw/wl_window.c
vendored
69
glfw/wl_window.c
vendored
@@ -373,6 +373,10 @@ _glfwWaylandAfterBufferSwap(_GLFWwindow* window) {
|
||||
// this is not really needed, since I think eglSwapBuffers() calls wl_surface_commit()
|
||||
// but lets be safe. See https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/egl/drivers/dri2/platform_wayland.c#L1510
|
||||
wl_surface_commit(window->wl.surface);
|
||||
if (window->wl.temp_buffer_used_during_window_creation) {
|
||||
wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
|
||||
window->wl.temp_buffer_used_during_window_creation = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,6 +752,51 @@ apply_xdg_configure_changes(_GLFWwindow *window) {
|
||||
window->wl.pending_state = 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
attach_temp_buffer_during_window_creation(_GLFWwindow *window) {
|
||||
int width, height;
|
||||
_glfwPlatformGetFramebufferSize(window, &width, &height);
|
||||
const size_t size = 4 * width * height;
|
||||
int fd = createAnonymousFile(size);
|
||||
if (fd < 0) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create anonymouse file");
|
||||
return false;
|
||||
}
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(_glfw.wl.shm, fd, size);
|
||||
if (!pool) {
|
||||
close(fd);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create wl_shm_pool of size: %zu", size);
|
||||
return false;
|
||||
}
|
||||
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_ARGB8888);
|
||||
wl_shm_pool_destroy(pool); close(fd);
|
||||
if (!buffer) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create wl_buffer of size: %zu", size);
|
||||
return false;
|
||||
}
|
||||
if (window->wl.temp_buffer_used_during_window_creation) wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
|
||||
window->wl.temp_buffer_used_during_window_creation = buffer;
|
||||
wl_surface_set_buffer_scale(window->wl.surface, window->wl.fractional_scale ? 1: _glfwWaylandIntegerWindowScale(window));
|
||||
wl_surface_attach(window->wl.surface, buffer, 0, 0);
|
||||
if (window->wl.wp_viewport) wp_viewport_set_destination(window->wl.wp_viewport, window->wl.width, window->wl.height);
|
||||
wl_surface_commit(window->wl.surface);
|
||||
debug("Attached temp buffer during window creation of size: %dx%d\n", width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
loop_till_window_fully_created(_GLFWwindow *window) {
|
||||
if (!window->wl.window_fully_created) {
|
||||
monotonic_t start = monotonic();
|
||||
while (!window->wl.window_fully_created && monotonic() - start < ms_to_monotonic_t(300)) {
|
||||
if (wl_display_roundtrip(_glfw.wl.display) == -1) {
|
||||
window->wl.window_fully_created = true;
|
||||
}
|
||||
}
|
||||
window->wl.window_fully_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xdgSurfaceHandleConfigure(void* data, struct xdg_surface* surface, uint32_t serial) {
|
||||
// The poorly documented pattern Wayland requires is:
|
||||
@@ -763,6 +812,9 @@ xdgSurfaceHandleConfigure(void* data, struct xdg_surface* surface, uint32_t seri
|
||||
xdg_surface_ack_configure(surface, serial);
|
||||
debug("XDG surface configure event received and acknowledged\n");
|
||||
apply_xdg_configure_changes(window);
|
||||
if (!window->wl.window_fully_created) {
|
||||
if (!attach_temp_buffer_during_window_creation(window)) window->wl.window_fully_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdgSurfaceListener = {
|
||||
@@ -877,6 +929,9 @@ layer_surface_handle_configure(void* data, struct zwlr_layer_surface_v1* surface
|
||||
layer_set_properties(window);
|
||||
}
|
||||
commit_window_surface_if_safe(window);
|
||||
if (!window->wl.window_fully_created) {
|
||||
if (!attach_temp_buffer_during_window_creation(window)) window->wl.window_fully_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1200,7 +1255,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
|
||||
window->wl.monitorsCount = 0;
|
||||
window->wl.monitorsSize = 1;
|
||||
|
||||
if (window->wl.visible) loop_till_window_fully_created(window);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1220,6 +1275,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocusId = 0;
|
||||
}
|
||||
|
||||
if (window->wl.temp_buffer_used_during_window_creation)
|
||||
wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
|
||||
|
||||
if (window->wl.wp_fractional_scale_v1)
|
||||
wp_fractional_scale_v1_destroy(window->wl.wp_fractional_scale_v1);
|
||||
if (window->wl.wp_viewport)
|
||||
@@ -1422,10 +1480,10 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
|
||||
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (!window->wl.visible)
|
||||
{
|
||||
if (!window->wl.visible) {
|
||||
create_window_desktop_surface(window);
|
||||
window->wl.visible = true;
|
||||
loop_till_window_fully_created(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2585,8 +2643,3 @@ GLFWAPI void glfwWaylandSetupLayerShellForNextWindow(GLFWLayerShellConfig c) {
|
||||
if (layer_shell_config_for_next_window.output_name && !layer_shell_config_for_next_window.output_name[0]) layer_shell_config_for_next_window.output_name = NULL;
|
||||
if (layer_shell_config_for_next_window.output_name) layer_shell_config_for_next_window.output_name = strdup(layer_shell_config_for_next_window.output_name);
|
||||
}
|
||||
|
||||
GLFWAPI bool glfwWaylandWindowFullyCreated(GLFWwindow *handle) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
return window->wl.window_fully_created;
|
||||
}
|
||||
|
||||
@@ -1574,5 +1574,4 @@ def replace_c0_codes_except_nl_space_tab(text: str) -> str:...
|
||||
def replace_c0_codes_except_nl_space_tab(text: Union[bytes, memoryview, bytearray]) -> bytes:...
|
||||
def terminfo_data() -> bytes:...
|
||||
def wayland_compositor_pid() -> int:...
|
||||
def window_fully_created(kitty_window_id: int) -> bool: ...
|
||||
def monotonic() -> float: ...
|
||||
|
||||
3
kitty/glfw-wrapper.c
generated
3
kitty/glfw-wrapper.c
generated
@@ -485,9 +485,6 @@ load_glfw(const char* path) {
|
||||
*(void **) (&glfwWaylandCompositorPID_impl) = dlsym(handle, "glfwWaylandCompositorPID");
|
||||
if (glfwWaylandCompositorPID_impl == NULL) dlerror(); // clear error indicator
|
||||
|
||||
*(void **) (&glfwWaylandWindowFullyCreated_impl) = dlsym(handle, "glfwWaylandWindowFullyCreated");
|
||||
if (glfwWaylandWindowFullyCreated_impl == NULL) dlerror(); // clear error indicator
|
||||
|
||||
*(void **) (&glfwDBusUserNotify_impl) = dlsym(handle, "glfwDBusUserNotify");
|
||||
if (glfwDBusUserNotify_impl == NULL) dlerror(); // clear error indicator
|
||||
|
||||
|
||||
4
kitty/glfw-wrapper.h
generated
4
kitty/glfw-wrapper.h
generated
@@ -2316,10 +2316,6 @@ typedef pid_t (*glfwWaylandCompositorPID_func)(void);
|
||||
GFW_EXTERN glfwWaylandCompositorPID_func glfwWaylandCompositorPID_impl;
|
||||
#define glfwWaylandCompositorPID glfwWaylandCompositorPID_impl
|
||||
|
||||
typedef bool (*glfwWaylandWindowFullyCreated_func)(GLFWwindow*);
|
||||
GFW_EXTERN glfwWaylandWindowFullyCreated_func glfwWaylandWindowFullyCreated_impl;
|
||||
#define glfwWaylandWindowFullyCreated glfwWaylandWindowFullyCreated_impl
|
||||
|
||||
typedef unsigned long long (*glfwDBusUserNotify_func)(const char*, const char*, const char*, const char*, const char*, int32_t, GLFWDBusnotificationcreatedfun, void*);
|
||||
GFW_EXTERN glfwDBusUserNotify_func glfwDBusUserNotify_impl;
|
||||
#define glfwDBusUserNotify glfwDBusUserNotify_impl
|
||||
|
||||
11
kitty/glfw.c
11
kitty/glfw.c
@@ -2210,16 +2210,6 @@ make_x11_window_a_dock_window(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
window_fully_created(PyObject *self UNUSED, PyObject *wid) {
|
||||
// On Wayland a window does not receive its final size by the time glfwCreateWindow returns
|
||||
if (!global_state.is_wayland) Py_RETURN_TRUE;
|
||||
OSWindow *w = os_window_for_kitty_window(PyLong_AsUnsignedLongLong(wid));
|
||||
if (!w || !w->handle) Py_RETURN_FALSE;
|
||||
if (glfwWaylandWindowFullyCreated(w->handle)) Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
@@ -2239,7 +2229,6 @@ static PyMethodDef module_methods[] = {
|
||||
METHODB(glfw_window_hint, METH_VARARGS),
|
||||
METHODB(x11_display, METH_NOARGS),
|
||||
METHODB(wayland_compositor_pid, METH_NOARGS),
|
||||
METHODB(window_fully_created, METH_O),
|
||||
METHODB(get_click_interval, METH_NOARGS),
|
||||
METHODB(x11_window_id, METH_O),
|
||||
METHODB(make_x11_window_a_dock_window, METH_VARARGS),
|
||||
|
||||
@@ -86,7 +86,6 @@ from .fast_data_types import (
|
||||
update_window_title,
|
||||
update_window_visibility,
|
||||
wakeup_main_loop,
|
||||
window_fully_created,
|
||||
)
|
||||
from .keys import keyboard_mode_name, mod_mask
|
||||
from .notify import (
|
||||
@@ -839,14 +838,14 @@ class Window:
|
||||
boss = get_boss()
|
||||
boss.child_monitor.resize_pty(self.id, *current_pty_size)
|
||||
self.last_resized_at = monotonic()
|
||||
if not self.child_is_launched and window_fully_created(self.id):
|
||||
if not self.child_is_launched:
|
||||
self.child.mark_terminal_ready()
|
||||
self.child_is_launched = True
|
||||
update_ime_position = True
|
||||
if boss.args.debug_rendering:
|
||||
now = monotonic()
|
||||
print(f'[{now:.3f}] Child launched {now - self.started_at:.2f} seconds after window creation', file=sys.stderr)
|
||||
if boss.args.debug_rendering and self.child_is_launched:
|
||||
print(f'[{now:.3f}] Child launched', file=sys.stderr)
|
||||
elif boss.args.debug_rendering:
|
||||
print(f'[{monotonic():.3f}] SIGWINCH sent to child in window: {self.id} with size: {current_pty_size}', file=sys.stderr)
|
||||
self.last_reported_pty_size = current_pty_size
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user