mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 08:26:56 +00:00
Rewrite rendering pipeline
This was needed to fix various corner cases when doing blending of colors in linear space. The new architecture has the same performance as the old in the common case of opaque rendering with no UI layers or images. In the case of only positive z-index images there is a performance decrease as the OS Window is now rendered to a offscreen texture and then blitted to screen. However, in the future when we move to Vulkan or I can figure out how to get Wayland to accept buffers with colors in linear space, this performance penalty can be removed. The performance penalty was not significant on my system but this is highly GPU dependent. Modern GPUs are supposedly optimised for rendering to offscreen buffers, so we will see. The awrit project might be a good test case. Now either we have 1-shot rendering for the case of opaque with only ext or all the various pieces are rendered in successive draw calls into an offscreen buffer that is blitted to the output buffer after all drawing is done. Fixes #8869
This commit is contained in:
parent
0afa0c4e50
commit
d52f2e7981
42 changed files with 1200 additions and 1176 deletions
|
|
@ -115,6 +115,9 @@ Detailed list of changes
|
|||
- macOS: Add the default :kbd:`Cmd+L` mapping from Terminal.app to erase the
|
||||
last command and its output (:disc:`6040`)
|
||||
|
||||
- Fix :opt:`background_opacity` being non-linear with light color themes
|
||||
(:iss:`8869`)
|
||||
|
||||
- Wayland: Fix incorrect window size calculation when transitioning from
|
||||
full screen to non-full screen with client side decorations (:iss:`8826`)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
|
||||
uniform sampler2D image;
|
||||
uniform float opacity;
|
||||
uniform float premult;
|
||||
uniform vec4 background;
|
||||
in vec2 texcoord;
|
||||
out vec4 color;
|
||||
out vec4 premult_color;
|
||||
|
||||
void main() {
|
||||
color = texture(image, texcoord);
|
||||
float alpha = color.a * opacity;
|
||||
vec4 premult_color = vec4(color.rgb * alpha, alpha);
|
||||
color = vec4(color.rgb, alpha);
|
||||
color = premult * premult_color + (1 - premult) * color;
|
||||
vec4 color = texture(image, texcoord);
|
||||
premult_color = alpha_blend(color, background);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@
|
|||
#define tex_top 0
|
||||
#define tex_right 1
|
||||
#define tex_bottom 1
|
||||
#define x_axis 0
|
||||
#define y_axis 1
|
||||
#define window i
|
||||
#define image i + 2
|
||||
|
||||
uniform float tiled;
|
||||
uniform vec4 sizes; // [ window_width, window_height, image_width, image_height ]
|
||||
|
|
@ -29,6 +25,8 @@ float scale_factor(float window_size, float image_size) {
|
|||
}
|
||||
|
||||
float tiling_factor(int i) {
|
||||
#define window i
|
||||
#define image i + 2
|
||||
return tiled * scale_factor(sizes[window], sizes[image]) + (1 - tiled);
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +38,8 @@ void main() {
|
|||
vec2(positions[right], positions[top])
|
||||
);
|
||||
vec2 tex_coords = tex_map[gl_VertexID];
|
||||
#define x_axis 0
|
||||
#define y_axis 1
|
||||
texcoord = vec2(
|
||||
tex_coords[x_axis] * tiling_factor(x_axis),
|
||||
tex_coords[y_axis] * tiling_factor(y_axis)
|
||||
|
|
|
|||
19
kitty/blit_common_vertex.glsl
Normal file
19
kitty/blit_common_vertex.glsl
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
out vec2 texcoord;
|
||||
|
||||
#define left 0
|
||||
#define top 1
|
||||
#define right 2
|
||||
#define bottom 3
|
||||
|
||||
const ivec2 vertex_pos_map[4] = ivec2[4](
|
||||
ivec2(right, top),
|
||||
ivec2(right, bottom),
|
||||
ivec2(left, bottom),
|
||||
ivec2(left, top)
|
||||
);
|
||||
|
||||
void main() {
|
||||
ivec2 pos = vertex_pos_map[gl_VertexID];
|
||||
texcoord = vec2(src_rect[pos.x], src_rect[pos.y]);
|
||||
gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1);
|
||||
}
|
||||
13
kitty/blit_fragment.glsl
Normal file
13
kitty/blit_fragment.glsl
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
|
||||
uniform sampler2D image;
|
||||
|
||||
in vec2 texcoord;
|
||||
out vec4 output_color;
|
||||
|
||||
void main() {
|
||||
vec4 color_premul = texture(image, texcoord);
|
||||
output_color = vec4_premul(linear2srgb(color_premul.rgb / color_premul.a), color_premul.a);
|
||||
}
|
||||
2
kitty/blit_vertex.glsl
Normal file
2
kitty/blit_vertex.glsl
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
uniform vec4 src_rect, dest_rect;
|
||||
#pragma kitty_include_shader <blit_common_vertex.glsl>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
in vec4 color;
|
||||
out vec4 final_color;
|
||||
in vec4 color_premul;
|
||||
out vec4 output_premul;
|
||||
|
||||
void main() {
|
||||
final_color = color;
|
||||
output_premul = color_premul;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
uniform uvec2 viewport;
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
#define DEFAULT_BG 0
|
||||
#define ACTIVE_BORDER_COLOR 1
|
||||
#define INACTIVE_BORDER_COLOR 2
|
||||
#define WINDOW_BACKGROUND_PLACEHOLDER 3
|
||||
#define BELL_BORDER_COLOR 4
|
||||
#define TAB_BAR_BG_COLOR 5
|
||||
#define TAB_BAR_MARGIN_COLOR 6
|
||||
#define TAB_BAR_EDGE_LEFT_COLOR 7
|
||||
#define TAB_BAR_EDGE_RIGHT_COLOR 8
|
||||
uniform uint colors[9];
|
||||
uniform float background_opacity;
|
||||
uniform float tint_opacity, tint_premult;
|
||||
uniform float gamma_lut[256];
|
||||
|
||||
in vec4 rect; // left, top, right, bottom
|
||||
in uint rect_color;
|
||||
out vec4 color;
|
||||
out vec4 color_premul;
|
||||
|
||||
// indices into the rect vector
|
||||
const int LEFT = 0;
|
||||
|
|
@ -25,8 +35,8 @@ float to_color(uint c) {
|
|||
return gamma_lut[c & FF];
|
||||
}
|
||||
|
||||
float is_integer_value(uint c, float x) {
|
||||
return 1. - step(0.5, abs(float(c) - x));
|
||||
float is_integer_value(uint c, int x) {
|
||||
return 1. - step(0.5, abs(float(c) - float(x)));
|
||||
}
|
||||
|
||||
vec3 as_color_vector(uint c, int shift) {
|
||||
|
|
@ -39,14 +49,13 @@ void main() {
|
|||
vec3 window_bg = as_color_vector(rect_color, 24);
|
||||
uint rc = rect_color & FF;
|
||||
vec3 color3 = as_color_vector(colors[rc], 16);
|
||||
float is_window_bg = is_integer_value(rc, 3.);
|
||||
float is_default_bg = is_integer_value(rc, 0.);
|
||||
color3 = is_window_bg * window_bg + (1. - is_window_bg) * color3;
|
||||
// Border must be always drawn opaque
|
||||
float is_border_bg = 1. - step(0.5, abs((float(rc) - 2.) * (float(rc) - 1.) * (float(rc) - 4.))); // 1 if rc in (1, 2, 4) else 0
|
||||
float final_opacity = is_default_bg * tint_opacity + (1. - is_default_bg) * background_opacity;
|
||||
final_opacity = is_border_bg + (1. - is_border_bg) * final_opacity;
|
||||
float final_premult_opacity = is_default_bg * tint_premult + (1. - is_default_bg) * background_opacity;
|
||||
final_premult_opacity = is_border_bg + (1. - is_border_bg) * final_premult_opacity;
|
||||
color = vec4(color3 * final_premult_opacity, final_opacity);
|
||||
float is_window_bg = is_integer_value(rc, WINDOW_BACKGROUND_PLACEHOLDER); // used by window padding areas
|
||||
float is_default_bg = is_integer_value(rc, DEFAULT_BG);
|
||||
color3 = if_one_then(is_window_bg, window_bg, color3);
|
||||
// Actual border quads must be always drawn opaque
|
||||
float is_not_a_border = zero_or_one(abs(
|
||||
(float(rc) - ACTIVE_BORDER_COLOR) * (float(rc) - INACTIVE_BORDER_COLOR) * (float(rc) - BELL_BORDER_COLOR)
|
||||
));
|
||||
float final_opacity = if_one_then(is_not_a_border, background_opacity, 1.);
|
||||
color_premul = vec4_premul(color3, final_opacity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from collections.abc import Iterable, Sequence
|
||||
from collections.abc import Iterable
|
||||
from enum import IntFlag
|
||||
from functools import partial
|
||||
from typing import NamedTuple
|
||||
|
||||
from .fast_data_types import BORDERS_PROGRAM, add_borders_rect, get_options, init_borders_program, os_window_has_background_image
|
||||
from .fast_data_types import BORDERS_PROGRAM, get_options, init_borders_program, set_borders_rects
|
||||
from .shaders import program_for
|
||||
from .typing_compat import LayoutType
|
||||
from .utils import color_as_int
|
||||
|
|
@ -25,17 +26,17 @@ class Border(NamedTuple):
|
|||
color: BorderColor
|
||||
|
||||
|
||||
def vertical_edge(os_window_id: int, tab_id: int, color: int, width: int, top: int, bottom: int, left: int) -> None:
|
||||
def vertical_edge(rects: list[Border], color: BorderColor, width: int, top: int, bottom: int, left: int) -> None:
|
||||
if width > 0:
|
||||
add_borders_rect(os_window_id, tab_id, left, top, left + width, bottom, color)
|
||||
rects.append(Border(left, top, left + width, bottom, color))
|
||||
|
||||
|
||||
def horizontal_edge(os_window_id: int, tab_id: int, color: int, height: int, left: int, right: int, top: int) -> None:
|
||||
def horizontal_edge(rects: list[Border], color: BorderColor, height: int, left: int, right: int, top: int) -> None:
|
||||
if height > 0:
|
||||
add_borders_rect(os_window_id, tab_id, left, top, right, top + height, color)
|
||||
rects.append(Border(left, top, right, top + height, color))
|
||||
|
||||
|
||||
def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], wg: WindowGroup, borders: bool = False) -> None:
|
||||
def add_borders(rects: list[Border], color: BorderColor, wg: WindowGroup) -> None:
|
||||
geometry = wg.geometry
|
||||
if geometry is None:
|
||||
return
|
||||
|
|
@ -47,19 +48,20 @@ def draw_edges(os_window_id: int, tab_id: int, colors: Sequence[int], wg: Window
|
|||
right = lr + pr
|
||||
bt = geometry.bottom
|
||||
bottom = bt + pb
|
||||
if borders:
|
||||
width = wg.effective_border()
|
||||
bt = bottom
|
||||
lr = right
|
||||
left -= width
|
||||
top -= width
|
||||
right += width
|
||||
bottom += width
|
||||
pl = pr = pb = pt = width
|
||||
horizontal_edge(os_window_id, tab_id, colors[1], pt, left, right, top)
|
||||
horizontal_edge(os_window_id, tab_id, colors[3], pb, left, right, bt)
|
||||
vertical_edge(os_window_id, tab_id, colors[0], pl, top, bottom, left)
|
||||
vertical_edge(os_window_id, tab_id, colors[2], pr, top, bottom, lr)
|
||||
h = partial(horizontal_edge, rects, color)
|
||||
v = partial(vertical_edge, rects, color)
|
||||
width = wg.effective_border()
|
||||
bt = bottom
|
||||
lr = right
|
||||
left -= width
|
||||
top -= width
|
||||
right += width
|
||||
bottom += width
|
||||
pl = pr = pb = pt = width
|
||||
h(pt, left, right, top)
|
||||
h(pb, left, right, bt)
|
||||
v(pl, top, bottom, left)
|
||||
v(pr, top, bottom, lr)
|
||||
|
||||
|
||||
def load_borders_program() -> None:
|
||||
|
|
@ -83,13 +85,10 @@ class Borders:
|
|||
opts = get_options()
|
||||
draw_active_borders = opts.active_border_color is not None
|
||||
draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1
|
||||
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
|
||||
has_background_image = os_window_has_background_image(self.os_window_id)
|
||||
if not has_background_image or opts.background_tint > 0.0:
|
||||
for br in current_layout.blank_rects:
|
||||
add_borders_rect(self.os_window_id, self.tab_id, *br, BorderColor.default_bg)
|
||||
for tbr in tab_bar_rects:
|
||||
add_borders_rect(self.os_window_id, self.tab_id, *tbr)
|
||||
rects: list[Border] = []
|
||||
for br in current_layout.blank_rects:
|
||||
rects.append(Border(*br, BorderColor.default_bg))
|
||||
rects.extend(tab_bar_rects)
|
||||
bw = 0
|
||||
groups = tuple(all_windows.iter_all_layoutable_groups(only_visible=True))
|
||||
if groups:
|
||||
|
|
@ -106,13 +105,9 @@ class Borders:
|
|||
color = BorderColor.active
|
||||
else:
|
||||
color = BorderColor.bell if wg.needs_attention else BorderColor.inactive
|
||||
draw_edges(self.os_window_id, self.tab_id, (color, color, color, color), wg, borders=True)
|
||||
if not has_background_image:
|
||||
# Draw the background rectangles over the padding region
|
||||
colors = window_bg, window_bg, window_bg, window_bg
|
||||
draw_edges(self.os_window_id, self.tab_id, colors, wg)
|
||||
add_borders(rects, color, wg)
|
||||
|
||||
if draw_minimal_borders:
|
||||
for border_line in current_layout.get_minimal_borders(all_windows):
|
||||
left, top, right, bottom = border_line.edges
|
||||
add_borders_rect(self.os_window_id, self.tab_id, left, top, right, bottom, border_line.color)
|
||||
rects.append(Border(*border_line.edges, border_line.color))
|
||||
set_borders_rects(self.os_window_id, self.tab_id, rects)
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ from .utils import (
|
|||
parse_uri_list,
|
||||
platform_window_id,
|
||||
safe_print,
|
||||
sanitize_url_for_dispay_to_user,
|
||||
sanitize_url_for_display_to_user,
|
||||
shlex_split,
|
||||
startup_notification_handler,
|
||||
timed_debug_print,
|
||||
|
|
@ -916,7 +916,7 @@ class Boss:
|
|||
window.send_cmd_response(response)
|
||||
|
||||
def mark_os_window_for_close(self, os_window_id: int, request_type: int = IMPERATIVE_CLOSE_REQUESTED) -> None:
|
||||
if self.current_visual_select is not None and self.current_visual_select.os_window_id == os_window_id and request_type == IMPERATIVE_CLOSE_REQUESTED:
|
||||
if self.current_visual_select is not None and self.current_visual_select.os_window_id == os_window_id:
|
||||
self.cancel_current_visual_select()
|
||||
mark_os_window_for_close(os_window_id, request_type)
|
||||
|
||||
|
|
@ -1504,6 +1504,7 @@ class Boss:
|
|||
if self.current_visual_select:
|
||||
self.current_visual_select.cancel()
|
||||
self.current_visual_select = None
|
||||
self.mappings.pop_keyboard_mode_if_is('__visual_select__')
|
||||
|
||||
def visual_window_select_action(
|
||||
self, tab: Tab,
|
||||
|
|
@ -1827,6 +1828,8 @@ class Boss:
|
|||
if tm is None:
|
||||
self.mark_os_window_for_close(os_window_id)
|
||||
return
|
||||
if self.current_visual_select is not None and self.current_visual_select.os_window_id == os_window_id:
|
||||
self.cancel_current_visual_select()
|
||||
active_window = tm.active_window
|
||||
windows = []
|
||||
for tab in tm:
|
||||
|
|
@ -3160,8 +3163,8 @@ class Boss:
|
|||
pass
|
||||
mouse_discard_event = discard_event
|
||||
|
||||
def sanitize_url_for_dispay_to_user(self, url: str) -> str:
|
||||
return sanitize_url_for_dispay_to_user(url)
|
||||
def sanitize_url_for_display_to_user(self, url: str) -> str:
|
||||
return sanitize_url_for_display_to_user(url)
|
||||
|
||||
def on_system_color_scheme_change(self, appearance: ColorSchemes, is_initial_value: bool) -> None:
|
||||
theme_colors.on_system_color_scheme_change(appearance, is_initial_value)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,3 @@
|
|||
#define PHASE_BOTH 1
|
||||
#define PHASE_BACKGROUND 2
|
||||
#define PHASE_SPECIAL 3
|
||||
#define PHASE_FOREGROUND 4
|
||||
|
||||
#define PHASE {WHICH_PHASE}
|
||||
#define HAS_TRANSPARENCY {TRANSPARENT}
|
||||
#define DO_FG_OVERRIDE {DO_FG_OVERRIDE}
|
||||
#define FG_OVERRIDE_THRESHOLD {FG_OVERRIDE_THRESHOLD}
|
||||
#define FG_OVERRIDE_ALGO {FG_OVERRIDE_ALGO}
|
||||
|
|
@ -19,16 +12,12 @@
|
|||
#define USE_SELECTION_FG
|
||||
#define NUM_COLORS 256
|
||||
|
||||
#if (PHASE == PHASE_BOTH) || (PHASE == PHASE_BACKGROUND) || (PHASE == PHASE_SPECIAL)
|
||||
#define NEEDS_BACKROUND
|
||||
#if {ONLY_BACKGROUND} == 1
|
||||
#define ONLY_BACKGROUND
|
||||
#endif
|
||||
|
||||
#if (PHASE == PHASE_BOTH) || (PHASE == PHASE_FOREGROUND)
|
||||
#define NEEDS_FOREGROUND
|
||||
#endif
|
||||
|
||||
#if (HAS_TRANSPARENCY == 1)
|
||||
#define TRANSPARENT
|
||||
#if {ONLY_FOREGROUND} == 1
|
||||
#define ONLY_FOREGROUND
|
||||
#endif
|
||||
|
||||
// sRGB luminance values
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
#pragma kitty_include_shader <cell_defines.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
in vec3 background;
|
||||
in float draw_bg;
|
||||
in float bg_alpha;
|
||||
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
uniform sampler2DArray sprites;
|
||||
uniform float text_contrast;
|
||||
uniform float text_gamma_adjustment;
|
||||
uniform sampler2DArray sprites;
|
||||
|
||||
in vec3 background;
|
||||
in vec4 effective_background_premul;
|
||||
#ifndef ONLY_BACKGROUND
|
||||
in float effective_text_alpha;
|
||||
in vec3 sprite_pos;
|
||||
in vec3 underline_pos;
|
||||
|
|
@ -24,49 +24,6 @@ in float colored_sprite;
|
|||
|
||||
out vec4 output_color;
|
||||
|
||||
// Util functions {{{
|
||||
vec4 vec4_premul(vec3 rgb, float a) {
|
||||
return vec4(rgb * a, a);
|
||||
}
|
||||
|
||||
vec4 vec4_premul(vec4 rgba) {
|
||||
return vec4(rgba.rgb * rgba.a, rgba.a);
|
||||
}
|
||||
// }}}
|
||||
|
||||
|
||||
/*
|
||||
* Explanation of rendering:
|
||||
* There are two types of rendering, single pass and multi-pass. Multi-pass rendering is used when there
|
||||
* are images that are below the foreground. Single pass rendering has PHASE=PHASE_BOTH. Otherwise, there
|
||||
* are three passes, PHASE=PHASE_BACKGROUND, PHASE=PHASE_SPECIAL, PHASE=PHASE_FOREGROUND.
|
||||
* 1) Single pass -- this path is used when there are either no images, or all images are
|
||||
* drawn on top of text. In this case, there is a single pass,
|
||||
* of this shader with cell foreground and background colors blended directly.
|
||||
* Expected output is either opaque colors or pre-multiplied colors.
|
||||
*
|
||||
* 2) Interleaved -- this path is used if background is not opaque and there are images or
|
||||
* if the background is opaque but there are images under text. Rendering happens in
|
||||
* multiple passes drawing the background and foreground separately and blending.
|
||||
*
|
||||
* 2a) Opaque bg with images under text
|
||||
* There are multiple passes, each pass is blended onto the previous using the opaque blend func (alpha, 1- alpha). TRANSPARENT is not
|
||||
* defined in the shaders.
|
||||
* 1) Draw background for all cells
|
||||
* 2) Draw the images that are supposed to be below both the background and text, if any. This happens in the graphics shader
|
||||
* 3) Draw the background of cells that don't have the default background if any images were drawn in 2 above
|
||||
* 4) Draw the images that are supposed to be below text but not background, again in graphics shader.
|
||||
* 5) Draw the special cells (selection/cursor). Output is same as from step 1, with bg_alpha 1 for special cells and 0 otherwise
|
||||
* 6) Draw the foreground -- expected output is color with alpha premultiplied which is blended using the premult blend func
|
||||
* 7) Draw the images that are supposed to be above text again in the graphics shader
|
||||
*
|
||||
* 2b) Transparent bg with images
|
||||
* Same as (2a) except blending is done with PREMULT_BLEND and TRANSPARENT is defined in the shaders. background_opacity
|
||||
* is applied to default colored background cells in step (1).
|
||||
*/
|
||||
|
||||
// foreground functions {{{
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
// Scaling factor for the extra text-alpha adjustment for luminance-difference.
|
||||
const float text_gamma_scaling = 0.5;
|
||||
|
||||
|
|
@ -75,6 +32,7 @@ float clamp_to_unit_float(float x) {
|
|||
return clamp(x, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
#ifndef ONLY_BACKGROUND
|
||||
#if TEXT_NEW_GAMMA == 1
|
||||
vec4 foreground_contrast(vec4 over, vec3 under) {
|
||||
float under_luminance = dot(under, Y);
|
||||
|
|
@ -128,60 +86,26 @@ vec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) {
|
|||
// to improve legibility based on the source and destination colors
|
||||
return foreground_contrast(text_fg, bg);
|
||||
}
|
||||
#endif // ifndef ONLY_BACKGROUND
|
||||
|
||||
#endif
|
||||
// end foreground functions }}}
|
||||
|
||||
float adjust_alpha_for_incorrect_blending_by_compositor(float text_fg_alpha, float final_alpha) {
|
||||
// Adjust the transparent alpha-channel to account for incorrect
|
||||
// gamma-blending performed by the compositor (true for at least wlroots, picom)
|
||||
// We have a linear alpha channel apply the sRGB curve to it once again to compensate
|
||||
// for the incorrect blending in the compositor.
|
||||
// We apply the correction only if there was actual text at this pixel, so as to not make
|
||||
// background_opacity non-linear
|
||||
// See https://github.com/kovidgoyal/kitty/issues/6209 for discussion.
|
||||
// ans = text_fg_alpha * linear2srgb(final_alpha) + (1 - text_fg_alpha) * final_alpha
|
||||
return mix(final_alpha, linear2srgb(final_alpha), text_fg_alpha);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 final_color;
|
||||
#if (PHASE == PHASE_BOTH)
|
||||
#ifdef ONLY_FOREGROUND
|
||||
vec4 ans_premul;
|
||||
#else
|
||||
vec4 ans_premul = effective_background_premul;
|
||||
#endif
|
||||
|
||||
#ifndef ONLY_BACKGROUND
|
||||
// blend in the foreground color
|
||||
vec4 text_fg = load_text_foreground_color();
|
||||
text_fg = adjust_foreground_contrast_with_background(text_fg, background);
|
||||
vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);
|
||||
#ifdef TRANSPARENT
|
||||
final_color = alpha_blend_premul(text_fg_premul, vec4_premul(background, bg_alpha));
|
||||
final_color.a = adjust_alpha_for_incorrect_blending_by_compositor(text_fg_premul.a, final_color.a);
|
||||
#ifdef ONLY_FOREGROUND
|
||||
ans_premul = text_fg_premul;
|
||||
#else
|
||||
final_color = alpha_blend_premul(text_fg_premul, background);
|
||||
ans_premul = alpha_blend_premul(text_fg_premul, ans_premul);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (PHASE == PHASE_SPECIAL)
|
||||
#ifdef TRANSPARENT
|
||||
final_color = vec4_premul(background, bg_alpha);
|
||||
#else
|
||||
final_color = vec4(background, bg_alpha);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (PHASE == PHASE_BACKGROUND)
|
||||
#ifdef TRANSPARENT
|
||||
final_color = vec4_premul(background, bg_alpha);
|
||||
#else
|
||||
final_color = vec4(background, draw_bg * bg_alpha);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (PHASE == PHASE_FOREGROUND)
|
||||
vec4 text_fg = load_text_foreground_color();
|
||||
text_fg = adjust_foreground_contrast_with_background(text_fg, background);
|
||||
vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);
|
||||
final_color = text_fg_premul;
|
||||
#ifdef TRANSPARENT
|
||||
final_color.a = adjust_alpha_for_incorrect_blending_by_compositor(text_fg_premul.a, final_color.a);
|
||||
#endif
|
||||
#endif
|
||||
output_color = final_color;
|
||||
#endif // ifndef ONLY_BACKGROUND
|
||||
output_color = ans_premul;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
#extension GL_ARB_explicit_attrib_location : require
|
||||
#pragma kitty_include_shader <cell_defines.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
|
||||
// Inputs {{{
|
||||
layout(std140) uniform CellRenderData {
|
||||
float xstart, ystart, dx, dy, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
||||
float use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
||||
|
||||
uint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
|
||||
|
||||
uint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx, cell_height;
|
||||
uint columns, lines, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx, cell_width, cell_height;
|
||||
uint cursor_x1, cursor_x2, cursor_y1, cursor_y2;
|
||||
float cursor_opacity;
|
||||
float cursor_opacity, inactive_text_alpha, dim_opacity;
|
||||
|
||||
// must have unique entries with 0 being default_bg and unset being UINT32_MAX
|
||||
uint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;
|
||||
|
|
@ -18,12 +19,8 @@ layout(std140) uniform CellRenderData {
|
|||
uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2];
|
||||
};
|
||||
uniform float gamma_lut[256];
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
uniform usampler2D sprite_decorations_map;
|
||||
#endif
|
||||
#if (PHASE == PHASE_BACKGROUND)
|
||||
uniform uint draw_bg_bitfield;
|
||||
#endif
|
||||
uniform usampler2D sprite_decorations_map;
|
||||
|
||||
// Have to use fixed locations here as all variants of the cell program share the same VAOs
|
||||
layout(location=0) in uvec3 colors;
|
||||
|
|
@ -41,12 +38,8 @@ const uvec2 cell_pos_map[] = uvec2[4](
|
|||
|
||||
|
||||
out vec3 background;
|
||||
out float draw_bg;
|
||||
out float bg_alpha;
|
||||
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
uniform float inactive_text_alpha;
|
||||
uniform float dim_opacity;
|
||||
out vec4 effective_background_premul;
|
||||
#ifndef ONLY_BACKGROUND
|
||||
out vec3 sprite_pos;
|
||||
out vec3 underline_pos;
|
||||
out vec3 cursor_pos;
|
||||
|
|
@ -100,7 +93,6 @@ vec3 to_color(uint c, uint defval) {
|
|||
return color_to_vec(resolve_color(c, defval));
|
||||
}
|
||||
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
|
||||
uvec3 to_sprite_coords(uint idx) {
|
||||
uint sprites_per_page = sprites_xnum * sprites_ynum;
|
||||
|
|
@ -144,16 +136,6 @@ uvec2 get_decorations_indices(uint in_url /* [0, 1] */, uint text_attrs) {
|
|||
uint has_underline = uint(step(0.5f, float(underline_style))); // [0, 1]
|
||||
return uvec2(strike_idx, has_underline * (decorations_idx + underline_style));
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 choose_color(float q, vec3 a, vec3 b) {
|
||||
return mix(b, a, q);
|
||||
}
|
||||
|
||||
float choose_alpha(float q, float a, float b) {
|
||||
return mix(b, a, q);
|
||||
}
|
||||
|
||||
|
||||
float is_cursor(uint x, uint y) {
|
||||
uint clamped_x = clamp(x, cursor_x1, cursor_x2);
|
||||
|
|
@ -169,24 +151,23 @@ struct CellData {
|
|||
|
||||
CellData set_vertex_position() {
|
||||
uint instance_id = uint(gl_InstanceID);
|
||||
float dx = 2.0 / float(columns);
|
||||
float dy = 2.0 / float(lines);
|
||||
/* The current cell being rendered */
|
||||
uint r = instance_id / xnum;
|
||||
uint c = instance_id - r * xnum;
|
||||
|
||||
uint row = instance_id / columns;
|
||||
uint column = instance_id - row * columns;
|
||||
/* The position of this vertex, at a corner of the cell */
|
||||
float left = xstart + c * dx;
|
||||
float top = ystart - r * dy;
|
||||
vec2 xpos = vec2(left, left + dx);
|
||||
vec2 ypos = vec2(top, top - dy);
|
||||
float left = -1.0 + column * dx;
|
||||
float top = 1.0 - row * dy;
|
||||
uvec2 pos = cell_pos_map[gl_VertexID];
|
||||
gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1);
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
gl_Position = vec4(vec2(left, left + dx)[pos.x], vec2(top, top - dy)[pos.y], 0, 1);
|
||||
// The character sprite being rendered
|
||||
#ifndef ONLY_BACKGROUND
|
||||
sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK);
|
||||
colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT);
|
||||
#endif
|
||||
float is_block_cursor = step(float(cursor_fg_sprite_idx), 0.5);
|
||||
float has_cursor = is_cursor(c, r);
|
||||
float has_cursor = is_cursor(column, row);
|
||||
return CellData(has_cursor, has_cursor * is_block_cursor, pos);
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +190,7 @@ float calc_background_opacity(uint bg) {
|
|||
}
|
||||
|
||||
// Overriding of foreground colors for contrast requirements {{{
|
||||
#if defined(NEEDS_FOREGROUND) && DO_FG_OVERRIDE == 1
|
||||
#if DO_FG_OVERRIDE == 1
|
||||
#define OVERRIDE_FG_COLORS
|
||||
#pragma kitty_include_shader <hsluv.glsl>
|
||||
#if (FG_OVERRIDE_ALGO == 1)
|
||||
|
|
@ -269,22 +250,22 @@ void main() {
|
|||
bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (ONE - has_mark) * bg_as_uint;
|
||||
vec3 bg = color_to_vec(bg_as_uint);
|
||||
uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]);
|
||||
float cell_has_default_bg = 1.f - step(1.f, abs(float(bg_as_uint - bg_colors0))); // 1 if has default bg else 0
|
||||
// }}}
|
||||
|
||||
// Foreground {{{
|
||||
#ifdef NEEDS_FOREGROUND
|
||||
// Foreground
|
||||
#ifndef ONLY_BACKGROUND // background does not depend on foreground
|
||||
fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (ONE - has_mark) * fg_as_uint;
|
||||
foreground = color_to_vec(fg_as_uint);
|
||||
float has_dim = float((text_attrs >> DIM_SHIFT) & ONE);
|
||||
effective_text_alpha = inactive_text_alpha * mix(1.0, dim_opacity, has_dim);
|
||||
float in_url = float((is_selected & TWO) >> 1);
|
||||
decoration_fg = choose_color(in_url, color_to_vec(url_color), to_color(colors[2], fg_as_uint));
|
||||
decoration_fg = if_one_then(in_url, color_to_vec(url_color), to_color(colors[2], fg_as_uint));
|
||||
// Selection
|
||||
vec3 selection_color = choose_color(use_cell_bg_for_selection_fg, bg, color_to_vec(highlight_fg));
|
||||
selection_color = choose_color(use_cell_fg_for_selection_fg, foreground, selection_color);
|
||||
foreground = choose_color(float(is_selected & ONE), selection_color, foreground);
|
||||
decoration_fg = choose_color(float(is_selected & ONE), selection_color, decoration_fg);
|
||||
vec3 selection_color = if_one_then(use_cell_bg_for_selection_fg, bg, color_to_vec(highlight_fg));
|
||||
selection_color = if_one_then(use_cell_fg_for_selection_fg, foreground, selection_color);
|
||||
foreground = if_one_then(float(is_selected & ONE), selection_color, foreground);
|
||||
decoration_fg = if_one_then(float(is_selected & ONE), selection_color, decoration_fg);
|
||||
// Underline and strike through (rendered via sprites)
|
||||
uvec2 decs = get_decorations_indices(uint(in_url), text_attrs);
|
||||
strike_pos = to_sprite_pos(cell_data.pos, decs[0]);
|
||||
|
|
@ -294,51 +275,43 @@ void main() {
|
|||
// Cursor
|
||||
cursor_color_premult = vec4(color_to_vec(cursor_bg) * cursor_opacity, cursor_opacity);
|
||||
vec3 final_cursor_text_color = mix(foreground, color_to_vec(cursor_fg), cursor_opacity);
|
||||
foreground = choose_color(cell_data.has_block_cursor, final_cursor_text_color, foreground);
|
||||
decoration_fg = choose_color(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg);
|
||||
foreground = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, foreground);
|
||||
decoration_fg = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg);
|
||||
cursor_pos = to_sprite_pos(cell_data.pos, cursor_fg_sprite_idx * uint(cell_data.has_cursor));
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
// Background {{{
|
||||
float orig_bg_alpha = 1;
|
||||
#if PHASE == PHASE_BOTH && !defined(TRANSPARENT) // fast case single pass opaque background
|
||||
bg_alpha = 1;
|
||||
draw_bg = 1;
|
||||
#else
|
||||
bg_alpha = calc_background_opacity(bg_as_uint);
|
||||
orig_bg_alpha = bg_alpha;
|
||||
#if (PHASE == PHASE_BACKGROUND)
|
||||
// draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells
|
||||
float cell_has_non_default_bg = step(1.f, abs(float(bg_as_uint - bg_colors0))); // 0 if has default bg else 1
|
||||
uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + (1.f - cell_has_non_default_bg)); // 1 if has default bg else 2
|
||||
draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask));
|
||||
#else
|
||||
draw_bg = 1;
|
||||
#endif
|
||||
|
||||
float bg_alpha = calc_background_opacity(bg_as_uint);
|
||||
// we use max so that opacity of the block cursor cell background goes from bg_alpha to 1
|
||||
float effective_cursor_opacity = max(cursor_opacity, bg_alpha);
|
||||
// is_special_cell is either 0 or 1
|
||||
float is_special_cell = cell_data.has_block_cursor + float(is_selected & ONE);
|
||||
#if PHASE == PHASE_SPECIAL
|
||||
// Only special cells must be drawn and they must have bg_alpha 1
|
||||
bg_alpha = step(0.5, is_special_cell); // bg_alpha = 1 if is_special_cell else 0
|
||||
#else
|
||||
is_special_cell += float(is_reversed); // bg_alpha should be 1 for reverse video cells as well
|
||||
is_special_cell = step(0.5, is_special_cell); // is_special_cell = 1 if is_special_cell else 0
|
||||
bg_alpha = bg_alpha * (1. - float(is_special_cell)) + is_special_cell; // bg_alpha = 1 if is_special_cell else bg_alpha
|
||||
#endif
|
||||
bg_alpha *= draw_bg;
|
||||
#endif // ends fast case #if else
|
||||
is_special_cell += float(is_reversed); // reverse video cells should be opaque as well
|
||||
is_special_cell = zero_or_one(is_special_cell);
|
||||
cell_has_default_bg = if_one_then(is_special_cell, 0., cell_has_default_bg);
|
||||
|
||||
// special cells must always be fully opaque, otherwise leave bg_alpha untouched
|
||||
bg_alpha = if_one_then(is_special_cell, 1.f, bg_alpha);
|
||||
// Selection and cursor
|
||||
bg = choose_color(float(is_selected & ONE), choose_color(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg);
|
||||
background = choose_color(cell_data.has_block_cursor, mix(bg, color_to_vec(cursor_bg), cursor_opacity), bg);
|
||||
// we use max so that opacity of the block cursor cell background goes from orig_bg_alpha to 1
|
||||
float effective_cursor_opacity = max(cursor_opacity, orig_bg_alpha) * draw_bg;
|
||||
bg_alpha = choose_alpha(cell_data.has_block_cursor, effective_cursor_opacity, bg_alpha);
|
||||
bg_alpha = if_one_then(cell_data.has_block_cursor, effective_cursor_opacity, bg_alpha);
|
||||
bg = if_one_then(float(is_selected & ONE), if_one_then(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg);
|
||||
vec3 background_rgb = if_one_then(cell_data.has_block_cursor, mix(bg, color_to_vec(cursor_bg), cursor_opacity), bg);
|
||||
background = background_rgb;
|
||||
// }}}
|
||||
|
||||
#ifdef OVERRIDE_FG_COLORS
|
||||
decoration_fg = override_foreground_color(decoration_fg, background);
|
||||
foreground = override_foreground_color(foreground, background);
|
||||
#if !defined(ONLY_BACKGROUND) && defined(OVERRIDE_FG_COLORS)
|
||||
decoration_fg = override_foreground_color(decoration_fg, background_rgb);
|
||||
foreground = override_foreground_color(foreground, background_rgb);
|
||||
#endif
|
||||
|
||||
#if !defined(ONLY_FOREGROUND)
|
||||
vec4 bgpremul = vec4_premul(background_rgb, bg_alpha);
|
||||
// draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells
|
||||
float cell_has_non_default_bg = 1.f - cell_has_default_bg;
|
||||
uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + cell_has_default_bg); // 1 if has default bg else 2
|
||||
float draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask));
|
||||
bgpremul *= draw_bg;
|
||||
effective_background_premul = bgpremul;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include "state.h"
|
||||
#include "threading.h"
|
||||
#include "screen.h"
|
||||
#include "fonts.h"
|
||||
#include "monotonic.h"
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -710,12 +709,15 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
|
|||
#define TD os_window->tab_bar_render_data
|
||||
bool needs_render = os_window->needs_render;
|
||||
os_window->needs_render = false;
|
||||
os_window->needs_layers = os_window->is_semi_transparent || os_window->live_resize.in_progress || (
|
||||
os_window->bgimage && os_window->bgimage->texture_id > 0);
|
||||
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) {
|
||||
if (!os_window->tab_bar_data_updated) {
|
||||
call_boss(update_tab_bar_data, "K", os_window->id);
|
||||
os_window->tab_bar_data_updated = true;
|
||||
}
|
||||
if (send_cell_data_to_gpu(TD.vao_idx, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true;
|
||||
if (send_cell_data_to_gpu(TD.vao_idx, TD.screen, os_window)) needs_render = true;
|
||||
os_window->needs_layers = os_window->needs_layers || screen_needs_rendering_in_layers(os_window, NULL, TD.screen);
|
||||
}
|
||||
if (OPT(mouse_hide.hide_wait) > 0 && !is_mouse_hidden(os_window)) {
|
||||
if (now - os_window->last_mouse_activity_at >= OPT(mouse_hide.hide_wait)) hide_mouse(os_window);
|
||||
|
|
@ -730,6 +732,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
|
|||
Window *w = tab->windows + i;
|
||||
#define WD w->render_data
|
||||
if (w->visible && WD.screen) {
|
||||
os_window->needs_layers = os_window->needs_layers || screen_needs_rendering_in_layers(os_window, w, WD.screen);
|
||||
screen_check_pause_rendering(WD.screen, now);
|
||||
*num_visible_windows += 1;
|
||||
color_type window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg).rgb;
|
||||
|
|
@ -781,36 +784,21 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
|
|||
set_maximum_wait(min_gap);
|
||||
}
|
||||
}
|
||||
if (send_cell_data_to_gpu(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true;
|
||||
if (send_cell_data_to_gpu(WD.vao_idx, WD.screen, os_window)) needs_render = true;
|
||||
if (WD.screen->start_visual_bell_at != 0) needs_render = true;
|
||||
}
|
||||
}
|
||||
return needs_render;
|
||||
}
|
||||
|
||||
static void
|
||||
draw_resizing_text(OSWindow *w) {
|
||||
if (monotonic() - w->created_at > ms_to_monotonic_t(1000) && w->live_resize.num_of_resize_events > 1) {
|
||||
char text[32] = {0};
|
||||
unsigned int width = w->live_resize.width, height = w->live_resize.height;
|
||||
snprintf(text, sizeof(text), "%u x %u cells", width / w->fonts_data->fcm.cell_width, height / w->fonts_data->fcm.cell_height);
|
||||
StringCanvas rendered = render_simple_text(w->fonts_data, text);
|
||||
if (rendered.canvas) {
|
||||
draw_centered_alpha_mask(w, width, height, rendered.width, rendered.height, rendered.canvas, OPT(background_opacity));
|
||||
free(rendered.canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
|
||||
// ensure all pixels are cleared to background color at least once in every buffer
|
||||
if (os_window->clear_count++ < 3) blank_os_window(os_window);
|
||||
setup_os_window_for_rendering(os_window, true);
|
||||
Tab *tab = os_window->tabs + os_window->active_tab;
|
||||
BorderRects *br = &tab->border_rects;
|
||||
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
|
||||
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window);
|
||||
br->is_dirty = false;
|
||||
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, &TD, os_window, true, true, false, NULL);
|
||||
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(&TD, os_window, true, true, false, NULL);
|
||||
unsigned int num_of_visible_windows = 0;
|
||||
Window *active_window = NULL;
|
||||
for (unsigned int i = 0; i < tab->num_windows; i++) { if (tab->windows[i].visible) num_of_visible_windows++; }
|
||||
|
|
@ -819,13 +807,13 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co
|
|||
if (w->visible && WD.screen) {
|
||||
bool is_active_window = i == tab->active_window;
|
||||
if (is_active_window) active_window = w;
|
||||
draw_cells(WD.vao_idx, &WD, os_window, is_active_window, false, num_of_visible_windows == 1, w);
|
||||
draw_cells(&WD, os_window, is_active_window, false, num_of_visible_windows == 1, w);
|
||||
if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT);
|
||||
w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
|
||||
}
|
||||
}
|
||||
if (OPT(cursor_trail) && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail, active_window);
|
||||
if (os_window->live_resize.in_progress) draw_resizing_text(os_window);
|
||||
setup_os_window_for_rendering(os_window, false);
|
||||
swap_window_buffers(os_window);
|
||||
os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id;
|
||||
os_window->focused_at_last_render = os_window->is_focused;
|
||||
|
|
@ -866,11 +854,9 @@ render_os_window(OSWindow *w, monotonic_t now, bool scan_for_animated_images) {
|
|||
}
|
||||
w->render_calls++;
|
||||
make_os_window_context_current(w);
|
||||
if (w->live_resize.in_progress) blank_os_window(w);
|
||||
bool needs_render = w->redraw_count > 0 || w->live_resize.in_progress;
|
||||
if (w->viewport_size_dirty) {
|
||||
w->clear_count = 0;
|
||||
update_surface_size(w->viewport_width, w->viewport_height, 0);
|
||||
set_gpu_viewport(w->viewport_width, w->viewport_height);
|
||||
w->viewport_size_dirty = false;
|
||||
needs_render = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#include <float.h>
|
||||
#include "state.h"
|
||||
|
||||
#define WD w->render_data
|
||||
#define EDGE(axis, index) ct->cursor_edge_##axis[index]
|
||||
|
||||
inline static float
|
||||
norm(float x, float y) {
|
||||
return sqrtf(x * x + y * y);
|
||||
|
|
@ -8,32 +11,32 @@ norm(float x, float y) {
|
|||
|
||||
static void
|
||||
update_cursor_trail_target(CursorTrail *ct, Window *w) {
|
||||
#define EDGE(axis, index) ct->cursor_edge_##axis[index]
|
||||
#define WD w->render_data
|
||||
float dy = 2.f/WD.screen->lines, dx = 2.f/WD.screen->columns;
|
||||
float left = FLT_MAX, right = FLT_MAX, top = FLT_MAX, bottom = FLT_MAX;
|
||||
static const float xstart = -1.f, ystart = 1.f;
|
||||
switch (WD.screen->cursor_render_info.shape) {
|
||||
case CURSOR_BLOCK:
|
||||
case CURSOR_HOLLOW:
|
||||
case CURSOR_BEAM:
|
||||
case CURSOR_UNDERLINE:
|
||||
left = WD.xstart + WD.screen->cursor_render_info.x * WD.dx;
|
||||
bottom = WD.ystart - (WD.screen->cursor_render_info.y + 1) * WD.dy;
|
||||
left = xstart + WD.screen->cursor_render_info.x * dx;
|
||||
bottom = ystart - (WD.screen->cursor_render_info.y + 1) * dy;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (WD.screen->cursor_render_info.shape) {
|
||||
case CURSOR_BLOCK:
|
||||
case CURSOR_HOLLOW:
|
||||
right = left + WD.dx;
|
||||
top = bottom + WD.dy;
|
||||
right = left + dx;
|
||||
top = bottom + dy;
|
||||
break;
|
||||
case CURSOR_BEAM:
|
||||
right = left + WD.dx / WD.screen->cell_size.width * OPT(cursor_beam_thickness);
|
||||
top = bottom + WD.dy;
|
||||
right = left + dx / WD.screen->cell_size.width * OPT(cursor_beam_thickness);
|
||||
top = bottom + dy;
|
||||
break;
|
||||
case CURSOR_UNDERLINE:
|
||||
right = left + WD.dx;
|
||||
top = bottom + WD.dy / WD.screen->cell_size.height * OPT(cursor_underline_thickness);
|
||||
right = left + dx;
|
||||
top = bottom + dy / WD.screen->cell_size.height * OPT(cursor_underline_thickness);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -53,8 +56,9 @@ should_skip_cursor_trail_update(CursorTrail *ct, Window *w, OSWindow *os_window)
|
|||
}
|
||||
|
||||
if (OPT(cursor_trail_start_threshold) > 0 && !ct->needs_render) {
|
||||
int dx = (int)round((ct->corner_x[0] - EDGE(x, 1)) / WD.dx);
|
||||
int dy = (int)round((ct->corner_y[0] - EDGE(y, 0)) / WD.dy);
|
||||
float fdy = 2.f/WD.screen->lines, fdx = 2.f/WD.screen->columns;
|
||||
int dx = (int)round((ct->corner_x[0] - EDGE(x, 1)) / fdx);
|
||||
int dy = (int)round((ct->corner_y[0] - EDGE(y, 0)) / fdy);
|
||||
if (abs(dx) + abs(dy) <= OPT(cursor_trail_start_threshold)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -140,10 +144,11 @@ static void
|
|||
update_cursor_trail_needs_render(CursorTrail *ct, Window *w) {
|
||||
static const int corner_index[2][4] = {{1, 1, 0, 0}, {0, 1, 1, 0}};
|
||||
ct->needs_render = false;
|
||||
float dy = 2.f/WD.screen->lines, dx = 2.f/WD.screen->columns;
|
||||
|
||||
// check if any corner is still far from the cursor corner, so it should be rendered
|
||||
const float dx_threshold = WD.dx / WD.screen->cell_size.width * 0.5f;
|
||||
const float dy_threshold = WD.dy / WD.screen->cell_size.height * 0.5f;
|
||||
const float dx_threshold = dx / WD.screen->cell_size.width * 0.5f;
|
||||
const float dy_threshold = dy / WD.screen->cell_size.height * 0.5f;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
float dx = fabsf(EDGE(x, corner_index[0][i]) - ct->corner_x[i]);
|
||||
float dy = fabsf(EDGE(y, corner_index[1][i]) - ct->corner_y[i]);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import termios
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, NewType, Optional, Tuple, TypedDict, Union, overload
|
||||
|
||||
from kitty.borders import Border
|
||||
from kitty.boss import Boss
|
||||
from kitty.fonts import VariableData
|
||||
from kitty.fonts.render import FontObject
|
||||
|
|
@ -270,15 +271,15 @@ NO_CURSOR_SHAPE: int
|
|||
CURSOR_UNDERLINE: int
|
||||
DECAWM: int
|
||||
BGIMAGE_PROGRAM: int
|
||||
CELL_BG_PROGRAM: int
|
||||
CELL_FG_PROGRAM: int
|
||||
CELL_PROGRAM: int
|
||||
CELL_SPECIAL_PROGRAM: int
|
||||
CELL_FG_PROGRAM: int
|
||||
CELL_BG_PROGRAM: int
|
||||
BLIT_PROGRAM: int
|
||||
DECORATION: int
|
||||
DIM: int
|
||||
GRAPHICS_ALPHA_MASK_PROGRAM: int
|
||||
GRAPHICS_PREMULT_PROGRAM: int
|
||||
GRAPHICS_PROGRAM: int
|
||||
GRAPHICS_PREMULT_PROGRAM: int
|
||||
MARK: int
|
||||
MARK_MASK: int
|
||||
DECORATION_MASK: int
|
||||
|
|
@ -545,19 +546,12 @@ def set_os_window_chrome(os_window_id: int) -> bool:
|
|||
pass
|
||||
|
||||
|
||||
def add_borders_rect(
|
||||
os_window_id: int, tab_id: int, left: int, top: int, right: int,
|
||||
bottom: int, color: int
|
||||
) -> None:
|
||||
pass
|
||||
def set_borders_rects(os_window_id: int, tab_id: int, rects: list[Border]) -> None: ...
|
||||
|
||||
|
||||
def init_borders_program() -> None:
|
||||
pass
|
||||
|
||||
def init_trail_program() -> None:
|
||||
pass
|
||||
|
||||
def os_window_has_background_image(os_window_id: int) -> bool:
|
||||
pass
|
||||
|
||||
|
|
@ -607,7 +601,7 @@ def create_os_window(
|
|||
wm_class_name: str,
|
||||
wm_class_class: str,
|
||||
window_state: Optional[int] = WINDOW_NORMAL,
|
||||
load_programs: Optional[Callable[[bool], None]] = None,
|
||||
load_programs: Optional[Callable[[], None]] = None,
|
||||
x: Optional[int] = None,
|
||||
y: Optional[int] = None,
|
||||
disallow_override_title: bool = False,
|
||||
|
|
|
|||
105
kitty/gl-wrapper.h
generated
105
kitty/gl-wrapper.h
generated
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Loader generated by glad 2.0.2 on Tue Dec 20 16:15:26 2022
|
||||
* Loader generated by glad 2.0.8 on Mon Aug 4 08:35:54 2025
|
||||
*
|
||||
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
|
||||
*
|
||||
|
|
@ -146,7 +146,7 @@ extern "C" {
|
|||
#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor)
|
||||
#define GLAD_VERSION_MAJOR(version) (version / 10000)
|
||||
#define GLAD_VERSION_MINOR(version) (version % 10000)
|
||||
#define GLAD_GENERATOR_VERSION "2.0.2"
|
||||
#define GLAD_GENERATOR_VERSION "2.0.8"
|
||||
typedef void (*GLADapiproc)(void);
|
||||
typedef GLADapiproc (*GLADloadfunc)(const char *name);
|
||||
typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name);
|
||||
|
|
@ -9991,35 +9991,24 @@ static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr)
|
|||
glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, "glPopDebugGroup");
|
||||
glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, "glPushDebugGroup");
|
||||
}
|
||||
#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
|
||||
#define GLAD_GL_IS_SOME_NEW_VERSION 1
|
||||
#else
|
||||
#define GLAD_GL_IS_SOME_NEW_VERSION 0
|
||||
#endif
|
||||
static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) {
|
||||
#if GLAD_GL_IS_SOME_NEW_VERSION
|
||||
if(GLAD_VERSION_MAJOR(version) < 3) {
|
||||
#else
|
||||
GLAD_UNUSED(version);
|
||||
GLAD_UNUSED(out_num_exts_i);
|
||||
GLAD_UNUSED(out_exts_i);
|
||||
#endif
|
||||
if (glad_glGetString == NULL) {
|
||||
return 0;
|
||||
static void glad_gl_free_extensions(char **exts_i) {
|
||||
if (exts_i != NULL) {
|
||||
unsigned int index;
|
||||
for(index = 0; exts_i[index]; index++) {
|
||||
free((void *) (exts_i[index]));
|
||||
}
|
||||
*out_exts = (const char *)glad_glGetString(GL_EXTENSIONS);
|
||||
#if GLAD_GL_IS_SOME_NEW_VERSION
|
||||
} else {
|
||||
free((void *)exts_i);
|
||||
exts_i = NULL;
|
||||
}
|
||||
}
|
||||
static int glad_gl_get_extensions( const char **out_exts, char ***out_exts_i) {
|
||||
#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
|
||||
if (glad_glGetStringi != NULL && glad_glGetIntegerv != NULL) {
|
||||
unsigned int index = 0;
|
||||
unsigned int num_exts_i = 0;
|
||||
char **exts_i = NULL;
|
||||
if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) {
|
||||
return 0;
|
||||
}
|
||||
glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i);
|
||||
if (num_exts_i > 0) {
|
||||
exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i));
|
||||
}
|
||||
exts_i = (char **) malloc((num_exts_i + 1) * (sizeof *exts_i));
|
||||
if (exts_i == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -10027,29 +10016,37 @@ static int glad_gl_get_extensions( int version, const char **out_exts, unsigned
|
|||
const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index);
|
||||
size_t len = strlen(gl_str_tmp) + 1;
|
||||
char *local_str = (char*) malloc(len * sizeof(char));
|
||||
if(local_str != NULL) {
|
||||
memcpy(local_str, gl_str_tmp, len * sizeof(char));
|
||||
if(local_str == NULL) {
|
||||
exts_i[index] = NULL;
|
||||
glad_gl_free_extensions(exts_i);
|
||||
return 0;
|
||||
}
|
||||
memcpy(local_str, gl_str_tmp, len * sizeof(char));
|
||||
exts_i[index] = local_str;
|
||||
}
|
||||
*out_num_exts_i = num_exts_i;
|
||||
exts_i[index] = NULL;
|
||||
*out_exts_i = exts_i;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
GLAD_UNUSED(out_exts_i);
|
||||
#endif
|
||||
if (glad_glGetString == NULL) {
|
||||
return 0;
|
||||
}
|
||||
*out_exts = (const char *)glad_glGetString(GL_EXTENSIONS);
|
||||
return 1;
|
||||
}
|
||||
static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) {
|
||||
if (exts_i != NULL) {
|
||||
static int glad_gl_has_extension(const char *exts, char **exts_i, const char *ext) {
|
||||
if(exts_i) {
|
||||
unsigned int index;
|
||||
for(index = 0; index < num_exts_i; index++) {
|
||||
free((void *) (exts_i[index]));
|
||||
for(index = 0; exts_i[index]; index++) {
|
||||
const char *e = exts_i[index];
|
||||
if(strcmp(e, ext) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
free((void *)exts_i);
|
||||
exts_i = NULL;
|
||||
}
|
||||
}
|
||||
static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) {
|
||||
if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) {
|
||||
} else {
|
||||
const char *extensions;
|
||||
const char *loc;
|
||||
const char *terminator;
|
||||
|
|
@ -10069,32 +10066,23 @@ static int glad_gl_has_extension(int version, const char *exts, unsigned int num
|
|||
}
|
||||
extensions = terminator;
|
||||
}
|
||||
} else {
|
||||
unsigned int index;
|
||||
for(index = 0; index < num_exts_i; index++) {
|
||||
const char *e = exts_i[index];
|
||||
if(strcmp(e, ext) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) {
|
||||
return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);
|
||||
}
|
||||
static int glad_gl_find_extensions_gl( int version) {
|
||||
static int glad_gl_find_extensions_gl(void) {
|
||||
const char *exts = NULL;
|
||||
unsigned int num_exts_i = 0;
|
||||
char **exts_i = NULL;
|
||||
if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0;
|
||||
GLAD_GL_ARB_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_copy_image");
|
||||
GLAD_GL_ARB_instanced_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_instanced_arrays");
|
||||
GLAD_GL_ARB_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multisample");
|
||||
GLAD_GL_ARB_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_robustness");
|
||||
GLAD_GL_ARB_texture_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_storage");
|
||||
GLAD_GL_KHR_debug = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_debug");
|
||||
glad_gl_free_extensions(exts_i, num_exts_i);
|
||||
if (!glad_gl_get_extensions(&exts, &exts_i)) return 0;
|
||||
GLAD_GL_ARB_copy_image = glad_gl_has_extension(exts, exts_i, "GL_ARB_copy_image");
|
||||
GLAD_GL_ARB_instanced_arrays = glad_gl_has_extension(exts, exts_i, "GL_ARB_instanced_arrays");
|
||||
GLAD_GL_ARB_multisample = glad_gl_has_extension(exts, exts_i, "GL_ARB_multisample");
|
||||
GLAD_GL_ARB_robustness = glad_gl_has_extension(exts, exts_i, "GL_ARB_robustness");
|
||||
GLAD_GL_ARB_texture_storage = glad_gl_has_extension(exts, exts_i, "GL_ARB_texture_storage");
|
||||
GLAD_GL_KHR_debug = glad_gl_has_extension(exts, exts_i, "GL_KHR_debug");
|
||||
glad_gl_free_extensions(exts_i);
|
||||
return 1;
|
||||
}
|
||||
static int glad_gl_find_core_gl(void) {
|
||||
|
|
@ -10135,7 +10123,6 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
|
|||
int version;
|
||||
glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString");
|
||||
if(glad_glGetString == NULL) return 0;
|
||||
if(glad_glGetString(GL_VERSION) == NULL) return 0;
|
||||
version = glad_gl_find_core_gl();
|
||||
glad_gl_load_GL_VERSION_1_0(load, userptr);
|
||||
glad_gl_load_GL_VERSION_1_1(load, userptr);
|
||||
|
|
@ -10147,7 +10134,7 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
|
|||
glad_gl_load_GL_VERSION_2_1(load, userptr);
|
||||
glad_gl_load_GL_VERSION_3_0(load, userptr);
|
||||
glad_gl_load_GL_VERSION_3_1(load, userptr);
|
||||
if (!glad_gl_find_extensions_gl(version)) return 0;
|
||||
if (!glad_gl_find_extensions_gl()) return 0;
|
||||
glad_gl_load_GL_ARB_copy_image(load, userptr);
|
||||
glad_gl_load_GL_ARB_instanced_arrays(load, userptr);
|
||||
glad_gl_load_GL_ARB_multisample(load, userptr);
|
||||
|
|
|
|||
124
kitty/gl.c
124
kitty/gl.c
|
|
@ -10,6 +10,7 @@
|
|||
#include <stddef.h>
|
||||
#include "glfw-wrapper.h"
|
||||
#include "state.h"
|
||||
#include "png-reader.h"
|
||||
|
||||
// GL setup and error handling {{{
|
||||
static void
|
||||
|
|
@ -76,15 +77,28 @@ gl_init(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
update_surface_size(int w, int h, GLuint offscreen_texture_id) {
|
||||
glViewport(0, 0, w, h);
|
||||
if (offscreen_texture_id) {
|
||||
glBindTexture(GL_TEXTURE_2D, offscreen_texture_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
static const char*
|
||||
check_framebuffer_status(void) {
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_COMPLETE: return NULL;
|
||||
case GL_FRAMEBUFFER_UNDEFINED: return("GL_FRAMEBUFFER_UNDEFINED");
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: return("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: return("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED: return("GL_FRAMEBUFFER_UNSUPPORTED");
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE");
|
||||
default: return("Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
check_framebuffer_status_or_die(void) {
|
||||
const char *err = check_framebuffer_status();
|
||||
if (err != NULL) fatal("Framebuffer not complete with error: %s", err);
|
||||
}
|
||||
|
||||
void
|
||||
free_texture(GLuint *tex_id) {
|
||||
glDeleteTextures(1, tex_id);
|
||||
|
|
@ -97,6 +111,102 @@ free_framebuffer(GLuint *fb_id) {
|
|||
*fb_id = 0;
|
||||
}
|
||||
|
||||
static GLuint output_framebuffer = 0;
|
||||
|
||||
void
|
||||
bind_framebuffer_for_output(unsigned fbid) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbid ? fbid : output_framebuffer);
|
||||
}
|
||||
|
||||
void
|
||||
set_framebuffer_to_use_for_output(unsigned fbid) {
|
||||
output_framebuffer = fbid;
|
||||
}
|
||||
|
||||
static void
|
||||
set_blending(bool allowed) {
|
||||
if (allowed) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } // blending of pre-multiplied colors
|
||||
else { glDisable(GL_BLEND); glBlendFunc(GL_ONE, GL_ZERO); } // no blending
|
||||
}
|
||||
|
||||
void
|
||||
draw_quad(bool blend, unsigned instance_count) {
|
||||
set_blending(blend);
|
||||
if (instance_count) glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, instance_count);
|
||||
else glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
}
|
||||
|
||||
static struct {
|
||||
GLsizei items[16][4];
|
||||
size_t used;
|
||||
} saved_viewports;
|
||||
|
||||
void
|
||||
set_gpu_viewport(unsigned w, unsigned h) { glViewport(0, 0, w, h); }
|
||||
|
||||
void
|
||||
save_viewport_using_bottom_left_origin(GLsizei newx, GLsizei newy, GLsizei width, GLsizei height) {
|
||||
if (saved_viewports.used >= arraysz(saved_viewports.items)) fatal("Too many nested saved viewports");
|
||||
GLsizei *saved_viewport = saved_viewports.items[saved_viewports.used++];
|
||||
glGetIntegerv(GL_VIEWPORT, saved_viewport);
|
||||
glViewport(newx, newy, width, height);
|
||||
}
|
||||
|
||||
void
|
||||
save_viewport_using_top_left_origin(GLsizei newx, GLsizei newy, GLsizei width, GLsizei height, GLsizei full_framebuffer_height) {
|
||||
// Converts the viewport defined by the specified arguments which are
|
||||
// assumed to be in the usual co-ord system with origin at top left to the
|
||||
// OpenGL viewport co-ord system with origin at bottom left.
|
||||
// Use restore_viewport() to restore the viewport to what it was before.
|
||||
if (saved_viewports.used >= arraysz(saved_viewports.items)) fatal("Too many nested saved viewports");
|
||||
GLsizei *saved_viewport = saved_viewports.items[saved_viewports.used++];
|
||||
glGetIntegerv(GL_VIEWPORT, saved_viewport);
|
||||
newy = full_framebuffer_height - (newy + height);
|
||||
glViewport(newx, newy, width, height);
|
||||
}
|
||||
|
||||
void
|
||||
restore_viewport(void) {
|
||||
if (!saved_viewports.used) fatal("Trying to restore a viewport when none is saved");
|
||||
GLsizei *saved_viewport = saved_viewports.items[--saved_viewports.used];
|
||||
glViewport(saved_viewport[0], saved_viewport[1], saved_viewport[2], saved_viewport[3]);
|
||||
}
|
||||
|
||||
static float
|
||||
linear_to_srgb(float c) { return (c <= 0.0031308f) ? 12.92f * c : 1.055f * powf(c, 1.0f / 2.4f) - 0.055f; }
|
||||
|
||||
void
|
||||
save_texture_as_png(uint32_t texture_id, const char *filename) {
|
||||
GLint prev_tex = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
int width = 0, height = 0;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
|
||||
size_t sz = width * height * sizeof(uint32_t);
|
||||
uint32_t* data = malloc(sz);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
// assume data is linear and pre0multiplied
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
uint32_t px = data[i];
|
||||
uint8_t r = (px >> 0) & 0xFF; uint8_t g = (px >> 8) & 0xFF; uint8_t b = (px >> 16) & 0xFF;
|
||||
uint8_t a = (px >> 24) & 0xFF; float alpha = a / 255.0f;
|
||||
float rf = 0, gf = 0, bf = 0;
|
||||
if (alpha > 0.0f) { rf = (r / 255.0f) / alpha; gf = (g / 255.0f) / alpha; bf = (b / 255.0f) / alpha; }
|
||||
rf = linear_to_srgb(rf); gf = linear_to_srgb(gf); bf = linear_to_srgb(bf);
|
||||
r = (uint8_t)(rf*255); g = (uint8_t)(gf * 255); b = (uint8_t)(bf * 255);
|
||||
data[i] = (r << 0) | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
|
||||
const char *png = png_from_32bit_rgba(data, width, height, &sz, true);
|
||||
if (!sz) fatal("Failed to save PNG to %s with error: %s", filename, png);
|
||||
free(data);
|
||||
FILE* file = fopen(filename, "wb");
|
||||
fwrite(png, 1, sz, file);
|
||||
fclose(file);
|
||||
glBindTexture(GL_TEXTURE_2D, prev_tex);
|
||||
}
|
||||
|
||||
|
||||
// }}}
|
||||
|
||||
// Programs {{{
|
||||
|
|
@ -180,7 +290,7 @@ attrib_location(int program, const char *name) {
|
|||
GLuint
|
||||
block_index(int program, const char *name) {
|
||||
GLuint ans = glGetUniformBlockIndex(programs[program].id, name);
|
||||
if (ans == GL_INVALID_INDEX) { fatal("Could not find block index"); }
|
||||
if (ans == GL_INVALID_INDEX) { fatal("Could not find block index for %s", name); }
|
||||
return ans;
|
||||
}
|
||||
|
||||
|
|
|
|||
10
kitty/gl.h
10
kitty/gl.h
|
|
@ -32,7 +32,9 @@ typedef struct {
|
|||
|
||||
void gl_init(void);
|
||||
const char* gl_version_string(void);
|
||||
void update_surface_size(int w, int h, GLuint offscreen_texture_id);
|
||||
void set_gpu_viewport(unsigned w, unsigned h);
|
||||
void draw_quad(bool blend, unsigned instance_count);
|
||||
void save_texture_as_png(uint32_t texture_id, const char *filename);
|
||||
void free_texture(GLuint *tex_id);
|
||||
void free_framebuffer(GLuint *fb_id);
|
||||
void remove_vao(ssize_t vao_idx);
|
||||
|
|
@ -57,3 +59,9 @@ void bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index)
|
|||
void unbind_vertex_array(void);
|
||||
void unbind_program(void);
|
||||
GLuint compile_shaders(GLenum shader_type, GLsizei count, const GLchar * const * string);
|
||||
void save_viewport_using_top_left_origin(GLsizei x, GLsizei y, GLsizei width, GLsizei height, GLsizei full_framebuffer_height);
|
||||
void save_viewport_using_bottom_left_origin(GLsizei x, GLsizei y, GLsizei width, GLsizei height);
|
||||
void check_framebuffer_status_or_die(void);
|
||||
void restore_viewport(void);
|
||||
void bind_framebuffer_for_output(unsigned fbid);
|
||||
void set_framebuffer_to_use_for_output(unsigned fbid);
|
||||
|
|
|
|||
12
kitty/glfw.c
12
kitty/glfw.c
|
|
@ -425,7 +425,7 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) {
|
|||
window->live_resize.width = MAX(0, width); window->live_resize.height = MAX(0, height);
|
||||
window->live_resize.num_of_resize_events++;
|
||||
make_os_window_context_current(window);
|
||||
update_surface_size(width, height, 0);
|
||||
set_gpu_viewport(width, height);
|
||||
request_tick_callback();
|
||||
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
|
||||
global_state.callback_os_window = NULL;
|
||||
|
|
@ -738,7 +738,7 @@ apple_url_open_callback(const char* url) {
|
|||
|
||||
|
||||
bool
|
||||
draw_window_title(OSWindow *window UNUSED, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {
|
||||
draw_window_title(double font_sz_pts UNUSED, double ydpi UNUSED, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {
|
||||
static char buf[2048];
|
||||
strip_csi_(text, buf, arraysz(buf));
|
||||
return cocoa_render_line_of_text(buf, fg, bg, output_buf, width, height);
|
||||
|
|
@ -786,11 +786,11 @@ draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t b
|
|||
}
|
||||
|
||||
bool
|
||||
draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {
|
||||
draw_window_title(double font_sz_pts, double ydpi, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) {
|
||||
if (!ensure_csd_title_render_ctx()) return false;
|
||||
static char buf[2048];
|
||||
strip_csi_(text, buf, arraysz(buf));
|
||||
unsigned px_sz = (unsigned)(window->fonts_data->font_sz_in_pts * window->fonts_data->logical_dpi_y / 72.);
|
||||
unsigned px_sz = (unsigned)(font_sz_pts * ydpi / 72.);
|
||||
px_sz = MIN(px_sz, 3 * height / 4);
|
||||
#define RGB2BGR(x) (x & 0xFF000000) | ((x & 0xFF0000) >> 16) | (x & 0x00FF00) | ((x & 0x0000FF) << 16)
|
||||
bool ok = render_single_line(csd_title_render_ctx, buf, px_sz, RGB2BGR(fg), RGB2BGR(bg), output_buf, width, height, 0, 0, 0, false);
|
||||
|
|
@ -1460,7 +1460,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
|||
glfwMakeContextCurrent(glfw_window);
|
||||
if (is_first_window) gl_init();
|
||||
// Will make the GPU automatically apply SRGB gamma curve on the resulting framebuffer
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
bool is_semi_transparent = glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER);
|
||||
// blank the window once so that there is no initial flash of color
|
||||
// changing, in case the background color is not black
|
||||
|
|
@ -1487,7 +1486,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
|||
}
|
||||
}
|
||||
if (is_first_window) {
|
||||
PyObject *ret = PyObject_CallFunction(load_programs, "O", is_semi_transparent ? Py_True : Py_False);
|
||||
PyObject *ret = PyObject_CallNoArgs(load_programs);
|
||||
if (ret == NULL) return NULL;
|
||||
Py_DECREF(ret);
|
||||
get_platform_dependent_config_values(glfw_window);
|
||||
|
|
@ -1495,7 +1494,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
|||
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &encoding);
|
||||
if (encoding != GL_SRGB) log_error("The output buffer does not support sRGB color encoding, colors will be incorrect.");
|
||||
is_first_window = false;
|
||||
|
||||
}
|
||||
OSWindow *w = add_os_window();
|
||||
w->handle = glfw_window;
|
||||
|
|
|
|||
|
|
@ -212,10 +212,15 @@ ref_by_client_id(const Image *img, uint32_t id) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
set_layers_dirty(GraphicsManager *self) {
|
||||
self->layers_dirty = true;
|
||||
}
|
||||
|
||||
static image_map_itr
|
||||
remove_image_itr(GraphicsManager *self, image_map_itr i) {
|
||||
free_image(self, i.data->val);
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
return vt_erase_itr(&self->images_by_internal_id, i);
|
||||
}
|
||||
|
||||
|
|
@ -697,7 +702,9 @@ upload_to_gpu(GraphicsManager *self, Image *img, const bool is_opaque, const boo
|
|||
if (!make_window_context_current(self->window_id)) return;
|
||||
self->context_made_current_for_this_command = true;
|
||||
}
|
||||
if (img->texture) send_image_to_gpu(&img->texture->id, data, img->width, img->height, is_opaque, is_4byte_aligned, true, REPEAT_CLAMP);
|
||||
if (img->texture) {
|
||||
send_image_to_gpu(&img->texture->id, data, img->width, img->height, is_opaque, is_4byte_aligned, true, REPEAT_CLAMP);
|
||||
}
|
||||
}
|
||||
|
||||
static Image*
|
||||
|
|
@ -720,7 +727,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||
img->current_frame_shown_at = 0;
|
||||
img->extra_framecnt = 0;
|
||||
*is_dirty = true;
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
} else {
|
||||
img->client_id = iid;
|
||||
img->client_number = g->image_number;
|
||||
|
|
@ -1010,7 +1017,7 @@ void grman_put_cell_image(GraphicsManager *self, uint32_t screen_row,
|
|||
ImageRef *real_ref = create_ref(img, &ref);
|
||||
|
||||
img->atime = monotonic();
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
|
||||
update_src_rect(real_ref, img);
|
||||
update_dest_rect(real_ref, ref.num_cols, ref.num_rows, cell);
|
||||
|
|
@ -1103,7 +1110,7 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
|||
if (ref == NULL) ref = create_ref(img, NULL);
|
||||
|
||||
*is_dirty = true;
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
img->atime = monotonic();
|
||||
ref->src_x = g->x_offset; ref->src_y = g->y_offset; ref->src_width = g->width ? g->width : img->width; ref->src_height = g->height ? g->height : img->height;
|
||||
ref->src_width = MIN(ref->src_width, img->width - ((float)img->width > ref->src_x ? ref->src_x : (float)img->width));
|
||||
|
|
@ -1140,17 +1147,6 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
|||
return img->client_id;
|
||||
}
|
||||
|
||||
void
|
||||
scale_rendered_graphic(ImageRenderData *rd, float xstart, float ystart, float x_scale, float y_scale) {
|
||||
// Scale the graphic so that it appears at the same position and size during a live resize
|
||||
// this means scale factors are applied to both the position and size of the graphic.
|
||||
float width = rd->dest_rect.right - rd->dest_rect.left, height = rd->dest_rect.bottom - rd->dest_rect.top;
|
||||
rd->dest_rect.left = xstart + (rd->dest_rect.left - xstart) * x_scale;
|
||||
rd->dest_rect.right = rd->dest_rect.left + width * x_scale;
|
||||
rd->dest_rect.top = ystart + (rd->dest_rect.top - ystart) * y_scale;
|
||||
rd->dest_rect.bottom = rd->dest_rect.top + height * y_scale;
|
||||
}
|
||||
|
||||
void
|
||||
gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom) {
|
||||
// For dest rect: x-axis is from -1 to 1, y axis is from 1 to -1
|
||||
|
|
@ -1203,7 +1199,7 @@ resolve_parent_offset(const GraphicsManager *self, const ImageRef *ref, int32_t
|
|||
|
||||
bool
|
||||
grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows, CellPixelSize cell) {
|
||||
if (self->last_scrolled_by != scrolled_by) self->layers_dirty = true;
|
||||
if (self->last_scrolled_by != scrolled_by) set_layers_dirty(self);
|
||||
self->last_scrolled_by = scrolled_by;
|
||||
if (!self->layers_dirty) return false;
|
||||
self->layers_dirty = false;
|
||||
|
|
@ -1900,7 +1896,7 @@ filter_refs(GraphicsManager *self, const void* data, bool free_images, bool (*fi
|
|||
for (ref_map_itr ri = vt_first(&img->refs_by_internal_id); !vt_is_end(ri); ) { ImageRef *ref = ri.data->val;
|
||||
if (filter_func(ref, img, data, cell)) {
|
||||
ri = remove_ref_itr(img, ri);
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
matched = true;
|
||||
} else ri = vt_next(ri);
|
||||
}
|
||||
|
|
@ -1980,7 +1976,7 @@ scroll_filter_margins_func(ImageRef* ref, Image* img, const void* data, CellPixe
|
|||
void
|
||||
grman_scroll_images(GraphicsManager *self, const ScrollData *data, CellPixelSize cell) {
|
||||
if (vt_size(&self->images_by_internal_id)) {
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
modify_refs(self, data, data->has_margins ? scroll_filter_margins_func : scroll_filter_func, cell);
|
||||
}
|
||||
}
|
||||
|
|
@ -2131,7 +2127,7 @@ handle_delete_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c
|
|||
for (ref_map_itr ri = vt_first(&img->refs_by_internal_id); !vt_is_end(ri); ) { ImageRef *ref = ri.data->val;
|
||||
if (!g->placement_id || g->placement_id == ref->client_id) {
|
||||
ri = remove_ref_itr(img, ri);
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
} else ri = vt_next(ri);
|
||||
}
|
||||
if (!vt_size(&img->refs_by_internal_id) && (g->delete_action == 'N' || img->client_id == 0)) remove_image(self, img);
|
||||
|
|
@ -2162,7 +2158,7 @@ end:
|
|||
void
|
||||
grman_resize(GraphicsManager *self, index_type old_lines UNUSED, index_type lines UNUSED, index_type old_columns, index_type columns, index_type num_content_lines_before, index_type num_content_lines_after) {
|
||||
ImageRef *ref; Image *img;
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
if (columns == old_columns && num_content_lines_before > num_content_lines_after) {
|
||||
const unsigned int vertical_shrink_size = num_content_lines_before - num_content_lines_after;
|
||||
iter_images(self) { img = i.data->val;
|
||||
|
|
@ -2177,7 +2173,7 @@ grman_resize(GraphicsManager *self, index_type old_lines UNUSED, index_type line
|
|||
void
|
||||
grman_rescale(GraphicsManager *self, CellPixelSize cell) {
|
||||
ImageRef *ref; Image *img;
|
||||
self->layers_dirty = true;
|
||||
set_layers_dirty(self);
|
||||
iter_images(self) { img = i.data->val;
|
||||
iter_refs(img) { ref = i.data->val;
|
||||
if (ref->is_virtual_ref || is_cell_image(ref)) continue;
|
||||
|
|
@ -2464,13 +2460,14 @@ init_graphics(PyObject *module) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void grman_mark_layers_dirty(GraphicsManager *self) { self->layers_dirty = true; }
|
||||
void grman_mark_layers_dirty(GraphicsManager *self) { set_layers_dirty(self); }
|
||||
void grman_set_window_id(GraphicsManager *self, id_type id) { self->window_id = id; }
|
||||
bool grman_has_images(GraphicsManager *self) { return self->num_of_below_refs + self->num_of_negative_refs + self->num_of_positive_refs > 0; }
|
||||
GraphicsRenderData grman_render_data(GraphicsManager *self) {
|
||||
GraphicsRenderData ans = {
|
||||
.count=self->render_data.count, .capacity=self->render_data.capacity, .images=self->render_data.item,
|
||||
.num_of_below_refs=self->num_of_below_refs, .num_of_negative_refs=self->num_of_negative_refs,
|
||||
.num_of_positive_refs=self->num_of_positive_refs
|
||||
.num_of_positive_refs=self->num_of_positive_refs,
|
||||
};
|
||||
return ans;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ typedef struct {
|
|||
uint32_t texture_id;
|
||||
unsigned int height, width;
|
||||
uint8_t* bitmap;
|
||||
uint32_t refcnt;
|
||||
uint32_t refcnt, id;
|
||||
size_t mmap_size;
|
||||
} BackgroundImage;
|
||||
|
||||
|
|
@ -170,26 +170,29 @@ gl_size(const unsigned int sz, const unsigned int viewport_size) {
|
|||
}
|
||||
|
||||
static inline float
|
||||
clamp_position_to_nearest_pixel(float pos, const unsigned int viewport_size) {
|
||||
// clamp the specified opengl position to the nearest pixel
|
||||
const float px = 2.f / viewport_size;
|
||||
const float distance = pos + 1.f;
|
||||
const float num_of_pixels = roundf(distance / px);
|
||||
return -1.f + num_of_pixels * px;
|
||||
}
|
||||
|
||||
static inline float
|
||||
gl_pos_x(const unsigned int px_from_left_margin, const unsigned int viewport_size) {
|
||||
gl_pos_x(const int px_from_left_margin, const unsigned int viewport_size) {
|
||||
const float px = 2.f / viewport_size;
|
||||
return -1.f + px_from_left_margin * px;
|
||||
}
|
||||
|
||||
static inline float
|
||||
gl_pos_y(const unsigned int px_from_top_margin, const unsigned int viewport_size) {
|
||||
tex_pos_x(const int px_from_left_margin, const unsigned texture_width) {
|
||||
return px_from_left_margin / (float)texture_width;
|
||||
}
|
||||
|
||||
static inline float
|
||||
gl_pos_y(const int px_from_top_margin, const unsigned int viewport_size) {
|
||||
const float px = 2.f / viewport_size;
|
||||
return 1.f - px_from_top_margin * px;
|
||||
}
|
||||
|
||||
static inline float
|
||||
tex_pos_y(const int px_from_top_margin, const unsigned texture_height) {
|
||||
const int px_from_bottom_margin = texture_height - px_from_top_margin;
|
||||
return px_from_bottom_margin / (float)texture_height;
|
||||
}
|
||||
|
||||
|
||||
typedef struct GraphicsRenderData {
|
||||
size_t count, capacity, num_of_below_refs, num_of_negative_refs, num_of_positive_refs;
|
||||
ImageRenderData *images;
|
||||
|
|
@ -211,8 +214,8 @@ bool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, u
|
|||
bool png_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);
|
||||
bool image_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);
|
||||
bool scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap, bool os_window_context_set);
|
||||
void scale_rendered_graphic(ImageRenderData*, float xstart, float ystart, float x_scale, float y_scale);
|
||||
void grman_pause_rendering(GraphicsManager *self, GraphicsManager *dest);
|
||||
void grman_mark_layers_dirty(GraphicsManager *self);
|
||||
void grman_set_window_id(GraphicsManager *self, id_type id);
|
||||
bool grman_has_images(GraphicsManager *self);
|
||||
GraphicsRenderData grman_render_data(GraphicsManager *self);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
#define ALPHA_TYPE
|
||||
|
||||
uniform sampler2D image;
|
||||
|
|
@ -6,22 +7,23 @@ uniform sampler2D image;
|
|||
uniform vec3 amask_fg;
|
||||
uniform vec4 amask_bg_premult;
|
||||
#else
|
||||
uniform float inactive_text_alpha;
|
||||
uniform float extra_alpha;
|
||||
#endif
|
||||
|
||||
in vec2 texcoord;
|
||||
out vec4 color;
|
||||
out vec4 output_color;
|
||||
|
||||
void main() {
|
||||
color = texture(image, texcoord);
|
||||
vec4 color = texture(image, texcoord);
|
||||
#ifdef ALPHA_MASK
|
||||
color = vec4(amask_fg, color.r);
|
||||
color = vec4(color.rgb * color.a, color.a);
|
||||
color = vec4_premul(color);
|
||||
color = alpha_blend_premul(color, amask_bg_premult);
|
||||
#else
|
||||
color.a *= inactive_text_alpha;
|
||||
#ifdef PREMULT
|
||||
color = vec4(color.rgb * color.a, color.a);
|
||||
color.a *= extra_alpha;
|
||||
#if TEXTURE_IS_NOT_PREMULTIPLIED
|
||||
color = vec4_premul(color);
|
||||
#endif
|
||||
#endif
|
||||
output_color = color;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,2 @@
|
|||
out vec2 texcoord;
|
||||
uniform vec4 src_rect, dest_rect, viewport;
|
||||
|
||||
#define left 0
|
||||
#define top 1
|
||||
#define right 2
|
||||
#define bottom 3
|
||||
|
||||
const ivec2 vertex_pos_map[4] = ivec2[4](
|
||||
ivec2(right, top),
|
||||
ivec2(right, bottom),
|
||||
ivec2(left, bottom),
|
||||
ivec2(left, top)
|
||||
);
|
||||
|
||||
void main() {
|
||||
ivec2 pos = vertex_pos_map[gl_VertexID];
|
||||
texcoord = vec2(src_rect[pos.x], src_rect[pos.y]);
|
||||
gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1);
|
||||
gl_ClipDistance[left] = gl_Position.x - viewport[left];
|
||||
gl_ClipDistance[right] = viewport[right] - gl_Position.x;
|
||||
gl_ClipDistance[top] = viewport[top] - gl_Position.y;
|
||||
gl_ClipDistance[bottom] = gl_Position.y - viewport[bottom];
|
||||
}
|
||||
uniform vec4 src_rect, dest_rect;
|
||||
#pragma kitty_include_shader <blit_common_vertex.glsl>
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ update_ime_focus(OSWindow *osw, bool focused) {
|
|||
void
|
||||
prepare_ime_position_update_event(OSWindow *osw, Window *w, Screen *screen, GLFWIMEUpdateEvent *ev) {
|
||||
unsigned int cell_width = osw->fonts_data->fcm.cell_width, cell_height = osw->fonts_data->fcm.cell_height;
|
||||
unsigned int left = w->geometry.left, top = w->geometry.top;
|
||||
unsigned int left = w->render_data.geometry.left, top = w->render_data.geometry.top;
|
||||
if (screen_is_overlay_active(screen)) {
|
||||
left += screen->overlay_line.cursor_x * cell_width;
|
||||
top += MIN(screen->overlay_line.ynum + screen->scrolled_by, screen->lines - 1) * cell_height;
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ class Layout:
|
|||
),)
|
||||
geom = layout_single_window(xdecoration_pairs, ydecoration_pairs, xalignment=lgd.alignment_x, yalignment=lgd.alignment_y)
|
||||
wg.set_geometry(geom)
|
||||
if add_blank_rects and wg:
|
||||
if add_blank_rects:
|
||||
self.blank_rects.extend(blank_rects_for_window(geom))
|
||||
|
||||
def xlayout(
|
||||
|
|
|
|||
|
|
@ -13,3 +13,11 @@ float linear2srgb(float x) {
|
|||
|
||||
return mix(lower, upper, step(0.0031308f, x));
|
||||
}
|
||||
|
||||
vec3 linear2srgb(vec3 c) {
|
||||
return vec3(linear2srgb(c.r), linear2srgb(c.g), linear2srgb(c.b));
|
||||
}
|
||||
|
||||
vec3 srgb2linear(vec3 c) {
|
||||
return vec3(srgb2linear(c.r), srgb2linear(c.g), srgb2linear(c.b));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ def set_custom_ibeam_cursor() -> None:
|
|||
log_error(f'Failed to set custom beam cursor with error: {e}')
|
||||
|
||||
|
||||
def load_all_shaders(semi_transparent: bool = False) -> None:
|
||||
def load_all_shaders() -> None:
|
||||
try:
|
||||
load_shader_programs(semi_transparent)
|
||||
load_shader_programs()
|
||||
load_borders_program()
|
||||
except CompileError as err:
|
||||
raise SystemExit(err)
|
||||
|
|
|
|||
|
|
@ -214,22 +214,22 @@ dispatch_mouse_event(Window *w, int button, int count, int modifiers, bool grabb
|
|||
|
||||
static unsigned int
|
||||
window_left(Window *w) {
|
||||
return w->geometry.left - w->padding.left;
|
||||
return w->render_data.geometry.left - w->padding.left;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
window_right(Window *w) {
|
||||
return w->geometry.right + w->padding.right;
|
||||
return w->render_data.geometry.right + w->padding.right;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
window_top(Window *w) {
|
||||
return w->geometry.top - w->padding.top;
|
||||
return w->render_data.geometry.top - w->padding.top;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
window_bottom(Window *w) {
|
||||
return w->geometry.bottom + w->padding.bottom;
|
||||
return w->render_data.geometry.bottom + w->padding.bottom;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -250,7 +250,7 @@ static bool clamp_to_window = false;
|
|||
|
||||
static bool
|
||||
cell_for_pos(Window *w, unsigned int *x, unsigned int *y, bool *in_left_half_of_cell, OSWindow *os_window) {
|
||||
WindowGeometry *g = &w->geometry;
|
||||
WindowGeometry *g = &w->render_data.geometry;
|
||||
Screen *screen = w->render_data.screen;
|
||||
if (!screen) return false;
|
||||
unsigned int qx = 0, qy = 0;
|
||||
|
|
@ -323,8 +323,8 @@ bool
|
|||
drag_scroll(Window *w, OSWindow *frame) {
|
||||
unsigned int margin = frame->fonts_data->fcm.cell_height / 2;
|
||||
double y = frame->mouse_y;
|
||||
bool upwards = y <= (w->geometry.top + margin);
|
||||
if (upwards || y >= w->geometry.bottom - margin) {
|
||||
bool upwards = y <= (w->render_data.geometry.top + margin);
|
||||
if (upwards || y >= w->render_data.geometry.bottom - margin) {
|
||||
if (do_drag_scroll(w, upwards)) {
|
||||
frame->last_mouse_activity_at = monotonic();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1592,11 +1592,7 @@ launch your editor. See also :opt:`transparent_background_colors`.
|
|||
Be aware that using a value less than 1.0 is a (possibly
|
||||
significant) performance hit. When using a low value for this setting, it is
|
||||
desirable that you set the :opt:`background` color to a color the matches the
|
||||
general color of the desktop background, for best text rendering. Note that
|
||||
to workaround window managers not doing gamma-corrected blending kitty
|
||||
makes background_opacity non-linear which means, especially for light backgrounds
|
||||
you might need to make the value much lower than you expect to get good results,
|
||||
see :iss:`6218` for details.
|
||||
general color of the desktop background, for best text rendering.
|
||||
|
||||
If you want to dynamically change transparency of windows, set
|
||||
:opt:`dynamic_background_opacity` to :code:`yes` (this is off by default as it
|
||||
|
|
@ -1632,6 +1628,7 @@ part is optional. When unspecified, the value of :opt:`background_opacity` is us
|
|||
|
||||
transparent_background_colors red@0.5 #00ff00@0.3
|
||||
|
||||
Note that you must also set :opt:`background_opacity` to something less than 1 for this setting to work properly.
|
||||
'''
|
||||
)
|
||||
|
||||
|
|
@ -1675,8 +1672,7 @@ opt('background_tint', '0.0',
|
|||
long_text='''
|
||||
How much to tint the background image by the background color. This option
|
||||
makes it easier to read the text. Tinting is done using the current background
|
||||
color for each window. This option applies only if :opt:`background_opacity` is
|
||||
set and transparent windows are supported or :opt:`background_image` is set.
|
||||
color for each window. This option applies only if :opt:`background_image` is set.
|
||||
Note that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.
|
||||
'''
|
||||
)
|
||||
|
|
|
|||
|
|
@ -131,6 +131,72 @@ err:
|
|||
return;
|
||||
}
|
||||
|
||||
// Structure to hold memory write state
|
||||
typedef struct {
|
||||
unsigned char* buffer;
|
||||
size_t size, capacity;
|
||||
} png_memory_write_state;
|
||||
|
||||
// Custom write function for writing PNG data to memory
|
||||
static void
|
||||
png_write_to_memory(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
png_memory_write_state* state = (png_memory_write_state*)png_get_io_ptr(png_ptr);
|
||||
if (state->size + length > state->capacity) {
|
||||
// Double the capacity or add enough space for the new data, whichever is larger
|
||||
size_t new_capacity = state->capacity * 2;
|
||||
if (new_capacity < state->size + length) new_capacity = state->size + length;
|
||||
unsigned char* new_buffer = realloc(state->buffer, new_capacity);
|
||||
if (!new_buffer) {
|
||||
png_error(png_ptr, "Failed to allocate memory for PNG buffer");
|
||||
return;
|
||||
}
|
||||
state->buffer = new_buffer;
|
||||
state->capacity = new_capacity;
|
||||
}
|
||||
// Copy the data to the buffer
|
||||
memcpy(state->buffer + state->size, data, length);
|
||||
state->size += length;
|
||||
}
|
||||
static void png_flush_memory(png_structp png_ptr) { (void)png_ptr; }
|
||||
|
||||
const char*
|
||||
png_from_32bit_rgba(uint32_t *data, size_t width, size_t height, size_t *out_size, bool flip_vertically) {
|
||||
*out_size = 0;
|
||||
png_memory_write_state state = {.capacity=width*height * sizeof(uint32_t)};
|
||||
state.buffer = malloc(state.capacity);
|
||||
if (!state.buffer) return "Out of memory";
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png_ptr) { free(state.buffer); return "Failed to create PNG write struct"; }
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
free(state.buffer); png_destroy_write_struct(&png_ptr, NULL);
|
||||
return "Failed to create PNG info struct";
|
||||
}
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr); free(state.buffer);
|
||||
return("Error during PNG creation\n");
|
||||
}
|
||||
png_set_write_fn(png_ptr, &state, png_write_to_memory, png_flush_memory);
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGBA,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
// Allocate memory for row pointers
|
||||
png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
|
||||
if (!row_pointers) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
free(state.buffer);
|
||||
return ("Failed to allocate memory for row pointers");
|
||||
}
|
||||
if (flip_vertically) for (size_t y = 0; y < height; y++) row_pointers[height - 1 - y] = (png_byte*)&data[y * width];
|
||||
else for (size_t y = 0; y < height; y++) row_pointers[y] = (png_byte*)&data[y * width];
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
png_write_end(png_ptr, NULL);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
free(row_pointers);
|
||||
*out_size = state.size;
|
||||
return (char*)state.buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
png_error_handler(png_read_data *d UNUSED, const char *code, const char *msg) {
|
||||
if (!PyErr_Occurred()) PyErr_Format(PyExc_ValueError, "[%s] %s", code, msg);
|
||||
|
|
|
|||
|
|
@ -28,3 +28,4 @@ typedef struct png_read_data {
|
|||
} png_read_data;
|
||||
|
||||
void inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz);
|
||||
const char* png_from_32bit_rgba(uint32_t *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "monotonic.h"
|
||||
#include "line-buf.h"
|
||||
#include "history.h"
|
||||
#include "window_logo.h"
|
||||
|
||||
typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;
|
||||
|
||||
|
|
|
|||
1177
kitty/shaders.c
1177
kitty/shaders.c
File diff suppressed because it is too large
Load diff
|
|
@ -10,10 +10,10 @@ from typing import Any, Literal, NamedTuple, Optional
|
|||
from .constants import read_kitty_resource
|
||||
from .fast_data_types import (
|
||||
BGIMAGE_PROGRAM,
|
||||
BLIT_PROGRAM,
|
||||
CELL_BG_PROGRAM,
|
||||
CELL_FG_PROGRAM,
|
||||
CELL_PROGRAM,
|
||||
CELL_SPECIAL_PROGRAM,
|
||||
DECORATION,
|
||||
DECORATION_MASK,
|
||||
DIM,
|
||||
|
|
@ -30,7 +30,6 @@ from .fast_data_types import (
|
|||
compile_program,
|
||||
get_options,
|
||||
init_cell_program,
|
||||
init_trail_program,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -139,7 +138,6 @@ class TextFgOverrideThreshold(NamedTuple):
|
|||
class LoadShaderPrograms:
|
||||
text_fg_override_threshold: TextFgOverrideThreshold = TextFgOverrideThreshold()
|
||||
text_old_gamma: bool = False
|
||||
semi_transparent: bool = False
|
||||
cell_program_replacer: MultiReplacer = null_replacer
|
||||
|
||||
@property
|
||||
|
|
@ -152,10 +150,9 @@ class LoadShaderPrograms:
|
|||
|
||||
def recompile_if_needed(self) -> None:
|
||||
if self.needs_recompile:
|
||||
self(self.semi_transparent, allow_recompile=True)
|
||||
self(allow_recompile=True)
|
||||
|
||||
def __call__(self, semi_transparent: bool = False, allow_recompile: bool = False) -> None:
|
||||
self.semi_transparent = semi_transparent
|
||||
def __call__(self, allow_recompile: bool = False) -> None:
|
||||
opts = get_options()
|
||||
self.text_old_gamma = opts.text_composition_strategy == 'legacy'
|
||||
|
||||
|
|
@ -180,47 +177,40 @@ class LoadShaderPrograms:
|
|||
DECORATION_MASK=DECORATION_MASK,
|
||||
)
|
||||
|
||||
def resolve_cell_defines(which: str, src: str) -> str:
|
||||
def resolve_cell_defines(only_fg: int, only_bg: int, src: str) -> str:
|
||||
r = self.cell_program_replacer.replacements
|
||||
r['WHICH_PHASE'] = f'PHASE_{which}'
|
||||
r['TRANSPARENT'] = '1' if semi_transparent else '0'
|
||||
r['ONLY_FOREGROUND'] = str(only_fg)
|
||||
r['ONLY_BACKGROUND'] = str(only_bg)
|
||||
r['DO_FG_OVERRIDE'] = '1' if self.text_fg_override_threshold.scaled_value else '0'
|
||||
r['FG_OVERRIDE_ALGO'] = '1' if self.text_fg_override_threshold.unit == '%' else '2'
|
||||
r['FG_OVERRIDE_THRESHOLD'] = str(self.text_fg_override_threshold.scaled_value)
|
||||
r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'
|
||||
return self.cell_program_replacer(src)
|
||||
|
||||
for which, p in {
|
||||
'BOTH': CELL_PROGRAM,
|
||||
'BACKGROUND': CELL_BG_PROGRAM,
|
||||
'SPECIAL': CELL_SPECIAL_PROGRAM,
|
||||
'FOREGROUND': CELL_FG_PROGRAM,
|
||||
for prog, (only_fg, only_bg) in {
|
||||
CELL_PROGRAM: (0, 0), CELL_FG_PROGRAM: (1, 0), CELL_BG_PROGRAM: (0, 1),
|
||||
}.items():
|
||||
cell.apply_to_sources(
|
||||
vertex=partial(resolve_cell_defines, which),
|
||||
frag=partial(resolve_cell_defines, which),
|
||||
)
|
||||
cell.compile(p, allow_recompile)
|
||||
|
||||
fn = partial(resolve_cell_defines, only_fg, only_bg)
|
||||
cell.apply_to_sources(vertex=fn, frag=fn)
|
||||
cell.compile(prog, allow_recompile)
|
||||
graphics = program_for('graphics')
|
||||
|
||||
def resolve_graphics_fragment_defines(which: str, f: str) -> str:
|
||||
return f.replace('#define ALPHA_TYPE', f'#define {which}', 1)
|
||||
def resolve_graphics_fragment_defines(which: str, is_premult: bool, f: str) -> str:
|
||||
ans = f.replace('#define ALPHA_TYPE', f'#define {which}', 1)
|
||||
return ans.replace('TEXTURE_IS_NOT_PREMULTIPLIED', '0' if is_premult else '1')
|
||||
|
||||
for which, p in {
|
||||
'SIMPLE': GRAPHICS_PROGRAM,
|
||||
'PREMULT': GRAPHICS_PREMULT_PROGRAM,
|
||||
'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM,
|
||||
for p, (which, is_premult) in {
|
||||
GRAPHICS_PROGRAM: ('IMAGE', False),
|
||||
GRAPHICS_ALPHA_MASK_PROGRAM: ('ALPHA_MASK', False),
|
||||
GRAPHICS_PREMULT_PROGRAM: ('IMAGE', True),
|
||||
}.items():
|
||||
graphics.apply_to_sources(frag=partial(resolve_graphics_fragment_defines, which))
|
||||
graphics.apply_to_sources(frag=partial(resolve_graphics_fragment_defines, which, is_premult))
|
||||
graphics.compile(p, allow_recompile)
|
||||
|
||||
program_for('bgimage').compile(BGIMAGE_PROGRAM, allow_recompile)
|
||||
program_for('tint').compile(TINT_PROGRAM, allow_recompile)
|
||||
init_cell_program()
|
||||
|
||||
program_for('trail').compile(TRAIL_PROGRAM, allow_recompile)
|
||||
init_trail_program()
|
||||
program_for('blit').compile(BLIT_PROGRAM, allow_recompile)
|
||||
init_cell_program()
|
||||
|
||||
|
||||
load_shader_programs = LoadShaderPrograms()
|
||||
|
|
|
|||
|
|
@ -489,7 +489,9 @@ destroy_os_window_item(OSWindow *w) {
|
|||
remove_vao(w->tab_bar_render_data.vao_idx);
|
||||
free(w->tabs); w->tabs = NULL;
|
||||
free_bgimage(&w->bgimage, true);
|
||||
w->bgimage = NULL;
|
||||
zero_at_ptr(&w->bgimage);
|
||||
if (w->indirect_output.texture_id) free_texture(&w->indirect_output.texture_id);
|
||||
if (w->indirect_output.framebuffer_id) free_framebuffer(&w->indirect_output.framebuffer_id);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -562,20 +564,29 @@ swap_tabs(id_type os_window_id, unsigned int a, unsigned int b) {
|
|||
END_WITH_OS_WINDOW
|
||||
}
|
||||
|
||||
static void
|
||||
add_borders_rect(id_type os_window_id, id_type tab_id, uint32_t left, uint32_t top, uint32_t right, uint32_t bottom, uint32_t color) {
|
||||
static PyObject*
|
||||
pyset_borders_rects(PyObject *self UNUSED, PyObject *args) {
|
||||
id_type os_window_id, tab_id;
|
||||
PyObject *rects;
|
||||
if (!PyArg_ParseTuple(args, "KKO!", &os_window_id, &tab_id, &PyList_Type, &rects)) return NULL;
|
||||
WITH_TAB(os_window_id, tab_id)
|
||||
BorderRects *br = &tab->border_rects;
|
||||
br->is_dirty = true;
|
||||
if (!left && !top && !right && !bottom) { br->num_border_rects = 0; return; }
|
||||
br->num_border_rects = PyList_GET_SIZE(rects);
|
||||
ensure_space_for(br, rect_buf, BorderRect, br->num_border_rects + 1, capacity, 32, false);
|
||||
BorderRect *r = br->rect_buf + br->num_border_rects++;
|
||||
r->left = gl_pos_x(left, osw->viewport_width);
|
||||
r->top = gl_pos_y(top, osw->viewport_height);
|
||||
r->right = r->left + gl_size(right - left, osw->viewport_width);
|
||||
r->bottom = r->top - gl_size(bottom - top, osw->viewport_height);
|
||||
r->color = color;
|
||||
for (unsigned i = 0; i < br->num_border_rects; i++) {
|
||||
PyObject *pr = PyList_GET_ITEM(rects, i);
|
||||
unsigned long left, top, right, bottom, color;
|
||||
if (!PyArg_ParseTuple(pr, "kkkkk", &left, &top, &right, &bottom, &color)) return NULL;
|
||||
BorderRect *r = br->rect_buf + i;
|
||||
r->left = gl_pos_x(left, osw->viewport_width);
|
||||
r->top = gl_pos_y(top, osw->viewport_height);
|
||||
r->right = r->left + gl_size(right - left, osw->viewport_width);
|
||||
r->bottom = r->top - gl_size(bottom - top, osw->viewport_height);
|
||||
r->color = color;
|
||||
}
|
||||
END_WITH_TAB
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -789,24 +800,18 @@ PYWRAP1(set_ignore_os_keyboard_processing) {
|
|||
}
|
||||
|
||||
static void
|
||||
init_window_render_data(OSWindow *osw, const WindowGeometry *g, WindowRenderData *d) {
|
||||
d->dx = gl_size(osw->fonts_data->fcm.cell_width, osw->viewport_width);
|
||||
d->dy = gl_size(osw->fonts_data->fcm.cell_height, osw->viewport_height);
|
||||
d->xstart = gl_pos_x(g->left, osw->viewport_width);
|
||||
d->ystart = gl_pos_y(g->top, osw->viewport_height);
|
||||
init_window_render_data(WindowRenderData *d, const WindowGeometry g, Screen *screen) {
|
||||
d->geometry = g;
|
||||
Py_CLEAR(d->screen); d->screen = (Screen*)Py_NewRef(screen);
|
||||
}
|
||||
|
||||
PYWRAP1(set_tab_bar_render_data) {
|
||||
WindowRenderData d = {0};
|
||||
WindowGeometry g = {0};
|
||||
WindowGeometry g;
|
||||
id_type os_window_id;
|
||||
PA("KOIIII", &os_window_id, &d.screen, &g.left, &g.top, &g.right, &g.bottom);
|
||||
Screen *screen;
|
||||
PA("KOIIII", &os_window_id, &screen, &g.left, &g.top, &g.right, &g.bottom);
|
||||
WITH_OS_WINDOW(os_window_id)
|
||||
Py_CLEAR(os_window->tab_bar_render_data.screen);
|
||||
d.vao_idx = os_window->tab_bar_render_data.vao_idx;
|
||||
init_window_render_data(os_window, &g, &d);
|
||||
os_window->tab_bar_render_data = d;
|
||||
Py_INCREF(os_window->tab_bar_render_data.screen);
|
||||
init_window_render_data(&os_window->tab_bar_render_data, g, screen);
|
||||
END_WITH_OS_WINDOW
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
@ -979,23 +984,16 @@ PYWRAP1(set_window_padding) {
|
|||
}
|
||||
|
||||
PYWRAP1(set_window_render_data) {
|
||||
#define A(name) &(d.name)
|
||||
#define B(name) &(g.name)
|
||||
id_type os_window_id, tab_id, window_id;
|
||||
WindowRenderData d = {0};
|
||||
WindowGeometry g = {0};
|
||||
PA("KKKOIIII", &os_window_id, &tab_id, &window_id, A(screen), B(left), B(top), B(right), B(bottom));
|
||||
Screen *screen;
|
||||
PA("KKKOIIII", &os_window_id, &tab_id, &window_id, &screen, B(left), B(top), B(right), B(bottom));
|
||||
|
||||
WITH_WINDOW(os_window_id, tab_id, window_id);
|
||||
Py_CLEAR(window->render_data.screen);
|
||||
d.vao_idx = window->render_data.vao_idx;
|
||||
init_window_render_data(osw, &g, &d);
|
||||
window->render_data = d;
|
||||
window->geometry = g;
|
||||
Py_INCREF(window->render_data.screen);
|
||||
init_window_render_data(&window->render_data, g, screen);
|
||||
END_WITH_WINDOW;
|
||||
Py_RETURN_NONE;
|
||||
#undef A
|
||||
#undef B
|
||||
}
|
||||
|
||||
|
|
@ -1240,6 +1238,8 @@ pyset_background_image(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
|||
free(bgimage);
|
||||
return NULL;
|
||||
}
|
||||
static uint32_t bgimage_id_counter = 0;
|
||||
bgimage->id = ++bgimage_id_counter;
|
||||
send_bgimage_to_gpu(layout, bgimage);
|
||||
bgimage->refcnt++;
|
||||
}
|
||||
|
|
@ -1411,7 +1411,6 @@ KI(set_active_tab)
|
|||
K(mark_os_window_dirty)
|
||||
KKK(set_active_window)
|
||||
KII(swap_tabs)
|
||||
KK5I(add_borders_rect)
|
||||
KKKK(set_redirect_keys_to_overlay)
|
||||
|
||||
static PyObject*
|
||||
|
|
@ -1475,7 +1474,7 @@ static PyMethodDef module_methods[] = {
|
|||
MW(buffer_keys_in_window, METH_VARARGS),
|
||||
MW(set_active_window, METH_VARARGS),
|
||||
MW(swap_tabs, METH_VARARGS),
|
||||
MW(add_borders_rect, METH_VARARGS),
|
||||
MW(set_borders_rects, METH_VARARGS),
|
||||
MW(set_tab_bar_render_data, METH_VARARGS),
|
||||
MW(set_window_render_data, METH_VARARGS),
|
||||
MW(set_window_padding, METH_VARARGS),
|
||||
|
|
|
|||
|
|
@ -142,16 +142,16 @@ typedef struct WindowLogoRenderData {
|
|||
bool using_default;
|
||||
} WindowLogoRenderData;
|
||||
|
||||
typedef struct {
|
||||
ssize_t vao_idx;
|
||||
float xstart, ystart, dx, dy;
|
||||
Screen *screen;
|
||||
} WindowRenderData;
|
||||
|
||||
typedef struct {
|
||||
unsigned int left, top, right, bottom;
|
||||
} WindowGeometry;
|
||||
|
||||
typedef struct {
|
||||
ssize_t vao_idx;
|
||||
WindowGeometry geometry;
|
||||
Screen *screen;
|
||||
} WindowRenderData;
|
||||
|
||||
typedef struct {
|
||||
monotonic_t at;
|
||||
int button, modifiers;
|
||||
|
|
@ -202,7 +202,6 @@ typedef struct {
|
|||
struct {
|
||||
unsigned int left, top, right, bottom;
|
||||
} padding;
|
||||
WindowGeometry geometry;
|
||||
ClickQueue click_queues[8];
|
||||
monotonic_t last_drag_scroll_at;
|
||||
uint32_t last_special_key_pressed;
|
||||
|
|
@ -272,6 +271,13 @@ typedef struct WindowChromeState {
|
|||
float background_opacity;
|
||||
} WindowChromeState;
|
||||
|
||||
typedef struct BackgroundImageRenderSettings {
|
||||
struct { unsigned width, height; } os_window;
|
||||
unsigned instance_id;
|
||||
BackgroundImageLayout layout;
|
||||
bool linear; uint32_t bgcolor; float opacity;
|
||||
} BackgroundImageRenderSettings;
|
||||
|
||||
typedef struct {
|
||||
void *handle;
|
||||
id_type id;
|
||||
|
|
@ -284,8 +290,12 @@ typedef struct {
|
|||
double viewport_x_ratio, viewport_y_ratio;
|
||||
Tab *tabs;
|
||||
BackgroundImage *bgimage;
|
||||
struct {
|
||||
uint32_t texture_id, framebuffer_id;
|
||||
int width, height;
|
||||
} indirect_output;
|
||||
unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id;
|
||||
bool focused_at_last_render, needs_render;
|
||||
bool focused_at_last_render, needs_render, needs_layers;
|
||||
unsigned keep_rendering_till_swap;
|
||||
WindowRenderData tab_bar_render_data;
|
||||
struct {
|
||||
|
|
@ -304,7 +314,7 @@ typedef struct {
|
|||
monotonic_t viewport_resized_at;
|
||||
LiveResizeInfo live_resize;
|
||||
bool has_pending_resizes, is_semi_transparent, shown_once, ignore_resize_events;
|
||||
unsigned int clear_count, redraw_count;
|
||||
unsigned int redraw_count;
|
||||
WindowChromeState last_window_chrome;
|
||||
float background_opacity;
|
||||
FONTS_DATA_HANDLE fonts_data;
|
||||
|
|
@ -371,7 +381,6 @@ void mark_os_window_for_close(OSWindow* w, CloseRequest cr);
|
|||
void update_os_window_viewport(OSWindow *window, bool notify_boss);
|
||||
bool should_os_window_be_rendered(OSWindow* w);
|
||||
void wakeup_main_loop(void);
|
||||
void swap_window_buffers(OSWindow *w);
|
||||
bool make_window_context_current(id_type);
|
||||
void hide_mouse(OSWindow *w);
|
||||
bool is_mouse_hidden(OSWindow *w);
|
||||
|
|
@ -385,16 +394,15 @@ OSWindow* add_os_window(void);
|
|||
OSWindow* current_os_window(void);
|
||||
void os_window_regions(OSWindow*, Region *main, Region *tab_bar);
|
||||
bool drag_scroll(Window *, OSWindow*);
|
||||
void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type, unsigned int, bool, OSWindow *w);
|
||||
void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, color_type, unsigned int, bool, OSWindow *w);
|
||||
ssize_t create_cell_vao(void);
|
||||
ssize_t create_graphics_vao(void);
|
||||
ssize_t create_border_vao(void);
|
||||
bool send_cell_data_to_gpu(ssize_t, float, float, float, float, Screen *, OSWindow *);
|
||||
void draw_cells(ssize_t, const WindowRenderData*, OSWindow *, bool, bool, bool, Window*);
|
||||
void draw_centered_alpha_mask(OSWindow *w, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas, float);
|
||||
bool send_cell_data_to_gpu(ssize_t, Screen *, OSWindow *);
|
||||
void draw_cells(const WindowRenderData*, OSWindow *, bool, bool, bool, Window*);
|
||||
void draw_cursor_trail(CursorTrail *trail, Window *active_window);
|
||||
bool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window);
|
||||
void update_surface_size(int, int, uint32_t);
|
||||
void set_gpu_viewport(unsigned w, unsigned h);
|
||||
void free_texture(uint32_t*);
|
||||
void free_framebuffer(uint32_t*);
|
||||
void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool, bool, RepeatStrategy);
|
||||
|
|
@ -430,7 +438,7 @@ const char* format_mods(unsigned mods);
|
|||
void dispatch_pending_clicks(id_type, void*);
|
||||
void send_pending_click_to_window(Window*, int);
|
||||
void get_platform_dependent_config_values(void *glfw_window);
|
||||
bool draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height);
|
||||
bool draw_window_title(double, double, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height);
|
||||
uint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *result_height);
|
||||
bool is_os_window_fullscreen(OSWindow *);
|
||||
void update_ime_focus(OSWindow* osw, bool focused);
|
||||
|
|
@ -443,3 +451,6 @@ bool render_os_window(OSWindow *w, monotonic_t now, bool scan_for_animated_image
|
|||
void update_mouse_pointer_shape(void);
|
||||
void adjust_window_size_for_csd(OSWindow *w, int width, int height, int *adjusted_width, int *adjusted_height);
|
||||
void dispatch_buffered_keys(Window *w);
|
||||
bool screen_needs_rendering_in_layers(OSWindow *os_window, Window *w, Screen *screen);
|
||||
void setup_os_window_for_rendering(OSWindow*, bool);
|
||||
void swap_window_buffers(OSWindow *w);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
uniform vec4 tint_color;
|
||||
out vec4 color;
|
||||
out vec4 color; // must be in linear space and pre-multiplied
|
||||
|
||||
void main() {
|
||||
color = tint_color;
|
||||
|
|
|
|||
12
kitty/utils.glsl
Normal file
12
kitty/utils.glsl
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// Return 0 if x < 1 otherwise 1
|
||||
#define zero_or_one(x) step(1.f, x)
|
||||
// condition must be zero or one. When 1 thenval is returned otherwise elseval
|
||||
#define if_one_then(condition, thenval, elseval) mix(elseval, thenval, condition)
|
||||
|
||||
vec4 vec4_premul(vec3 rgb, float a) {
|
||||
return vec4(rgb * a, a);
|
||||
}
|
||||
|
||||
vec4 vec4_premul(vec4 rgba) {
|
||||
return vec4(rgba.rgb * rgba.a, rgba.a);
|
||||
}
|
||||
|
|
@ -1021,7 +1021,7 @@ def sanitize_for_bracketed_paste(text: bytes) -> bytes:
|
|||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def sanitize_url_for_dispay_to_user(url: str) -> str:
|
||||
def sanitize_url_for_display_to_user(url: str) -> str:
|
||||
from urllib.parse import unquote, urlparse, urlunparse
|
||||
try:
|
||||
purl = urlparse(url)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ from .utils import (
|
|||
sanitize_control_codes,
|
||||
sanitize_for_bracketed_paste,
|
||||
sanitize_title,
|
||||
sanitize_url_for_dispay_to_user,
|
||||
sanitize_url_for_display_to_user,
|
||||
shlex_split,
|
||||
)
|
||||
|
||||
|
|
@ -1196,7 +1196,7 @@ class Window:
|
|||
if opts.allow_hyperlinks & 0b10:
|
||||
from kittens.tui.operations import styled
|
||||
boss.choose(
|
||||
'What would you like to do with this URL:\n' + styled(sanitize_url_for_dispay_to_user(url), fg='yellow'),
|
||||
'What would you like to do with this URL:\n' + styled(sanitize_url_for_display_to_user(url), fg='yellow'),
|
||||
partial(self.hyperlink_open_confirmed, url, cwd),
|
||||
'o:Open', 'c:Copy to clipboard', 'n;red:Nothing', default='o',
|
||||
window=self, title=_('Hyperlink activated'),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from kitty.fast_data_types import (
|
|||
)
|
||||
from kitty.fast_data_types import Cursor as C
|
||||
from kitty.rgb import to_color
|
||||
from kitty.utils import is_ok_to_read_image_file, is_path_in_temp_dir, sanitize_title, sanitize_url_for_dispay_to_user, shlex_split, shlex_split_with_positions
|
||||
from kitty.utils import is_ok_to_read_image_file, is_path_in_temp_dir, sanitize_title, sanitize_url_for_display_to_user, shlex_split, shlex_split_with_positions
|
||||
|
||||
from . import BaseTest, filled_cursor, filled_history_buf, filled_line_buf
|
||||
|
||||
|
|
@ -466,7 +466,7 @@ class TestDataTypes(BaseTest):
|
|||
if os.path.isdir('/dev/shm'):
|
||||
with tempfile.NamedTemporaryFile(dir='/dev/shm') as tf:
|
||||
self.assertTrue(is_ok_to_read_image_file(tf.name, tf.fileno()), fifo)
|
||||
self.ae(sanitize_url_for_dispay_to_user(
|
||||
self.ae(sanitize_url_for_display_to_user(
|
||||
'h://a\u0430b.com/El%20Ni%C3%B1o/'), 'h://xn--ab-7kc.com/El Niño/')
|
||||
for x in ('~', '~/', '', '~root', '~root/~', '/~', '/a/b/', '~xx/a', '~~'):
|
||||
self.assertEqual(os.path.expanduser(x), expanduser(x), x)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue