!! Implementation of System tray !!

This commit is contained in:
Martin Duquesnoy 2010-07-22 20:12:27 +02:00
parent d32ee3295e
commit 908978cca1
7 changed files with 455 additions and 16 deletions

View File

@ -162,6 +162,7 @@ void
clientmessageevent(XClientMessageEvent *ev)
{
Client *c;
Systray *sy;
int s, i, mess_t = 0;
Atom rt;
int rf;
@ -195,8 +196,28 @@ clientmessageevent(XClientMessageEvent *ev)
/* Manage _NET_ACTIVE_WINDOW */
else if(mess_t == net_active_window)
{
if((c = client_gb_win(ev->window)))
client_focus(c);
else if((sy = systray_find(ev->data.l[0])))
XSetInputFocus(dpy, sy->win, RevertToNone, CurrentTime);
}
}
else if(ev->window == traywin)
{
/* Manage _NET_WM_SYSTEM_TRAY_OPCODE */
if(mess_t == net_wm_system_tray_opcode)
{
if(ev->data.l[1] == XEMBED_EMBEDDED_NOTIFY)
{
systray_add(ev->data.l[2]);
systray_update();
}
else if(ev->data.l[1] == XEMBED_REQUEST_FOCUS)
if((sy = systray_find(ev->data.l[2])))
ewmh_send_message(sy->win, sy->win, "_XEMBED",
XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0, 0);
}
}
/* Manage _NET_WM_STATE */
@ -214,6 +235,7 @@ clientmessageevent(XClientMessageEvent *ev)
if((c = client_gb_win(ev->window)))
tag_transfert(c, ev->data.l[0]);
/* Manage _WMFS_STATUSTEXT_x */
if(mess_t >= wmfs_statustext && ev->data.l[4] == True)
{
@ -262,8 +284,8 @@ clientmessageevent(XClientMessageEvent *ev)
return;
}
/** ConfigureRequest & ConfigureNotify handle events
* \param ev XEvent pointer
/** ConfigureRequesthandle events
* \param ev XConfigureRequestEvent pointer
*/
void
configureevent(XConfigureRequestEvent *ev)
@ -318,12 +340,19 @@ void
destroynotify(XDestroyWindowEvent *ev)
{
Client *c;
Systray *s;
if((c = client_gb_win(ev->window)))
{
client_unmanage(c);
XSetErrorHandler(errorhandler);
}
else if((s = systray_find(ev->window)))
{
setwinstate(s->win, WithdrawnState);
systray_del(s->win);
systray_update();
}
return;
}
@ -335,6 +364,7 @@ void
enternotify(XCrossingEvent *ev)
{
Client *c;
Systray *s;
int n;
if((ev->mode != NotifyNormal
@ -342,6 +372,9 @@ enternotify(XCrossingEvent *ev)
&& ev->window != ROOT)
return;
if((s = systray_find(ev->window)))
XSetInputFocus(dpy, s->win, RevertToNone, CurrentTime);
if(conf.focus_fmouse)
{
if((c = client_gb_win(ev->window))
@ -441,17 +474,49 @@ keypress(XKeyPressedEvent *ev)
return;
}
/** MapNotify handle event
/** MappingNotify handle event
* \param ev XMappingEvent pointer
*/
void
mappingnotify(XMappingEvent *ev)
{
Systray *s;
XRefreshKeyboardMapping(ev);
if(ev->request == MappingKeyboard)
grabkeys();
if(!(s = systray_find(ev->window)))
{
setwinstate(s->win, NormalState);
ewmh_send_message(s->win, s->win, "_XEMBED", CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, 0, 0);
}
return;
}
/** MapNotify handle event
* \param ev XMapEvent pointer
*/
void
mapnotify(XMapEvent *ev)
{
Client *c;
Systray *s;
if(ev->window != ev->event && !ev->send_event)
return;
if((c = client_gb_win(ev->window)))
setwinstate(c->win, NormalState);
else if((s = systray_find(ev->window)))
{
setwinstate(s->win, NormalState);
ewmh_send_message(s->win, s->win, "_XEMBED", CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, 0, 0);
}
return;
}
@ -480,12 +545,27 @@ void
propertynotify(XPropertyEvent *ev)
{
Client *c;
Systray *s;
Window trans;
XWMHints *h;
if(ev->state == PropertyDelete)
return;
if((s = systray_find(ev->window)))
{
if(ev->atom == XA_WM_NORMAL_HINTS)
{
systray_configure(s);
systray_update();
}
else if(ev->atom == net_atom[xembedinfo])
{
systray_state(s);
systray_update();
}
}
if((c = client_gb_win(ev->window)))
{
switch(ev->atom)
@ -524,6 +604,33 @@ propertynotify(XPropertyEvent *ev)
return;
}
/** XReparentEvent handle event
* \param ev XReparentEvent pointer
*/
void
reparentnotify(XReparentEvent *ev)
{
return;
}
/** SelectionClearEvent handle event
* \param ev XSelectionClearEvent pointer
*/
void
selectionclearevent(XSelectionClearEvent *ev)
{
/* Getting selection if lost it */
if(ev->window == traywin)
systray_acquire();
systray_update();
return;
}
/** UnmapNotify handle event
* \param ev XUnmapEvent pointer
*/
@ -531,6 +638,7 @@ void
unmapnotify(XUnmapEvent *ev)
{
Client *c;
Systray *s;
if((c = client_gb_win(ev->window))
&& ev->send_event
@ -540,6 +648,13 @@ unmapnotify(XUnmapEvent *ev)
XSetErrorHandler(errorhandler);
}
if((s = systray_find(ev->window)))
{
setwinstate(s->win, WithdrawnState);
systray_del(s->win);
systray_update();
}
return;
}
@ -579,18 +694,21 @@ getevent(XEvent ev)
switch(ev.type)
{
case ButtonPress: buttonpress(&ev.xbutton); break;
case ClientMessage: clientmessageevent(&ev.xclient); break;
case ConfigureRequest: configureevent(&ev.xconfigurerequest); break;
case DestroyNotify: destroynotify(&ev.xdestroywindow); break;
case EnterNotify: enternotify(&ev.xcrossing); break;
case Expose: expose(&ev.xexpose); break;
case FocusIn: focusin(&ev.xfocus); break;
case KeyPress: keypress(&ev.xkey); break;
case MapRequest: maprequest(&ev.xmaprequest); break;
case MappingNotify: mappingnotify(&ev.xmapping); break;
case PropertyNotify: propertynotify(&ev.xproperty); break;
case UnmapNotify: unmapnotify(&ev.xunmap); break;
case ButtonPress: buttonpress(&ev.xbutton); break;
case ClientMessage: clientmessageevent(&ev.xclient); break;
case ConfigureRequest: configureevent(&ev.xconfigurerequest); break;
case DestroyNotify: destroynotify(&ev.xdestroywindow); break;
case EnterNotify: enternotify(&ev.xcrossing); break;
case Expose: expose(&ev.xexpose); break;
case FocusIn: focusin(&ev.xfocus); break;
case KeyPress: keypress(&ev.xkey); break;
case MapNotify: mapnotify(&ev.xmap); break;
case MapRequest: maprequest(&ev.xmaprequest); break;
case MappingNotify: mappingnotify(&ev.xmapping); break;
case PropertyNotify: propertynotify(&ev.xproperty); break;
case ReparentNotify: reparentnotify(&ev.xreparent); break;
case SelectionClear: selectionclearevent(&ev.xselectionclear); break;
case UnmapNotify: unmapnotify(&ev.xunmap); break;
default:
#ifdef HAVE_XRANDR

View File

@ -77,6 +77,11 @@ ewmh_init_hints(void)
net_atom[net_wm_state_fullscreen] = ATOM("_NET_WM_STATE_FULLSCREEN");
net_atom[net_wm_state_sticky] = ATOM("_NET_WM_STATE_STICKY");
net_atom[net_wm_state_demands_attention] = ATOM("_NET_WM_STATE_DEMANDS_ATTENTION");
net_atom[net_wm_system_tray_opcode] = ATOM("_NET_SYSTEM_TRAY_OPCODE");
net_atom[net_system_tray_message_data] = ATOM("_NET_SYSTEM_TRAY_MESSAGE_DATA");
net_atom[net_system_tray_s] = ATOM("_NET_SYSTEM_TRAY_S");
net_atom[xembed] = ATOM("_XEMBED");
net_atom[xembedinfo] = ATOM("_XEMBED_INFO");
net_atom[utf8_string] = ATOM("UTF8_STRING");
/* WMFS hints */
@ -128,6 +133,57 @@ ewmh_init_hints(void)
return;
}
/** Send ewmh message
*/
void
ewmh_send_message(Window d, Window w, char *atom, long d0, long d1, long d2, long d3, long d4)
{
XClientMessageEvent e;
e.type = ClientMessage;
e.message_type = ATOM(atom);
e.window = w;
e.format = 32;
e.data.l[0] = d0;
e.data.l[1] = d1;
e.data.l[2] = d2;
e.data.l[3] = d3;
e.data.l[4] = d4;
XSendEvent(dpy, d, False, NoEventMask, (XEvent*)&e);
return;
}
/** Get xembed state
*/
long
ewmh_get_xembed_state(Window win)
{
xembed_info *xi = NULL;
Atom rf;
int f;
ulong n, il;
long flags;
uchar *data = NULL;
if(XGetWindowProperty(dpy, win, net_atom[xembedinfo], 0L, 0x7FFFFFFFL,
False, net_atom[xembedinfo], &rf, &f, &n, &il, &data) == Success && n)
xi = (xembed_info*)data;
else
{
XFree(data);
return 0;
}
flags = xi->flags;
XFree(xi);
return flags;
}
/** Get the number of desktop (tag)
*/
void

View File

@ -74,6 +74,7 @@ init(void)
init_status();
ewmh_update_current_tag_prop();
grabkeys();
systray_init();
return;
}

View File

@ -125,6 +125,11 @@ enum
net_wm_state_fullscreen,
net_wm_state_sticky,
net_wm_state_demands_attention,
net_wm_system_tray_opcode,
net_system_tray_message_data,
net_system_tray_s,
xembed,
xembedinfo,
utf8_string,
/* WMFS HINTS */
wmfs_running,
@ -252,6 +257,15 @@ typedef struct
void (*func)(int screen);
} Layout;
/* Systray Structure */
typedef struct Systray Systray;
struct Systray
{
Window win;
XRectangle geo;
Systray *next, *prev;
};
/* Tag Structure */
typedef struct
{
@ -500,4 +514,9 @@ typedef struct
char *uicb;
} vicmd_to_uicb;
typedef struct
{
int flags;
} xembed_info;
#endif /* STRUCTS_H */

View File

@ -32,3 +32,220 @@
#include "wmfs.h"
#define TRAY_DWIDTH 18
Bool
systray_acquire(void)
{
char systray_atom[32];
snprintf(systray_atom, sizeof(systray_atom), "_NET_SYSTEM_TRAY_S%u", SCREEN);
trayatom = XInternAtom(dpy, systray_atom, False);
XSetSelectionOwner(dpy, ATOM(systray_atom), traywin, CurrentTime);
if(XGetSelectionOwner(dpy, trayatom) != traywin)
return False;
ewmh_send_message(ROOT, ROOT, "MANAGER", CurrentTime, trayatom, traywin, 0, 0);
return True;
}
void
systray_init(void)
{
XSetWindowAttributes wattr;
/* Init traywin window */
wattr.event_mask = ButtonPressMask|ExposureMask;
wattr.override_redirect = True;
wattr.background_pixel = conf.colors.bar;
traywin = XCreateSimpleWindow(dpy, infobar[0].bar->win, 0, 0, 1, 1, 0, 0, conf.colors.bar);
XChangeWindowAttributes(dpy, traywin, CWEventMask | CWOverrideRedirect | CWBackPixel, &wattr);
XSelectInput(dpy, traywin, KeyPressMask | ButtonPressMask);
XMapRaised(dpy, traywin);
/* Select tray */
if(!systray_acquire())
warnx("Can't initialize system tray: owned by another process");
return;
}
void
systray_kill(void)
{
XSetSelectionOwner(dpy, trayatom, None, CurrentTime);
XUnmapWindow(dpy, traywin);
return;
}
void
systray_add(Window win)
{
Systray *s = emalloc(1, sizeof(Systray));
s->win = win;
s->geo.height = infobar[0].bar->geo.height;
s->geo.width = TRAY_DWIDTH;
setwinstate(s->win, WithdrawnState);
XSelectInput(dpy, s->win, StructureNotifyMask | PropertyChangeMask| EnterWindowMask | FocusChangeMask);
XReparentWindow(dpy, s->win, traywin, 0, 0);
ewmh_send_message(s->win, s->win, "_XEMBED", CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, traywin, 0);
/* Attach */
if(trayicons)
trayicons->prev = s;
s->next = trayicons;
trayicons = s;
return;
}
void
systray_del(Window win)
{
Systray *t, **ss;
if(!(t = systray_find(win)))
return;
for(ss = &trayicons; *ss && *ss != t; ss = &(*ss)->next);
*ss = t->next;
return;
}
void
systray_configure(Systray *s)
{
long d = 0;
XSizeHints *sh = NULL;
if(!(sh = XAllocSizeHints()))
return;
XGetWMNormalHints(dpy, s->win, sh, &d);
/* TODO: Improve this.. */
if(d > 0)
if(sh->flags & (USSize|PSize))
s->geo.width = sh->width;
XFree(sh);
return;
}
void
systray_state(Systray *s)
{
long flags;
int code = 0;
if(!(flags = ewmh_get_xembed_state(s->win)))
return;
if(flags & XEMBED_MAPPED)
{
code = XEMBED_WINDOW_ACTIVATE;
XMapRaised(dpy, s->win);
setwinstate(s->win, NormalState);
}
else
{
code = XEMBED_WINDOW_DEACTIVATE;
XUnmapWindow(dpy, s->win);
setwinstate(s->win, WithdrawnState);
}
ewmh_send_message(s->win, s->win, "_XEMBED", CurrentTime, code, 0, 0, 0);
return;
}
void
systray_freeicons(void)
{
Systray *i, *next;
for(i = trayicons; i; i = next)
{
next = i->next;
XReparentWindow(dpy, i->win, ROOT, 0, 0);
IFREE(i);
}
XSync(dpy, 0);
return;
}
Systray*
systray_find(Window win)
{
Systray *i;
for(i = trayicons; i; i = i->next)
if(i->win == win)
return i;
return NULL;
}
int
systray_get_width(void)
{
int w = 0;
Systray *i;
for(i = trayicons; i; i = i->next)
w += i->geo.width + 2;
return w;
}
void
systray_update(void)
{
Systray *i;
XWindowAttributes xa;
int x = 0;
if(!trayicons)
{
XMoveResizeWindow(dpy, traywin, infobar[0].bar->geo.width - 1, 0, 1, 1);
return;
}
for(i = trayicons; i; i = i->next)
{
memset(&xa, 0, sizeof(xa));
XGetWindowAttributes(dpy, i->win, &xa);
XMapWindow(dpy, i->win);
if(xa.width < (i->geo.width = TRAY_DWIDTH))
i->geo.width = xa.width;
if(xa.height < (i->geo.height = infobar[0].bar->geo.height))
i->geo.height = xa.height;
XMoveResizeWindow(dpy, i->win, (i->geo.x = x), 0, i->geo.width, i->geo.height);
x += i->geo.width + 2;
}
XMoveResizeWindow(dpy, traywin, infobar[0].bar->geo.width - x, 0, x, infobar[0].bar->geo.height);
return;
}

View File

@ -98,6 +98,9 @@ quit(void)
XFreeGC(dpy, gc_stipple);
infobar_destroy();
systray_freeicons();
systray_kill();
IFREE(sgeo);
IFREE(spgeo);
IFREE(infobar);
@ -207,6 +210,7 @@ scan(void)
Atom rt;
int s, rf, tag = -1, screen = -1, free = -1;
ulong ir, il;
long flags;
uchar *ret;
Client *c;
@ -218,6 +222,9 @@ scan(void)
&& !(wa.override_redirect || XGetTransientForHint(dpy, w[i], &usl))
&& wa.map_state == IsViewable)
{
if((flags = ewmh_get_xembed_state(w[i])))
systray_add(w[i]);
if(XGetWindowProperty(dpy, w[i], ATOM("_WMFS_TAG"), 0, 32,
False, XA_CARDINAL, &rt, &rf, &ir, &il, &ret) == Success && ret)
{

View File

@ -54,6 +54,8 @@
#include <sys/wait.h>
#include <sys/stat.h>
#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xft/Xft.h>
@ -211,6 +213,8 @@ void uicb_client_ignore_tag(uicb_t);
/* ewmh.c */
void ewmh_init_hints(void);
void ewmh_send_message(Window d, Window w, char *atom, long d0, long d1, long d2, long d3, long d4);
long ewmh_get_xembed_state(Window win);
void ewmh_get_number_of_desktop(void);
void ewmh_update_current_tag_prop(void);
void ewmh_get_client_list(void);
@ -240,7 +244,10 @@ void focusin(XFocusChangeEvent *ev);
void grabkeys(void);
void keypress(XKeyPressedEvent *ev);
void mappingnotify(XMappingEvent *ev);
void mapnotify(XMapEvent *ev);
void maprequest(XMapRequestEvent *ev);
void reparentnotify(XReparentEvent *ev);
void selectionclearevent(XSelectionClearEvent *ev);
void propertynotify(XPropertyEvent *ev);
void unmapnotify(XUnmapEvent *ev);
void send_client_event(long data[5], char *atom_name);
@ -341,7 +348,17 @@ void statustext_normal(int sc, char *str);
void statustext_handle(int sc, char *str);
/* systray.c */
Bool systray_acquire(void);
void systray_init(void);
void systray_kill(void);
void systray_add(Window win);
void systray_del(Window win);
void systray_configure(Systray *s);
void systray_state(Systray *s);
void systray_freeicons(void);
Systray* systray_find(Window win);
int systray_get_width(void);
void systray_update(void);
/* layout.c */
void arrange(int screen, Bool update_layout);
@ -435,6 +452,7 @@ XftFont *font;
/* Atoms list */
Atom *net_atom;
Atom trayatom;
/* InfoBar/Tags */
InfoBar *infobar;
@ -458,6 +476,9 @@ Client *sel;
func_name_list_t *func_list;
extern const func_name_list_t layout_list[];
uint numlockmask;
Systray *trayicons;
Window traywin;
int tray_width;
#endif /* WMFS_H */