macOS: When toggling in the quick access terminal move it to the currently active screen

Fixes #9003
This commit is contained in:
Kovid Goyal 2025-09-22 20:04:42 +05:30
parent 9a6cf492aa
commit 982b5156e1
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
11 changed files with 69 additions and 22 deletions

View file

@ -155,6 +155,9 @@ Detailed list of changes
it was last active on, after full screening some application causes the quick
access terminal to appear on the old space (:iss:`8740`)
- macOS: When toggling open the quick access terminal move it to the currently
active monitor (the monitor with the mouse pointer on it) (:iss:`9003`)
- macOS: Fix closing an OS Window when another OS Window is minimized causing
the minimized window to be un-minimized (:iss:`8913`)

View file

@ -1971,6 +1971,40 @@ screen_for_window_center(_GLFWwindow *window) {
return NSScreen.mainScreen;
}
static NSScreen*
active_screen(void) {
NSPoint mouseLocation = [NSEvent mouseLocation];
NSArray<NSScreen *> *screens = [NSScreen screens];
for (NSScreen *screen in screens) {
if (NSPointInRect(mouseLocation, [screen frame])) {
return screen;
}
}
// As a fallback, return the main screen
return [NSScreen mainScreen];
}
static bool
is_same_screen(NSScreen *screenA, NSScreen * screenB) {
if (screenA == screenB) return true;
NSDictionary<NSDeviceDescriptionKey, id> *deviceDescriptionA = [screenA deviceDescription];
NSDictionary<NSDeviceDescriptionKey, id> *deviceDescriptionB = [screenB deviceDescription];
NSNumber *screenNumberA = deviceDescriptionA[@"NSScreenNumber"];
NSNumber *screenNumberB = deviceDescriptionB[@"NSScreenNumber"];
return [screenNumberA isEqualToNumber:screenNumberB];
}
static void
move_window_to_screen(_GLFWwindow *window, NSScreen *target) {
NSRect screenFrame = [target visibleFrame];
NSRect windowFrame = [window->ns.object frame];
CGFloat newX = NSMidX(screenFrame) - (windowFrame.size.width / 2.0);
CGFloat newY = NSMidY(screenFrame) - (windowFrame.size.height / 2.0);
NSRect newWindowFrame = NSMakeRect(newX, newY, windowFrame.size.width, windowFrame.size.height);
[window->ns.object setFrame:newWindowFrame display:NO animate:NO];
if (window->ns.layer_shell.is_active) _glfwPlatformSetLayerShellConfig(window, NULL);
}
const GLFWLayerShellConfig*
_glfwPlatformGetLayerShellConfig(_GLFWwindow *window) {
return &window->ns.layer_shell.config;
@ -2257,10 +2291,18 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
}
}
void _glfwPlatformShowWindow(_GLFWwindow* window)
void _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen)
{
const bool is_background = window->ns.layer_shell.is_active && window->ns.layer_shell.config.type == GLFW_LAYER_SHELL_BACKGROUND;
NSWindow *nw = window->ns.object;
if (move_to_active_screen) {
NSScreen *current_screen = screen_for_window_center(window);
NSScreen *target_screen = active_screen();
if (!is_same_screen(current_screen, target_screen)) {
debug_rendering("Moving OS window %llu to active screen\n", window->id);
move_window_to_screen(window, target_screen);
}
}
if (is_background) {
[nw orderBack:nil];
} else {

2
glfw/glfw3.h vendored
View file

@ -3579,7 +3579,7 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
*
* @ingroup window
*/
GLFWAPI void glfwShowWindow(GLFWwindow* window);
GLFWAPI void glfwShowWindow(GLFWwindow* window, bool move_to_active_screen);
/*! @brief Hides the specified window.
*

2
glfw/internal.h vendored
View file

@ -740,7 +740,7 @@ monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);
void _glfwPlatformIconifyWindow(_GLFWwindow* window);
void _glfwPlatformRestoreWindow(_GLFWwindow* window);
void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
void _glfwPlatformShowWindow(_GLFWwindow* window);
void _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen);
void _glfwPlatformHideWindow(_GLFWwindow* window);
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
int _glfwPlatformWindowBell(_GLFWwindow* window);

6
glfw/window.c vendored
View file

@ -279,7 +279,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, G
{
if (wndconfig.visible)
{
_glfwPlatformShowWindow(window);
_glfwPlatformShowWindow(window, false);
#ifndef _GLFW_WAYLAND
if (wndconfig.focused)
_glfwPlatformFocusWindow(window);
@ -855,7 +855,7 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle)
_glfwPlatformMaximizeWindow(window);
}
GLFWAPI void glfwShowWindow(GLFWwindow* handle)
GLFWAPI void glfwShowWindow(GLFWwindow* handle, bool move_to_active_screen)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
@ -865,7 +865,7 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle)
if (window->monitor)
return;
_glfwPlatformShowWindow(window);
_glfwPlatformShowWindow(window, move_to_active_screen);
#ifndef _GLFW_WAYLAND
if (window->focusOnShow)

2
glfw/wl_window.c vendored
View file

@ -1756,7 +1756,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
}
}
void _glfwPlatformShowWindow(_GLFWwindow* window)
void _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen UNUSED)
{
if (!window->wl.visible) {
if (!window->wl.created) {

4
glfw/x11_window.c vendored
View file

@ -2061,7 +2061,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconf
if (window->monitor)
{
_glfwPlatformShowWindow(window);
_glfwPlatformShowWindow(window, false);
updateWindowMode(window);
acquireMonitor(window);
}
@ -2458,7 +2458,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
XFlush(_glfw.x11.display);
}
void _glfwPlatformShowWindow(_GLFWwindow* window)
void _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen UNUSED)
{
if (_glfwPlatformWindowVisible(window))
return;

View file

@ -957,7 +957,7 @@ class Boss:
def quick_access_terminal_invoked(self) -> None:
for os_window_id in self.os_window_map:
toggle_os_window_visibility(os_window_id)
toggle_os_window_visibility(os_window_id, move_to_active_screen=True)
def handle_remote_cmd(self, cmd: memoryview, window: Window | None = None) -> None:
response = self._handle_remote_command(cmd, window)

View file

@ -1719,7 +1719,7 @@ def get_docs_ref_map() -> bytes: ...
def set_clipboard_data_types(ct: int, mime_types: Tuple[str, ...]) -> None: ...
def get_clipboard_mime(ct: int, mime: Optional[str], callback: Callable[[bytes], None]) -> None: ...
def run_with_activation_token(func: Callable[[str], None]) -> bool: ...
def toggle_os_window_visibility(os_window_id: int, visible: Literal[True, False] = ...) -> bool: ...
def toggle_os_window_visibility(os_window_id: int, visible: bool | Literal[-1] = -1, move_to_active_screen: bool = False) -> bool: ...
def parse_cli_from_spec(args: list[str], names_map: dict[str, OptionDict], defval_map: dict[str, Any]) -> tuple[dict[str, tuple[Any, bool]], list[str]]: ...
def layer_shell_config_for_os_window(os_window_id: int) -> dict[str, Any] | None: ...
def set_layer_shell_config(os_window_id: int, cfg: LayerShellConfig) -> bool: ...

2
kitty/glfw-wrapper.h generated
View file

@ -1974,7 +1974,7 @@ typedef void (*glfwMaximizeWindow_func)(GLFWwindow*);
GFW_EXTERN glfwMaximizeWindow_func glfwMaximizeWindow_impl;
#define glfwMaximizeWindow glfwMaximizeWindow_impl
typedef void (*glfwShowWindow_func)(GLFWwindow*);
typedef void (*glfwShowWindow_func)(GLFWwindow*, bool);
GFW_EXTERN glfwShowWindow_func glfwShowWindow_impl;
#define glfwShowWindow glfwShowWindow_impl

View file

@ -586,9 +586,9 @@ scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags, int mo
static id_type focus_counter = 0;
static void
set_os_window_visibility(OSWindow *w, int set_visible) {
set_os_window_visibility(OSWindow *w, int set_visible, bool move_to_active_screen) {
if (set_visible) {
glfwShowWindow(w->handle);
glfwShowWindow(w->handle, move_to_active_screen);
w->needs_render = true;
w->keep_rendering_till_swap = 256; // try this many times
request_tick_callback();
@ -598,7 +598,7 @@ set_os_window_visibility(OSWindow *w, int set_visible) {
static void
update_os_window_visibility_based_on_focus(id_type timer_id UNUSED, void*d) {
OSWindow * osw = os_window_for_id((uintptr_t)d);
if (osw && osw->hide_on_focus_loss && !osw->is_focused) set_os_window_visibility(osw, 0);
if (osw && osw->hide_on_focus_loss && !osw->is_focused) set_os_window_visibility(osw, 0, false);
}
static void
@ -1458,7 +1458,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
if (pret == NULL) return NULL;
Py_DECREF(pret);
if (x != INT_MIN && y != INT_MIN) glfwSetWindowPos(glfw_window, x, y);
if (!global_state.is_apple && !global_state.is_wayland && window_state != WINDOW_HIDDEN) glfwShowWindow(glfw_window);
if (!global_state.is_apple && !global_state.is_wayland && window_state != WINDOW_HIDDEN) glfwShowWindow(glfw_window, false);
if (global_state.is_wayland || global_state.is_apple) {
float n_xscale, n_yscale;
double n_xdpi, n_ydpi;
@ -1551,7 +1551,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
if (window_state != WINDOW_NORMAL) change_state_for_os_window(w, window_state);
#ifdef __APPLE__
// macOS: Show the window after it is ready
if (window_state != WINDOW_HIDDEN) glfwShowWindow(glfw_window);
if (window_state != WINDOW_HIDDEN) glfwShowWindow(glfw_window, false);
#endif
w->redraw_count = 1;
debug("OS Window created\n");
@ -2550,16 +2550,18 @@ is_layer_shell_supported(PyObject *self UNUSED, PyObject *args UNUSED) {
}
static PyObject*
toggle_os_window_visibility(PyObject *self UNUSED, PyObject *args) {
toggle_os_window_visibility(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
unsigned long long wid;
int set_visible = -1;
if (!PyArg_ParseTuple(args, "K|p", &wid, &set_visible)) return NULL;
int set_visible = -1, move_to_active_screen = 0;
static const char* kwlist[] = {"os_window_id", "visible", "move_to_active_screen", NULL};
if (!PyArg_ParseTupleAndKeywords(
args, kw, "K|pp", (char**)kwlist, &wid, &set_visible, &move_to_active_screen)) return NULL;
OSWindow *w = os_window_for_id(wid);
if (!w || !w->handle) Py_RETURN_FALSE;
bool is_visible = glfwGetWindowAttrib(w->handle, GLFW_VISIBLE) != 0;
if (set_visible == -1) set_visible = !is_visible;
else if (set_visible == is_visible) Py_RETURN_FALSE;
set_os_window_visibility(w, set_visible);
set_os_window_visibility(w, set_visible, move_to_active_screen);
Py_RETURN_TRUE;
}
@ -2595,7 +2597,7 @@ grab_keyboard(PyObject *self UNUSED, PyObject *action) {
static PyMethodDef module_methods[] = {
METHODB(set_custom_cursor, METH_VARARGS),
METHODB(is_css_pointer_name_valid, METH_O),
METHODB(toggle_os_window_visibility, METH_VARARGS),
{"toggle_os_window_visibility", (PyCFunction)(void (*) (void))(toggle_os_window_visibility), METH_VARARGS | METH_KEYWORDS, NULL},
METHODB(layer_shell_config_for_os_window, METH_O),
METHODB(set_layer_shell_config, METH_VARARGS),
METHODB(grab_keyboard, METH_O),