/* treewm - an X11 window manager.
* Copyright (c) 2001-2003 Thomas Jäger <TheHunter2000 at web dot de>
* This code is released under the terms of the GNU GPL.
* See the included file LICENSE for details.
*
* changed<05.02.2003> by Rudolf Polzer: Refreshing root window on startup
* so background pixmap works again.
* Updating toolbar when reparenting multiple windows at once into a new
* desktop.
*/
#include "desktop.h"
#include "clienttree.h"
#include "icon.h"
#include "sceme.h"
#include "action.h"
#include "manager.h"
#include "clientinfo.h"
#include "tile.h"
#include <signal.h>
#include <string.h>
unsigned long Desktop::GetRegionMask(int x, int y, Client * &t) {
x -= x_root;
y += THeight - y_root;
if (0 <= y && y <= THeight && 0 <= x && x <= width) {
if (!t)
for (t=firstchild;t;t=t->next)
if (t->mapped && (t->flags & CF_HASTBENTRY) && t->TBEx <= x && x <= t->TBEy)
break;
if (!t && TBx <= x && x <= TBy)
t=this;
if (t) {
if (!(flags & ((t==this) ? CF_TITLEBARBUTTONS : DF_TASKBARBUTTONS)))
return T_BAR;
return ((x >= (t == this ? TBy : t->TBEy) - ((t->flags & CF_FIXEDSIZE) ? 1 : 2)*BUTTONWIDTH)
? (x >= (t == this ? TBy : t->TBEy) - BUTTONWIDTH) ? T_F2 : T_F1 :
T_BAR);
}
}
return 0;
}
void Desktop::AutoClose() {
if (!autoclose || !parent)
return;
for (Client *i = firstchild;i;i = i->next)
if (i->mapped || i->icon)
return;
SendWMDelete(DEL_NORMAL);
}
Desktop::Desktop(Desktop *p) : Client(p) {
childmask = 0;
firstchild = NULL;
firsticon = NULL;
PosX = 0;
PosY = 0;
for (int i=0; i!=3; ++i)
LastRaised[i] = 0;
focus = NULL;
wmname = new char[8];
strcpy(wmname,"Desktop");
if (parent)
name = 0; else
name = strdup("Root Desktop");
VirtualX = parent ? 1 : 3;
VirtualY = parent ? 1 : 2;
bgpm = 0;
}
void Desktop::SetClientInfo(ClientInfo *ci) {
if (!ci)
return;
if (ci && (ci->flags & CI_VIRTUALX))
VirtualX = ci->VirtualX;
if (ci && (ci->flags & CI_VIRTUALY))
VirtualY = ci->VirtualY;
Client::SetClientInfo(ci);
if (ci->name) {
if (name)
free(name);
name = strdup(ci->name);
}
for (ActionList *al = ci->actions;al;al=al->next)
AddIcon(new Icon(this,0,al->a));
#define NOPIXMAP (RPixmap *)(-1)
if (ci->bgpm && bgpm != NOPIXMAP)
bgpm = ci->bgpm;
if (!parent && (ci->flags & CI_NOBACKGROUND))
bgpm = NOPIXMAP;
}
bool Desktop::Init() {
if (!Sc)
Sc = (Sceme *)rman->GetInfo(SE_SCEME,"");
focus = 0;
if (!bgpm)
bgpm = Sc->bgpm;
XSetWindowAttributes pattr;
unsigned long Mask = 0;
if (bgpm) {
if (bgpm != NOPIXMAP) {
pattr.background_pixmap = bgpm->GetPixmap();
Mask |= CWBackPixmap;
}
} else {
pattr.background_pixel = Sc->colors[C_BG].pixel;
Mask |= CWBackPixel;
}
if (parent) {
Mask |= (CWOverrideRedirect|CWEventMask);
pattr.override_redirect = true;
pattr.event_mask = ChildMask|ButtonMask|ExposureMask|EnterWindowMask|LeaveWindowMask|KeyPressMask|KeyReleaseMask;
int X,Y;
GetMousePosition(parent->window,X,Y);
window = XCreateWindow(dpy, root, X,Y,300,300,0,
DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
Mask, &pattr);
XMapWindow(dpy,window); // remove this!
} else {
window = 0;
XChangeWindowAttributes(dpy,root,Mask,&pattr);
XClearWindow(dpy,root);
}
if (!Client::Init())
return false;
if (window) {
XMapWindow(dpy,window);
XMapWindow(dpy,frame);
}
man->WantFocus = this;
if (!parent)
window = root;
pattr.override_redirect = true;
for (int i = 0; i!=3; ++i) {
StackRef[i] = XCreateWindow(dpy, window,
0,0,1,1, 0,
DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect, &pattr);
XRaiseWindow(dpy,StackRef[i]);
}
// RP: Fix for "no-refresh" bug
XClearWindow(dpy,window);
XRaiseWindow(dpy,title);
autoclose = false;
for (Icon *i = firsticon; i; i=i->next)
i->Init();
flags |= DF_ICONSRAISED;
if (!parent)
Raise(0);
for (Client *c = firstchild;c;c=c->next)
c->Init();
for (int i=0;i!=4;++i)
if (ct->RootBorder[i])
XRaiseWindow(dpy,ct->RootBorder[i]);
return true;
}
Client *Desktop::AddChild(Client *c) {
// Check if there's a default ClientInfo
if (firstchild) {
Client *cc;
for (cc=firstchild;cc->next;cc=cc->next);
cc->next = c;
} else {
firstchild = c;
}
c->next = NULL;
if (!c->Init()) {
RemoveChild(c,R_WITHDRAW);
return 0;
}
if (c->flags & CF_HASTBENTRY)
UpdateTB();
if (ct->Focus == 0 || ct->Focus == this || ct->Focus == focus)
man->WantFocus = c;
Tile();
AutoResize();
return c;
}
void Desktop::UpdateFlags(int act,unsigned long flag) {
if (!act)
return;
switch (act) {
case FLAG_CREMOVE:
childmask &= ~flag;
break;
case FLAG_CSET:
childflags |= flag;
childmask |= flag;
break;
case FLAG_CUNSET:
childflags &= ~flag;
childmask |= flag;
break;
case FLAG_CTOGGLE:
childflags &= childmask;
childflags |= ~childmask & flags;
childflags ^= flag;
childmask |= flag;
break;
default:
Client::UpdateFlags(act,flag);
}
for (Client *c = firstchild; c; c=c->next)
c->UpdateFlags(FLAG_UPDATE,0);
if (flag & flags & DF_AUTORESIZE)
AutoResize();
}
Desktop *Desktop::AddDesktop(Desktop* c,Client *copyopts) {
if (copyopts) {
c->flags = copyopts->flags;
c->mask = copyopts->mask;
c->parentmask = copyopts->parentmask;
}
if (firstchild) {
Client *cc;
for (cc=firstchild;cc->next;cc=cc->next);
cc->next = c;
} else {
firstchild = c;
}
c->next = NULL;
if (!c->Init()) {
RemoveChild(c,R_WITHDRAW);
return 0;
}
if (c->flags & CF_HASTBENTRY)
UpdateTB();
if (!focus)
man->WantFocus = c;
return c;
}
Desktop *Desktop::DesktopAbove() {
return this;
}
Client* Desktop::FindPointerClient() {
Window w, dummyw;
int d1,d2,root_x,root_y;
unsigned int d3;
Client *tb = 0;
if (XQueryPointer(dpy,window,&dummyw,&w,&root_x,&root_y,&d1,&d2,&d3)) {
for (Client *c = firstchild; c; c=c->next) {
if (w==c->frame)
return c->FindPointerClient();
}
}
GetRegionMask(root_x-x_root,root_y-y_root+THeight,tb);
return tb ? tb : this;
}
bool Desktop::GetWindowList(MenuItem *m,int &n,bool mouse,int mx,int my,int ind,char *key) {
if (!Client::GetWindowList(m,n,mouse,mx,my,ind,key))
return false;
if (!mapped)
return true;
++ind;
for (Client *i=firstchild;i;i=i->next)
i->GetWindowList(m,n,mouse,mx,my,ind,key);
return true;
}
void Desktop::RemoveClientReferences(Client *c) {
Client::RemoveClientReferences(c);
for (int i = 0; i!=3; ++i)
if (c == LastRaised[i])
LastRaised[i] = 0;
for (Client *cc=firstchild;cc;cc=cc->next) {
cc->RemoveClientReferences(c);
}
for (Icon *i = firsticon; i; i=i->next)
if (i->client == c) {
RemoveIcon(i);
i = firsticon; // I first forgot this
}
}
void Desktop::RemoveChild(Client *c, int mode) {
if (ct->MaxWindow == c)
c->Maximize(MAX_FULLSCREEN);
c->GiveFocus();
c->Remove(mode);
if (c==firstchild)
firstchild = c->next; else
for (Client *cc=firstchild;cc->next;cc=cc->next)
if (cc->next==c) {
cc->next=c->next;
break;
}
if (c->flags & CF_HASTBENTRY)
UpdateTB();
delete c;
Tile();
AutoResize();
AutoClose();
}
void Desktop::GiveAway(Client *c, bool autoclose) {
c->flags &= ~CF_ICONPOSKNOWN; // New Desktop may be smaller
c->GiveFocus();
if (c==firstchild)
firstchild = c->next; else
for (Client *cc=firstchild;cc->next;cc=cc->next)
if (cc->next==c) {
cc->next=c->next;
break;
}
if (!autoclose)
return;
if (c->mapped && (c->flags & CF_HASTBENTRY))
UpdateTB();
Tile();
AutoResize();
AutoClose();
}
void Desktop::SendEvent(XAnyEvent &e) {
for (Client *i= firstchild;i;i=i->next)
i->SendEvent(e);
}
void Desktop::SendWMDelete(int mode) {
// Show a dialog box here...
if (!parent) {
if (mode != DEL_RELEASE)
man->alive = false;
return;
}
while (firsticon) {
if (firsticon->client)
firsticon->client->Map(); else
RemoveIcon(firsticon);
}
if (firstchild) {
Client *cc; // if c is deleted we can't access c->next any more
for (Client *c = firstchild; c; c=cc) {
cc = c->next;
if (c) {
if ((c->mapped || c->icon) && mode != DEL_RELEASE) {
c->SendWMDelete(mode);
} else {
GiveAway(c,false);
parent->Take(c,c->x + (x_root - parent->x_root),c->y + (y_root - parent->y_root));
}
}
}
}
autoclose = true;
if (!firstchild)
man->AddDeleteClient(this);
}
void Desktop::Remove(int mode) {
if (mode != R_WITHDRAW) {
Goto(-PosX,-PosY,GOTO_RELATIVE);
for (Client *c=firstchild; c; c=c->next)
c->Remove(mode);
} else {
for (Client *c=firstchild; c; c=firstchild) {
// That bug was hard to find, the destructor accesses this desktop in
// RemoveClientReferences
firstchild = c->next;
c->Remove(mode);
delete c;
}
}
for (Icon *i = firsticon;i;i=i->next)
i->Remove();
Client::Remove(mode);
if (wmname)
delete [] wmname;
wmname = 0;
if (bgpm && bgpm != NOPIXMAP)
bgpm->FreePixmap();
x += Sc->BW;
y += Sc->BW + THeight;
}
void Desktop::ChangeSize() {
Client::ChangeSize();
if (!parent)
return;
for (Client *c=firstchild;c;c=c->next)
if (c->MaxW || c->MaxH)
c->Maximize(MAX_REMAX);
Tile();
}
void Desktop::AutoResize() {
if (!parent || !(flags & DF_AUTORESIZE))
return;
int X = width;
int Y = height;
int W = 0;
int H = 0;
for (Client *c=firstchild; c; c=c->next) {
if (!c->mapped)
continue;
if (c->x + c->width > W)
W = c->x +c->width;
if (c->y + c->height > H)
H = c->y +c->height;
if (c->x+c->Sc->BW < X)
X = c->x+c->Sc->BW;
if (c->y+c->Sc->BW-THeight < Y)
Y = c->y+c->Sc->BW-THeight;
}
if (!W || !H)
return;
width = W + Sc->BW - X;
height = H + Sc->BW - Y;
x += X;
y += Y;
ChangeSize();
if (X || Y)
for (Client *c=firstchild; c; c=c->next) {
c->x -= X;
c->y -= Y;
c->ChangePos(false);
}
}
// Translates coordinates when children want to move their windows
void Desktop::Translate(int *X,int *Y) {
if (parent)
return;
if (X)
*X += Sc->BW;
if (Y)
*Y += THeight + Sc->BW;
}
void Desktop::ReDraw() {
if (!(flags & CF_HASTITLE) || !visible)
return;
XDrawLine(dpy, title, Sc->border_gc,
0, THeight - 1,
width, THeight - 1);
for (Client *t=firstchild;t;t=t->next)
if (t->mapped && (t->flags & CF_HASTBENTRY))
ReDrawTBEntry(t);
ReDrawTBEntry(this);
}
void Desktop::UpdateTB() {
if (!(flags & CF_HASTITLE))
return;
int i=0;
int TBNum=1;
for (Client *t=firstchild;t;t=t->next)
if (t->mapped && (t->flags & CF_HASTBENTRY))
++TBNum;
for (Client *t=firstchild;t;t=t->next)
if (t->mapped && (t->flags & CF_HASTBENTRY)) {
t->TBEx=i*width/TBNum;
++i;
t->TBEy=i*width/TBNum - 1;
}
TBx=(TBNum-1)*width/TBNum;
TBy=width;
Clear();
}
void Desktop::Raise(int mode) {
if (!(mode & R_INDIRECT))
if (parent) {
ShowIcons(parent->LastRaised[GetStacking()] == this);
} else {
ShowIcons(true);
}
Client::Raise(mode);
}
void Desktop::GrabButtons(bool first) {
if (first) {
for (Client *c=firstchild; c; c=c->next)
c->GrabButtons(true);
return;
}
if (flags & CF_RAISEONCLICK)
for (int i = 0; i!=3; ++i)
if (LastRaised[i])
LastRaised[i]->GrabButtons(first);
}
void Desktop::ShowIcons(bool raise) {
if (raise && !(flags & DF_ICONSRAISED)) {
for (Icon *i = firsticon;i; i = i->next) {
XRaiseWindow(dpy,i->window);
if (i->iconwin)
XRaiseWindow(dpy,i->iconwin);
}
if (firsticon && (!UEH->CurrentIcon || UEH->CurrentIcon->parent != this)) {
UEH->SetCurrent(0,firsticon);
}
flags |= DF_ICONSRAISED;
} else
if (flags & DF_ICONSRAISED) {
for (Icon *i = firsticon;i; i = i->next) {
XLowerWindow(dpy,i->window);
if (i->iconwin)
XLowerWindow(dpy,i->iconwin);
}
flags &= ~DF_ICONSRAISED;
}
}
Icon *Desktop::AddIcon(Icon *i) {
Icon *ii = firsticon;
if (ii) {
for (;ii->next;ii = ii->next);
ii->next = i;
} else {
firsticon = i;
}
i->next = 0;
return i;
}
void Desktop::RemoveIcon(Icon *i) {
if (i==firsticon)
firsticon = i->next; else
for (Icon *ii=firsticon;ii->next;ii=ii->next)
if (ii->next==i) {
ii->next=i->next;
break;
}
if (i->client)
i->client->icon = 0;
i->Remove();
delete i;
}
void Desktop::NextIcon(Icon *i, int dir) {
Icon *best = 0;
switch (dir) {
case NI_UP:
for (Icon *ii = firsticon; ii; ii = ii->next) {
if (ii == i)
continue;
if (ii->y >= i->y)
continue;
if (!best) {
best = ii;
continue;
}
if (ii->y > best->y)
best = ii;
if (ii->y == best->y && abs(ii->x-i->x) < abs(best->x-i->x))
best = ii;
}
break;
case NI_DOWN:
for (Icon *ii = firsticon; ii; ii = ii->next) {
if (ii == i)
continue;
if (ii->y <= i->y)
continue;
if (!best) {
best = ii;
continue;
}
if (ii->y < best->y)
best = ii;
if (ii->y == best->y && abs(ii->x-i->x) < abs(best->x-i->x))
best = ii;
}
break;
case NI_LEFT:
for (Icon *ii = firsticon; ii; ii = ii->next) {
if (ii == i)
continue;
if (ii->x >= i->x)
continue;
if (!best) {
best = ii;
continue;
}
if (ii->x > best->x)
best = ii;
if (ii->x == best->x && abs(ii->y-i->y) < abs(best->y-i->y))
best = ii;
}
break;
case NI_RIGHT:
for (Icon *ii = firsticon; ii; ii = ii->next) {
if (ii == i)
continue;
if (ii->x <= i->x)
continue;
if (!best) {
best = ii;
continue;
}
if (ii->x < best->x)
best = ii;
if (ii->x == best->x && abs(ii->y-i->y) < abs(best->y-i->y))
best = ii;
}
break;
};
if (best)
UEH->SetCurrent(0,best);
}
void Desktop::Take(Client *c, int x, int y) {
c->parent=this;
c->UpdateFlags(FLAG_UPDATE,0);
if (firstchild) {
Client *cc;
for (cc=firstchild;cc->next;cc=cc->next);
cc->next = c;
} else {
firstchild = c;
focus = c; // give first window on this Desktop the focus
focus->UpdateName(false); // we must be very careful with this call
}
c->next = NULL;
if (c->MaxW || c->MaxH) {
if (c->MaxW == c->width && c->MaxH == c->height) {
c->MaxW = 0;
c->MaxH = 0;
} else c->Maximize(MAX_UNMAX);
}
c->x = x;
c->y = y;
Translate(&x,&y);
XReparentWindow(dpy,c->frame,window,x,y - c->THeight);
SendConfig();
c->Raise(0);
if (c->mapped && (c->flags & CF_HASTBENTRY))
UpdateTB();
if (ct->Focus == 0 || ct->Focus == this || ct->Focus == focus)
man->WantFocus = c;
Tile();
AutoResize();
}
void Desktop::RequestDesktop(bool collect) {
Desktop *d = new Desktop(this);
AddDesktop(d,0);
if (!d)
return;
d->MoveResize(0,true,0,0,true);
d->MoveResize(S_BOTTOM | S_RIGHT,true,0,0);
if (!collect)
return;
Client *last = 0, *i = firstchild;
while (i) {
if ((d != i) &&
(i->x >= d->x) &&
(i->y >= d->y) &&
(d->x+d->width >= i->x) &&
(d->y+d->height+i->THeight >= i->y)
) {
GiveAway(i,false);
d->Take(i,i->x - (d->x_root - x_root), i->y - (d->y_root - y_root));
i = last ? last->next : firstchild;
} else {
last = i;
i = i->next;
}
}
UpdateTB();
if (d->mapped && (d->flags & CF_HASTBENTRY))
UpdateTB();
}
void Desktop::GetFocus() {
ct->CurrentDesktop = this;
if (parent) {
if (parent->focus)
parent->focus->Clear();;
parent->focus=this;
parent->Clear();
}
if (focus) {
// The child will ask me to ReDraw() myself
if (flags & DF_GRABKEYBOARD)
XSetInputFocus(dpy,man->input,RevertToPointerRoot,CurrentTime); else
focus->GetFocus();
} else {
Client::GetFocus();
Clear();
}
UEH->SetCurrent(this,0);
}
Client *Desktop::Focus() {
if (focus)
return focus->Focus();
return this;
}
/* returns true if the virtual position was changed */
bool Desktop::Goto(int x, int y, int mode) {
if (ct->MaxWindow)
return false;
int oldPosX = PosX;
int oldPosY = PosY;
int diff;
// width - Sc->BW ???
// !!!
width += Sc->BW;
height += Sc->BW;
// i hate this implemetation of the / - operator
if (mode & GOTO_RELATIVE) {
diff = x;
} else {
if (x >= width - 2*Sc->BW)
++x;
if (x < 0)
--x;
diff = ((x + width*VirtualX) / width - VirtualX) * width;
// Actually, diff = (x / width) * width
}
if (diff) {
PosX += diff;
if ((mode & GOTO_SNAP) && (flags & CF_SNAP)) {
diff = (PosX + snap) % width;
if (diff <= 2*snap)
PosX += snap - diff;
}
if (PosX < 0)
PosX = 0;
if (PosX > width*(VirtualX - 1))
PosX = width*(VirtualX - 1);
}
if (mode & GOTO_RELATIVE) {
diff = y;
} else {
if (y >= height - 2*Sc->BW)
++y;
if (y < 0)
--y;
diff = ((y + height*VirtualY) / height - VirtualY) * height;
}
if (diff) {
PosY += diff;
if (mode & GOTO_SNAP) {
diff = (PosY + snap) % height;
if (diff <= 2*snap)
PosY += snap - diff;
}
if (PosY < 0)
PosY = 0;
if (PosY > height*(VirtualY - 1))
PosY = height*(VirtualY - 1);
}
width -= Sc->BW;
height -= Sc->BW;
if (PosX == oldPosX && PosY == oldPosY)
return false;
for (Client *i= firstchild;i;i=i->next)
if (i->mapped && !(i->flags & CF_STICKY)) {
i->x -= PosX - oldPosX;
i->y -= PosY - oldPosY;
i->ChangePos(true);
}
if (mode & (GOTO_MOVEFULL | GOTO_MOVEHALF)) {
oldPosX -= PosX;
oldPosY -= PosY;
if (mode & GOTO_MOVEHALF) {
oldPosX /= 2;
oldPosY /= 2;
}
XWarpPointer(dpy,root,0,0,0,0,0,oldPosX,oldPosY);
ct->CurrentDesktop = this;
}
return true;
}
bool Desktop::Leave(int x,int y,bool half) {
return Goto(x - x_root,y - y_root,half ? GOTO_MOVEHALF : GOTO_MOVEFULL);
}
void Desktop::Tile() {
if (!(flags & DF_TILE))
return;
Client *c;
int n = 0;
for (c = firstchild; c; c=c->next)
if (c->mapped)
++n;
if (!n)
return;
if (n == 1) {
for (c = firstchild; c; c=c->next)
if (c->mapped)
break;
c->x = - Sc->BW;
c->y = THeight - Sc->BW;
c->ChangePos(false);
} else {
RectRec rects[n];
int idNo=0;
for (c = firstchild; c; c=c->next)
if (c->mapped) {
rects[idNo].assign(c->width,c->height+c->THeight,idNo);
++idNo;
}
bool tilingOK;
if (parent && (flags & DF_AUTORESIZE))
tilingOK = tileArea(parent->width - (x % parent->width),
parent->height - (y % parent->height),idNo,rects); else
tilingOK = tileArea(width,height,idNo,rects);
if(tilingOK){
/*this going be nasty because tileArea has sorted the rects list by
area so we need to search for the correct entry*/
idNo=0;
for (c = firstchild; c; c=c->next)
if (c->mapped) {
int k=0;
while(rects[k].idNo!=idNo){
++k;
}
c->x=rects[k].x - Sc->BW;
c->y=rects[k].y+c->THeight - Sc->BW;
c->ChangePos(false);
++idNo;
}
}
}
if (parent->flags & DF_AUTORESIZE)
parent->AutoResize();
}
void Desktop::SetWMState(){
for (Client *c=firstchild;c;c=c->next)
if (c->mapped) {
c->visible = visible;
c->SetWMState();
}
}
void Desktop::SendConfig() {
Client::SendConfig();
for (Client *c = firstchild; c; c=c->next) {
c->SendConfig();
}
}
Desktop::~Desktop() {
Icon *savei;
for (Icon *i = firsticon;i;i=savei) {
savei = i->next;
if (i->client)
i->client->icon = 0;
delete i;
}
for (Client *c=firstchild;c;firstchild = c) {
c = firstchild->next;
delete firstchild;
}
}
syntax highlighted by Code2HTML, v. 0.9.1