diff --git a/docs/changelog.rst b/docs/changelog.rst index 5bb739b7a..ba8e73c15 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,9 @@ To update |kitty|, :doc:`follow the instructions `. - Allow choosing OpenType features for individual fonts via the :opt:`font_features` option. +- Add a :opt:`resize_in_steps` option that can be used to resize the OS window + in steps as large as character cells (:pull:`2131`) + 0.15.1 [2019-12-21] -------------------- diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 7ada57e5e..e431995b1 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1578,10 +1578,18 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { - if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; - else + if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; + else + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; +} + +void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr) +{ + if (widthincr != GLFW_DONT_CARE && heightincr != GLFW_DONT_CARE) + [window->ns.object setResizeIncrements:NSMakeSize(widthincr, heightincr)]; + else + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index fccf38ebc..acc9f6844 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -3036,6 +3036,46 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe * * @ingroup window */ + +GLFWAPI void glfwSetWindowSizeIncrements(GLFWwindow* window, int widthincr, int heightincr); + +/*! @brief Sets the size increments of the specified window. + * + * This function sets the size increments of the content area of the specified + * window. If the window is full screen, the size limits only take effect + * once it is made windowed. If the window is not resizable, this function + * does nothing. + * + * The size increments are applied immediately to a windowed mode window and + * may cause it to be resized. + * + * The dimension increments must be greater than zero. + * + * @param[in] window The window to set limits for. + * @param[in] widthincr The width increments, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * @param[in] heightincr The height increments, in screen coordinates, of the + * content area, or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowSizeLimits + * + * @since Added in version 3.2. + * + * @ingroup window + */ + GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); /*! @brief Sets the size of the content area of the specified window. diff --git a/glfw/internal.h b/glfw/internal.h index 5e6f00c1f..17a7b5446 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -418,6 +418,7 @@ struct _GLFWwindow int minwidth, minheight; int maxwidth, maxheight; int numer, denom; + int widthincr, heightincr; bool stickyKeys; bool stickyMouseButtons; @@ -673,6 +674,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); +void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr); void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); void _glfwInputLiveResize(_GLFWwindow* window, bool started); void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, diff --git a/glfw/null_window.c b/glfw/null_window.c index c7a759ade..ceed84cdc 100644 --- a/glfw/null_window.c +++ b/glfw/null_window.c @@ -128,6 +128,10 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED, int n UNUSED, { } +void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED, int widthincr UNUSED, int heightincr UNUSED) +{ +} + void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { if (width) diff --git a/glfw/window.c b/glfw/window.c index c693a0a02..20d9926a2 100644 --- a/glfw/window.c +++ b/glfw/window.c @@ -254,6 +254,8 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->maxheight = GLFW_DONT_CARE; window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; + window->widthincr = GLFW_DONT_CARE; + window->heightincr = GLFW_DONT_CARE; // Open the actual window and create its context if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) @@ -690,6 +692,20 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) _glfwPlatformSetWindowAspectRatio(window, numer, denom); } +GLFWAPI void glfwSetWindowSizeIncrements(GLFWwindow* handle, int widthincr, int heightincr) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(widthincr >= 0); + assert(heightincr >= 0); + + _GLFW_REQUIRE_INIT(); + + window->widthincr = widthincr; + window->heightincr = heightincr; + _glfwPlatformSetWindowSizeIncrements(window, window->widthincr, window->heightincr); +} + GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index c9e94e799..d891da485 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -1028,6 +1028,13 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED, // The actual limits are checked in the xdg_toplevel::configure handler. } +void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED, + int widthincr UNUSED, int heightincr UNUSED) +{ + // TODO: find out how to trigger a resize. + // The actual limits are checked in the xdg_toplevel::configure handler. +} + void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { diff --git a/glfw/x11_window.c b/glfw/x11_window.c index f7ca9ad3f..fa65e0a1b 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -255,6 +255,14 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height) hints->min_aspect.x = hints->max_aspect.x = window->numer; hints->min_aspect.y = hints->max_aspect.y = window->denom; } + + if (window->widthincr != GLFW_DONT_CARE && + window->heightincr != GLFW_DONT_CARE) + { + hints->flags |= PResizeInc; + hints->width_inc = window->widthincr; + hints->height_inc = window->heightincr; + } } else { @@ -2028,6 +2036,14 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer UNUSED, in XFlush(_glfw.x11.display); } +void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr UNUSED, int heightincr UNUSED) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); +} + void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); diff --git a/kitty/config_data.py b/kitty/config_data.py index 45ef161a8..ed336cb00 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -688,6 +688,13 @@ A value of :code:`blank` means draw a blank window. A value of :code:`size` means show the window size in cells. ''')) +o('resize_in_steps', False, long_text=_(''' +Resize the OS window in steps as large as the cells, instead of with the usual pixel accuracy. +Combined with an :opt:`initial_window_width` and :opt:`initial_window_height` in number of cells, +this option can be used to keep the margins as small as possible when resizing the OS window. +Note that this does not currently work on Wayland. +''')) + # }}} g('tabbar') # {{{ diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 521df07c6..e43660099 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -140,6 +140,9 @@ load_glfw(const char* path) { *(void **) (&glfwSetWindowAspectRatio_impl) = dlsym(handle, "glfwSetWindowAspectRatio"); if (glfwSetWindowAspectRatio_impl == NULL) fail("Failed to load glfw function glfwSetWindowAspectRatio with error: %s", dlerror()); + *(void **) (&glfwSetWindowSizeIncrements_impl) = dlsym(handle, "glfwSetWindowSizeIncrements"); + if (glfwSetWindowSizeIncrements_impl == NULL) fail("Failed to load glfw function glfwSetWindowSizeIncrements with error: %s", dlerror()); + *(void **) (&glfwSetWindowSize_impl) = dlsym(handle, "glfwSetWindowSize"); if (glfwSetWindowSize_impl == NULL) fail("Failed to load glfw function glfwSetWindowSize with error: %s", dlerror()); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 11c83b173..359ae3fc9 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1751,6 +1751,10 @@ typedef void (*glfwSetWindowAspectRatio_func)(GLFWwindow*, int, int); glfwSetWindowAspectRatio_func glfwSetWindowAspectRatio_impl; #define glfwSetWindowAspectRatio glfwSetWindowAspectRatio_impl +typedef void (*glfwSetWindowSizeIncrements_func)(GLFWwindow*, int, int); +glfwSetWindowSizeIncrements_func glfwSetWindowSizeIncrements_impl; +#define glfwSetWindowSizeIncrements glfwSetWindowSizeIncrements_impl + typedef void (*glfwSetWindowSize_func)(GLFWwindow*, int, int); glfwSetWindowSize_func glfwSetWindowSize_impl; #define glfwSetWindowSize glfwSetWindowSize_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 605a10f3e..4953f9e21 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -624,6 +624,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { w->fonts_data = fonts_data; w->shown_once = true; w->last_focused_counter = ++focus_counter; + if (OPT(resize_in_steps)) os_window_update_size_increments(w); #ifdef __APPLE__ if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option); glfwSetCocoaToggleFullscreenIntercept(glfw_window, intercept_cocoa_fullscreen); @@ -672,6 +673,12 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { return PyLong_FromUnsignedLongLong(w->id); } +void +os_window_update_size_increments(OSWindow *window) { + if (window->handle && window->fonts_data) glfwSetWindowSizeIncrements( + window->handle, window->fonts_data->cell_width, window->fonts_data->cell_height); +} + #ifdef __APPLE__ static inline bool window_in_same_cocoa_workspace(void *w, size_t *source_workspaces, size_t source_workspace_count) { diff --git a/kitty/state.c b/kitty/state.c index e100b0313..d71392c73 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -522,6 +522,7 @@ PYWRAP1(set_options) { S(tab_bar_min_tabs, PyLong_AsUnsignedLong); S(disable_ligatures, PyLong_AsLong); S(resize_draw_strategy, PyLong_AsLong); + S(resize_in_steps, PyObject_IsTrue); S(pointer_shape_when_grabbed, pointer_shape); GA(tab_bar_style); @@ -781,6 +782,7 @@ PYWRAP1(os_window_font_size) { resize_screen(os_window, w->render_data.screen, true); } } + if (OPT(resize_in_steps)) os_window_update_size_increments(os_window); } return Py_BuildValue("d", os_window->font_sz_in_pts); END_WITH_OS_WINDOW diff --git a/kitty/state.h b/kitty/state.h index 484d32bd3..051a46daf 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -42,6 +42,7 @@ typedef struct { unsigned long tab_bar_min_tabs; DisableLigature disable_ligatures; ResizeDrawStrategy resize_draw_strategy; + bool resize_in_steps; bool sync_to_monitor; bool close_on_child_death; bool window_alert_on_bell; @@ -245,3 +246,4 @@ void remove_main_loop_timer(id_type timer_id); 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);