Composite systray with alpha channel and support stale systray windows without it. Add a beautiful.systray_skip_bg to skip background drawing, so it can blend nicely with other widgets.
This commit is contained in:
parent
9334dc71a9
commit
7464c8e5c4
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
4
event.c
4
event.c
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
1
luaa.c
1
luaa.c
|
@ -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 },
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
#include <cairo-xcb.h>
|
||||
#include <xcb/shape.h>
|
||||
#include <xcb/composite.h>
|
||||
|
||||
lua_class_t drawin_class;
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
95
systray.c
95
systray.c
|
@ -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 */
|
||||
|
||||
|
@ -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,
|
||||
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");
|
||||
|
||||
|
@ -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[] =
|
||||
{
|
||||
|
@ -142,6 +162,12 @@ 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);
|
||||
|
@ -149,6 +175,15 @@ systray_request_handle(xcb_window_t 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.
|
||||
*/
|
||||
|
@ -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,
|
||||
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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -65,8 +65,8 @@ 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;
|
||||
|
@ -74,6 +74,19 @@ static uint32_t get_color(xcb_connection_t *conn, xcb_screen_t *screen, uint16_t
|
|||
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);
|
||||
|
@ -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(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);
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -2,7 +2,7 @@ local spawn = require("awful.spawn")
|
|||
local wibox = require("wibox")
|
||||
local beautiful = require("beautiful")
|
||||
|
||||
local steps, pid1, pid2, draw_w, st = {}
|
||||
local steps, pid1, pid2, draw_w, wb, st = {}
|
||||
|
||||
table.insert(steps, function()
|
||||
screen[1].mywibox:remove()
|
||||
|
@ -17,7 +17,7 @@ table.insert(steps, function()
|
|||
draw_w = width
|
||||
end
|
||||
|
||||
local wb = wibox {
|
||||
wb = wibox {
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
|
@ -81,6 +81,58 @@ table.insert(steps, function()
|
|||
return true
|
||||
end)
|
||||
|
||||
table.insert(steps, function()
|
||||
if draw_w ~= 100 then return end
|
||||
return true
|
||||
end)
|
||||
|
||||
table.insert(steps, function()
|
||||
st.base_size = 20
|
||||
pid1 = spawn("env USE_TRAY_VISUAL_HINT=1 ./test-systray")
|
||||
return true
|
||||
end)
|
||||
|
||||
local lgi_core = require("lgi.core")
|
||||
local lgi_ffi = require("lgi.ffi")
|
||||
local lgi_ti = lgi_ffi.types
|
||||
local lgi_record = require("lgi.record")
|
||||
local lgi_component = require("lgi.component")
|
||||
local cairo = require("lgi").cairo
|
||||
local wrapped_uchar = lgi_component.create(nil, lgi_record.struct_mt, "wrapped_uchar")
|
||||
lgi_ffi.load_fields(wrapped_uchar, { { 'v', lgi_ti.uchar } })
|
||||
|
||||
table.insert(steps, function()
|
||||
if draw_w ~= 80 then return end
|
||||
local systray_surface_raw = awesome.systray_surface(20, 20)
|
||||
if systray_surface_raw then
|
||||
local src = cairo.Surface(systray_surface_raw, true)
|
||||
local s = cairo.ImageSurface("ARGB32", 20, 20)
|
||||
local cr = cairo.Context(s)
|
||||
cr:set_source_surface(src, 0, 0)
|
||||
cr:paint()
|
||||
-- Read the first pixel (a,r,g,b) as 4 8-bit integers.
|
||||
local data = s:get_data()
|
||||
local array = lgi_core.record.new(wrapped_uchar, data, false)
|
||||
local argb = {}
|
||||
for i = 0, 3 do
|
||||
argb[i + 1] = lgi_core.record.fromarray(array, i).v
|
||||
end
|
||||
-- Check with the pixel from test-systray.c
|
||||
return
|
||||
argb[1] == 0 and
|
||||
argb[2] == 153 and
|
||||
argb[3] == 255 and
|
||||
argb[4] == 255
|
||||
else
|
||||
print("Systray composition test is disabled.")
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(steps, function()
|
||||
awesome.kill(pid1, 9)
|
||||
return true
|
||||
end)
|
||||
|
||||
table.insert(steps, function()
|
||||
if draw_w ~= 100 then return end
|
||||
|
|
Loading…
Reference in New Issue