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. Add a beautiful.systray_skip_bg to skip background drawing, so it can blend nicely with other widgets.
  • Loading branch information
xinhaoyuan committed Sep 24, 2022
1 parent 9334dc7 commit 7464c8e
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 21 deletions.
2 changes: 2 additions & 0 deletions awesome.c
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,8 @@ main(int argc, char **argv)
xcb_discard_reply(globalconf.connection,
xcb_damage_query_version(globalconf.connection, 1, 0).sequence);

globalconf.is_compositing = globalconf.have_composite && globalconf.have_damage;

event_init();

/* Allocate the key symbols */
Expand Down
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 @@ -130,6 +130,8 @@ typedef struct
bool have_composite;
/** Check for Damage extenion */
bool have_damage;
/** Enable compositing features? */
bool is_compositing;
/** Custom searchpaths are present, the runtime is tinted */
bool have_searchpaths;
/** When --no-argb is used in the modeline or command line */
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 skip drawing the systray background when compositing the systray icons.
--
-- @beautiful beautiful.systray_skip_bg
-- @tparam[opt=0] boolean Whether to skip drawing 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 surf_width, surf_height =
base * rows + spacing * (rows - 1),
base * cols + spacing * (cols - 1)
if is_rotated then
surf_width, surf_height = surf_height, surf_width
end
local surf_raw = capi.awesome.systray_surface(surf_width, surf_height)
if surf_raw then
local surf = cairo.Surface(surf_raw, true)
if not beautiful.systray_skip_bg 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.is_compositing)
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
5 changes: 4 additions & 1 deletion spec/wibox/widget/systray_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ _G.awesome = {
systray_arguments = { first_arg, ... }
end
return num_systray_icons
end
end,
systray_surface = function()
return nil
end,
}
_G.screen = {
connect_signal = function() end
Expand Down
95 changes: 82 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,29 @@ 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 });
if (globalconf.is_compositing) {
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,

Check warning on line 64 in systray.c

View check run for this annotation

Codecov / codecov/patch

systray.c#L64

Added line #L64 was not covered by tests
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 });

Check warning on line 70 in systray.c

View check run for this annotation

Codecov / codecov/patch

systray.c#L70

Added line #L70 was not covered by tests
}
xwindow_set_class_instance(globalconf.systray.window);
xwindow_set_name_static(globalconf.systray.window, "Awesome systray window");

Expand Down Expand Up @@ -130,6 +148,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 @@ -142,13 +162,28 @@ systray_request_handle(xcb_window_t embed_win)
if(xembed_getbywin(&globalconf.embedded, embed_win))
return -1;

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);

p_clear(&em_cookie, 1);

em_cookie = xembed_info_get_unchecked(globalconf.connection, embed_win);

xcb_change_window_attributes(globalconf.connection, embed_win, XCB_CW_EVENT_MASK,
select_input_val);


if (globalconf.is_compositing && em.depth != globalconf.default_depth) {
/* Disable the message because the test runner is not happy with warnings. This should rarely happen anyway. */
/* 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 +411,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.is_compositing) {
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,

Check warning on line 423 in systray.c

View check run for this annotation

Codecov / codecov/patch

systray.c#L422-L423

Added lines #L422 - L423 were not covered by tests
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;

Check warning on line 427 in systray.c

View check run for this annotation

Codecov / codecov/patch

systray.c#L426-L427

Added lines #L426 - L427 were not covered by tests
}
}

if(globalconf.systray.parent != w)
Expand Down Expand Up @@ -413,4 +457,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.is_compositing) {
lua_pushnil(L);
return 1;

Check warning on line 472 in systray.c

View check run for this annotation

Codecov / codecov/patch

systray.c#L471-L472

Added lines #L471 - L472 were not covered by tests
}

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);

Check warning on line 69 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L68-L69

Added lines #L68 - L69 were not covered by tests
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)

Check warning on line 77 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L77

Added line #L77 was not covered by tests
{
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(s);

Check warning on line 79 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L79

Added line #L79 was not covered by tests

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;

Check warning on line 87 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L81-L87

Added lines #L81 - L87 were not covered by tests
}

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));

Check warning on line 98 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L98

Added line #L98 was not covered by tests

// 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;

Check warning on line 102 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L101-L102

Added lines #L101 - L102 were not covered by tests
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(

Check warning on line 107 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L105-L107

Added lines #L105 - L107 were not covered by tests
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(tray_visual_r != NULL && 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);

Check warning on line 115 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L112-L115

Added lines #L112 - L115 were not covered by tests
}
tray_depth = find_visual_depth(screen, tray_visual_id);
if (tray_depth == 0) {
fatal("Error getting visual hint\n");

Check warning on line 119 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L117-L119

Added lines #L117 - L119 were not covered by tests
}
} else {
tray_visual_id= screen->root_visual;
tray_depth = screen->root_depth;

Check warning on line 123 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L122-L123

Added lines #L122 - L123 were not covered by tests
}

xcb_colormap_t cm = xcb_generate_id(conn);
xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE,

Check warning on line 127 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L126-L127

Added lines #L126 - L127 were not covered by tests
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,

Check warning on line 133 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L133

Added line #L133 was not covered by tests
tray_visual_id,
(use_tray_visual_hint ? XCB_CW_BACK_PIXEL : XCB_CW_BACK_PIXMAP)
| XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP,
(uint32_t[]) {

Check warning on line 137 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L137

Added line #L137 was not covered by tests
use_tray_visual_hint
? get_color(conn, cm, 0xffff, 0x9999, 0x0000)
: XCB_BACK_PIXMAP_PARENT_RELATIVE,
screen->black_pixel, cm

Check warning on line 141 in tests/test-systray.c

View check run for this annotation

Codecov / codecov/patch

tests/test-systray.c#L139-L141

Added lines #L139 - L141 were not covered by tests
});
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 7464c8e

Please sign in to comment.