/* 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: corrected initialization of root
* window's dimensions in Client::Init.
* Added debugging event output in MoveMouse
*/
#include "client.h"
#include "clienttree.h"
#include "sceme.h"
#include "clientinfo.h"
#include "icon.h"
#include "MwmUtil.h"
#include "textdialog.h"
#include <X11/Xmd.h>
#include <string.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#ifdef VIDMODE
#include <X11/extensions/xf86vmode.h>
#endif
void GetMousePosition(Window w, int &x, int &y) {
Window mouse_root, mouse_win;
int win_x, win_y;
unsigned int mask;
XQueryPointer(dpy, w, &mouse_root, &mouse_win,
&x, &y, &win_x, &win_y, &mask);
}
void FetchName(Window window, char **name) {
XTextProperty text_prop;
char** cl;
int n;
if (XGetWMName(dpy, window, &text_prop) != 0) {
if (text_prop.encoding == XA_STRING) {
*name = (char *)text_prop.value;
} else {
XmbTextPropertyToTextList(dpy, &text_prop, &cl, &n);
if (cl) {
*name = strdup(cl[0]);
XFreeStringList(cl);
} else {
*name = NULL;
}
}
} else {
*name = NULL;
}
}
void Client::Clear() {
if (flags & CF_IGNOREEXPOSE)
return;
flags |= CF_IGNOREEXPOSE;
if (visible)
man->AddReDrawClient(this);
}
unsigned long Client::GetRegionMask(int x, int y, Client * &t) {
x -= x_root;
y += THeight - y_root;
if (0 <= y && y <= THeight && 0 <= x && x <= width) {
t = this;
if (!(flags & CF_TITLEBARBUTTONS))
return T_BAR;
return ((x >= TBy - ((flags & CF_FIXEDSIZE) ? 1 : 2)*BUTTONWIDTH) ? (x >= TBy - BUTTONWIDTH) ? T_F2 : T_F1 : T_BAR);
}
return 0;
}
Client::Client(Desktop *p, Window win) {
parent = p;
flags = DEFAULTFLAGS;
parentmask = 0;
mask = DEFAULTMASK;
window = win;
wmname = ct->notitle;
name = 0;
Sc = parent ? parent->Sc : 0;
iconpm = 0;
x = UNDEF;
y = UNDEF;
width = UNDEF;
height = UNDEF;
GetFlags();
if (window)
GetMWMOptions();
ignoreunmap = 0;
MaxX = 0;MaxW = 0;
MaxY = 0;MaxH = 0;
target = 0;
trans = 0;
transfor = 0;
icon = 0;
}
void Client::SetClientInfo(ClientInfo *ci) {
if (!ci)
return;
if (ci->Sc)
Sc = ci->Sc;
if (ci->iconpm)
iconpm = ci->iconpm; // We do not need to reserve extra memory
if ((ci->flags & CI_ICONX) && (ci->flags & CI_ICONY)) {
IconX = ci->IconX;
IconY = ci->IconY;
flags |= CF_ICONPOSKNOWN;
}
mask |= ci->clientmask;
flags &= ~ci->clientmask;
flags |= ci->clientflags;
if (parent) {
if (ci->flags & CI_X)
x = ci->x;
if (ci->flags & CI_Y)
y = ci->y;
if (ci->flags & CI_WIDTH)
width = ci->width;
if (ci->flags & CI_HEIGHT)
height = ci->height;
}
}
// it is important that everything's correct by the time this function is called
bool Client::Init() {
XWindowAttributes attr;
long dummy;
XGrabServer(dpy);
// Get options
if (!Sc && parent)
Sc = parent->Sc;
if (!Sc)
Sc = (Sceme *)rman->GetInfo(SE_SCEME,"");
if (!ISDESKTOP(this)) {
Window tfor;
XGetTransientForHint(dpy, window, &tfor);
if (tfor) {
transfor = ct->FindClient(tfor, 0);
if (transfor) {
for (;transfor->trans;transfor=transfor->trans);
transfor->trans=this;
}
}
}
ChangeName();
frame = 0;
title = 0;
size = 0;
if (parent && XGetWindowAttributes(dpy, window, &attr) != 1)
return false;
if (flags & CF_HASTITLE) {
XFontSetExtents *e;
e = XExtentsOfFontSet(Sc->fonts[FO_STD]);
THeight= e->max_logical_extent.height*4/5 + e->max_logical_extent.height/5 + 2*SPACE;
} else
THeight=0;
cmap = attr.colormap;
size = XAllocSizeHints();
if (parent) {
XGetWMNormalHints(dpy, window, size, &dummy);
if ((size->flags & PMinSize) && (size->flags & PMaxSize) &&
(size->min_width==size->max_width) && (size->min_height==size->max_height)) {
flags |= CF_FIXEDSIZE;
}
} else {
flags |= CF_FIXEDSIZE;
}
mapped = (attr.map_state == IsViewable);
if (parent) {
if (mapped) {
if (width == UNDEF)
width = attr.width;
if (height == UNDEF)
height = attr.height;
if (attr.x == 0 && attr.y == 0) { //started without window manager running
if (x == UNDEF)
x = Sc->BW;
if (y == UNDEF)
y = x + THeight;
} else {
if (x == UNDEF) {
x = attr.x;
x -= parent->x_root;
}
if (y == UNDEF) {
y = attr.y;
y -= parent->y_root;
}
}
} else {
if (width == UNDEF)
width = attr.width;
if (height == UNDEF)
height = attr.height;
if (x == UNDEF || y == UNDEF) {
InitPosition();
Snap(0);
}
}
} else {
x=0;
y=0;
width = DWidth - 2*Sc->BW;
height = DHeight - 2*Sc->BW - THeight;
}
Gravitate(1);
Reparent();
if (THeight)
XMapWindow(dpy, title);
if (!icon) {
if (window) {
XMapWindow(dpy, window);
XMapWindow(dpy, frame);
}
mapped = true;
visible = true;
}
SetWMState();
UpdateTB();
if (parent && !parent->focus) {
parent->focus = this;
UpdateName(false);
}
Raise(R_INDIRECT);
GrabButtons(true);
if (window) {
ct->windows.insert(WmapPair(window,this));
ct->windows.insert(WmapPair(frame,this));
}
ct->windows.insert(WmapPair(title,this));
#ifdef DEBUG
dump();
#endif
XSync(dpy, False);
XUngrabServer(dpy);
return true;
}
Client* Client::FindPointerClient() {
return this;
}
bool Client::GetWindowList(MenuItem *m,int &i,bool mouse,int mx,int my,int ind,char *key) {
if (!mapped && !icon)
return false;
if (mouse &&!(x_root <= mx && mx <= x_root+width &&
y_root-THeight <= my && my <= y_root+height))
return false;
m[i].text = name ? name : wmname;
if (!m[i].text)
m[i].text = "";
m[i].key = key;
m[i].client = this;
m[i].flags = ind * MF_INDENT;
m[i].submenu = 0;
++i;
return true;
}
void Client::GetFlags() {
unsigned long todo = ~(mask | parentmask);
flags &= (mask | parentmask);
Desktop *d = parent;
while (d && todo) {
flags |= (todo & d->childmask & d->childflags);
todo &= ~(d->childmask);
flags |= (todo & ~d->parentmask & d->flags);
todo &= d->parentmask;
d = d->parent;
}
flags |= (todo & DEFAULTFLAGS);
}
void Client::UpdateFlags(int act,unsigned long flag) {
unsigned long changeflags = flags;
switch (act) {
case FLAG_DREMOVE:
parentmask &= ~flag;
GetFlags();
break;
case FLAG_REMOVE:
mask &= ~flag;
GetFlags();
break;
case FLAG_UPDATE:
GetFlags();
break;
case FLAG_DSET:
parentmask |= flag;
flags |= flag;
break;
case FLAG_SET:
flags |= flag;
mask |= flag;
break;
case FLAG_DUNSET:
parentmask |= flag;
flags &= ~flag;
break;
case FLAG_UNSET:
flags &= ~flag;
mask |= flag;
break;
case FLAG_DTOGGLE:
parentmask |= flag;
flags ^= flag;
break;
case FLAG_TOGGLE:
flags ^= flag;
mask |= flag;
break;
default:
return;
}
changeflags ^= flags;
if (changeflags & CF_HASTITLE) {
flags ^= CF_HASTITLE;
ToggleHasTitle();
}
if (changeflags & CF_HIDEMOUSE) {
XDefineCursor(dpy,window,(flags & CF_HIDEMOUSE) ? Sc->cursors[CU_NONE] : None);
}
if (parent && (changeflags & CF_HASTBENTRY))
parent->UpdateTB();
if (changeflags & (CF_RAISEONCLICK | CF_FOCUSONCLICK | CF_GRABALTCLICK))
GrabButtons(true);
if (changeflags & (DF_TASKBARBUTTONS | CF_TITLEBARBUTTONS))
Clear();
}
void Client::RemoveClientReferences(Client *c) {
if (c==target)
target = 0;
}
/* MwmHints are so crappy! */
void Client::GetMWMOptions() {
if (ISDESKTOP(this))
return;
Atom real_type; int real_format;
unsigned long items_read, items_left;
PropMwmHints *mhints;
if (XGetWindowProperty(dpy, window, mwm_hints, 0L, 20L, False,
mwm_hints, &real_type, &real_format, &items_read, &items_left,
(unsigned char **) &mhints) == Success
&& items_read >= PROP_MOTIF_WM_HINTS_ELEMENTS) {
// decorations (no title is not ignored, configure requests are treated differently, thank you freeamp)
if (mhints->flags & MWM_HINTS_DECORATIONS &&
!(mhints->decorations & MWM_DECOR_ALL) &&
!(mhints->decorations & MWM_DECOR_TITLE)) {
flags |= CF_FORCESTATIC;
}
if (mhints->flags & MWM_HINTS_FUNCTIONS &&
!(mhints->functions & MWM_FUNC_ALL)) {
if (!(mhints->functions & MWM_FUNC_RESIZE)) {
flags |= CF_FIXEDSIZE;
flags |= CF_FIXEDSIZE;
}
if (!(mhints->functions & MWM_FUNC_MINIMIZE)) {
flags |= CF_NOMINIMIZE;
mask |= CF_NOCLOSE;
}
if (!(mhints->functions & MWM_FUNC_CLOSE)) {
flags |= CF_NOCLOSE;
mask |= CF_NOCLOSE;
}
}
XFree(mhints);
}
}
Desktop* Client::DesktopAbove() {
return parent;
}
Client* Client::Next(bool thisdesktop) {
if (thisdesktop) {
if (!parent)
return 0;
Client *n=this;
do {
n = n->next ? n->next : (parent ? parent->firstchild : 0);
} while (n && !n->mapped && n!=this);
if (n == this)
return 0;
return n;
} else {
// it's funny how difficult this is if not solved recursively
Desktop *d;
Client *n;
if (parent) {
d = parent;
n = next;
} else {
d = DesktopAbove();
n = d->firstchild;
}
int RD = 2;
for (;RD;) {
if (n) {
if (n->mapped) { // go down
d = n->DesktopAbove();
if (n!=d)
return n; // this is no desktop
n = d->firstchild;
} else n = n->next; // go right
} else { // go up + right
if (d) {
if (d->next) {
n = d->next;
d = d->parent;
} else {
d = d->parent;
n = 0;
}
} else {
n = ct->RootDesktop;
--RD;
}
}
}
}
return 0;
}
Client* Client::Prev(bool thisdesktop) {
if (thisdesktop) {
if (!parent)
return 0;
Client *prev = 0;
for (Client *i = parent->firstchild;i!=this;i=i->next)
if (i->mapped)
prev = i;
if (prev)
return prev;
for (Client *i = this;i;i=i->next)
if (i->mapped)
prev = i;
return prev;
} else {
// this is even more difficult
Desktop *d;
Client *p = 0,*c; // previous, current
if (parent) {
d = parent;
c = this;
} else {
d = DesktopAbove();
c = d->firstchild;
}
for (Client *i = d->firstchild;i!=c;i=i->next) // move left
if (i->mapped)
p = i;
c = p;
int RD = 2;
for (;RD;) {
if (c) {
d = c->DesktopAbove();
if (c!=d)
return c;
c = 0;
p = 0;
} else {
if (d && d->parent) {
c = d;
d = d->parent;
} else {
d = ct->RootDesktop;
--RD;
c = 0;
}
}
// p == 0;
if (d) {
for (Client *i = d->firstchild;i && i!=c;i=i->next) // find last window in desktop
if (i->mapped)
p = i;
}
c = p;
}
}
return 0;
}
void Client::MoveLeft() {
if (!parent)
return;
// find client to put me after
Client *newprev = 0,*newprev2=0;
for (Client *i=parent->firstchild;i!=this;i=i->next) // must find me in list
if (i->mapped && (i->flags & CF_HASTBENTRY)) {
newprev = newprev2;
newprev2 = i;
}
// remove me
if (parent->firstchild == this) {
parent->firstchild = next;
} else {
Client *prev = parent->firstchild;
for (;prev->next != this;prev=prev->next); // must find me in the list
prev->next = next;
}
// insert me
if (newprev2) { // is there at least one client before me?
if (newprev) { // are there at least two?
Client *newnext = newprev->next;
newprev->next = this;
next = newnext;
} else {
// i must be the firstchild
next = parent->firstchild;
parent->firstchild = this;
}
} else {
// i must be the last child
Client *last = parent->firstchild;
if (last) {
for (;last->next;last=last->next);
last->next = this;
} else {
parent->firstchild = this;
}
next = 0;
}
parent->UpdateTB();
}
void Client::MoveRight() {
if (!parent)
return;
// remove me
if (parent->firstchild == this) {
parent->firstchild = next;
} else {
Client *prev = parent->firstchild;
for (;prev->next != this;prev=prev->next); // must find me in the list
prev->next = next;
}
// find client to put me after
Client *newprev = next;
for (;newprev;newprev = newprev->next)
if (newprev->mapped && (newprev->flags & CF_HASTBENTRY))
break;
// insert me
if (newprev) {
Client *newnext = newprev->next;
newprev->next = this;
next = newnext;
} else {
// i must be the firstchild
next = parent->firstchild;
parent->firstchild = this;
}
parent->UpdateTB();
}
void Client::MoveUp() {
if (!parent || !parent->parent)
return;
Desktop *p = parent;
parent->GiveAway(this,true);
parent->parent->Take(this,x + parent->x_root - parent->parent->x_root,
y + parent->y_root - parent->parent->y_root);
p->AutoClose();
man->WantFocus = this;
}
void Client::MoveDown() {
if (!parent)
return;
Client *n=this;
Desktop *d=0;
do {
n = n->next ? n->next : (parent ? parent->firstchild : 0);
d = n ? n->DesktopAbove() : 0;
} while (n && (!n->mapped || (n!=this && d!=n)));
if (!d || n==this)
return;
parent->GiveAway(this,true);
d->Take(this,x,y);
man->WantFocus = this;
}
void Client::Stacking(bool up) {
if (!parent)
return;
if (up) {
switch (flags & (CF_BELOW | CF_ABOVE)) {
case (CF_BELOW | CF_ABOVE):
flags &= ~CF_BELOW;
break;
case 0:
flags |= CF_ABOVE;
break;
case CF_ABOVE:
return;
case CF_BELOW:
flags &= ~CF_BELOW;
break;
}
} else {
switch (flags & (CF_BELOW | CF_ABOVE)) {
case (CF_BELOW | CF_ABOVE):
flags &= ~CF_ABOVE;
break;
case 0:
flags |= CF_BELOW;
break;
case CF_ABOVE:
flags &= ~CF_ABOVE;
break;
case CF_BELOW:
return;
}
}
mask |= CF_BELOW | CF_ABOVE;
parentmask |= CF_BELOW | CF_ABOVE;
for (int i=0;i!=3;++i)
parent->LastRaised[i] = 0;
Raise(0);
}
int Client::GetStacking() {
if (flags & CF_ABOVE)
return 2;
if (flags & CF_BELOW)
return 0;
return 1;
}
void Client::Raise(int mode) {
if (!parent)
return;
if (!mapped) {
if (mode & R_MAP)
Map(); else
return;
}
int stacking = GetStacking();
if (mapped && (parent->LastRaised[stacking] != this)) {
Client *c = parent->LastRaised[stacking];
parent->LastRaised[stacking] = this;
GrabButtons();
if (c)
c->GrabButtons();
if (stacking == 2 && parent->parent) {
XRaiseWindow(dpy, frame);
} else {
XWindowChanges wc;
wc.sibling = (stacking==2) ? parent->title :
parent->StackRef[stacking+1];
wc.stack_mode = Below;
XConfigureWindow(dpy,frame,CWSibling | CWStackMode,&wc);
}
}
if (trans) {
if (trans->mapped)
trans->Raise(mode);
} else {
if (mode & R_PARENT)
parent->Raise(R_INDIRECT | R_PARENT);
if (mode & R_GOTO)
Scroll(mode & R_MOVEMOUSE);
}
}
void Client::Scroll(bool MoveMouse) {
if (!parent || ct->MaxWindow)
return;
parent->Scroll(false);
parent->Goto(x + width/3,y + height/3,0);
if (!MoveMouse)
return;
if (MaxW || MaxH)
setmouse(parent->frame,(TBEx+TBEy)/2,THeight/2); else
setmouse(frame,(TBx+TBy)/2,THeight/2);
}
void Client::Lower(unsigned int mode) {
if (!parent)
return;
int stacking = GetStacking();
if (mapped) {
if ((mode & L_BOTTOM) || !(~flags & (CF_BELOW | CF_ABOVE))) {
XLowerWindow(dpy,frame);
} else {
XWindowChanges wc;
wc.sibling = parent->StackRef[stacking];
wc.stack_mode = Above;
XConfigureWindow(dpy,frame,CWSibling | CWStackMode,&wc);
}
}
if (parent->LastRaised[stacking] == this) {
GrabButtons();
parent->LastRaised[stacking] = 0;
}
if (mapped)
GrabButtons();
if (transfor) {
transfor->Lower(mode);
} else {
if (mode & L_PARENT)
parent->Lower(mode);
}
}
/* This function restacks the window below c
* if both windows don't have the same parent the first ancestors
* with the same parent are used
*/
void Client::LowerBelow(Client *c) {
if (!parent || !c || !c->parent)
return;
Client *i = this;
if (i->parent != c->parent) {
ClientList *tmp;
ClientList *il=0;
ClientList *cl=0;
// Create two stacks (linked lists, top is first element)
for (;c;c=c->parent) {
tmp = cl;
cl = new ClientList;
cl->next = tmp;
cl->client = c;
}
for (;i;i=i->parent) {
tmp = il;
il = new ClientList;
il->next = tmp;
il->client = i;
}
while (il && cl && il->client==cl->client) {
tmp = il;
il = il->next;
delete tmp;
tmp = cl;
cl = cl->next;
delete tmp;
}
i = il ? il->client : 0;
c = cl ? cl->client : 0;
while (il) {
tmp = il;
il = il->next;
delete tmp;
}
while (cl) {
tmp = cl;
cl = cl->next;
delete tmp;
}
}
if (!i || !c || !i->parent)
return;
XWindowChanges wc;
wc.sibling = c->frame;
wc.stack_mode = Below;
XConfigureWindow(dpy,i->frame,CWStackMode|CWSibling,&wc);
int istacking = i->GetStacking();
if (i->mapped && i->parent->LastRaised[istacking] == this) {
i->GrabButtons();
i->parent->LastRaised[istacking] = 0;
}
}
void Client::GrabButtons(bool first) {
bool MustBeGrabbed = false;
if ( ((flags & CF_FOCUSONCLICK) && ct->Focus != this)) {
MustBeGrabbed = true;
} else {
if (flags & CF_RAISEONCLICK) {
for (Client *c = this; c->parent; c = c->parent)
if (c->parent->LastRaised[c->GetStacking()] != c) {
MustBeGrabbed = true;
break;
}
}
}
if (MustBeGrabbed) {
if (!(flags & CF_GRABBED) || first) { // must be grabbed
flags |= CF_GRABBED;// sync, async
XGrabButton(dpy,AnyButton,AnyModifier,window,True,ButtonPressMask,GrabModeSync,GrabModeAsync,None,None);
}
} else {
if ((flags & CF_GRABBED) || first) { // must be ungrabbed
flags &= ~CF_GRABBED;
XUngrabButton(dpy,AnyButton,AnyModifier,window);
if (flags & (CF_GRABALTCLICK)) {
XGrabButton(dpy,AnyButton,Mod1Mask,window,false,ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,GrabModeAsync,GrabModeAsync,None,None);
XGrabButton(dpy,AnyButton,Mod1Mask | ControlMask,window,false,ButtonPressMask|ButtonReleaseMask|Button1MotionMask|Button3MotionMask,GrabModeAsync,GrabModeAsync,None,None);
XGrabButton(dpy,AnyButton,Mod1Mask | ShiftMask,window,false,ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,GrabModeAsync,GrabModeAsync,None,None);
}
}
}
}
void Client::GiveFocus() {
if (!parent || parent->focus != this)
return;
ct->Focus = 0;
parent->focus = 0;
if (!parent->name) {
delete [] parent->wmname;
parent->wmname = new char[8];
strcpy(parent->wmname,"Desktop");
parent->UpdateName(false);
}
if (ct->Focus == this)
ct->Focus = 0;
}
void Client::GetFocus() {
if (!parent) {
ct->CurrentDesktop = (Desktop *)this;
UEH->SetCurrent(this,0);
ct->Focus = this;
return;
}
if (trans) {
Client *lasttrans = 0;
for (Client *i = this;i;i=i->trans)
if (i->mapped)
lasttrans=i;
if (lasttrans && lasttrans != this) {
lasttrans->GetFocus();
return;
}
}
if (!visible)
return;
bool fullscreen = false;
Client *oldfocus = ct->Focus;
XSetInputFocus(dpy, window, RevertToPointerRoot, CurrentTime);
XInstallColormap(dpy, cmap);
ct->Focus = this;
GrabButtons();
if (oldfocus)
oldfocus->GrabButtons();
Desktop *d,*cd;
if (target) {
d = target;
} else {
d = DesktopAbove();
if (d->target)
d = d->target;
}
cd=ct->CurrentDesktop;
ct->CurrentDesktop = d;
Client *c = 0;
if (parent) {
c = parent->focus;
parent->focus = this;
}
if (oldfocus != this)
UpdateName(true);
// ReDraw old and new focus
Clear();
if (c)
c->Clear();
// Their parents and the old and the new CurrentDesktop must be ReDrawed
// i.e. c->parent, lasttrans->parent, cd, d
if (parent)
parent->Clear();
if (cd)
cd->Clear();
if (d)
d->Clear();
if (c && c->parent)
c->parent->Clear();
UEH->SetCurrent(this,0);
}
Client *Client::Focus() {
return this;
}
void Client::Map() {
if (visible || !parent)
return;
if (!parent->visible)
parent->Map();
visible = true;
if (icon) {
icon->parent->RemoveIcon(icon);
icon = 0;
}
if (!mapped) {
XMapWindow(dpy, window);
XMapWindow(dpy, frame);
mapped = true;
Raise(0);
SetWMState();
if (flags & CF_HASTBENTRY)
parent->UpdateTB();
}
man->WantFocus = this;
}
void Client::Unmap(bool event) {
if (ignoreunmap && event) {
--ignoreunmap;
return;
}
if (!mapped || !parent)
return;
mapped = false;
visible = false;
XUnmapWindow(dpy, window);
XUnmapWindow(dpy, frame);
if (ct->MaxWindow == this) {
Maximize(MAX_FULLSCREEN);
}
SetWMState();
GiveFocus();
if (flags & CF_HASTBENTRY)
parent->UpdateTB();
parent->AutoClose();
}
void Client::Hide() {
if (!parent)
return;
if (flags & CF_NOMINIMIZE)
return;
Unmap(false);
icon = parent->AddIcon(new Icon(parent,this,0));
icon->Init();
SetWMState();
}
/* Attempt to follow the ICCCM by explicity specifying 32 bits for
* this property. Does this goof up on 64 bit systems? */
void Client::SetWMState() {
if (ISDESKTOP(this))
return;
CARD32 data[2];
data[0] = visible ? NormalState : ((icon || mapped) ? IconicState : WithdrawnState);
data[1] = icon ? icon->window : None;
XChangeProperty(dpy, window, wm_state, wm_state,
32, PropModeReplace, (unsigned char *)data, 2);
}
/* If we can't find a WM_STATE we're going to have to assume
* Withdrawn. This is not exactly optimal, since we can't really
* distinguish between the case where no WM has run yet and when the
* state was explicitly removed (Clients are allowed to either set the
* atom to Withdrawn or just remove it... yuck.) */
long Client::GetWMState() {
if (ISDESKTOP(this))
return NormalState;
Atom real_type;
int real_format;
unsigned long items_read, items_left;
long *data, state = WithdrawnState;
if (XGetWindowProperty(dpy, window, wm_state, 0L, 2L, False,
wm_state, &real_type, &real_format, &items_read, &items_left,
(unsigned char **) &data) == Success && items_read) {
state = *data;
XFree(data);
}
return state;
}
// This will need to be called whenever we update our Client stuff.
// it's a bit difficult:
// y_root is the StaticGravity position of the window
// the origin of y is the NorthWestGravity origin of the desktop
void Client::SendConfig() {
XConfigureEvent ce;
bool Max = ct->MaxWindow == this;
if (Max) {
x_root = VX;
y_root = VY;
} else {
if (parent) {
x_root = parent->x_root + x + Sc->BW;
y_root = parent->y_root + y + Sc->BW;
} else {
x_root = x + Sc->BW;
y_root = y + Sc->BW; // + THeight ?
}
}
if (!window)
return;
ce.type = ConfigureNotify;
ce.event = window;
ce.window = window;
ce.x = x_root;
ce.y = y_root;
ce.width = Max ? VWidth : width;
ce.height = Max ? VHeight : height;
ce.border_width = 0;
ce.above = None;
ce.override_redirect = 0;
XSendEvent(dpy, window, False, StructureNotifyMask, (XEvent *)&ce);
}
void Client::Configure(XConfigureRequestEvent e) {
if (ct->MaxWindow == this) {
ChangeSize();
return;
}
XWindowChanges wc;
if (e.value_mask & CWX) {
x = e.x - (parent ? parent->x_root : 0) - Sc->BW;
}
if (e.value_mask & CWY)
y = e.y + (parent ? -parent->y_root : 0) - Sc->BW;
if (e.value_mask & CWWidth) {
width = e.width;
}
if (e.value_mask & CWHeight) {
height = e.height;
}
if (e.value_mask & (CWX | CWY | CWHeight | CWWidth)) {
if (e.value_mask & CWY)
Gravitate(1);
if (e.value_mask & (CWHeight | CWWidth)) {
ChangeSize();
} else {
// if (!(parent->flags & DF_AUTORESIZE)) // i think we should ignore this
ChangePos(true);
}
}
// Applications are so stupid, they don't know what they do if they for example
// put a window on top of the stack
// if applications specify shit like BottomIf or Opposite, it's their own fault that this doesn't work...
// i currently pass a configurerequest with a sibling hoping that apps know what they're doing
if (e.value_mask & CWStackMode)
if (e.value_mask & ~CWSibling) {
e.value_mask &= ~CWSibling & ~CWStackMode;
if (e.detail == Below)
Lower(0); else
Raise(R_INDIRECT);
} else {
wc.sibling = e.above;
wc.stack_mode = e.detail;
int stacking = GetStacking();
if (parent && parent->LastRaised[stacking] == this)
parent->LastRaised[stacking] = 0;
}
}
/* All toolkits suck. GTK sucks in particular, because if you even
* -look- at it funny, it will put a PSize hint on your window, and
* then gleefully leave it at the default setting of 200x200 until you
* change it. So PSize is pretty useless for us these days.
*
* After we get the mouse position, we check for a reasonable position
* hint, and set x/y relative to the mouse if there is none. To
* account for window gravity when using the mouse, we add theight in
* the calculation and then degravitate. Don't think about it too
* hard, or your head will explode. */
void Client::InitPosition(){
int xmax = parent->width;
int ymax = parent->height;
int mouse_x, mouse_y;
if (size->flags & (/*PSize|*/USSize)) { // How should the program know where it's placed best?
width = size->width;
height = size->height;
}
/* make sure it's big enough to click at */
if (width < 2 * THeight)
width = 2 * THeight;
if (height < THeight)
height = THeight;
GetMousePosition(parent ? parent->window : root,mouse_x, mouse_y);
/* position the frame */
if (width == xmax && height == ymax && !(parent->flags & (DF_TILE | DF_AUTORESIZE))) {
x = 0;
y = - THeight;
MaxX = - Sc->BW;
MaxY = - Sc->BW;
MaxW = width;
MaxH = height;
} else {
if (0 <= mouse_x && mouse_x <= xmax && 0 <= mouse_y && mouse_y <= ymax) {
if (width > xmax) {
x = 0;
} else {
x = (mouse_x * (xmax - width) / xmax);
if (x + width > xmax)
x = xmax - width;
}
if (height + THeight > ymax) {
y = 0;
} else {
y = (mouse_y * (ymax - height - THeight) / ymax);
if (y + height > ymax)
y = ymax - height;
}
} else {
// perhaps we should introduce a policy that maximizes the totally used space
x=0;y=0;
}
}
/* now align the client properly within it */
y += THeight;
Gravitate(-1);
/* hints will override mouse position */
if (size->flags & (/*PPosition|*/USPosition)) {
if (size->x > 0 && size->x < xmax)
x = size->x;
if (size->y > 0 && size->y < ymax)
y = size->y;
}
x-=Sc->BW;y-=Sc->BW;
}
void Client::Gravitate(int multiplier) {
int dy = 0;
int gravity;
if (flags & CF_FORCESTATIC)
gravity = StaticGravity; else
gravity = (size && (size->flags & PWinGravity)) ?
size->win_gravity : NorthWestGravity;
switch (gravity) {
case NorthWestGravity:
case NorthEastGravity:
case NorthGravity:
dy = THeight;
break;
case EastGravity:
case WestGravity:
case CenterGravity:
dy = THeight/2;
break;
}
y += multiplier * dy;
y_root += multiplier *dy;
}
void Client::Reparent() {
XSetWindowAttributes pattr;
pattr.override_redirect = True;
pattr.background_pixel = Sc->colors[C_BG].pixel;
pattr.border_pixel = Sc->colors[C_BD].pixel;
pattr.event_mask = ChildMask|ButtonMask|ExposureMask|EnterWindowMask|LeaveWindowMask;
int X = x;
int Y = y;
if (parent) {
parent->Translate(&X,&Y);
frame = XCreateWindow(dpy, parent ? parent->window : root,
X, Y - THeight, width, height + THeight, Sc->BW,
DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &pattr);
pattr.event_mask = ButtonMask|ExposureMask|LeaveWindowMask;
} else {
frame = root;
}
title = XCreateWindow(dpy, parent ? frame : root,
parent ? 0 : Sc->BW, parent ? 0 : Sc->BW, width, THeight ? THeight : 1, 0,
DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWEventMask, &pattr);
#ifdef SHAPE
if (shape && window) {
XShapeSelectInput(dpy, window, ShapeNotifyMask);
SetShape();
}
#endif
// only for non-treewm objects i.e. for non-desktops
if (!ISDESKTOP(this)) {
XAddToSaveSet(dpy, window);
XSelectInput(dpy, window, /*ColormapChangeMask|*/PropertyChangeMask|LeaveWindowMask);
}
if (window) {
XSetWindowBorderWidth(dpy, window, 0);
XResizeWindow(dpy, window, width, height);
XReparentWindow(dpy, window, frame, 0, THeight);
if (mapped)
++ignoreunmap;
}
SendConfig();
}
void Client::SendEvent(XAnyEvent &e) {
e.window = window;
XSendEvent(dpy,window,false,
(e.type == KeyPress) ? KeyPressMask : KeyReleaseMask,(XEvent *)&e);
}
void Client::SendWMDelete(int mode) {
if (/*soft &&*/(flags & CF_NOCLOSE) || mode == DEL_RELEASE)
return;
int i, n, found=0;
Atom *protocols;
if (mode != DEL_FORCE) {
if (XGetWMProtocols(dpy, window, &protocols, &n)) {
for (i=0; i<n; ++i)
if (protocols[i] == wm_delete)
++found;
XFree(protocols);
}
if (found) {
XEvent e;
e.type = ClientMessage;
e.xclient.window = window;
e.xclient.message_type = wm_protos;
e.xclient.format = 32;
e.xclient.data.l[0] = wm_delete;
e.xclient.data.l[1] = CurrentTime;
XSendEvent(dpy, window, False, NoEventMask, &e);
}
return;
}
// User should notice that he is destroying a client
XKillClient(dpy, window);
}
void Client::RequestDesktop(bool) {
if (!parent)
return;
Gravitate(-1);
parent->GiveAway(this,false);
Desktop *d = new Desktop(parent);
d->x = x;
d->y = y;
d->width = width;
d->height = height;
d = parent->AddDesktop(d,this);
if (!d)
return;
d->autoclose = true;
MaxX = -Sc->BW;
MaxY = -Sc->BW;
d->Take(this,MaxX,MaxY);
Maximize(MAX_FULL);
}
#include <unistd.h>
void Client::ChangeSize() {
if (!parent)
return;
if (ct->MaxWindow == this) {
SendConfig();
return;
}
if (flags & CF_HASTITLE)
XResizeWindow(dpy, title, width, THeight);
int X = x;
int Y = y-THeight;
parent->Translate(&X,&Y);
XMoveResizeWindow(dpy, frame, X, Y, width, height + THeight);
XMoveResizeWindow(dpy, window, 0, THeight, width, height);
if (parent) {
parent->Tile();
parent->AutoResize();
}
SendConfig();
#ifdef SHAPE
if (shape)
SetShape();
#endif
UpdateTB();
}
void Client::ChangePos(bool autoresize) {
if (!parent)
return;
if (ct->MaxWindow == this) {
SendConfig();
return;
}
int X = x;
int Y = y-THeight;
parent->Translate(&X,&Y);
XMoveWindow(dpy,frame,X,Y);
SendConfig();
if (!(parent->flags & DF_TILE) && autoresize)
parent->AutoResize();
}
void Client::Maximize(int type) {
if (type == MAX_FULLSCREEN || ct->MaxWindow == this) {
if (!mapped && ct->MaxWindow != this)
return;
if (type != MAX_FULLSCREEN)
return;
Client *oldmax = ct->MaxWindow;
if (ct->MaxWindow == this) {
++ignoreunmap;
#ifdef VIDMODE
if (DHeight != VHeight || DWidth != VHeight) {
// XUngrabPointer(dpy,CurrentTime);
// XUngrabButton(dpy,Button3,AnyModifier,window);
}
#endif
XReparentWindow(dpy,window,frame,0,0);
ct->MaxWindow = 0;
ChangeSize();
} else {
if (parent) {
#ifdef VIDMODE
XF86VidModeModeLine ml;
int dotclock;
XF86VidModeGetModeLine(dpy,screen,&dotclock,&ml);
VWidth = ml.hdisplay;
VHeight = ml.vdisplay;
if (DHeight != VHeight || DWidth != VHeight)
XF86VidModeGetViewPort(dpy,screen,&VX,&VY);
#endif
XMoveResizeWindow(dpy,window,0,0, VWidth, VHeight);
++ignoreunmap;
XReparentWindow(dpy,window,root,VX,VY);
XRaiseWindow(dpy,window);
if (UEH->dialog)
XRaiseWindow(dpy,UEH->dialog->window);
XSetInputFocus(dpy,window, RevertToPointerRoot, CurrentTime);
#ifdef VIDMODE
if (DHeight != VHeight || DWidth != VHeight) {
// XGrabButton(dpy,AnyButton,AnyModifier,window,True,MouseMask,GrabModeSync,GrabModeAsync,None,None);
// XGrabPointer(dpy,window,True,0,GrabModeAsync,GrabModeAsync,window,None,CurrentTime);
// XGrabButton(dpy,Button3,AnyModifier,root,true,0,GrabModeAsync,GrabModeAsync,window,None);
}
#endif
}
if (oldmax)
oldmax->Maximize(MAX_FULLSCREEN);
if (parent)
ct->MaxWindow = this;
SendConfig();
}
return;
}
if (!parent)
return;
if (type == MAX_UNMAX && !MaxW && !MaxH)
return;
if (type == MAX_REMAX) {
if (parent->flags & DF_AUTORESIZE)
return;
height = parent->height - Sc->BW - y;
width = parent->width;
} else {
int MaxOld;
if (MaxH) {
if (y == -Sc->BW)
MaxOld = MaxW ? MAX_FULL : MAX_VERTFULL; else
MaxOld = MaxW ? MAX_HALF : MAX_VERTHALF;
} else {
MaxOld = MaxW ? MAX_HORI : 0;
}
if (MaxW) {
x = MaxX; width = MaxW;
MaxX = 0; MaxW = 0;
}
if (MaxH) {
y = MaxY; height = MaxH;
MaxY = 0; MaxH = 0;
}
if (type != MAX_UNMAX && type != MaxOld) {
if (type != MAX_VERTHALF && type != MAX_VERTFULL) {
MaxX = x; MaxW = width;
x = -Sc->BW;
width=parent->width;
}
if (type != MAX_HORI) {
MaxY = y; MaxH = height;
y = -Sc->BW + ((type==MAX_FULL || type==MAX_VERTFULL) ? 0 : THeight);
height=parent->height - Sc->BW - y;
}
}
}
RecalcSweep(S_RIGHT | S_BOTTOM);
ChangeSize();
}
void Client::Shade() {
if (!parent)
return;
int X = x;
int Y = y;
parent->Translate(&X,&Y);
XMoveResizeWindow(dpy,frame,X,Y-THeight,width,THeight + ((flags & CF_SHADED) ? height : -1));
flags ^= CF_SHADED;
mask |= CF_SHADED;
}
/* After pulling my hair out trying to find some way to tell if a
* window is still valid, I've decided to instead carefully ignore any
* errors raised by this function. We know that the X calls are, and
* we know the only reason why they could fail -- a window has removed
* itself completely before the Unmap and Destroy events get through
* the queue to us. It's not absolutely perfect, but it works.
* Ick. Argh. You didn't see this function. */
int ignore_xerror(Display *dpy, XErrorEvent *e)
{
return 0;
}
Client::~Client() {
if (icon) {
icon->parent->RemoveIcon(icon);
icon = 0;
}
}
void Client::Remove(int mode) {
XGrabServer(dpy);
XSetErrorHandler(ignore_xerror);
XUngrabButton(dpy,AnyButton,AnyModifier,window);
#ifdef DEBUG
err("removing %s, %d: %d left", name ? name : wmname, mode, XPending(dpy));
#endif
Gravitate(-1);
if (parent) {
ct->windows.erase(window);
}
ct->windows.erase(frame);
ct->windows.erase(title);
if (mode != R_RESTART)
if (mode == R_WITHDRAW) {
visible = false;
mapped = false;
SetWMState();
} else { // R_REMAP
if (icon)
Map();
if (parent)
if (mapped/* || icon*/)
XMapWindow(dpy, window); else
XUnmapWindow(dpy, window);
}
if (!ISDESKTOP(this)) {
XReparentWindow(dpy, window, root, x_root - Sc->BW, y_root - Sc->BW);
XRemoveFromSaveSet(dpy, window);
if (mapped)
++ignoreunmap;
} else {
if (parent)
XDestroyWindow(dpy, window);
}
int stacking = GetStacking();
if (parent && parent->LastRaised[stacking] == this)
parent->LastRaised[stacking] = 0;
if (parent)
XSetWindowBorderWidth(dpy, window, 1);
if (title)
XDestroyWindow(dpy, title);
if (frame)
XDestroyWindow(dpy, frame);
if (size)
XFree(size);
if (name) {
free(name);
name = 0;
}
if (trans)
trans->transfor=transfor;
if (transfor) {
transfor->trans=trans;
}
if (mode == R_WITHDRAW)
ct->RemoveClientReferences(this);
XSync(dpy, False);
XSetErrorHandler(handle_xerror);
XUngrabServer(dpy);
}
void Client::ToggleHasTitle() {
Gravitate(-1);
flags ^= CF_HASTITLE;
if (flags & CF_HASTITLE) {
XFontSetExtents *e;
e = XExtentsOfFontSet(Sc->fonts[FO_STD]);
THeight= e->max_logical_extent.height*4/5 + e->max_logical_extent.height/5 + 2*SPACE;
Desktop *d = ISDESKTOP(this);
if (d)
d->UpdateTB();
if (!parent)
height -= THeight;
XMapWindow(dpy,title);
} else {
if (!parent)
height += THeight;
THeight = 0;
XUnmapWindow(dpy,title);
}
Gravitate(1);
ChangeSize();
}
void Client::UpdateTB() {
TBx = 0;
TBy = width;
Clear();
}
void Client::ChangeName() {
if (ISDESKTOP(this))
return;
if (name)
free(name);
char *windowname = 0;
FetchName(window, &windowname);
if (!windowname || !windowname[0]) {
name = 0;
} else {
int length = strlen(windowname);
if (length > MAXTITLE)
length = MAXTITLE;
windowname[length]=0;
name = strdup(windowname);
}
if (windowname)
XFree(windowname);
}
/* UpdatName() is called whenever a window gets the focus or changes its name
*/
void Client::UpdateName(bool setFocus) {
Clear();
if (!parent)
return;
if (setFocus) {
parent->focus = this;
} else {
if (parent->focus != this) {
parent->Clear();
return;
}
}
for (Desktop *ud=parent;;ud = ud->parent) {
ud->Clear();
if (ud->name)
return;
char *fname = ud->focus->name ? ud->focus->name : ud->focus->wmname;
int n = strlen(fname);
if (ud->wmname)
delete [] ud->wmname;
ud->wmname = new char[n+3];
ud->wmname[0] = '[';
ud->wmname[1] = '\0';
strcat(ud->wmname,fname);
strcat(ud->wmname,"]");
if (!ud->parent)
return;
if (setFocus) {
ud->parent->focus = ud;
} else {
if (ud->parent->focus != ud) {
ud->parent->Clear();
return;
}
}
}
}
void Client::ReDraw() {
if (!(flags & CF_HASTITLE) || !visible)
return;
XDrawLine(dpy, title, Sc->border_gc,
0, THeight - 1,
width, THeight - 1);
ReDrawTBEntry(this);
}
void Client::ReDrawTBEntry(Client *t,bool all) {
int tx,ty;
bool NoButtons;
if (t==this) {
tx = TBx;
ty = TBy;
NoButtons = !(flags & CF_TITLEBARBUTTONS);
} else {
tx = t->TBEx;
ty = t->TBEy;
NoButtons = !(flags & DF_TASKBARBUTTONS);
}
int hl=0;
if (t == this) {
if (!t->parent || t->parent->focus==t)
++hl;
} else {
if (t->parent->focus == t)
++hl;
}
if (t == UEH->Current)
hl = 2;
if (all) {
XFontSetExtents *e;
e = XExtentsOfFontSet(Sc->fonts[FO_STD]);
XFillRectangle(dpy, title, Sc->title_gc[hl],
tx, 0,
ty, THeight - 1);
char *str = t->name ? t->name : t->wmname;
XmbDrawString(dpy, title, Sc->fonts[FO_STD], Sc->string_gc,
tx + SPACE, SPACE + e->max_logical_extent.height*4/5,
str, strlen(str));
XDrawLine(dpy, title, Sc->border_gc,
ty, 0,
ty, THeight);
if (NoButtons)
return;
XDrawLine(dpy, title, Sc->border_gc,
ty - BUTTONWIDTH, 0,
ty - BUTTONWIDTH, THeight);
if (!(t->flags & CF_FIXEDSIZE))
XDrawLine(dpy, title, Sc->border_gc,
ty - 2*BUTTONWIDTH, 0,
ty - 2*BUTTONWIDTH, THeight);
}
if (NoButtons)
return;
RPixmap *pm;
int b=0;
if (UEH->SelClient == this && UEH->SelEntry ==t)
if ((UEH->Seltype & T_BUTTON) &&
(UEH->Seltype & T_BUTTON) != T_BAR) {
if ((UEH->Seltype & T_BUTTON) == T_F2)
b+=4;
b += (UEH->Seltype & T_MOUSEBUTTON);
}
pm = Sc->button[b>=4 ? b : BU_20];
if (pm && pm->pm)
XCopyArea(dpy,pm->pm,title,Sc->border_gc,
0,0,pm->w,pm->h,ty - (BUTTONWIDTH+pm->w)/2,(THeight-pm->h)/2); else
XFillRectangle(dpy,title,Sc->title_gc[hl],
ty - BUTTONWIDTH+1,0,BUTTONWIDTH-1,THeight-1);
if (!(t->flags & CF_FIXEDSIZE)) {
pm = Sc->button[(!b || b>=4) ? BU_10 : b];
if (pm && pm->pm)
XCopyArea(dpy,pm->pm,title,Sc->border_gc,
0,0,pm->w,pm->h,ty - (3*BUTTONWIDTH+pm->w)/2,(THeight-pm->h)/2); else
XFillRectangle(dpy,title,Sc->title_gc[hl],
ty - 2*BUTTONWIDTH+1,0,BUTTONWIDTH-1,THeight-1);
}
}
void Client::DrawOutline() {
if (!parent)
return;
char buf[32];
int w, h;
int X = x, Y = y;
parent->Translate(&X,&Y);
XDrawRectangle(dpy, parent ? parent->window : root , Sc->invert_gc,
X + Sc->BW/2, Y - THeight + Sc->BW/2,
width + Sc->BW, height + THeight + Sc->BW);
if (THeight)
XDrawLine(dpy, parent ? parent->window : root, Sc->invert_gc, X + Sc->BW/2+1, Y + Sc->BW - 1,
X + width + 3*Sc->BW/2, Y + Sc->BW - 1);
int basex, basey;
if (size->flags & PResizeInc) {
basex = (size->flags & PBaseSize) ? size->base_width :
(size->flags & PMinSize) ? size->min_width : 0;
basey = (size->flags & PBaseSize) ? size->base_height :
(size->flags & PMinSize) ? size->min_height : 0;
w = (width - basex) / size->width_inc;
h = (height - basey) / size->height_inc;
} else {
w = width;
h = height;
}
Gravitate(-1);
snprintf(buf, sizeof buf, "%dx%d+%d+%d", w, h, x, y);
Gravitate(1);
XmbDrawString(dpy, parent ? parent->window : root, Sc->fonts[FO_STD], Sc->invert_gc,
X + width - XmbTextEscapement(Sc->fonts[FO_STD], buf, strlen(buf)) - SPACE,
Y + height - SPACE,
buf, strlen(buf));
}
void MoveMouse(int key) {
if (key == XKeysymToKeycode(dpy,XK_Left)) {
XWarpPointer(dpy,None,None,0,0,0,0,-10,0);
}
if (key == XKeysymToKeycode(dpy,XK_Right)) {
XWarpPointer(dpy,None,None,0,0,0,0,10,0);
}
if (key == XKeysymToKeycode(dpy,XK_Up)) {
XWarpPointer(dpy,None,None,0,0,0,0,0,-10);
}
if (key == XKeysymToKeycode(dpy,XK_Down)) {
XWarpPointer(dpy,None,None,0,0,0,0,0,10);
}
}
void Client::MoveResize(int mode, bool movemouse,int x1, int y1, bool firstevent) {
if (!parent)
return;
if (ct->MaxWindow == this)
return;
if (MaxW || MaxH) {
Maximize(MAX_UNMAX);
ReDraw();
}
XEvent ev;
int old_x = x;
int old_y = y;
int old_w = width;
int old_h = height;
if (!grabp(root, MouseMask, Sc->cursors[CU_MOVE]))
return;
XGrabKeyboard(dpy,root,false,GrabModeAsync,GrabModeAsync,CurrentTime);
// XGrabKey(dpy,AnyKey,AnyModifier,parent->window,false,GrabModeAsync,GrabModeAsync);
XGrabServer(dpy);
if (movemouse) {
if (mode & S_LEFT)
x1 = x_root; else
if (mode & S_RIGHT)
x1 = x_root+width; else
x1 = x_root+width/2;
if (mode & S_TOP)
y1 = y_root - THeight; else
if (mode & S_BOTTOM)
y1 = y_root+height; else
y1 = y_root-THeight/2;
setmouse(root,x1,y1);
}
#ifdef DEBUG
ShowEvent(ev);
#endif
DrawOutline();
for (bool alive = true;alive;) {
XMaskEvent(dpy, MouseMask | KeyPressMask, &ev);
switch (ev.type) {
case KeyPress:
MoveMouse(ev.xkey.keycode);
if (ev.xkey.keycode == XKeysymToKeycode(dpy,XK_Escape)) {
alive = false;
DrawOutline(); /* clear */
x = old_x; width = old_w;
y = old_y; height = old_h;
DrawOutline();
} else if (ev.xkey.keycode == XKeysymToKeycode(dpy,XK_space) ||
ev.xkey.keycode == XKeysymToKeycode(dpy,XK_Return))
alive = false;
break;
case MotionNotify:
DrawOutline(); /* clear */
switch (mode & (S_LEFT|S_RIGHT)) {
case S_RIGHT:
width = old_w + ev.xmotion.x - x1;
break;
case S_LEFT:
width = old_w - ev.xmotion.x + x1;
case 0:
x = old_x + ev.xmotion.x - x1;
break;
}
switch (mode & (S_TOP|S_BOTTOM)) {
case S_BOTTOM:
height = old_h + ev.xmotion.y - y1;
break;
case S_TOP:
height = old_h - ev.xmotion.y + y1;
case 0:
y = old_y + ev.xmotion.y - y1;
break;
}
Snap(mode);
if (mode)
RecalcSweep(mode);
DrawOutline();
break;
case ButtonPress:
firstevent = false;
break;
case ButtonRelease:
if (firstevent)
firstevent = false; else
alive = false;
break;
}
}
DrawOutline(); /* clear */
XUngrabServer(dpy);
XUngrabPointer(dpy, CurrentTime);
if (!UEH->dialog)
XUngrabKeyboard(dpy,CurrentTime);
if (mode)
ChangeSize(); else
ChangePos(true);
}
#define MAX(x,y) (x)>(y) ? (x) : (y)
void Client::Snap(int mode) {
if (!parent || !(flags & CF_SNAP))
return;
int x1[2],x2[2],y1[3],y2[3];
int minx=snap+1,miny=snap+1,dist;
int dminus = - (Sc->BW / 2);
int dplus = (Sc->BW + 1) / 2;
x1[0] = x + dminus;
x1[1] = x + width + dplus;
y1[0] = y - THeight + dminus;
y1[1] = y + dminus;
y1[2] = y + height + dplus;
for (Client *c=parent->firstchild;c;c=c->next) {
if (!c->mapped)
continue;
dminus = - (c->Sc->BW / 2);
dplus = (c->Sc->BW + 1) / 2;
if (c!=this) {
x2[0] = c->x + dminus;
x2[1] = c->x + c->width + dplus;
y2[0] = c->y - c->THeight + dminus;
y2[1] = c->y + dminus;
y2[2] = c->y + c->height + dplus;
if (!(x1[0] <= x2[1]+snap && x2[0] <= x1[1]+snap &&
y1[0] <= y2[2]+snap && y2[0] <= y1[2]+snap))
continue;
} else {
c = parent; // temporalily look at the parent window
x2[0] = - Sc->BW + dminus;
y2[0] = - Sc->BW - c->THeight + dminus;
y2[1] = - Sc->BW + dminus;
if (parent->flags & DF_AUTORESIZE) {
x2[1] = x2[0];
y2[2] = y2[0];
} else {
x2[1] = - Sc->BW + c->width + dplus;
y2[2] = - Sc->BW + c->height + dplus;
}
}
int min = (mode & S_RIGHT) ? 1 : 0;
int max = (mode & S_LEFT) ? 1 : 2;
for (int i = min; i!=max; ++i)
for (int j = 0; j!=2; ++j) {
dist = x2[j] - x1[i];
if (abs(dist) < abs(minx))
minx = dist;
}
min = (mode & S_BOTTOM) ? 2 : 0;
max = (mode & S_TOP) ? 2 : 3;
for (int i = min; i!=max; ++i)
for (int j = 0; j!=3; ++j) {
dist = y2[j] - y1[i];
if (abs(dist) < abs(miny))
miny = dist;
}
if (c==parent)
c=this; // We must not forget this
}
if (abs(minx) <= snap)
switch (mode & (S_LEFT | S_RIGHT)) {
case S_RIGHT:
width += minx;
break;
case S_LEFT:
width -= minx;
default:
x += minx;
}
if (abs(miny) <= snap)
switch (mode & (S_TOP | S_BOTTOM)) {
case S_BOTTOM:
height += miny;
break;
case S_TOP:
height -= miny;
default:
y += miny;
}
}
void Client::RecalcSweep(int mode) {
int basex, basey,dx,dy;
if (size->flags & PResizeInc) {
basex = (size->flags & PBaseSize) ? size->base_width :
(size->flags & PMinSize) ? size->min_width : 0;
basey = (size->flags & PBaseSize) ? size->base_height :
(size->flags & PMinSize) ? size->min_height : 0;
dx = ((width - basex) % size->width_inc);
dy = ((height - basey) % size->height_inc);
} else {
dx = 0; dy = 0;
}
if (size->flags & PMinSize) {
if (width - dx < size->min_width)
dx = width - size->min_width;
if (height - dy < size->min_height)
dy = height - size->min_height;
} else {
if (width - dx < 10)
dx = width - 10;
if (height - dy < 10)
dy = height - 10;
}
if (size->flags & PMaxSize) {
if (width > size->max_width)
dx = width - size->max_width;
if (height > size->max_height)
dy = height - size->max_height;
}
width -= dx;
height -= dy;
if (mode & S_LEFT)
x += dx;
if (mode & S_TOP)
y += dy;
}
#ifdef SHAPE
void Client::MakeHole(bool add) {
if (!parent)
return;
XEvent ev;
int x1=0,x2=0,y1=0,y2=0;
if (!grabp(root, MouseMask, Sc->cursors[CU_RESIZE]))
return;
for (int up = 1;up;) {
XMaskEvent(dpy, MouseMask | KeyPressMask, &ev);
switch (ev.type) {
case MotionNotify:
break;
case ButtonPress:
x1 = ev.xbutton.x_root;
y1 = ev.xbutton.y_root;
break;
case ButtonRelease:
x2 = ev.xbutton.x_root;
y2 = ev.xbutton.y_root;
--up;
break;
}
}
XUngrabPointer(dpy, CurrentTime);
if (x1 > x2) {
int x = x1;
x1 = x2;
x2 = x;
}
if (y1 > y2) {
int y = y1;
y1 = y2;
y2 = y;
}
if (x1<0 || x2<0)
return;
x1 -= x_root;
if (x1<0)
x1 = 0;
x2 -= x_root;
if (x2>width)
x2 = width;
y1 -= y_root;
if (y1<0)
y1 = 0;
y2 -= y_root;
if (y2>height)
y2 = height;
XRectangle temp;
temp.x = x1;
temp.y = y1;
temp.width = x2 - x1;
temp.height = y2 - y1;
XShapeCombineRectangles(dpy, frame, ShapeBounding,
0, THeight, &temp, 1, add ? ShapeSubtract : ShapeUnion, YXBanded);
if (add)
flags |= CF_HASBEENSHAPED;
}
void Client::SetShape() {
if (!parent)
return;
int n, order;
XRectangle temp, *dummy;
dummy = XShapeGetRectangles(dpy, window, ShapeBounding, &n, &order);
if (n > 1) {
XShapeCombineShape(dpy, frame, ShapeBounding,
0, THeight, window, ShapeBounding, ShapeSet);
temp.x = -Sc->BW;
temp.y = -Sc->BW;
temp.width = width + 2*Sc->BW;
temp.height = THeight + Sc->BW;
XShapeCombineRectangles(dpy, frame, ShapeBounding,
0, 0, &temp, 1, ShapeUnion, YXBanded);
temp.x = 0;
temp.y = 0;
temp.width = width;
temp.height = THeight - Sc->BW;
XShapeCombineRectangles(dpy, frame, ShapeClip,
0, THeight, &temp, 1, ShapeUnion, YXBanded);
flags |= CF_HASBEENSHAPED;
} else if (flags & CF_HASBEENSHAPED) {
/* I can't find a 'remove all shaping' function... */
temp.x = -Sc->BW;
temp.y = -Sc->BW;
temp.width = width + 2*Sc->BW;
temp.height = height + THeight + 2*Sc->BW;
XShapeCombineRectangles(dpy, frame, ShapeBounding,
0, 0, &temp, 1, ShapeSet, YXBanded);
}
XFree(dummy);
}
#endif
#ifdef DEBUG
#define SHOW(name) \
case name: return #name;
const char *Client::ShowGrav() {
if (!size || !(size->flags & PWinGravity))
return "no grav (NW)";
switch (size->win_gravity) {
SHOW(UnmapGravity)
SHOW(NorthWestGravity)
SHOW(NorthGravity)
SHOW(NorthEastGravity)
SHOW(WestGravity)
SHOW(CenterGravity)
SHOW(EastGravity)
SHOW(SouthWestGravity)
SHOW(SouthGravity)
SHOW(SouthEastGravity)
SHOW(StaticGravity)
default: return "unknown grav";
}
}
const char *Client::ShowState() {
switch (GetWMState()) {
SHOW(WithdrawnState)
SHOW(NormalState)
SHOW(IconicState)
default: return "unknown state";
}
}
void Client::dump() {
err("%s\n\t%s, %s, mapped %d\n"
"\tframe %#lx, title %#lx, win %#lx, geom %dx%d+%d+%d",
name ? name : wmname, ShowState(), ShowGrav(), mapped,
frame,title, window, width, height, x, y);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1