diff --git a/kitty/boss.py b/kitty/boss.py index 1e07b9dd3..b1be0507e 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -12,7 +12,7 @@ from weakref import WeakValueDictionary from .cli import create_opts, parse_args from .config import ( - MINIMUM_FONT_SIZE, initial_window_size, 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 ( @@ -99,11 +99,11 @@ class Boss: startup_session = create_session(opts, args) self.add_os_window(startup_session, os_window_id=os_window_id) - def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, size=None, startup_id=None): + def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, opts_for_size=None, startup_id=None): if os_window_id is None: - w, h = initial_window_size(self.opts, self.cached_values) if size is None else size + opts_for_size = opts_for_size or self.opts cls = wclass or self.args.cls or appname - os_window_id = create_os_window(w, h, appname, wname or self.args.name or cls, cls) + 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) @@ -238,7 +238,7 @@ class Boss: if not os.path.isabs(args.directory): args.directory = os.path.join(msg['cwd'], args.directory) session = create_session(opts, args, respect_cwd=True) - self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id) + self.add_os_window(session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id) else: log_error('Unknown message received from peer, ignoring') diff --git a/kitty/config.py b/kitty/config.py index 882c429bf..b1fee586d 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -361,6 +361,12 @@ def url_style(x): return url_style.map.get(x, url_style.map['curly']) +def window_size(val): + val = val.lower() + unit = 'cells' if val.endswith('c') else 'px' + return positive_int(val.rstrip('c')), unit + + url_style.map = dict( ((v, i) for i, v in enumerate('none single double curly'.split())) ) @@ -393,8 +399,8 @@ type_map = { 'cursor_stop_blinking_after': positive_float, 'enabled_layouts': to_layout_names, 'remember_window_size': to_bool, - 'initial_window_width': positive_int, - 'initial_window_height': positive_int, + 'initial_window_width': window_size, + 'initial_window_height': window_size, 'macos_hide_titlebar': to_bool, 'macos_hide_from_tasks': to_bool, 'macos_option_as_alt': to_bool, @@ -549,15 +555,34 @@ def cached_values_for(name): err)) -def initial_window_size(opts, cached_values): - w, h = opts.initial_window_width, opts.initial_window_height +def initial_window_size_func(opts, cached_values): + if 'window-size' in cached_values and opts.remember_window_size: ws = cached_values['window-size'] try: w, h = map(int, ws) + + def initial_window_size(*a): + return w, h + return initial_window_size except Exception: log_error('Invalid cached window size, ignoring') - return w, h + + w, w_unit = opts.initial_window_width + h, h_unit = opts.initial_window_height + + def get_window_size(cell_width, cell_height, dpi_x, dpi_y): + if w_unit == 'cells': + width = cell_width * w + (dpi_x / 72) * (opts.window_margin_width + opts.window_padding_width) + 1 + else: + width = w + if h_unit == 'cells': + height = cell_height * h + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1 + else: + height = h + return width, height + + return get_window_size def commented_out_default_config(): diff --git a/kitty/fonts.c b/kitty/fonts.c index 79e8ef08e..c5d2e83cc 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -1123,11 +1123,10 @@ initialize_font_group(FontGroup *fg) { send_prerendered_sprites(fg); } -void -load_fonts_for_window(OSWindow *w) { - w->fonts_data = NULL; - FontGroup *fg = font_group_for(w->font_sz_in_pts, w->logical_dpi_x, w->logical_dpi_y); - w->fonts_data = (FONTS_DATA_HANDLE)fg; +FONTS_DATA_HANDLE +load_fonts_data(double font_sz_in_pts, double dpi_x, double dpi_y) { + FontGroup *fg = font_group_for(font_sz_in_pts, dpi_x, dpi_y); + return (FONTS_DATA_HANDLE)fg; } static void diff --git a/kitty/glfw.c b/kitty/glfw.c index bf3a853ad..84aa19eaf 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -303,11 +303,10 @@ current_monitor(GLFWwindow *window) { return bestmonitor; } - -void -set_os_window_dpi(OSWindow *w) { +static inline void +get_window_dpi(GLFWwindow *w, double *x, double *y) { GLFWmonitor *monitor = NULL; - if (w && w->handle) { monitor = current_monitor(w->handle); } + if (w) monitor = current_monitor(w); if (monitor == NULL) monitor = glfwGetPrimaryMonitor(); float xscale = 1, yscale = 1; if (monitor) glfwGetMonitorContentScale(monitor, &xscale, &yscale); @@ -316,8 +315,13 @@ set_os_window_dpi(OSWindow *w) { #else double factor = 96.0; #endif - w->logical_dpi_x = xscale * factor; - w->logical_dpi_y = yscale * factor; + *x = xscale * factor; + *y = yscale * factor; +} + +void +set_os_window_dpi(OSWindow *w) { + get_window_dpi(w->handle, &w->logical_dpi_x, &w->logical_dpi_y); } static bool is_first_window = true; @@ -341,10 +345,10 @@ set_titlebar_color(OSWindow *w, color_type color) { static PyObject* create_os_window(PyObject UNUSED *self, PyObject *args) { - int width, height, x = -1, y = -1; + int x = -1, y = -1; char *title, *wm_class_class, *wm_class_name; - PyObject *load_programs = NULL; - if (!PyArg_ParseTuple(args, "iisss|Oii", &width, &height, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL; + 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; if (is_first_window) { glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); @@ -379,21 +383,32 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { } bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) || OPT(dynamic_background_opacity); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent); - GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL); - if (glfw_window == NULL) { - log_error("Failed to create a window at size: %dx%d, using a standard size instead.", width, height); - glfw_window = glfwCreateWindow(640, 400, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL); + // We use a temp window to avoid the need to set the window size after + // creation, which causes a resize event and all the associated processing. + // The temp window is used to get the DPI. + GLFWwindow *temp_window = glfwCreateWindow(640, 480, "temp", NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL); + double dpi_x, dpi_y; + get_window_dpi(temp_window, &dpi_x, &dpi_y); + glfwMakeContextCurrent(temp_window); + if (is_first_window) { + gl_init(); + PyObject *ret = PyObject_CallFunction(load_programs, "i", glfwGetWindowAttrib(temp_window, GLFW_TRANSPARENT_FRAMEBUFFER)); + if (ret == NULL) return NULL; + Py_DECREF(ret); } + FONTS_DATA_HANDLE fonts_data = load_fonts_data(global_state.font_sz_in_pts, dpi_x, dpi_y); + PyObject *ret = PyObject_CallFunction(get_window_size, "IIdd", fonts_data->cell_width, fonts_data->cell_height, fonts_data->logical_dpi_x, fonts_data->logical_dpi_y); + if (ret == NULL) return NULL; + int width = PyLong_AsLong(PyTuple_GET_ITEM(ret, 0)), height = PyLong_AsLong(PyTuple_GET_ITEM(ret, 1)); + Py_CLEAR(ret); + 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; } glfwMakeContextCurrent(glfw_window); if (x != -1 && y != -1) glfwSetWindowPos(glfw_window, x, y); current_os_window_ctx = glfw_window; glfwSwapInterval(OPT(sync_to_monitor) ? 1 : 0); // a value of 1 makes mouse selection laggy if (is_first_window) { - gl_init(); - PyObject *ret = PyObject_CallFunction(load_programs, "i", glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER)); - if (ret == NULL) return NULL; - Py_DECREF(ret); #ifdef __APPLE__ cocoa_create_global_menu(); // This needs to be done only after the first window has been created, because glfw only sets the activation policy once upon initialization. @@ -413,8 +428,8 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { OSWindow *q = global_state.os_windows + i; q->is_focused = q == w ? true : false; } - set_os_window_dpi(w); - load_fonts_for_window(w); + w->logical_dpi_x = dpi_x; w->logical_dpi_y = dpi_y; + w->fonts_data = fonts_data; if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo); glfwSetCursor(glfw_window, standard_cursor); update_os_window_viewport(w, false); diff --git a/kitty/kitty.conf b/kitty/kitty.conf index 2931edca4..ec0067764 100644 --- a/kitty/kitty.conf +++ b/kitty/kitty.conf @@ -177,7 +177,8 @@ bell_on_tab yes # Window layout {{{ # If enabled, the window size will be remembered so that new instances of kitty will have the same # size as the previous instance. If disabled, the window will initially have size configured -# by initial_window_width/height, in pixels. +# by initial_window_width/height, in pixels. You can use a suffix of "c" on the width/height values +# to have them interpreted as number of cells instead of pixels. remember_window_size yes initial_window_width 640 initial_window_height 400 diff --git a/kitty/main.py b/kitty/main.py index 4392670d4..fcac02ea6 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -10,7 +10,7 @@ from contextlib import contextmanager from .borders import load_borders_program from .boss import Boss from .cli import create_opts, parse_args -from .config import cached_values_for, initial_window_size +from .config import cached_values_for, initial_window_size_func from .constants import ( appname, config_dir, glfw_path, is_macos, is_wayland, logo_data_file ) @@ -44,8 +44,10 @@ 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: - w, h = run_app.initial_window_size(opts, cached_values) - window_id = create_os_window(w, h, appname, args.name or args.cls or appname, args.cls or appname, load_all_shaders) + 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) @@ -63,7 +65,7 @@ def run_app(opts, args): run_app.cached_values_name = 'main' run_app.first_window_callback = lambda opts, window_id: None -run_app.initial_window_size = initial_window_size +run_app.initial_window_size_func = initial_window_size_func def ensure_osx_locale(): diff --git a/kitty/state.c b/kitty/state.c index 2a0e493aa..49477cda7 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -626,7 +626,8 @@ PYWRAP1(os_window_font_size) { WITH_OS_WINDOW(os_window_id) if (new_sz > 0 && (force || new_sz != os_window->font_sz_in_pts)) { os_window->font_sz_in_pts = new_sz; - load_fonts_for_window(os_window); + os_window->fonts_data = NULL; + os_window->fonts_data = load_fonts_data(os_window->font_sz_in_pts, os_window->logical_dpi_x, os_window->logical_dpi_y); resize_screen(os_window, os_window->tab_bar_render_data.screen, false); for (size_t ti = 0; ti < os_window->num_tabs; ti++) { Tab *tab = os_window->tabs + ti; diff --git a/kitty/state.h b/kitty/state.h index b08f6ba3d..61e9e1055 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -186,4 +186,4 @@ void free_texture(uint32_t*); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool); void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*); void set_titlebar_color(OSWindow *w, color_type color); -void load_fonts_for_window(OSWindow*); +FONTS_DATA_HANDLE load_fonts_data(double, double, double);