diff --git a/kitty/boss.py b/kitty/boss.py index b1be0507e..c341ff9da 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -12,7 +12,8 @@ from weakref import WeakValueDictionary from .cli import create_opts, parse_args from .config import ( - MINIMUM_FONT_SIZE, initial_window_size_func, prepare_config_file_for_editing + MINIMUM_FONT_SIZE, initial_window_size_func, + prepare_config_file_for_editing ) from .config_utils import to_cmdline from .constants import ( @@ -23,7 +24,7 @@ from .fast_data_types import ( create_os_window, current_os_window, destroy_global_data, get_clipboard_string, glfw_post_empty_event, global_font_size, mark_os_window_for_close, os_window_font_size, set_clipboard_string, - set_in_sequence_mode, show_window, toggle_fullscreen + set_in_sequence_mode, toggle_fullscreen ) from .keys import get_shortcut, shortcut_matches from .remote_control import handle_cmd @@ -31,9 +32,9 @@ from .rgb import Color, color_from_int from .session import create_session from .tabs import SpecialWindow, SpecialWindowInstance, TabManager from .utils import ( - end_startup_notification, get_primary_selection, init_startup_notification, - log_error, open_url, parse_address_spec, remove_socket_file, safe_print, - set_primary_selection, single_instance + get_primary_selection, log_error, open_url, parse_address_spec, + remove_socket_file, safe_print, set_primary_selection, single_instance, + startup_notification_handler ) @@ -103,12 +104,11 @@ class Boss: if os_window_id is None: opts_for_size = opts_for_size or self.opts cls = wclass or self.args.cls or appname - os_window_id = create_os_window(initial_window_size_func(opts_for_size, self.cached_values), appname, wname or self.args.name or cls, cls) - if startup_id: - ctx = init_startup_notification(os_window_id, startup_id) - show_window(os_window_id) - if startup_id: - end_startup_notification(ctx) + with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback: + os_window_id = create_os_window( + initial_window_size_func(opts_for_size, self.cached_values), + pre_show_callback, + appname, wname or self.args.name or cls, cls) tm = TabManager(os_window_id, self.opts, self.args, startup_session) self.os_window_map[os_window_id] = tm return os_window_id diff --git a/kitty/glfw.c b/kitty/glfw.c index 890f62695..131a95221 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -342,12 +342,22 @@ set_titlebar_color(OSWindow *w, color_type color) { } } +static inline PyObject* +native_window_handle(GLFWwindow *w) { +#ifdef __APPLE__ + void *ans = glfwGetCocoaWindow(w); + return PyLong_FromVoidPtr(ans); +#endif + if (glfwGetX11Window) return PyLong_FromLong((long)glfwGetX11Window(w)); + return Py_None; +} + static PyObject* create_os_window(PyObject UNUSED *self, PyObject *args) { int x = -1, y = -1; char *title, *wm_class_class, *wm_class_name; - PyObject *load_programs = NULL, *get_window_size; - if (!PyArg_ParseTuple(args, "Osss|Oii", &get_window_size, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL; + PyObject *load_programs = NULL, *get_window_size, *pre_show_callback; + if (!PyArg_ParseTuple(args, "OOsss|Oii", &get_window_size, &pre_show_callback, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL; static bool is_first_window = true; if (is_first_window) { @@ -391,8 +401,12 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, temp_window); glfwDestroyWindow(temp_window); temp_window = NULL; if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; } + PyObject *pret = PyObject_CallFunction(pre_show_callback, "N", native_window_handle(glfw_window)); + if (pret == NULL) return NULL; + Py_DECREF(pret); glfwMakeContextCurrent(glfw_window); if (x != -1 && y != -1) glfwSetWindowPos(glfw_window, x, y); + glfwShowWindow(glfw_window); if (is_first_window) { gl_init(); PyObject *ret = PyObject_CallFunction(load_programs, "i", glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER)); @@ -421,6 +435,8 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { w->logical_dpi_x = dpi_x; w->logical_dpi_y = dpi_y; w->fonts_data = fonts_data; current_os_window_ctx = glfw_window; + w->shown_once = true; + push_focus_history(w); glfwSwapInterval(OPT(sync_to_monitor) ? 1 : 0); #ifdef __APPLE__ if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option); @@ -459,25 +475,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { return PyLong_FromUnsignedLongLong(w->id); } -static PyObject* -show_window(PyObject UNUSED *self, PyObject *args) { - id_type os_window_id; - int yes = 1; - if (!PyArg_ParseTuple(args, "K|p", &os_window_id, &yes)) return NULL; - for (size_t i = 0; i < global_state.num_os_windows; i++) { - OSWindow *w = global_state.os_windows + i; - if (w->id == os_window_id) { - if (yes) { - glfwShowWindow(w->handle); - w->shown_once = true; - push_focus_history(w); - } else glfwHideWindow(w->handle); - break; - } - } - Py_RETURN_NONE; -} - void destroy_os_window(OSWindow *w) { if (w->handle) { @@ -804,7 +801,6 @@ static PyMethodDef module_methods[] = { METHODB(get_content_scale_for_window, METH_NOARGS), METHODB(set_clipboard_string, METH_VARARGS), METHODB(toggle_fullscreen, METH_NOARGS), - METHODB(show_window, METH_VARARGS), METHODB(glfw_window_hint, METH_VARARGS), METHODB(os_window_should_close, METH_VARARGS), METHODB(os_window_swap_buffers, METH_VARARGS), diff --git a/kitty/main.py b/kitty/main.py index fcac02ea6..4d660b478 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -16,13 +16,12 @@ from .constants import ( ) from .fast_data_types import ( create_os_window, glfw_init, glfw_terminate, set_default_window_icon, - set_options, show_window + set_options ) from .fonts.box_drawing import set_scale from .fonts.render import set_font_family from .utils import ( - detach, end_startup_notification, init_startup_notification, log_error, - single_instance + detach, log_error, single_instance, startup_notification_handler ) from .window import load_shader_programs @@ -44,19 +43,17 @@ def run_app(opts, args): set_options(opts, is_wayland, args.debug_gl, args.debug_font_fallback) set_font_family(opts) with cached_values_for(run_app.cached_values_name) as cached_values: - window_id = create_os_window( - run_app.initial_window_size_func(opts, cached_values), - appname, args.name or args.cls or appname, - args.cls or appname, load_all_shaders) - run_app.first_window_callback(opts, window_id) - startup_ctx = init_startup_notification(window_id) - show_window(window_id) + with startup_notification_handler(extra_callback=run_app.first_window_callback) as pre_show_callback: + window_id = create_os_window( + run_app.initial_window_size_func(opts, cached_values), + pre_show_callback, + appname, args.name or args.cls or appname, + args.cls or appname, load_all_shaders) if not is_wayland and not is_macos: # no window icons on wayland with open(logo_data_file, 'rb') as f: set_default_window_icon(f.read(), 256, 256) boss = Boss(window_id, opts, args, cached_values) boss.start() - end_startup_notification(startup_ctx) try: boss.child_monitor.main_loop() finally: @@ -64,7 +61,7 @@ def run_app(opts, args): run_app.cached_values_name = 'main' -run_app.first_window_callback = lambda opts, window_id: None +run_app.first_window_callback = lambda window_handle: None run_app.initial_window_size_func = initial_window_size_func diff --git a/kitty/utils.py b/kitty/utils.py index 46b5213b9..3b48df120 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -17,8 +17,7 @@ from .constants import ( appname, is_macos, is_wayland, supports_primary_selection ) from .fast_data_types import ( - GLSL_VERSION, log_error_string, redirect_std_streams, x11_display, - x11_window_id + GLSL_VERSION, log_error_string, redirect_std_streams, x11_display ) from .rgb import Color, to_color @@ -190,7 +189,7 @@ def adjust_line_height(cell_height, val): return int(cell_height * val) -def init_startup_notification_x11(window_id, startup_id=None): +def init_startup_notification_x11(window_handle, startup_id=None): # https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt from kitty.fast_data_types import init_x11_startup_notification sid = startup_id or os.environ.pop('DESKTOP_STARTUP_ID', None) # ensure child processes dont get this env var @@ -199,8 +198,7 @@ def init_startup_notification_x11(window_id, startup_id=None): display = x11_display() if not display: return - window_id = x11_window_id(window_id) - return init_x11_startup_notification(display, window_id, sid) + return init_x11_startup_notification(display, window_handle, sid) def end_startup_notification_x11(ctx): @@ -208,11 +206,14 @@ def end_startup_notification_x11(ctx): end_x11_startup_notification(ctx) -def init_startup_notification(window, startup_id=None): +def init_startup_notification(window_handle, startup_id=None): if is_macos or is_wayland: return + if window_handle is None: + log_error('Could not perform startup notification as window handle not present') + return try: - return init_startup_notification_x11(window, startup_id) + return init_startup_notification_x11(window_handle, startup_id) except Exception: import traceback traceback.print_exc() @@ -230,6 +231,29 @@ def end_startup_notification(ctx): traceback.print_exc() +class startup_notification_handler: + + def __init__(self, do_notify=True, startup_id=None, extra_callback=None): + self.do_notify = do_notify + self.startup_id = startup_id + self.extra_callback = extra_callback + self.ctx = None + + def __enter__(self): + + def pre_show_callback(window_handle): + if self.extra_callback is not None: + self.extra_callback(window_handle) + if self.do_notify: + self.ctx = init_startup_notification(window_handle, self.startup_id) + + return pre_show_callback + + def __exit__(self, *a): + if self.ctx is not None: + end_startup_notification(self.ctx) + + def remove_socket_file(s, path=None): try: s.close()