Skip to content

Commit

Permalink
Composite systray with alpha channel and support stale systray window…
Browse files Browse the repository at this point in the history
…s without it.
  • Loading branch information
xinhaoyuan committed Jul 22, 2022
1 parent de45de1 commit d3c857a
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 20 deletions.
1 change: 1 addition & 0 deletions common/atoms.list
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ ESETROOT_PMAP_ID
WM_STATE
_NET_WM_WINDOW_OPACITY
_NET_SYSTEM_TRAY_ORIENTATION
_NET_SYSTEM_TRAY_VISUAL
WM_CHANGE_STATE
WM_WINDOW_ROLE
WM_CLIENT_LEADER
Expand Down
1 change: 1 addition & 0 deletions common/xembed.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ typedef struct xembed_window xembed_window_t;
struct xembed_window
{
xcb_window_t win;
uint8_t depth;
xembed_info_t info;
};

Expand Down
4 changes: 4 additions & 0 deletions event.c
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,10 @@ event_handle_selectionclear(xcb_selection_clear_event_t *ev)

static void
event_handle_damage_notify(xcb_damage_notify_event_t *ev) {
if (ev->drawable == globalconf.systray.window) {
luaA_systray_invalidate();
xcb_damage_subtract(globalconf.connection, ev->damage, None, None);
}
}

/** \brief awesome xerror function.
Expand Down
2 changes: 2 additions & 0 deletions globalconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ typedef struct
drawin_t *parent;
/** Background color */
uint32_t background_pixel;
/** Whether systray is composited */
bool is_composited;
} systray;
/** The monitor of startup notifications */
SnMonitorContext *snmonitor;
Expand Down
24 changes: 24 additions & 0 deletions lib/wibox/widget/systray.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

local wbase = require("wibox.widget.base")
local drawable = require("wibox.drawable")
local cairo = require("lgi").cairo
local beautiful = require("beautiful")
local gcolor = require("gears.color")
local gtable = require("gears.table")
local capi = {
awesome = awesome,
Expand Down Expand Up @@ -42,6 +44,11 @@ local display_on_screen = "primary"
-- @beautiful beautiful.systray_icon_spacing
-- @tparam[opt=0] integer The icon spacing

--- Whether to ignore systray background when compositing the systray icons.
--
-- @beautiful beautiful.systray_transparent
-- @tparam[opt=0] boolean Whether to ignore the systray background

local function should_display_on(s)
if display_on_screen == "primary" then
return s == capi.screen.primary
Expand Down Expand Up @@ -90,6 +97,23 @@ function systray:draw(context, cr, width, height)
end
capi.awesome.systray(context.wibox.drawin, math.ceil(x), math.ceil(y),
base, is_rotated, bg, reverse, spacing, rows)

local width, height =
base * rows + spacing * (rows - 1),
base * cols + spacing * (cols - 1)
if is_rotated then
width, height = height, width
end
local surf_raw = capi.awesome.systray_surface(width, height)
if surf_raw then
local surf = cairo.Surface(surf_raw)
if not beautiful.systray_transparent then
cr:set_source(gcolor(bg))
cr:paint()
end
cr:set_source_surface(surf, 0, 0)
cr:paint()
end
end

-- Private API. Does not appear in LDoc on purpose. This function is called
Expand Down
1 change: 1 addition & 0 deletions luaa.c
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,7 @@ luaA_init(xdgHandle* xdg, string_array_t *searchpath)
{ "disconnect_signal", luaA_awesome_disconnect_signal },
{ "emit_signal", luaA_awesome_emit_signal },
{ "systray", luaA_systray },
{ "systray_surface", luaA_systray_surface },
{ "load_image", luaA_load_image },
{ "pixbuf_to_surface", luaA_pixbuf_to_surface },
{ "set_preferred_icon_size", luaA_set_preferred_icon_size },
Expand Down
3 changes: 3 additions & 0 deletions objects/drawin.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@

#include <cairo-xcb.h>
#include <xcb/shape.h>
#include <xcb/composite.h>

lua_class_t drawin_class;

Expand Down Expand Up @@ -451,6 +452,8 @@ drawin_allocator(lua_State *L)
globalconf.default_cmap,
xcursor_new(globalconf.cursor_ctx, xcursor_font_fromstr(w->cursor))
});
if (globalconf.have_composite)
xcb_composite_redirect_subwindows(globalconf.connection, w->window, XCB_COMPOSITE_REDIRECT_MANUAL);
xwindow_set_class_instance(w->window);
xwindow_set_name_static(w->window, "Awesome drawin");

Expand Down
94 changes: 81 additions & 13 deletions systray.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <xcb/xcb.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_atom.h>
#include <xcb/damage.h>
#include <cairo-xcb.h>

#define SYSTEM_TRAY_REQUEST_DOCK 0 /* Begin icon docking */

Expand All @@ -44,13 +46,30 @@ systray_init(void)

globalconf.systray.window = xcb_generate_id(globalconf.connection);
globalconf.systray.background_pixel = xscreen->black_pixel;
xcb_create_window(globalconf.connection, xscreen->root_depth,
globalconf.systray.window,
xscreen->root,
-1, -1, 1, 1, 0,
XCB_COPY_FROM_PARENT, xscreen->root_visual,
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (const uint32_t [])
{ xscreen->black_pixel, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT });
globalconf.systray.is_composited = globalconf.have_composite && globalconf.have_damage;
if (globalconf.systray.is_composited) {
xcb_create_window(globalconf.connection, globalconf.default_depth,
globalconf.systray.window,
xscreen->root,
-1, -1, 1, 1, 0,
XCB_COPY_FROM_PARENT, globalconf.visual->visual_id,
XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
(const uint32_t [])
{ xscreen->black_pixel, xscreen->black_pixel, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, globalconf.default_cmap });
xcb_damage_create(globalconf.connection, xcb_generate_id(globalconf.connection), globalconf.systray.window, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
globalconf.systray.window, _NET_SYSTEM_TRAY_VISUAL,
XCB_ATOM_VISUALID, 32, 1, (const uint32_t [])
{ globalconf.visual->visual_id });
} else {
xcb_create_window(globalconf.connection, xscreen->root_depth,
globalconf.systray.window,
xscreen->root,
-1, -1, 1, 1, 0,
XCB_COPY_FROM_PARENT, xscreen->root_visual,
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (const uint32_t [])
{ xscreen->black_pixel, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT });
}
xwindow_set_class_instance(globalconf.systray.window);
xwindow_set_name_static(globalconf.systray.window, "Awesome systray window");

Expand Down Expand Up @@ -130,6 +149,8 @@ int
systray_request_handle(xcb_window_t embed_win)
{
xembed_window_t em;
xcb_get_geometry_cookie_t geom_c;
xcb_get_geometry_reply_t *geom_r;
xcb_get_property_cookie_t em_cookie;
const uint32_t select_input_val[] =
{
Expand All @@ -149,6 +170,19 @@ systray_request_handle(xcb_window_t embed_win)
xcb_change_window_attributes(globalconf.connection, embed_win, XCB_CW_EVENT_MASK,
select_input_val);


geom_c = xcb_get_geometry(globalconf.connection, embed_win);
if(!(geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL)))
return -1;
em.depth = geom_r->depth;
p_delete(&geom_r);
if (globalconf.systray.is_composited && em.depth != globalconf.default_depth) {
warn("Fixing the background of the systray window 0x%x possibly because the client does not support composition.", embed_win);
xcb_change_window_attributes(globalconf.connection, embed_win, XCB_CW_BACK_PIXEL,
(const uint32_t []){ globalconf.systray.background_pixel });
xcb_clear_area(globalconf.connection, 1, embed_win, 0, 0, 0, 0);
}

/* we grab the window, but also make sure it's automatically reparented back
* to the root window if we should die.
*/
Expand Down Expand Up @@ -376,12 +410,21 @@ luaA_systray(lua_State *L)
&& globalconf.systray.background_pixel != bg_color.pixel)
{
uint32_t config_back[] = { bg_color.pixel };
globalconf.systray.background_pixel = bg_color.pixel;
xcb_change_window_attributes(globalconf.connection,
globalconf.systray.window,
XCB_CW_BACK_PIXEL, config_back);
xcb_clear_area(globalconf.connection, 1, globalconf.systray.window, 0, 0, 0, 0);
force_redraw = true;
if (globalconf.systray.is_composited) {
foreach(em, globalconf.embedded)
if (em->depth != globalconf.default_depth) {
xcb_change_window_attributes(
globalconf.connection, em->win, XCB_CW_BACK_PIXEL, config_back);
xcb_clear_area(globalconf.connection, 1, em->win, 0, 0, 0, 0);
}
} else {
globalconf.systray.background_pixel = bg_color.pixel;
xcb_change_window_attributes(globalconf.connection,
globalconf.systray.window,
XCB_CW_BACK_PIXEL, config_back);
xcb_clear_area(globalconf.connection, 1, globalconf.systray.window, 0, 0, 0, 0);
force_redraw = true;
}
}

if(globalconf.systray.parent != w)
Expand Down Expand Up @@ -413,4 +456,29 @@ luaA_systray(lua_State *L)
return 2;
}

/** Return the native surface of the systray if composite is enabled.
* \param L The Lua VM state.
* \return the number of element returned. (1)
* \luastack
* \lparam width The width of the systray surface.
* \lparam height The height of the systray surface.
*/
int
luaA_systray_surface(lua_State *L)
{
if (!globalconf.systray.is_composited) {
lua_pushnil(L);
return 1;
}

int width = luaL_checkinteger(L, 1);
int height = luaL_checkinteger(L, 2);
/* Lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(
L, cairo_xcb_surface_create(
globalconf.connection, globalconf.systray.window, globalconf.visual,
width, height));
return 1;
}

// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
1 change: 1 addition & 0 deletions systray.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ bool systray_iskdedockapp(xcb_window_t);
int systray_process_client_message(xcb_client_message_event_t *);
int xembed_process_client_message(xcb_client_message_event_t *);
int luaA_systray(lua_State *);
int luaA_systray_surface(lua_State *);

#endif
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
62 changes: 57 additions & 5 deletions tests/test-systray.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,28 @@ static xcb_window_t find_systray(xcb_connection_t *conn, xcb_atom_t net_system_t
return owner;
}

static uint32_t get_color(xcb_connection_t *conn, xcb_screen_t *screen, uint16_t red, uint16_t green, uint16_t blue) {
xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, screen->default_colormap, red, green, blue), NULL);
static uint32_t get_color(xcb_connection_t *conn, xcb_colormap_t cm, uint16_t red, uint16_t green, uint16_t blue) {
xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, cm, red, green, blue), NULL);
if (!reply)
fatal("Error allocating color");
uint32_t pixel = reply->pixel;
free(reply);
return pixel;
}

static uint8_t find_visual_depth(const xcb_screen_t *s, xcb_visualid_t visual)
{
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(s);

if(depth_iter.data)
for(; depth_iter.rem; xcb_depth_next (&depth_iter))
for(xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
visual_iter.rem; xcb_visualtype_next (&visual_iter))
if(visual == visual_iter.data->visual_id)
return depth_iter.data->depth;
return 0;
}

int main() {
int default_screen;
xcb_connection_t* conn = xcb_connect(NULL, &default_screen);
Expand All @@ -82,15 +95,54 @@ int main() {
}
atoms_init(conn);
xcb_screen_t* screen = xcb_aux_get_screen(conn, default_screen);
xcb_window_t systray_owner = find_systray(conn, systray_atom(conn, default_screen));

// Try to use tray visual hint, if requested.
char *use_hint_var = getenv("USE_TRAY_VISUAL_HINT");
bool use_tray_visual_hint = use_hint_var && *use_hint_var;
xcb_visualid_t tray_visual_id;
uint8_t tray_depth;
if (use_tray_visual_hint) {
xcb_get_property_reply_t *tray_visual_r =
xcb_get_property_reply(
conn, xcb_get_property(conn, false, systray_owner,
_NET_SYSTEM_TRAY_VISUAL,
XCB_ATOM_VISUALID, 0, 1),
NULL);
tray_visual_id = screen->root_visual;
if(xcb_get_property_value_length(tray_visual_r)) {
tray_visual_id = *(uint32_t *)xcb_get_property_value(tray_visual_r);
p_delete(&tray_visual_r);
}
tray_depth = find_visual_depth(screen, tray_visual_id);
if (tray_depth == 0) {
fatal("Error getting visual hint\n");
}
} else {
tray_visual_id= screen->root_visual;
tray_depth = screen->root_depth;
}

xcb_colormap_t cm = xcb_generate_id(conn);
xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE,
cm, screen->root,
tray_visual_id);

// Create a window for the systray icon
xcb_window_t window = xcb_generate_id(conn);
xcb_create_window(conn, screen->root_depth, window, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual, XCB_CW_BACK_PIXEL, (uint32_t[]) { get_color(conn, screen, 0xffff, 0x9999, 0x0000) });
xcb_create_window(conn, tray_depth, window, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
tray_visual_id,
(use_tray_visual_hint ? XCB_CW_BACK_PIXEL : XCB_CW_BACK_PIXMAP)
| XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP,
(uint32_t[]) {
use_tray_visual_hint
? get_color(conn, cm, 0xffff, 0x9999, 0x0000)
: XCB_BACK_PIXMAP_PARENT_RELATIVE,
screen->black_pixel, cm
});
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, _XEMBED_INFO, _XEMBED_INFO, 32, 2, (uint32_t[]) { 0, 1 });

// Make our window a systray icon
xcb_window_t systray_owner = find_systray(conn, systray_atom(conn, default_screen));
xcb_client_message_event_t ev;

p_clear(&ev, 1);
Expand Down
Loading

0 comments on commit d3c857a

Please sign in to comment.