mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-07-01 21:36:09 +00:00
macOS: Implement background blurring
Uses a private API that allows us to control the amount of blurring. While using a private API is obviously not ideal, it is used by both iTerm.app and Apple's own Terminal.app, so hopefully it should stick around. Fixes #6135
This commit is contained in:
parent
326b81a970
commit
7a1bdb4ff1
15 changed files with 89 additions and 1 deletions
1
glfw/cocoa_platform.h
vendored
1
glfw/cocoa_platform.h
vendored
|
|
@ -139,6 +139,7 @@ typedef struct _GLFWwindowNS
|
|||
int width, height;
|
||||
int fbWidth, fbHeight;
|
||||
float xscale, yscale;
|
||||
int blur_radius;
|
||||
|
||||
// The total sum of the distances the cursor has been warped
|
||||
// since the last cursor motion event was processed
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
#include <string.h>
|
||||
|
||||
|
||||
GLFWAPI int glfwCocoaSetBackgroundBlur(GLFWwindow *w, int radius);
|
||||
|
||||
static const char*
|
||||
polymorphic_string_as_utf8(id string) {
|
||||
if (string == nil) return "(nil)";
|
||||
|
|
@ -1835,6 +1837,7 @@ static bool createNativeWindow(_GLFWwindow* window,
|
|||
|
||||
_glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height);
|
||||
_glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight);
|
||||
if (wndconfig->ns.blur_radius > 0) glfwCocoaSetBackgroundBlur((GLFWwindow*)window, wndconfig->ns.blur_radius);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2969,6 +2972,18 @@ GLFWAPI void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun
|
|||
requestRenderFrame((_GLFWwindow*)w, callback);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwCocoaSetBackgroundBlur(GLFWwindow *w, int radius) {
|
||||
_GLFWwindow* window = (_GLFWwindow*)w;
|
||||
int orig = window->ns.blur_radius;
|
||||
if (radius > -1 && radius != window->ns.blur_radius) {
|
||||
extern OSStatus CGSSetWindowBackgroundBlurRadius(void* connection, NSInteger windowNumber, int radius);
|
||||
extern void* CGSDefaultConnectionForThread(void);
|
||||
CGSSetWindowBackgroundBlurRadius(CGSDefaultConnectionForThread(), [window->ns.object windowNumber], radius);
|
||||
window->ns.blur_radius = radius;
|
||||
}
|
||||
return orig;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetCurrentSystemColorTheme(void) {
|
||||
int theme_type = 0;
|
||||
NSAppearance *changedAppearance = NSApp.effectiveAppearance;
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ def generate_wrappers(glfw_header: str) -> None:
|
|||
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
|
||||
uint32_t glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int* cocoa_mods)
|
||||
void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
|
||||
int glfwCocoaSetBackgroundBlur(GLFWwindow *w, int blur_radius)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
|
||||
|
|
|
|||
4
glfw/glfw3.h
vendored
4
glfw/glfw3.h
vendored
|
|
@ -1034,6 +1034,10 @@ typedef enum {
|
|||
SRGB_COLORSPACE = 1,
|
||||
DISPLAY_P3_COLORSPACE = 2,
|
||||
} GlfwCocoaColorSpaces;
|
||||
/*! @brief macOS specific
|
||||
* [window hint](@ref GLFW_COCOA_BLUR_RADIUS_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_BLUR_RADIUS 0x00023005
|
||||
|
||||
/*! @brief X11 specific
|
||||
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
|
||||
|
|
|
|||
1
glfw/internal.h
vendored
1
glfw/internal.h
vendored
|
|
@ -313,6 +313,7 @@ struct _GLFWwndconfig
|
|||
struct {
|
||||
bool retina;
|
||||
int color_space;
|
||||
int blur_radius;
|
||||
char frameName[256];
|
||||
} ns;
|
||||
struct {
|
||||
|
|
|
|||
5
glfw/window.c
vendored
5
glfw/window.c
vendored
|
|
@ -334,6 +334,8 @@ void glfwDefaultWindowHints(void)
|
|||
_glfw.hints.window.ns.retina = true;
|
||||
// use the default colorspace assigned by the system
|
||||
_glfw.hints.window.ns.color_space = 0;
|
||||
// no blur
|
||||
_glfw.hints.window.ns.blur_radius = 0;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwWindowHint(int hint, int value)
|
||||
|
|
@ -417,6 +419,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
|
|||
case GLFW_COCOA_COLOR_SPACE:
|
||||
_glfw.hints.window.ns.color_space = value;
|
||||
return;
|
||||
case GLFW_COCOA_BLUR_RADIUS:
|
||||
_glfw.hints.window.ns.blur_radius = value;
|
||||
return;
|
||||
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
||||
_glfw.hints.context.nsgl.offline = value ? true : false;
|
||||
return;
|
||||
|
|
|
|||
3
kitty/glfw-wrapper.c
generated
3
kitty/glfw-wrapper.c
generated
|
|
@ -443,6 +443,9 @@ load_glfw(const char* path) {
|
|||
*(void **) (&glfwCocoaRequestRenderFrame_impl) = dlsym(handle, "glfwCocoaRequestRenderFrame");
|
||||
if (glfwCocoaRequestRenderFrame_impl == NULL) dlerror(); // clear error indicator
|
||||
|
||||
*(void **) (&glfwCocoaSetBackgroundBlur_impl) = dlsym(handle, "glfwCocoaSetBackgroundBlur");
|
||||
if (glfwCocoaSetBackgroundBlur_impl == NULL) dlerror(); // clear error indicator
|
||||
|
||||
*(void **) (&glfwGetX11Display_impl) = dlsym(handle, "glfwGetX11Display");
|
||||
if (glfwGetX11Display_impl == NULL) dlerror(); // clear error indicator
|
||||
|
||||
|
|
|
|||
8
kitty/glfw-wrapper.h
generated
8
kitty/glfw-wrapper.h
generated
|
|
@ -772,6 +772,10 @@ typedef enum {
|
|||
SRGB_COLORSPACE = 1,
|
||||
DISPLAY_P3_COLORSPACE = 2,
|
||||
} GlfwCocoaColorSpaces;
|
||||
/*! @brief macOS specific
|
||||
* [window hint](@ref GLFW_COCOA_BLUR_RADIUS_hint).
|
||||
*/
|
||||
#define GLFW_COCOA_BLUR_RADIUS 0x00023005
|
||||
|
||||
/*! @brief X11 specific
|
||||
* [window hint](@ref GLFW_X11_CLASS_NAME_hint).
|
||||
|
|
@ -2216,6 +2220,10 @@ typedef void (*glfwCocoaRequestRenderFrame_func)(GLFWwindow*, GLFWcocoarenderfra
|
|||
GFW_EXTERN glfwCocoaRequestRenderFrame_func glfwCocoaRequestRenderFrame_impl;
|
||||
#define glfwCocoaRequestRenderFrame glfwCocoaRequestRenderFrame_impl
|
||||
|
||||
typedef int (*glfwCocoaSetBackgroundBlur_func)(GLFWwindow*, int);
|
||||
GFW_EXTERN glfwCocoaSetBackgroundBlur_func glfwCocoaSetBackgroundBlur_impl;
|
||||
#define glfwCocoaSetBackgroundBlur glfwCocoaSetBackgroundBlur_impl
|
||||
|
||||
typedef void* (*glfwGetX11Display_func)(void);
|
||||
GFW_EXTERN glfwGetX11Display_func glfwGetX11Display_impl;
|
||||
#define glfwGetX11Display glfwGetX11Display_impl
|
||||
|
|
|
|||
16
kitty/glfw.c
16
kitty/glfw.c
|
|
@ -902,6 +902,11 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
|||
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_COCOA_COLOR_SPACE, OPT(macos_colorspace));
|
||||
if (OPT(background_blur) > 0 && OPT(background_opacity) < 1.f) {
|
||||
glfwWindowHint(GLFW_COCOA_BLUR_RADIUS, MIN(OPT(background_blur), 128));
|
||||
} else {
|
||||
glfwWindowHint(GLFW_COCOA_BLUR_RADIUS, 0);
|
||||
}
|
||||
#else
|
||||
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, wm_class_name);
|
||||
glfwWindowHintString(GLFW_X11_CLASS_NAME, wm_class_class);
|
||||
|
|
@ -1081,6 +1086,17 @@ os_window_update_size_increments(OSWindow *window) {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
update_background_blur(OSWindow *os_window) {
|
||||
#ifdef __APPLE__
|
||||
int new_blur_radius = 0;
|
||||
if (os_window->background_opacity < 1.f && OPT(background_blur) > -1) new_blur_radius = OPT(background_blur);
|
||||
glfwCocoaSetBackgroundBlur(os_window->handle, new_blur_radius);
|
||||
#else
|
||||
(void)os_window;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static bool
|
||||
window_in_same_cocoa_workspace(void *w, size_t *source_workspaces, size_t source_workspace_count) {
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,17 @@ config.
|
|||
'''
|
||||
)
|
||||
|
||||
opt('background_blur', '0', option_type='int', ctype='int',
|
||||
long_text='''
|
||||
Set to a positive value to enable background blur (blurring of the visuals
|
||||
behind a transparent window) on platforms that support it. Only takes effect
|
||||
when :opt:`background_opacity` is less than one. On macOS, this will also
|
||||
control the :italic:`blur radius` (amount of blurring). Setting it to too high
|
||||
a value will cause severe performance issues and/or rendering artifacts.
|
||||
Usually, values up to 64 work well. Note that this might cause performance issues,
|
||||
depending on how the platform implements it, so use with care.
|
||||
''')
|
||||
|
||||
opt('background_image', 'none',
|
||||
option_type='config_or_absolute_path', ctype='!background_image',
|
||||
long_text='Path to a background image. Must be in PNG format.'
|
||||
|
|
|
|||
3
kitty/options/parse.py
generated
3
kitty/options/parse.py
generated
|
|
@ -65,6 +65,9 @@ class Parser:
|
|||
def background(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['background'] = to_color(val)
|
||||
|
||||
def background_blur(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['background_blur'] = int(val)
|
||||
|
||||
def background_image(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['background_image'] = config_or_absolute_path(val)
|
||||
|
||||
|
|
|
|||
15
kitty/options/to-c-generated.h
generated
15
kitty/options/to-c-generated.h
generated
|
|
@ -733,6 +733,19 @@ convert_from_opts_background_opacity(PyObject *py_opts, Options *opts) {
|
|||
Py_DECREF(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_from_python_background_blur(PyObject *val, Options *opts) {
|
||||
opts->background_blur = PyLong_AsLong(val);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_from_opts_background_blur(PyObject *py_opts, Options *opts) {
|
||||
PyObject *ret = PyObject_GetAttrString(py_opts, "background_blur");
|
||||
if (ret == NULL) return;
|
||||
convert_from_python_background_blur(ret, opts);
|
||||
Py_DECREF(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_from_python_background_image(PyObject *val, Options *opts) {
|
||||
background_image(val, opts);
|
||||
|
|
@ -1159,6 +1172,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
|
|||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_background_opacity(py_opts, opts);
|
||||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_background_blur(py_opts, opts);
|
||||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_background_image(py_opts, opts);
|
||||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_background_image_layout(py_opts, opts);
|
||||
|
|
|
|||
4
kitty/options/types.py
generated
4
kitty/options/types.py
generated
|
|
@ -61,6 +61,7 @@ option_names = ( # {{{
|
|||
'allow_hyperlinks',
|
||||
'allow_remote_control',
|
||||
'background',
|
||||
'background_blur',
|
||||
'background_image',
|
||||
'background_image_layout',
|
||||
'background_image_linear',
|
||||
|
|
@ -480,6 +481,7 @@ class Options:
|
|||
allow_hyperlinks: int = 1
|
||||
allow_remote_control: choices_for_allow_remote_control = 'no'
|
||||
background: Color = Color(0, 0, 0)
|
||||
background_blur: int = 0
|
||||
background_image: typing.Optional[str] = None
|
||||
background_image_layout: choices_for_background_image_layout = 'tiled'
|
||||
background_image_linear: bool = False
|
||||
|
|
@ -511,7 +513,7 @@ class Options:
|
|||
cursor_underline_thickness: float = 2.0
|
||||
default_pointer_shape: choices_for_default_pointer_shape = 'beam'
|
||||
detect_urls: bool = True
|
||||
dim_opacity: float = 0.75
|
||||
dim_opacity: float = 0.4
|
||||
disable_ligatures: int = 0
|
||||
draw_minimal_borders: bool = True
|
||||
dynamic_background_opacity: bool = False
|
||||
|
|
|
|||
|
|
@ -1093,6 +1093,7 @@ PYWRAP0(apply_options_update) {
|
|||
get_platform_dependent_config_values(os_window->handle);
|
||||
os_window->background_opacity = OPT(background_opacity);
|
||||
os_window->is_damaged = true;
|
||||
update_background_blur(os_window);
|
||||
for (size_t t = 0; t < os_window->num_tabs; t++) {
|
||||
Tab *tab = os_window->tabs + t;
|
||||
for (size_t w = 0; w < tab->num_windows; w++) {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ typedef struct {
|
|||
float val; AdjustmentUnit unit;
|
||||
} underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline;
|
||||
bool show_hyperlink_targets;
|
||||
int background_blur;
|
||||
} Options;
|
||||
|
||||
typedef struct WindowLogoRenderData {
|
||||
|
|
@ -365,3 +366,4 @@ void update_ime_position(Window* w, Screen *screen);
|
|||
bool update_ime_position_for_window(id_type window_id, bool force, int update_focus);
|
||||
void set_ignore_os_keyboard_processing(bool enabled);
|
||||
void update_menu_bar_title(PyObject *title UNUSED);
|
||||
void update_background_blur(OSWindow *);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue