Я пытаюсь выполнить постобработку (манипулирование цветами, повышение резкости, размытие и т. д.) произвольного окна приложения. Я использую расширение Composite, чтобы получить контент в виде закадрового растрового изображения, а затем применяю к нему эффекты. Чтобы избежать мерцания, я пытаюсь использовать обновление ВРУЧНУЮ, чтобы позже отображать полученные пиксели в одном кадре. Согласно документации , это должно быть возможно по замыслу:
Этот механизм автоматического обновления можно отключить, чтобы родительский содержимое окна может быть полностью определено внешним приложение.
Однако здесь ничего не говорится о том, как на самом деле выполнить обновление. Думаю, мне не следует просто копировать растровое изображение за кадром обратно в окно, поскольку источник и место назначения по сути одинаковы?
ОБНОВЛЕНИЕ: я просмотрел исходный код расширения и нашел, где оно выполняет копирование (compWindowUpdateAutomatic()), но использование xcb_render_composite()
дает тот же результат.
Это моя текущая попытка, но копирование явно не работает:
#include <cairo/cairo-xcb.h>
#include <stdio.h>
#include <stdlib.h>
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_event.h>
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <window_id>\n", argv[0]);
return -1;
}
xcb_window_t wid = (xcb_window_t)strtoul(argv[1], NULL, 0);
xcb_connection_t *conn = xcb_connect(NULL, NULL);
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
xcb_visualtype_t *visual = xcb_aux_find_visual_by_attrs(screen, XCB_VISUAL_CLASS_TRUE_COLOR, 24);
xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, wid), NULL);
xcb_damage_query_version(conn, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION);
uint8_t DAMAGE_EVENT = xcb_get_extension_data(conn, &xcb_damage_id)->first_event;
{
//track damage in subwindows:
xcb_query_tree_reply_t *r = xcb_query_tree_reply(conn, xcb_query_tree_unchecked(conn, wid), NULL);
xcb_window_t *w = xcb_query_tree_children(r);
for (int i = 0; i < r->children_len; ++i) {
xcb_damage_damage_t damage = xcb_generate_id(conn);
xcb_damage_create(conn, damage, w[i], XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
xcb_flush(conn);
}
{
xcb_damage_damage_t damage = xcb_generate_id(conn);
xcb_damage_create(conn, damage, wid, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
xcb_flush(conn);
}
free(r);
}
xcb_composite_redirect_window(conn, wid, XCB_COMPOSITE_REDIRECT_MANUAL);
xcb_pixmap_t pixmap = xcb_generate_id(conn);
xcb_composite_name_window_pixmap(conn, wid, pixmap);
xcb_gcontext_t gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, wid, 0, NULL);
xcb_generic_event_t *event;
while ((event = xcb_wait_for_event(conn))) {
if (XCB_EVENT_RESPONSE_TYPE(event) == DAMAGE_EVENT + XCB_DAMAGE_NOTIFY) {
xcb_damage_notify_event_t *de = (xcb_damage_notify_event_t *)event;
{ // draw a translucent layer:
cairo_surface_t *surface = cairo_xcb_surface_create(conn, pixmap, visual, geom->width, geom->height);
cairo_t *cr = cairo_create(surface);
cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.5);
cairo_paint(cr);
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
{ // copy back:
xcb_copy_area(conn, pixmap, wid, gc, 0, 0, 0, 0, geom->width, geom->height);
}
xcb_damage_subtract(conn, de->damage, XCB_NONE, XCB_NONE);
xcb_flush(conn);
}
free(event);
}
return -1;
}
cc main.c -o main -lxcb-composite -lxcb-damage -lcairo -lxcb-render -lxcb-util -lxcb
Спасибо за предложение, я вижу, что XEmbed может заставить мое приложение работать как оконный менеджер, я рассмотрю это. Да, я пробовал АВТОМАТИЧЕСКИЙ режим и рисовал непосредственно в стороннем окне, но я не могу контролировать и откладывать его перерисовку. Я всегда опаздываю на один кадр, и это приводит к разрывам и мерцанию.
Вероятно, я делаю это неправильно, но после переназначения window
на parent
события порчи перестают поступать. XEmbed требует больше хитростей и много дополнительной работы для реализации базовой логики оконного менеджера и передачи ввода с клавиатуры и мыши. Я надеюсь, что есть более простой способ.
После более тщательного изучения compWindowUpdateAutomatic() и многократного прочтения документации по расширению Composite я обнаружил, что делаю неправильно. Местом копирования должно быть не перенаправленное окно, а его родительское окно.
- xcb_create_gc(conn, gc, wid, 0, NULL);
+ xcb_window_t parent;
+ {
+ xcb_query_tree_reply_t *r = xcb_query_tree_reply(conn, xcb_query_tree(conn, wid), NULL);
+ parent = r->parent;
+ free(r);
+ }
+ xcb_create_gc(conn, gc, parent, XCB_GC_SUBWINDOW_MODE, (const uint32_t[]){XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
...
- xcb_copy_area(conn, pixmap, wid, gc, 0, 0, 0, 0, geom->width, geom->height);
+ xcb_copy_area(conn, pixmap, parent, gc, 0, 0, geom->x, geom->y, geom->width, geom->height);
Случайная идея после небольшого рассмотрения: может быть, вариант использования похож на XEmbed? У вас есть еще одно окно (
window
), встроенное в ваше окно (parent
). Вы перенаправляетеwindow
, а затем можете напрямую рисовать его содержимоеparent
, когда это необходимо. Посмотрев немного больше на gitlab.freedesktop.org/xorg/proto/xorgproto/-/blob/master/… я прочитал:In automatic update mode, the X server is itself responsible for presenting the child window contents within the parent.