diff --git a/Makefile.am b/Makefile.am index 5b40f1227..e648ed73c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,7 @@ WIDGETS += widgets/iconbox.c WIDGETS += widgets/progressbar.c WIDGETS += widgets/tasklist.c WIDGETS += widgets/graph.c +WIDGETS += widgets/systray.c doc_DATA += README @@ -122,6 +123,7 @@ awesome_SOURCES = \ common/version.c common/version.h \ common/util.c common/util.h \ common/xutil.c common/xutil.h \ + common/xembed.c common/xembed.h \ common/configopts.h common/configopts.c \ common/xscreen.h common/xscreen.c \ common/draw.c common/draw.h \ @@ -129,6 +131,7 @@ awesome_SOURCES = \ common/list.h common/refcount.h \ structs.h \ client.c client.h \ + systray.c systray.h \ titlebar.c titlebar.h \ placement.c placement.h \ focus.c focus.h \ diff --git a/awesome.c b/awesome.c index 0c528bb1a..f71ea1270 100644 --- a/awesome.c +++ b/awesome.c @@ -55,6 +55,7 @@ #include "tag.h" #include "dbus.h" #include "statusbar.h" +#include "systray.h" #include "common/socket.h" #include "common/version.h" #include "common/configopts.h" @@ -85,6 +86,7 @@ scan() xcb_get_geometry_cookie_t **geom_wins = NULL; xcb_get_window_attributes_reply_t *attr_r; xcb_get_geometry_reply_t *geom_r; + xembed_info_t eminfo; for(screen = 0; screen < screen_max; screen++) { @@ -105,8 +107,7 @@ scan() if(!tree_r) continue; - /* Get the tree of the children Windows of the current root - * Window */ + /* Get the tree of the children windows of the current root window */ if(!(wins = xcb_query_tree_children(tree_r))) eprint("E: cannot get tree children"); tree_c_len = xcb_query_tree_children_length(tree_r); @@ -153,7 +154,10 @@ scan() real_screen = screen_get_bycoord(globalconf.screens_info, screen, geom_r->x, geom_r->y); - client_manage(wins[i], geom_r, real_screen); + if(xembed_info_get(globalconf.connection, wins[i], &eminfo)) + systray_request_handle(wins[i], screen, &eminfo); + else + client_manage(wins[i], geom_r, real_screen); p_delete(&geom_r); p_delete(&geom_wins[i]); diff --git a/awesomerc.5.txt b/awesomerc.5.txt index 218320aa8..335a0e192 100644 --- a/awesomerc.5.txt +++ b/awesomerc.5.txt @@ -52,6 +52,7 @@ The current list of available widget is: - taglist - tasklist - textbox +- systray Each widget as its own set of properties, described below, that can bet modified with the set() method. diff --git a/awesomerc.lua.in b/awesomerc.lua.in index 5271654bc..9c12a56f2 100644 --- a/awesomerc.lua.in +++ b/awesomerc.lua.in @@ -97,6 +97,9 @@ mymenubox = widget.new({ type = "textbox", name = "mytextbox", align = "left" }) myiconbox = widget.new({ type = "iconbox", name = "myiconbox", align = "left" }) myiconbox:set("image", "@iconsdir@/awesome16.png") +-- Create a systray +mysystray = widget.new({ type = "systray", name = "mysystray", align = "right" }) + -- Create an iconbox widget which will contains an icon indicating which layout we're using. -- We need one layoutbox per screen. mylayoutbox = {} @@ -110,18 +113,20 @@ for s = 1, screen.count() do end -- Create a statusbar for each screen and add it +mystatusbar = {} for s = 1, screen.count() do - mystatusbar = statusbar.new({ position = "top", name = "mystatusbar" .. s, - fg = fg_normal, bg = bg_normal }) + mystatusbar[s] = statusbar.new({ position = "top", name = "mystatusbar" .. s, + fg = fg_normal, bg = bg_normal }) -- Add widgets to the statusbar - order matters - mystatusbar:widget_add(mytaglist) - mystatusbar:widget_add(myiconbox) - mystatusbar:widget_add(mytasklist) - mystatusbar:widget_add(mymenubox) - mystatusbar:widget_add(mytextbox) - mystatusbar:widget_add(mylayoutbox[s]) - mystatusbar:add(s) + mystatusbar[s]:widget_add(mytaglist) + mystatusbar[s]:widget_add(myiconbox) + mystatusbar[s]:widget_add(mytasklist) + mystatusbar[s]:widget_add(mymenubox) + mystatusbar[s]:widget_add(mytextbox) + mystatusbar[s]:widget_add(mylayoutbox[s]) + mystatusbar[s]:add(s) end +mystatusbar[screen.count()]:widget_add(mysystray) -- }}} -- {{{ Mouse bindings diff --git a/common/xembed.c b/common/xembed.c new file mode 100644 index 000000000..3f0d50fa4 --- /dev/null +++ b/common/xembed.c @@ -0,0 +1,164 @@ +/* + * common/xembed.c - XEMBED functions + * + * Copyright © 2007-2008 Julien Danjou + * Copyright © 2004 Matthew Reppert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "common/xembed.h" +#include "common/xutil.h" +#include "common/util.h" + +/** Have the embedder end XEMBED protocol communication with a child. + * \param connection The X connection. + * \param child The window to unembed. + * \param root The root window to reparent to. + */ +static inline void +xembed_window_unembed(xcb_connection_t *connection, xcb_window_t child, xcb_window_t root) +{ + xcb_unmap_window(connection, child); + xcb_reparent_window(connection, child, root, 0, 0); +} + +/** Indicate to an embedded window that it has lost focus. + * \param c The X connection. + * \param client The client to send message to. + */ +static inline void +xembed_focus_out (xcb_connection_t *c, xcb_window_t client) +{ + xembed_message_send(c, client, XEMBED_FOCUS_OUT, 0, 0, 0); +} + +/** Send an XEMBED message to a window. + * \param connection Connection to the X server. + * \param towin Destination window + * \param message The message. + * \param d1 Element 3 of message. + * \param d2 Element 4 of message. + * \param d3 Element 5 of message. + */ +void +xembed_message_send(xcb_connection_t *connection, xcb_window_t towin, + long message, long d1, long d2, long d3) +{ + xcb_client_message_event_t ev; + xutil_intern_atom_request_t atom_q; + xcb_atom_t atom; + + /** \todo use atom cache */ + atom_q = xutil_intern_atom(connection, NULL, "_XEMBED"); + p_clear(&ev, 1); + ev.response_type = XCB_CLIENT_MESSAGE; + ev.window = towin; + ev.format = 32; + ev.data.data32[0] = XCB_CURRENT_TIME; + ev.data.data32[1] = message; + ev.data.data32[2] = d1; + ev.data.data32[3] = d2; + ev.data.data32[4] = d3; + atom = xutil_intern_atom_reply(connection, NULL, atom_q); + ev.type = atom; + xcb_send_event(connection, false, towin, XCB_EVENT_MASK_NO_EVENT, (char *) &ev); +} + +/** Get the XEMBED info for a window. + * \param connection The X connection. + * \param win The window. + * \param info The xembed_info_t structure to fill. + */ +bool +xembed_info_get(xcb_connection_t *connection, xcb_window_t win, xembed_info_t *info) +{ + xutil_intern_atom_request_t atom_q; + xcb_atom_t atom; + xcb_get_property_cookie_t prop_c; + xcb_get_property_reply_t *prop_r; + uint32_t *data; + bool ret = false; + + /** \todo use atom cache */ + atom_q = xutil_intern_atom(connection, NULL, "_XEMBED_INFO"); + atom = xutil_intern_atom_reply(connection, NULL, atom_q); + + prop_c = xcb_get_property(connection, false, win, atom, XCB_GET_PROPERTY_TYPE_ANY, 0L, 2); + prop_r = xcb_get_property_reply(connection, prop_c, NULL); + + if(!prop_r || !prop_r->value_len) + goto bailout; + + if(!(data = (uint32_t *) xcb_get_property_value(prop_r))) + goto bailout; + + info->version = data[0]; + info->flags = data[1] & XEMBED_INFO_FLAGS_ALL; + ret = true; + +bailout: + p_delete(&prop_r); + return ret; +} + +/** Get a XEMBED window from a xembed_window_t list. + * \param list The xembed window list. + * \param win The window to look for. + */ +xembed_window_t * +xembed_getbywin(xembed_window_t *list, xcb_window_t win) +{ + xembed_window_t *n; + for(n = list; n; n = n->next) + if(win == n->win) + return n; + return NULL; +} + +/** Update embedded window properties. + * \param connection The X connection. + * \param emwin The embedded window. + */ +void +xembed_property_update(xcb_connection_t *connection, xembed_window_t *emwin) +{ + int flags_changed; + xembed_info_t info = { 0, 0 }; + + xembed_info_get(connection, emwin->win, &info); + /* test if it changed */ + if(!(flags_changed = info.flags ^ emwin->info.flags)) + return; + + emwin->info.flags = info.flags; + if(flags_changed & XEMBED_MAPPED) + { + if(info.flags & XEMBED_MAPPED) + { + xcb_map_window(connection, emwin->win); + xembed_window_activate(connection, emwin->win); + } + else + { + xcb_unmap_window(connection, emwin->win); + xembed_window_deactivate(connection, emwin->win); + xembed_focus_out(connection, emwin->win); + } + } +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/common/xembed.h b/common/xembed.h new file mode 100644 index 000000000..7030a46a2 --- /dev/null +++ b/common/xembed.h @@ -0,0 +1,144 @@ +/* + * common/xembed.h - XEMBED functions header + * + * Copyright © 2007-2008 Julien Danjou + * Copyright © 2004 Matthew Reppert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_COMMON_XEMBED_H +#define AWESOME_COMMON_XEMBED_H + +#include + +#include + +#include "common/list.h" +#include "common/util.h" + +/** XEMBED information for a window. + */ +typedef struct +{ + unsigned long version; + unsigned long flags; +} xembed_info_t; + +typedef struct xembed_window_t xembed_window_t; +struct xembed_window_t +{ + xcb_window_t win; + int phys_screen; + xembed_info_t info; + xembed_window_t *prev, *next; +}; + +DO_SLIST(xembed_window_t, xembed_window, p_delete) + +/** The version of the XEMBED protocol that this library supports. */ +#define XEMBED_VERSION 0 + +/** Flags for _XEMBED_INFO */ +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_INFO_FLAGS_ALL 1 + +/** XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 +#define XEMBED_REQUEST_FOCUS 3 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 +#define XEMBED_FOCUS_NEXT 6 +#define XEMBED_FOCUS_PREV 7 +/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ +#define XEMBED_MODALITY_ON 10 +#define XEMBED_MODALITY_OFF 11 +#define XEMBED_REGISTER_ACCELERATOR 12 +#define XEMBED_UNREGISTER_ACCELERATOR 13 +#define XEMBED_ACTIVATE_ACCELERATOR 14 + +/** Details for XEMBED_FOCUS_IN */ +#define XEMBED_FOCUS_CURRENT 0 +#define XEMBED_FOCUS_FIRST 1 +#define XEMBED_FOCUS_LAST 2 + + +/** Modifiers field for XEMBED_REGISTER_ACCELERATOR */ +#define XEMBED_MODIFIER_SHIFT (1 << 0) +#define XEMBED_MODIFIER_CONTROL (1 << 1) +#define XEMBED_MODIFIER_ALT (1 << 2) +#define XEMBED_MODIFIER_SUPER (1 << 3) +#define XEMBED_MODIFIER_HYPER (1 << 4) + + +/** Flags for XEMBED_ACTIVATE_ACCELERATOR */ +#define XEMBED_ACCELERATOR_OVERLOADED (1 << 0) + + +void xembed_message_send(xcb_connection_t *, xcb_window_t, long, long, long, long); +xembed_window_t * xembed_getbywin(xembed_window_t *, xcb_window_t); +void xembed_property_update(xcb_connection_t *, xembed_window_t *); +bool xembed_info_get(xcb_connection_t *, xcb_window_t, xembed_info_t *); + +/** Indicate to an embedded window that it has focus. + * \param c The X connection. + * \param client The client. + * \param focus_type The type of focus. + */ +static inline void +xembed_focus_in (xcb_connection_t *c, xcb_window_t client, long focus_type) +{ + xembed_message_send(c, client, XEMBED_FOCUS_IN, focus_type, 0, 0); +} + +/** Notify a window that it has become active. + * \param c The X connection. + * \param client The window to notify. + */ +static inline void +xembed_window_activate(xcb_connection_t *c, xcb_window_t client) +{ + xembed_message_send(c, client, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); +} + +/** Notify a window that it has become inactive. + * \param c The X connection. + * \param client The window to notify. + */ +static inline +void xembed_window_deactivate(xcb_connection_t *c, xcb_window_t client) +{ + xembed_message_send(c, client, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0); +} + +/** Notify a window that its embed request has been received and accepted. + * \param c The X connection. + * \param client The client to send message to. + * \param embedder The embedder window. + * \param version The version. + */ +static inline void +xembed_embedded_notify(xcb_connection_t *c, + xcb_window_t client, xcb_window_t embedder, + long version) +{ + xembed_message_send(c, client, XEMBED_EMBEDDED_NOTIFY, 0, embedder, version); +} + +#endif +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/event.c b/event.c index 57d7a46d4..4889a870e 100644 --- a/event.c +++ b/event.c @@ -35,6 +35,7 @@ #include "titlebar.h" #include "keygrabber.h" #include "lua.h" +#include "systray.h" #include "layouts/floating.h" #include "common/xscreen.h" #include "common/xutil.h" @@ -313,10 +314,11 @@ event_handle_destroynotify(void *data __attribute__ ((unused)), */ int event_handle_enternotify(void *data __attribute__ ((unused)), - xcb_connection_t *connection __attribute__ ((unused)), + xcb_connection_t *connection, xcb_enter_notify_event_t *ev) { - client_t *c = NULL; + client_t *c; + xembed_window_t *emwin; if(ev->mode != XCB_NOTIFY_MODE_NORMAL || (ev->root_x == globalconf.pointer_x @@ -327,7 +329,7 @@ event_handle_enternotify(void *data __attribute__ ((unused)), || (c = client_getbywin(ev->event))) { window_grabbuttons(c->win, c->phys_screen, c->buttons); - /* the idea behind saving pointer_x and pointer_y is Bob Marley powered + /* The idea behind saving pointer_x and pointer_y is Bob Marley powered. * this will allow us top drop some EnterNotify events and thus not giving * focus to windows appering under the cursor without a cursor move */ globalconf.pointer_x = ev->root_x; @@ -336,6 +338,10 @@ event_handle_enternotify(void *data __attribute__ ((unused)), luaA_client_userdata_new(c); luaA_dofunction(globalconf.L, globalconf.hooks.mouseover, 1); } + else if((emwin = xembed_getbywin(globalconf.embedded, ev->event))) + xcb_ungrab_button(globalconf.connection, XCB_BUTTON_INDEX_ANY, + xcb_aux_get_screen(connection, emwin->phys_screen)->root, + ANY_MODIFIER); else window_root_grabbuttons(); @@ -442,7 +448,12 @@ event_handle_maprequest(void *data __attribute__ ((unused)), if(wa_r->override_redirect) goto bailout; - if(!(c = client_getbywin(ev->window))) + if(xembed_getbywin(globalconf.embedded, ev->window)) + { + xcb_map_window(connection, ev->window); + xembed_window_activate(connection, ev->window); + } + else if(!(c = client_getbywin(ev->window))) { geom_c = xcb_get_geometry(connection, ev->window); @@ -491,10 +502,13 @@ event_handle_propertynotify(void *data __attribute__ ((unused)), { client_t *c; xcb_window_t trans; + xembed_window_t *emwin; if(ev->state == XCB_PROPERTY_DELETE) return 0; /* ignore */ - if((c = client_getbywin(ev->window))) + if((emwin = xembed_getbywin(globalconf.embedded, ev->window))) + xembed_property_update(connection, emwin); + else if((c = client_getbywin(ev->window))) { if(ev->atom == WM_TRANSIENT_FOR) { @@ -529,12 +543,12 @@ event_handle_unmapnotify(void *data __attribute__ ((unused)), xcb_connection_t *connection, xcb_unmap_notify_event_t *ev) { client_t *c; + xembed_window_t *em; + int i; - /* - * event->send_event (Xlib) is quivalent to (ev->response_type & + /* event->send_event (Xlib) is quivalent to (ev->response_type & * 0x80) in XCB because the SendEvent bit is available in the - * response_type field - */ + * response_type field */ bool send_event = ((ev->response_type & 0x80) >> 7); if((c = client_getbywin(ev->window)) @@ -542,6 +556,14 @@ event_handle_unmapnotify(void *data __attribute__ ((unused)), && send_event && window_getstate(c->win) == XCB_WM_NORMAL_STATE) client_unmanage(c); + /** \todo invalidate for all screen might be too much */ + if((em = xembed_getbywin(globalconf.embedded, ev->window))) + { + xembed_window_list_detach(&globalconf.embedded, em); + for(i = 0; i < globalconf.screens_info->nscreen; i++) + widget_invalidate_cache(i, WIDGET_CACHE_EMBEDDED); + } + return 0; } @@ -604,11 +626,23 @@ event_handle_randr_screen_change_notify(void *data __attribute__ ((unused)), */ int event_handle_clientmessage(void *data __attribute__ ((unused)), - xcb_connection_t *connection __attribute__ ((unused)), + xcb_connection_t *connection, xcb_client_message_event_t *ev) { - ewmh_process_client_message(ev); - return 0; + xutil_intern_atom_request_t atom_xem_q, atom_systray_q; + xcb_atom_t atom_xem, atom_systray; + + atom_xem_q = xutil_intern_atom(connection, &globalconf.atoms, "_XEMBED"); + atom_systray_q = xutil_intern_atom(connection, &globalconf.atoms, "_NET_SYSTEM_TRAY_OPCODE"); + + atom_xem = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, atom_xem_q); + atom_systray = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, atom_systray_q); + + if(ev->type == atom_xem) + return xembed_process_client_message(ev); + else if(ev->type == atom_systray) + return systray_process_client_message(ev); + return ewmh_process_client_message(ev); } // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/ewmh.c b/ewmh.c index 014cbb9eb..9dc1f6eef 100644 --- a/ewmh.c +++ b/ewmh.c @@ -381,7 +381,7 @@ ewmh_process_window_type_atom(client_t *c, xcb_atom_t state) client_setfloating(c, true, LAYER_MODAL); } -void +int ewmh_process_client_message(xcb_client_message_event_t *ev) { client_t *c; @@ -411,6 +411,8 @@ ewmh_process_client_message(xcb_client_message_event_t *ev) ev->data.data32[0]); } } + + return 0; } void diff --git a/ewmh.h b/ewmh.h index 24b6b5435..6c6045ae6 100644 --- a/ewmh.h +++ b/ewmh.h @@ -38,7 +38,7 @@ void ewmh_update_net_numbers_of_desktop(int); void ewmh_update_net_current_desktop(int); void ewmh_update_net_desktop_names(int); void ewmh_update_net_active_window(int); -void ewmh_process_client_message(xcb_client_message_event_t *); +int ewmh_process_client_message(xcb_client_message_event_t *); void ewmh_check_client_hints(client_t *); NetWMIcon * ewmh_get_window_icon(xcb_window_t); diff --git a/statusbar.c b/statusbar.c index c81df3cb9..a1f904a06 100644 --- a/statusbar.c +++ b/statusbar.c @@ -377,6 +377,14 @@ luaA_statusbar_widget_add(lua_State *L) widget_t **widget = luaA_checkudata(L, 2, "widget"); widget_node_t *w = p_new(widget_node_t, 1); + if((*widget)->type == systray_new) + { + if(globalconf.systray) + luaL_error(L, "system tray already added and only one is allowed"); + else + globalconf.systray = *sb; + } + (*sb)->need_update = true; w->widget = *widget; widget_node_list_append(&(*sb)->widgets, w); @@ -403,6 +411,8 @@ widget_remove_loop: for(w = (*sb)->widgets; w; w = w->next) if(w->widget == *widget) { + if(*sb == globalconf.systray && (*widget)->type == systray_new) + globalconf.systray = NULL; widget_unref(widget); widget_node_list_detach(&(*sb)->widgets, w); p_delete(&w); diff --git a/structs.h b/structs.h index 943542f30..e495abd83 100644 --- a/structs.h +++ b/structs.h @@ -30,6 +30,7 @@ #include "common/draw.h" #include "common/swindow.h" #include "common/xscreen.h" +#include "common/xembed.h" #include "common/refcount.h" /** Stacking layout layers */ @@ -63,6 +64,7 @@ typedef struct client_node_t client_node_t; typedef struct _tag_t tag_t; typedef struct tag_client_node_t tag_client_node_t; typedef area_t (FloatingPlacement)(client_t *); +typedef widget_t *(widget_constructor_t)(alignment_t); typedef struct awesome_t awesome_t; /** Widget tell status code */ @@ -102,6 +104,8 @@ struct widget_t int refcount; /** widget_t name */ char *name; + /** Widget type is constructor */ + widget_constructor_t *type; /** Draw function */ int (*draw)(draw_context_t *, int, widget_node_t *, int, int, void *); /** Update function */ @@ -401,10 +405,14 @@ struct awesome_t xcb_cursor_t cursor[CurLast]; /** Clients list */ client_t *clients; + /** Embedded windows */ + xembed_window_t *embedded; /** Path to config file */ char *configpath; /** Floating window placement algo */ FloatingPlacement *floating_placement; + /** Statusbar that contains the systray */ + statusbar_t *systray; /** Selected clients history */ client_node_t *focus; /** Stack client history */ diff --git a/systray.c b/systray.c new file mode 100644 index 000000000..cb69934f1 --- /dev/null +++ b/systray.c @@ -0,0 +1,121 @@ +/* + * systray.c - systray handling + * + * Copyright © 2008 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include "structs.h" +#include "systray.h" +#include "window.h" +#include "common/xembed.h" + +#define SYSTEM_TRAY_REQUEST_DOCK 0 /* Begin icon docking */ + +extern awesome_t globalconf; + +/** Handle a systray request. + * \param embed_win The window to embed. + */ +int +systray_request_handle(xcb_window_t embed_win, int phys_screen, xembed_info_t *info) +{ + xembed_window_t *em; + const uint32_t select_input_val[] = + { + XCB_EVENT_MASK_STRUCTURE_NOTIFY + | XCB_EVENT_MASK_PROPERTY_CHANGE + | XCB_EVENT_MASK_ENTER_WINDOW + }; + + + xcb_change_window_attributes(globalconf.connection, embed_win, XCB_CW_EVENT_MASK, + select_input_val); + window_setstate(embed_win, XCB_WM_WITHDRAWN_STATE); + + em = p_new(xembed_window_t, 1); + em->win = embed_win; + em->phys_screen = phys_screen; + + if(info) + em->info = *info; + else + xembed_info_get(globalconf.connection, em->win, &em->info); + + xembed_window_list_append(&globalconf.embedded, em); + + xembed_embedded_notify(globalconf.connection, em->win, + globalconf.systray->sw->window, + MIN(XEMBED_VERSION, em->info.version)); + + if(em->info.flags & XEMBED_MAPPED) + xcb_map_window(globalconf.connection, em->win); + + return 0; +} + +/** Handle systray message. + * \param ev The event. + * \return 0 on no error. + */ +int +systray_process_client_message(xcb_client_message_event_t *ev) +{ + int screen_nbr = 0; + xcb_get_geometry_cookie_t geom_c; + xcb_get_geometry_reply_t *geom_r; + xcb_screen_iterator_t iter; + + switch(ev->data.data32[1]) + { + case SYSTEM_TRAY_REQUEST_DOCK: + geom_c = xcb_get_geometry(globalconf.connection, ev->window); + + if(!(geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL))) + return -1; + + for(iter = xcb_setup_roots_iterator(xcb_get_setup(globalconf.connection)), screen_nbr = 0; + iter.rem && iter.data->root != geom_r->root; xcb_screen_next (&iter), ++screen_nbr); + + p_delete(&geom_r); + + systray_request_handle(ev->data.data32[2], screen_nbr, NULL); + break; + } + return 0; +} + +/** Handle xembed client message. + * \param ev The event. + * \return 0 on no error. + */ +int +xembed_process_client_message(xcb_client_message_event_t *ev) +{ + switch(ev->data.data32[1]) + { + case XEMBED_REQUEST_FOCUS: + xembed_focus_in(globalconf.connection, ev->window, XEMBED_FOCUS_CURRENT); + break; + } + return 0; +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/systray.h b/systray.h new file mode 100644 index 000000000..7f0988c59 --- /dev/null +++ b/systray.h @@ -0,0 +1,33 @@ +/* + * systray.h - systray handlers header + * + * Copyright © 2008 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_SYSTRAY_H +#define AWESOME_SYSTRAY_H + +#include +#include "common/xembed.h" + +int systray_request_handle(xcb_window_t, int, xembed_info_t *); +int systray_process_client_message(xcb_client_message_event_t *); +int xembed_process_client_message(xcb_client_message_event_t *); + +#endif +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 diff --git a/titlebar.c b/titlebar.c index da0f57399..b20bd047e 100644 --- a/titlebar.c +++ b/titlebar.c @@ -453,6 +453,9 @@ luaA_titlebar_widget_add(lua_State *L) widget_node_t *w = p_new(widget_node_t, 1); client_t *c; + if((*widget)->type == systray_new) + luaL_error(L, "cannot add systray widget to titlebar"); + w->widget = *widget; widget_node_list_append(&(*tb)->widgets, w); widget_ref(widget); diff --git a/widget.c b/widget.c index 873b84dcd..77e426dc0 100644 --- a/widget.c +++ b/widget.c @@ -337,6 +337,8 @@ luaA_widget_new(lua_State *L) else luaL_error(L, "unkown widget type: %s", type); + w->type = wc; + /* Set visible by default. */ w->isvisible = true; diff --git a/widget.h b/widget.h index 9f5b54f10..3040aa19f 100644 --- a/widget.h +++ b/widget.h @@ -28,9 +28,7 @@ #define WIDGET_CACHE_CLIENTS (1<<0) #define WIDGET_CACHE_LAYOUTS (1<<1) #define WIDGET_CACHE_TAGS (1<<2) -#define WIDGET_CACHE_ALL (WIDGET_CACHE_CLIENTS | WIDGET_CACHE_LAYOUTS | WIDGET_CACHE_TAGS) - -typedef widget_t *(widget_constructor_t)(alignment_t); +#define WIDGET_CACHE_EMBEDDED (1<<3) void widget_invalidate_cache(int, int); int widget_calculate_offset(int, int, int, int); @@ -47,6 +45,7 @@ widget_constructor_t iconbox_new; widget_constructor_t progressbar_new; widget_constructor_t graph_new; widget_constructor_t tasklist_new; +widget_constructor_t systray_new; #endif diff --git a/widgets/systray.c b/widgets/systray.c new file mode 100644 index 000000000..66782c38d --- /dev/null +++ b/widgets/systray.c @@ -0,0 +1,192 @@ +/* + * systray.c - systray widget + * + * Copyright © 2008 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "widget.h" +#include "common/xembed.h" + +extern awesome_t globalconf; + +typedef struct +{ + bool init; +} systray_data_t; + +static bool +systray_init(void) +{ + xutil_intern_atom_request_t atom_systray_q, atom_manager_q; + xcb_atom_t atom_systray; + xcb_client_message_event_t ev; + char atom_name[22]; + + /* Send requests */ + atom_manager_q = xutil_intern_atom(globalconf.connection, &globalconf.atoms, atom_name); + snprintf(atom_name, sizeof(atom_name), "_NET_SYSTEM_TRAY_S%d", globalconf.default_screen); + atom_systray_q = xutil_intern_atom(globalconf.connection, &globalconf.atoms, atom_name); + + /* Fill event */ + ev.format = 32; + ev.data.data32[0] = XCB_CURRENT_TIME; + ev.data.data32[2] = globalconf.systray->sw->window; + ev.data.data32[3] = ev.data.data32[4] = 0; + ev.response_type = xutil_intern_atom_reply(globalconf.connection, + &globalconf.atoms, atom_manager_q); + + ev.data.data32[1] = atom_systray = xutil_intern_atom_reply(globalconf.connection, + &globalconf.atoms, + atom_systray_q); + + xcb_set_selection_owner(globalconf.connection, + globalconf.systray->sw->window, + atom_systray, + XCB_CURRENT_TIME); + + return true; +} + +static int +systray_draw(draw_context_t *ctx, + int screen __attribute__ ((unused)), + widget_node_t *w, + int offset, int used __attribute__ ((unused)), + void *p __attribute__ ((unused))) +{ + int i = 0; + xembed_window_t *em; + uint32_t config_win_vals[6]; + systray_data_t *d = w->widget->data; + + + if(!d->init) + d->init = systray_init(); + + for(em = globalconf.embedded; em; em = em->next) + i++; + + if(ctx->width - used > 0) + w->area.width = MIN(i * ctx->height, ctx->width - used); + else + w->area.width = 0; + w->area.height = ctx->height; + w->area.x = widget_calculate_offset(ctx->width, + w->area.width, + offset, + w->widget->align); + w->area.y = 0; + + /* width */ + config_win_vals[2] = w->area.height; + /* height */ + config_win_vals[3] = w->area.height; + /* sibling */ + config_win_vals[4] = globalconf.systray->sw->window; + /* stack mode */ + config_win_vals[5] = XCB_STACK_MODE_ABOVE; + + switch(globalconf.systray->position) + { + case Left: + config_win_vals[0] = globalconf.systray->sw->geometry.x + w->area.y; + config_win_vals[1] = globalconf.systray->sw->geometry.y + globalconf.systray->sw->geometry.height + - w->area.x - config_win_vals[3]; + for(em = globalconf.embedded; em; em = em->next) + if(config_win_vals[1] - config_win_vals[2] >= (uint32_t) globalconf.systray->sw->geometry.y) + { + xcb_map_window(globalconf.connection, em->win); + xcb_configure_window(globalconf.connection, em->win, + XCB_CONFIG_WINDOW_X + | XCB_CONFIG_WINDOW_Y + | XCB_CONFIG_WINDOW_WIDTH + | XCB_CONFIG_WINDOW_HEIGHT + | XCB_CONFIG_WINDOW_SIBLING + | XCB_CONFIG_WINDOW_STACK_MODE, + config_win_vals); + config_win_vals[1] -= config_win_vals[3]; + } + else + xcb_unmap_window(globalconf.connection, em->win); + break; + case Right: + config_win_vals[0] = globalconf.systray->sw->geometry.x - w->area.y; + config_win_vals[1] = globalconf.systray->sw->geometry.y + w->area.x; + for(em = globalconf.embedded; em; em = em->next) + if(config_win_vals[1] + config_win_vals[3] <= (uint32_t) globalconf.systray->sw->geometry.y + ctx->width) + { + xcb_map_window(globalconf.connection, em->win); + xcb_configure_window(globalconf.connection, em->win, + XCB_CONFIG_WINDOW_X + | XCB_CONFIG_WINDOW_Y + | XCB_CONFIG_WINDOW_WIDTH + | XCB_CONFIG_WINDOW_HEIGHT + | XCB_CONFIG_WINDOW_SIBLING + | XCB_CONFIG_WINDOW_STACK_MODE, + config_win_vals); + config_win_vals[1] += config_win_vals[3]; + } + else + xcb_unmap_window(globalconf.connection, em->win); + break; + default: + /* x */ + config_win_vals[0] = globalconf.systray->sw->geometry.x + w->area.x; + /* y */ + config_win_vals[1] = globalconf.systray->sw->geometry.y + w->area.y; + + for(em = globalconf.embedded; em; em = em->next) + /* if(x + width < systray.x + systray.width) */ + if(config_win_vals[0] + config_win_vals[2] <= (uint32_t) AREA_RIGHT(w->area) + globalconf.systray->sw->geometry.x) + { + xcb_map_window(globalconf.connection, em->win); + xcb_configure_window(globalconf.connection, em->win, + XCB_CONFIG_WINDOW_X + | XCB_CONFIG_WINDOW_Y + | XCB_CONFIG_WINDOW_WIDTH + | XCB_CONFIG_WINDOW_HEIGHT + | XCB_CONFIG_WINDOW_SIBLING + | XCB_CONFIG_WINDOW_STACK_MODE, + config_win_vals); + config_win_vals[0] += config_win_vals[2]; + } + else + xcb_unmap_window(globalconf.connection, em->win); + break; + } + + return w->area.width; +} + +widget_t * +systray_new(alignment_t align) +{ + widget_t *w; + + w = p_new(widget_t, 1); + widget_common_new(w); + w->align = align; + w->draw = systray_draw; + w->cache_flags = WIDGET_CACHE_EMBEDDED; + w->data = p_new(systray_data_t, 1); + + return w; +} +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80