mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-22 08:18:08 +02:00
API for changing the background image of OS windows
Uses reference counting
This commit is contained in:
@@ -689,7 +689,7 @@ render(monotonic_t now, bool input_read) {
|
|||||||
if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); continue; }
|
if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); continue; }
|
||||||
if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows, &all_windows_have_same_bg)) needs_render = true;
|
if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows, &all_windows_have_same_bg)) needs_render = true;
|
||||||
if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true;
|
if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true;
|
||||||
if (w->render_calls < 3 && w->bgimage.texture_id) needs_render = true;
|
if (w->render_calls < 3 && w->bgimage && w->bgimage->texture_id) needs_render = true;
|
||||||
if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg);
|
if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg);
|
||||||
}
|
}
|
||||||
last_render_at = now;
|
last_render_at = now;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ typedef struct {
|
|||||||
uint32_t texture_id;
|
uint32_t texture_id;
|
||||||
unsigned int height, width;
|
unsigned int height, width;
|
||||||
uint8_t* bitmap;
|
uint8_t* bitmap;
|
||||||
bool load_failed, needs_free;
|
uint32_t refcnt;
|
||||||
} BackgroundImage;
|
} BackgroundImage;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -355,9 +355,9 @@ draw_bg(OSWindow *w) {
|
|||||||
bgimage_constants_set = true;
|
bgimage_constants_set = true;
|
||||||
}
|
}
|
||||||
glUniform4f(bgimage_program_layout.sizes_location,
|
glUniform4f(bgimage_program_layout.sizes_location,
|
||||||
(GLfloat)w->window_width, (GLfloat)w->window_height, (GLfloat)w->bgimage.width, (GLfloat)w->bgimage.height);
|
(GLfloat)w->window_width, (GLfloat)w->window_height, (GLfloat)w->bgimage->width, (GLfloat)w->bgimage->height);
|
||||||
glActiveTexture(GL_TEXTURE0 + BGIMAGE_UNIT);
|
glActiveTexture(GL_TEXTURE0 + BGIMAGE_UNIT);
|
||||||
glBindTexture(GL_TEXTURE_2D, w->bgimage.texture_id);
|
glBindTexture(GL_TEXTURE_2D, w->bgimage->texture_id);
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
unbind_vertex_array();
|
unbind_vertex_array();
|
||||||
unbind_program();
|
unbind_program();
|
||||||
@@ -428,6 +428,11 @@ draw_cells_simple(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
has_bgimage(OSWindow *w) {
|
||||||
|
return w->bgimage && w->bgimage->texture_id > 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *w) {
|
draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *w) {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
@@ -435,12 +440,12 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind
|
|||||||
|
|
||||||
bind_program(CELL_BG_PROGRAM);
|
bind_program(CELL_BG_PROGRAM);
|
||||||
// draw background for all cells
|
// draw background for all cells
|
||||||
if (!w->bgimage.texture_id) {
|
if (!has_bgimage(w)) {
|
||||||
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
|
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
|
||||||
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
|
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screen->grman->num_of_below_refs || w->bgimage.texture_id) {
|
if (screen->grman->num_of_below_refs || has_bgimage(w)) {
|
||||||
if (screen->grman->num_of_below_refs) draw_graphics(
|
if (screen->grman->num_of_below_refs) draw_graphics(
|
||||||
GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
|
GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
|
||||||
bind_program(CELL_BG_PROGRAM);
|
bind_program(CELL_BG_PROGRAM);
|
||||||
@@ -479,7 +484,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
|
|||||||
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, os_window->offscreen_texture_id, 0);
|
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, os_window->offscreen_texture_id, 0);
|
||||||
/* if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("Offscreen framebuffer not complete"); */
|
/* if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("Offscreen framebuffer not complete"); */
|
||||||
bind_program(CELL_BG_PROGRAM);
|
bind_program(CELL_BG_PROGRAM);
|
||||||
if (!os_window->bgimage.texture_id) {
|
if (!has_bgimage(os_window)) {
|
||||||
// draw background for all cells
|
// draw background for all cells
|
||||||
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
|
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
|
||||||
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
|
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
|
||||||
@@ -487,7 +492,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
|
|||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
BLEND_PREMULT;
|
BLEND_PREMULT;
|
||||||
|
|
||||||
if (screen->grman->num_of_below_refs || os_window->bgimage.texture_id) {
|
if (screen->grman->num_of_below_refs || has_bgimage(os_window)) {
|
||||||
if (screen->grman->num_of_below_refs) draw_graphics(
|
if (screen->grman->num_of_below_refs) draw_graphics(
|
||||||
GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
|
GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
|
||||||
bind_program(CELL_BG_PROGRAM);
|
bind_program(CELL_BG_PROGRAM);
|
||||||
@@ -513,7 +518,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
|
|||||||
if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs);
|
if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs);
|
||||||
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
if (!os_window->bgimage.texture_id) glDisable(GL_BLEND);
|
if (!has_bgimage(os_window)) glDisable(GL_BLEND);
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
// Now render the framebuffer to the screen
|
// Now render the framebuffer to the screen
|
||||||
@@ -602,10 +607,10 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL
|
|||||||
);
|
);
|
||||||
#undef SCALE
|
#undef SCALE
|
||||||
if (os_window->is_semi_transparent) {
|
if (os_window->is_semi_transparent) {
|
||||||
if (screen->grman->count || os_window->bgimage.texture_id) draw_cells_interleaved_premult(vao_idx, gvao_idx, screen, os_window);
|
if (screen->grman->count || has_bgimage(os_window)) draw_cells_interleaved_premult(vao_idx, gvao_idx, screen, os_window);
|
||||||
else draw_cells_simple(vao_idx, gvao_idx, screen);
|
else draw_cells_simple(vao_idx, gvao_idx, screen);
|
||||||
} else {
|
} else {
|
||||||
if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || os_window->bgimage.texture_id) draw_cells_interleaved(vao_idx, gvao_idx, screen, os_window);
|
if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || has_bgimage(os_window)) draw_cells_interleaved(vao_idx, gvao_idx, screen, os_window);
|
||||||
else draw_cells_simple(vao_idx, gvao_idx, screen);
|
else draw_cells_simple(vao_idx, gvao_idx, screen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -649,7 +654,7 @@ create_border_vao(void) {
|
|||||||
void
|
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 active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) {
|
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 active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) {
|
||||||
|
|
||||||
if (w->bgimage.texture_id) {
|
if (has_bgimage(w)) {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
BLEND_ONTO_OPAQUE;
|
BLEND_ONTO_OPAQUE;
|
||||||
draw_bg(w);
|
draw_bg(w);
|
||||||
@@ -677,7 +682,7 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
|||||||
unbind_vertex_array();
|
unbind_vertex_array();
|
||||||
unbind_program();
|
unbind_program();
|
||||||
}
|
}
|
||||||
if (w->bgimage.texture_id) glDisable(GL_BLEND);
|
if (has_bgimage(w)) glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|||||||
118
kitty/state.c
118
kitty/state.c
@@ -71,6 +71,37 @@ os_window_for_kitty_window(id_type kitty_window_id) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_bgimage_to_gpu(BackgroundImageLayout layout, BackgroundImage *bgimage) {
|
||||||
|
RepeatStrategy r;
|
||||||
|
switch (layout) {
|
||||||
|
case SCALED:
|
||||||
|
r = REPEAT_CLAMP; break;
|
||||||
|
case MIRRORED:
|
||||||
|
r = REPEAT_MIRROR; break;
|
||||||
|
case TILING:
|
||||||
|
default:
|
||||||
|
r = REPEAT_DEFAULT; break;
|
||||||
|
}
|
||||||
|
bgimage->texture_id = 0;
|
||||||
|
send_image_to_gpu(&bgimage->texture_id, bgimage->bitmap, bgimage->width,
|
||||||
|
bgimage->height, false, true, OPT(background_image_linear), r);
|
||||||
|
free(bgimage->bitmap); bgimage->bitmap = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_bgimage(BackgroundImage *bgimage, bool release_texture) {
|
||||||
|
if (bgimage && bgimage->refcnt) {
|
||||||
|
bgimage->refcnt--;
|
||||||
|
if (bgimage->refcnt == 0) {
|
||||||
|
free(bgimage->bitmap); bgimage->bitmap = NULL;
|
||||||
|
if (release_texture) free_texture(&bgimage->texture_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BackgroundImage global_bg_image = {0};
|
||||||
|
|
||||||
OSWindow*
|
OSWindow*
|
||||||
add_os_window() {
|
add_os_window() {
|
||||||
WITH_OS_WINDOW_REFS
|
WITH_OS_WINDOW_REFS
|
||||||
@@ -84,29 +115,17 @@ add_os_window() {
|
|||||||
|
|
||||||
bool wants_bg = OPT(background_image) && OPT(background_image)[0] != 0;
|
bool wants_bg = OPT(background_image) && OPT(background_image)[0] != 0;
|
||||||
if (wants_bg) {
|
if (wants_bg) {
|
||||||
if (!global_state.bgimage.texture_id && !global_state.bgimage.load_failed) {
|
if (!global_state.bgimage) {
|
||||||
|
global_state.bgimage = &global_bg_image;
|
||||||
|
global_state.bgimage->refcnt++;
|
||||||
size_t size;
|
size_t size;
|
||||||
if (png_path_to_bitmap(OPT(background_image), &global_state.bgimage.bitmap, &global_state.bgimage.width, &global_state.bgimage.height, &size)) {
|
if (png_path_to_bitmap(OPT(background_image), &global_state.bgimage->bitmap, &global_state.bgimage->width, &global_state.bgimage->height, &size)) {
|
||||||
RepeatStrategy r;
|
send_bgimage_to_gpu(OPT(background_image_layout), global_state.bgimage);
|
||||||
switch (OPT(background_image_layout)) {
|
}
|
||||||
case SCALED:
|
|
||||||
r = REPEAT_CLAMP; break;
|
|
||||||
case MIRRORED:
|
|
||||||
r = REPEAT_MIRROR; break;
|
|
||||||
case TILING:
|
|
||||||
default:
|
|
||||||
r = REPEAT_DEFAULT; break;
|
|
||||||
}
|
|
||||||
global_state.bgimage.texture_id = 0;
|
|
||||||
send_image_to_gpu(&global_state.bgimage.texture_id, global_state.bgimage.bitmap, global_state.bgimage.width,
|
|
||||||
global_state.bgimage.height, false, true, OPT(background_image_linear), r);
|
|
||||||
global_state.bgimage.needs_free = true;
|
|
||||||
free(global_state.bgimage.bitmap); global_state.bgimage.bitmap = NULL;
|
|
||||||
} else global_state.bgimage.load_failed = true;
|
|
||||||
}
|
}
|
||||||
if (global_state.bgimage.texture_id) {
|
if (global_state.bgimage->texture_id) {
|
||||||
memcpy(&ans->bgimage, &global_state.bgimage, sizeof(global_state.bgimage));
|
ans->bgimage = global_state.bgimage;
|
||||||
ans->bgimage.needs_free = false;
|
ans->bgimage->refcnt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,9 +307,8 @@ destroy_os_window_item(OSWindow *w) {
|
|||||||
remove_vao(w->tab_bar_render_data.vao_idx);
|
remove_vao(w->tab_bar_render_data.vao_idx);
|
||||||
remove_vao(w->gvao_idx);
|
remove_vao(w->gvao_idx);
|
||||||
free(w->tabs); w->tabs = NULL;
|
free(w->tabs); w->tabs = NULL;
|
||||||
if (w->bgimage.needs_free) {
|
free_bgimage(w->bgimage, true);
|
||||||
free(w->bgimage.bitmap); free_texture(&w->bgimage.texture_id);
|
w->bgimage = NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -884,6 +902,44 @@ PYWRAP1(patch_global_colors) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
pyset_background_image(PyObject *self UNUSED, PyObject *args) {
|
||||||
|
const char *path;
|
||||||
|
PyObject *layout_name = NULL;
|
||||||
|
PyObject *os_window_ids;
|
||||||
|
int configured = 0;
|
||||||
|
PA("sO&|pU", &path, &PyTuple_Type, &os_window_ids, &configured, &layout_name);
|
||||||
|
size_t size;
|
||||||
|
BackgroundImageLayout layout = layout_name ? bglayout(layout_name) : OPT(background_image_layout);
|
||||||
|
BackgroundImage *bgimage = calloc(1, sizeof(BackgroundImage));
|
||||||
|
if (!bgimage) return PyErr_NoMemory();
|
||||||
|
if (!png_path_to_bitmap(path, &bgimage->bitmap, &bgimage->width, &bgimage->height, &size)) {
|
||||||
|
PyErr_Format(PyExc_ValueError, "Failed to load image from: %s", path);
|
||||||
|
free(bgimage);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
send_bgimage_to_gpu(layout, bgimage);
|
||||||
|
bgimage->refcnt++;
|
||||||
|
if (configured) {
|
||||||
|
free_bgimage(global_state.bgimage, true);
|
||||||
|
global_state.bgimage = bgimage;
|
||||||
|
bgimage->refcnt++;
|
||||||
|
OPT(background_image_layout) = layout;
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(os_window_ids); i++) {
|
||||||
|
id_type os_window_id = PyLong_AsUnsignedLongLong(PyTuple_GET_ITEM(os_window_ids, i));
|
||||||
|
WITH_OS_WINDOW(os_window_id)
|
||||||
|
make_os_window_context_current(os_window);
|
||||||
|
free_bgimage(os_window->bgimage, true);
|
||||||
|
os_window->bgimage = bgimage;
|
||||||
|
os_window->render_calls = 0;
|
||||||
|
bgimage->refcnt++;
|
||||||
|
END_WITH_OS_WINDOW
|
||||||
|
}
|
||||||
|
free_bgimage(bgimage, true);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
PYWRAP0(destroy_global_data) {
|
PYWRAP0(destroy_global_data) {
|
||||||
Py_CLEAR(global_state.boss);
|
Py_CLEAR(global_state.boss);
|
||||||
free(global_state.os_windows); global_state.os_windows = NULL;
|
free(global_state.os_windows); global_state.os_windows = NULL;
|
||||||
@@ -940,6 +996,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
MW(background_opacity_of, METH_O),
|
MW(background_opacity_of, METH_O),
|
||||||
MW(update_window_visibility, METH_VARARGS),
|
MW(update_window_visibility, METH_VARARGS),
|
||||||
MW(global_font_size, METH_VARARGS),
|
MW(global_font_size, METH_VARARGS),
|
||||||
|
MW(set_background_image, METH_VARARGS),
|
||||||
MW(os_window_font_size, METH_VARARGS),
|
MW(os_window_font_size, METH_VARARGS),
|
||||||
MW(set_boss, METH_O),
|
MW(set_boss, METH_O),
|
||||||
MW(patch_global_colors, METH_VARARGS),
|
MW(patch_global_colors, METH_VARARGS),
|
||||||
@@ -956,13 +1013,12 @@ finalize(void) {
|
|||||||
if (detached_windows.windows) free(detached_windows.windows);
|
if (detached_windows.windows) free(detached_windows.windows);
|
||||||
detached_windows.capacity = 0;
|
detached_windows.capacity = 0;
|
||||||
if (OPT(background_image)) free(OPT(background_image));
|
if (OPT(background_image)) free(OPT(background_image));
|
||||||
if (global_state.bgimage.needs_free) {
|
// we leak the texture here since it is not guaranteed
|
||||||
free(global_state.bgimage.bitmap);
|
// that freeing the texture will work during shutdown and
|
||||||
// we leak the texture here since it is not guaranteed
|
// the GPU driver should take care of it when the OpenGL context is
|
||||||
// that freeing the texture will work during shutdown and
|
// destroyed.
|
||||||
// the GPU driver should take care of it when the OpenGL context is
|
free_bgimage(global_state.bgimage, false);
|
||||||
// destroyed.
|
global_state.bgimage = NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ typedef struct {
|
|||||||
int viewport_width, viewport_height, window_width, window_height;
|
int viewport_width, viewport_height, window_width, window_height;
|
||||||
double viewport_x_ratio, viewport_y_ratio;
|
double viewport_x_ratio, viewport_y_ratio;
|
||||||
Tab *tabs;
|
Tab *tabs;
|
||||||
BackgroundImage bgimage;
|
BackgroundImage *bgimage;
|
||||||
unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id;
|
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;
|
||||||
ScreenRenderData tab_bar_render_data;
|
ScreenRenderData tab_bar_render_data;
|
||||||
@@ -176,7 +176,7 @@ typedef struct {
|
|||||||
|
|
||||||
id_type os_window_id_counter, tab_id_counter, window_id_counter;
|
id_type os_window_id_counter, tab_id_counter, window_id_counter;
|
||||||
PyObject *boss;
|
PyObject *boss;
|
||||||
BackgroundImage bgimage;
|
BackgroundImage *bgimage;
|
||||||
OSWindow *os_windows;
|
OSWindow *os_windows;
|
||||||
size_t num_os_windows, capacity;
|
size_t num_os_windows, capacity;
|
||||||
OSWindow *callback_os_window;
|
OSWindow *callback_os_window;
|
||||||
|
|||||||
Reference in New Issue
Block a user