mirror of
https://github.com/0intro/wmii
synced 2024-11-22 13:52:17 +03:00
424 lines
10 KiB
C
424 lines
10 KiB
C
/* Copyright ©2008-2010 Kris Maglione <maglione.k at Gmail>
|
|
* See LICENSE file for license details.
|
|
*/
|
|
#include "dat.h"
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include "fns.h"
|
|
|
|
static Handlers handlers;
|
|
static Handlers root_handlers;
|
|
|
|
void
|
|
restrut(Window *w, int orientation) {
|
|
enum { Left, Right, Top, Bottom };
|
|
Rectangle strut[4];
|
|
Rectangle r;
|
|
|
|
r = w->r;
|
|
memset(strut, 0, sizeof strut);
|
|
if(Dx(r) < Dx(scr.rect)/2 && orientation != OHorizontal) {
|
|
if(r.min.x <= scr.rect.min.x) {
|
|
strut[Left] = r;
|
|
strut[Left].min.x = 0;
|
|
strut[Left].max.x -= scr.rect.min.x;
|
|
}
|
|
if(r.max.x >= scr.rect.max.x) {
|
|
strut[Right] = r;
|
|
strut[Right].min.x -= scr.rect.max.x;
|
|
strut[Right].max.x = 0;
|
|
}
|
|
}
|
|
if(Dy(r) < Dy(scr.rect)/2 && orientation != OVertical) {
|
|
if(r.min.y <= scr.rect.min.y) {
|
|
strut[Top] = r;
|
|
strut[Top].min.y = 0;
|
|
strut[Top].max.y -= scr.rect.min.y;
|
|
}
|
|
if(r.max.y >= scr.rect.max.y) {
|
|
strut[Bottom] = r;
|
|
strut[Bottom].min.y -= scr.rect.max.y;
|
|
strut[Bottom].max.y = 0;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
#define pstrut(name) \
|
|
if(!eqrect(strut[name], ZR)) \
|
|
fprint(2, "strut["#name"] = %R\n", strut[name])
|
|
pstrut(Left);
|
|
pstrut(Right);
|
|
pstrut(Top);
|
|
pstrut(Bottom);
|
|
#endif
|
|
|
|
ewmh_setstrut(w, strut);
|
|
}
|
|
|
|
void
|
|
tray_init(void) {
|
|
WinAttr wa;
|
|
XWMHints hints = { 0, };
|
|
|
|
wa.background_pixmap = None;
|
|
wa.bit_gravity = NorthEastGravity;
|
|
wa.border_pixel = 0;
|
|
wa.event_mask = ExposureMask
|
|
| ButtonPressMask
|
|
| ButtonReleaseMask
|
|
| StructureNotifyMask
|
|
| SubstructureNotifyMask
|
|
/* Disallow clients reconfiguring themselves. */
|
|
| SubstructureRedirectMask;
|
|
tray.win = createwindow(&scr.root, Rect(0, 0, 1, 1), scr.depth, InputOutput,
|
|
&wa, CWBackPixmap
|
|
| CWBitGravity
|
|
| CWEventMask);
|
|
|
|
sethandler(tray.win, &handlers);
|
|
pushhandler(&scr.root, &root_handlers, nil);
|
|
selectinput(&scr.root, scr.root.eventmask | PropertyChangeMask);
|
|
|
|
|
|
changeprop_string(tray.win, "_WMII_TAGS", tray.tags);
|
|
|
|
changeprop_ulong(tray.win, "XdndAware", "ATOM", (ulong[1]){ 5 }, 1);
|
|
|
|
changeprop_ulong(tray.selection->owner, Net("SYSTEM_TRAY_VISUAL"), "VISUALID",
|
|
&scr.visual->visualid, 1);
|
|
changeprop_long(tray.win, Net("WM_WINDOW_TYPE"), "ATOM",
|
|
(long[1]){ TYPE("DOCK") }, 1);
|
|
|
|
changeprop_string(tray.win, Net("WM_NAME"), "witray");
|
|
changeprop_textlist(tray.win, "WM_NAME", "STRING",
|
|
(char*[2]){ "witray", nil });
|
|
changeprop_textlist(tray.win, "WM_CLASS", "STRING",
|
|
(char*[3]){ "witray", "witray", nil });
|
|
changeprop_textlist(tray.win, "WM_COMMAND", "STRING", program_args);
|
|
|
|
hints.flags = InputHint;
|
|
hints.input = false;
|
|
XSetWMHints(display, tray.win->xid, &hints);
|
|
tray_resize(tray.win->r);
|
|
}
|
|
|
|
static void
|
|
tray_unmap(void) {
|
|
unmapwin(tray.win);
|
|
sendevent(&scr.root, false, SubstructureNotifyMask,
|
|
&(XUnmapEvent){
|
|
.type = UnmapNotify,
|
|
.event = scr.root.xid,
|
|
.window = tray.win->xid
|
|
});
|
|
}
|
|
|
|
static void
|
|
tray_draw(Rectangle r) {
|
|
int borderwidth;
|
|
|
|
if(!tray.pixmap)
|
|
return;
|
|
|
|
borderwidth = 1;
|
|
|
|
r = rectsetorigin(r, ZP);
|
|
border(tray.pixmap, r, borderwidth, &tray.selcolors.border);
|
|
r = insetrect(r, borderwidth);
|
|
fill(tray.pixmap, r, &tray.selcolors.bg);
|
|
XClearWindow(display, tray.win->xid);
|
|
}
|
|
|
|
void
|
|
tray_resize(Rectangle r) {
|
|
WinHints hints;
|
|
Image *oldimage;
|
|
WinAttr wa;
|
|
|
|
hints = ZWinHints;
|
|
hints.position = true;
|
|
hints.min = hints.max = Pt(Dx(r), Dy(r));
|
|
hints.grav = Pt(tray.edge & East ? 0 :
|
|
tray.edge & West ? 2 : 1,
|
|
tray.edge & North ? 0 :
|
|
tray.edge & South ? 2 : 1);
|
|
/* Not necessary, since we specify fixed size, but... */
|
|
// hints.base = Pt(2, 2);
|
|
// hints.inc = Pt(tray.iconsize, tray.iconsize);
|
|
sethints(tray.win, &hints);
|
|
|
|
if(!eqrect(tray.win->r, r)) {
|
|
oldimage = tray.pixmap;
|
|
|
|
tray.pixmap = allocimage(Dx(r), Dy(r), tray.win->depth);
|
|
tray_draw(r);
|
|
wa.background_pixmap = tray.pixmap->xid;
|
|
setwinattr(tray.win, &wa, CWBackPixmap);
|
|
|
|
freeimage(oldimage);
|
|
}
|
|
|
|
tray.r = r;
|
|
tray.win->r = ZR; /* Force the configure event. */
|
|
reshapewin(tray.win, r);
|
|
restrut(tray.win, tray.orientation);
|
|
}
|
|
|
|
void
|
|
tray_update(void) {
|
|
Rectangle r;
|
|
Point p, offset, padding;
|
|
Client *c;
|
|
|
|
r = Rect(0, 0, tray.iconsize, tray.iconsize);
|
|
padding = Pt(tray.padding, tray.padding);
|
|
offset = padding;
|
|
Dprint("tray_update()\n");
|
|
for(c=tray.clients; c; c=c->next) {
|
|
if(c->w.mapped) {
|
|
reshapewin(&c->w, rectaddpt(r, offset));
|
|
/* This seems, sadly, to be necessary. */
|
|
sendevent(&c->w, false, StructureNotifyMask, &(XEvent){
|
|
.xconfigure = {
|
|
.type = ConfigureNotify,
|
|
.event = c->w.xid,
|
|
.window = c->w.xid,
|
|
.above = None,
|
|
.x = c->w.r.min.x,
|
|
.y = c->w.r.min.y,
|
|
.width = Dx(c->w.r),
|
|
.height = Dy(c->w.r),
|
|
.border_width = 0,
|
|
}
|
|
});
|
|
|
|
movewin(c->indicator, addpt(offset, Pt(2, 2)));
|
|
if(tray.orientation == OHorizontal)
|
|
offset.x += tray.iconsize + tray.padding;
|
|
else
|
|
offset.y += tray.iconsize + tray.padding;
|
|
}
|
|
if(c->w.mapped && client_hasmessage(c))
|
|
mapwin(c->indicator);
|
|
else
|
|
unmapwin(c->indicator);
|
|
}
|
|
|
|
if(eqpt(offset, padding))
|
|
tray_unmap();
|
|
else {
|
|
if(tray.orientation == OHorizontal)
|
|
offset.y += tray.iconsize + tray.padding;
|
|
else
|
|
offset.x += tray.iconsize + tray.padding;
|
|
|
|
r = Rpt(ZP, offset);
|
|
p = subpt(scr.rect.max, r.max);
|
|
if(tray.edge & East)
|
|
p.x = 0;
|
|
if(tray.edge & North)
|
|
p.y = 0;
|
|
tray_resize(rectaddpt(r, p));
|
|
mapwin(tray.win);
|
|
}
|
|
|
|
tray_draw(tray.win->r);
|
|
}
|
|
|
|
static bool
|
|
config_event(Window *w, void *aux, XConfigureEvent *ev) {
|
|
|
|
USED(aux);
|
|
if(ev->send_event) {
|
|
/*
|
|
* Per ICCCM §4.2.3, the window manager sends
|
|
* synthetic configure events in the root coordinate
|
|
* space when it changes the window configuration.
|
|
* This code assumes wmii's generous behavior of
|
|
* supplying all relevant members in every configure
|
|
* notify event.
|
|
*/
|
|
w->r = rectaddpt(rectsetorigin(Rect(0, 0, ev->width, ev->height),
|
|
Pt(ev->x, ev->y)),
|
|
Pt(ev->border_width, ev->border_width));
|
|
restrut(w, tray.orientation);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
expose_event(Window *w, void *aux, XExposeEvent *ev) {
|
|
|
|
USED(w, aux, ev);
|
|
tray_draw(tray.win->r);
|
|
return false;
|
|
}
|
|
|
|
typedef struct Dnd Dnd;
|
|
|
|
struct Dnd {
|
|
ulong source;
|
|
ulong dest;
|
|
long data[4];
|
|
Point p;
|
|
bool have_actions;
|
|
};
|
|
|
|
static Dnd dnd;
|
|
|
|
#define Point(l) Pt((ulong)(l) >> 16, (ulong)(l) & 0xffff)
|
|
#define Long(p) ((long)(((ulong)(p).x << 16) | (ulong)(p).y))
|
|
#define sendmessage(...) BLOCK( \
|
|
Dprint("(%W) %s 0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx\n", __VA_ARGS__); \
|
|
sendmessage(__VA_ARGS__); \
|
|
)
|
|
|
|
static void
|
|
dnd_updatestatus(ulong dest) {
|
|
if(dest == dnd.dest)
|
|
return;
|
|
|
|
if(dnd.dest && dnd.dest != ~0UL)
|
|
sendmessage(window(dnd.dest), "XdndLeave", tray.win->xid, 0, 0, 0, 0);
|
|
dnd.dest = dest;
|
|
if(dest)
|
|
sendmessage(window(dest), "XdndEnter", tray.win->xid,
|
|
dnd.data[0], dnd.data[1], dnd.data[2], dnd.data[3]);
|
|
else
|
|
sendmessage(window(dnd.source), "XdndStatus", tray.win->xid, (1<<1),
|
|
Long(tray.win->r.min), (Dx(tray.win->r)<<16) | Dy(tray.win->r), 0UL);
|
|
}
|
|
|
|
static void
|
|
copyprop_long(Window *src, Window *dst, char *atom, char *type, long max) {
|
|
long *data;
|
|
long n;
|
|
|
|
/* Round trip. Really need to switch to XCB. */
|
|
if((n = getprop_long(src, atom, type, 0, &data, max)))
|
|
changeprop_long(dst, atom, type, data, n);
|
|
free(data);
|
|
}
|
|
|
|
static void
|
|
copyprop_char(Window *src, Window *dst, char *atom, char *type, long max) {
|
|
uchar *data;
|
|
ulong actual, n;
|
|
int format;
|
|
|
|
n = getprop(src, atom, type, &actual, &format, 0, &data, max);
|
|
if(n > 0 && format == 8 && xatom(type) == actual)
|
|
changeprop_char(dst, atom, type, (char*)data, n);
|
|
free(data);
|
|
}
|
|
|
|
static bool
|
|
message_event(Window *w, void *aux, XClientMessageEvent *e) {
|
|
Client *c;
|
|
long *l;
|
|
Rectangle r;
|
|
Point p;
|
|
ulong msg;
|
|
|
|
msg = e->message_type;
|
|
l = e->data.l;
|
|
Dprint("ClientMessage: %A\n", msg);
|
|
if(e->format == 32)
|
|
Dprint("\t0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx, 0x%ulx\n",
|
|
l[0], l[1], l[2], l[3], l[4]);
|
|
|
|
if(msg == xatom("XdndEnter")) {
|
|
if(e->format != 32)
|
|
return false;
|
|
dnd = (Dnd){0};
|
|
dnd.dest = ~0UL;
|
|
dnd.source = l[0];
|
|
bcopy(&l[1], dnd.data, sizeof dnd.data);
|
|
|
|
copyprop_long(window(dnd.source), tray.win, "XdndSelection", "ATOM", 128);
|
|
if(l[1] & 0x01)
|
|
copyprop_long(window(dnd.source), tray.win, "XdndTypeList", "ATOM", 128);
|
|
return false;
|
|
}else
|
|
if(msg == xatom("XdndLeave")) {
|
|
if(e->format != 32)
|
|
return false;
|
|
dnd.source = 0UL;
|
|
if(dnd.dest)
|
|
sendmessage(window(dnd.dest), "XdndLeave", tray.win->xid, l[1], 0, 0, 0);
|
|
return false;
|
|
}else
|
|
if(msg == xatom("XdndPosition")) {
|
|
if(e->format != 32)
|
|
return false;
|
|
|
|
if(!dnd.have_actions && l[4] == xatom("XdndActionAsk")) {
|
|
dnd.have_actions = true;
|
|
copyprop_long(window(dnd.source), tray.win, "XdndActionList", "ATOM", 16);
|
|
copyprop_char(window(dnd.source), tray.win, "XdndActionDescription", "ATOM", 16 * 32);
|
|
}
|
|
|
|
dnd.p = subpt(Point(l[2]), tray.win->r.min);
|
|
for(c=tray.clients; c; c=c->next)
|
|
if(rect_haspoint_p(c->w.r, dnd.p)) {
|
|
dnd_updatestatus(c->w.xid);
|
|
sendmessage(&c->w, "XdndPosition", tray.win->xid, l[1], l[2], l[3], l[4]);
|
|
return false;
|
|
}
|
|
dnd_updatestatus(0UL);
|
|
return false;
|
|
}else
|
|
if(msg == xatom("XdndStatus")) {
|
|
if(e->format != 32)
|
|
return false;
|
|
if(l[0] != dnd.dest)
|
|
return false;
|
|
|
|
for(c=tray.clients; c; c=c->next)
|
|
if(c->w.xid == dnd.dest) {
|
|
p = Point(l[2]);
|
|
r = Rpt(p, addpt(p, Point(l[3])));
|
|
r = rect_intersection(r, rectaddpt(c->w.r, tray.win->r.min));
|
|
|
|
sendmessage(window(dnd.source), "XdndStatus", tray.win->xid, l[1],
|
|
Long(r.min), (Dx(r)<<16) | Dy(r), l[4]);
|
|
break;
|
|
}
|
|
return false;
|
|
}else
|
|
if(msg == xatom("XdndDrop") || msg == xatom("XdndFinished")) {
|
|
if(e->format != 32)
|
|
return false;
|
|
|
|
for(c=tray.clients; c; c=c->next)
|
|
if(c->w.xid == dnd.dest) {
|
|
sendmessage(&c->w, atomname(msg),
|
|
tray.win->xid, l[1], l[2], 0L, 0L);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static Handlers handlers = {
|
|
.message = message_event,
|
|
.config = config_event,
|
|
.expose = expose_event,
|
|
};
|
|
|
|
static bool
|
|
property_event(Window *w, void *aux, XPropertyEvent *ev) {
|
|
if(ev->atom == NET("CURRENT_DESKTOP"))
|
|
tray_resize(tray.r);
|
|
Debug if(ev->atom == NET("CURRENT_DESKTOP"))
|
|
print("property_event(_NET_CURRENT_DESKTOP)\n");
|
|
return false;
|
|
}
|
|
|
|
static Handlers root_handlers = {
|
|
.property = property_event,
|
|
};
|
|
|