/* 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: sscanf format string bug (was
* [^\\0], now is a[^\n]
*/
#include "uehandler.h"
#include "clienttree.h"
#include "icon.h"
#include "sceme.h"
#include "action.h"
#include "menu.h"
#include "menuinfo.h"
#include "textdialog.h"
#include <X11/keysym.h>
#include <ctype.h>
MenuItem OptionMenu[] = {
{"Shaded","shaded",0,0},
{"AutoShade","autoshade",0,0},
{"Fixed Size","fixedsize",0,0},
{"Don't Close","noclose",0,0},
{"Don't Minimize","nominimize",0,0},
{"Sticky","sticky",0,0},
{"Title","hastitle",0,0},
{"TaskBar Entry","hastbentry",0,0},
{"Grab Alt Clicks","grabaltclick",0,0},
{"Pass First Click","passfirstclick",0,0},
{"TitleBar Buttons","titlebarbuttons",0,0},
{"Snap","snap",0,0},
{"Always on Top","above",0,0},
{"Below","below",0,0},
{"Hide Mouse","hidemouse",0,0},
{"Raise On Click","raiseonclick",0,0},
{"Raise On Enter","raiseonenter",0,0},
{"Focus On Click","focusonclick",0,0},
{"Focus On Enter","focusonenter",0,0},
{"Buttons","Buttons",0,0},
{"TaskBar Buttons","taskbarbuttons",0,0},
{"Simultaneous Input","grabkeyboard",0,0},
{"Autoscroll","autoscroll",0,0},
{"Tile","tile",0,0},
{"Autoresize","autoresize",0,0}
};
const int ClientOptionMenuNum = 19;
const int DesktopOptionMenuNum = 25;
SubMenuInfo OptionMenuInfo = {(MenuItem *)OptionMenu, DesktopOptionMenuNum};
#define OMI &OptionMenuInfo
MenuItem ClientMenu[] = {
{"Menu","?",0,0,0},
{"Raise"," ",0,0,0},
{"Lower","x",0,0,0},
{"Create Desktop","D",0,0,0},
{"Iconify","I",0,0,0},
{"Shade","S",0,0,0},
{"(Un)Stick","s",0,0,0},
{"Maximize Full","M",0,0,0},
{"Maximize Half","m",0,0,0},
{"Maximize to Fullscreen","f",0,0,0},
{"Move","c",0,0,0},
{"Resize","C",0,0,0},
{"Move Up","a",0,0,0},
{"Move Down","A",0,0,0},
#ifdef SHAPE
{"Add Hole","o",0,0,0},
{"Delete Holes","O",0,0,0},
#endif
{"Destroy!","Q",0,0,0},
{"Close","q",0,0,0},
{"Set Option",":set ",0,0,OMI},
{"Unset Option",":unset ",0,0,OMI},
{"Toggle Option",":toggle ",0,0,OMI},
{"Remove Option",":remove ",0,0,OMI},
/* ----Desktop---- */
{"Toggle (only me)",":dtoggle ",0,0,OMI},
{"Remove (only me)",":dremove ",0,0,OMI},
{"Toggle (children)",":ctoggle ",0,0,OMI},
{"Remove (children)",":cremove ",0,0,OMI},
{"Show/Hide Icons","i",0,0,0},
{"Release Windows","e",0,0,0},
{"Rename","+=",0,0,0}
};
MenuItem IconMenu[] = {
{"Menu","?",0,0,0},
{"Execute"," ",0,0,0},
{"Rename","+=",0,0,0},
{"Remove","q",0,0,0}
};
const int IconMenuNum = 4;
#ifdef SHAPE
const int ClientMenuNum = 22;
const int DesktopMenuNum = 29;
#else
const int ClientMenuNum = 20;
const int DesktopMenuNum = 27;
#endif
const char *ModNames[] = {
"Shift", "Lock", "Control", "Alt",
"Mod1", "Mod2", "Mod3", "Mod4", "Mod5",
"Button1", "Button2", "Button3", "Button4", "Button5", "AnyButton",
0
};
unsigned int ModValues[] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask,
Button1, Button2, Button3, Button4, Button5, AnyButton
};
UEHandler *UEH;
void UEHandler::AddKey(KeySym key,unsigned int mod,char *cmd) {
KeySym code = XKeysymToKeycode(dpy,key);
ModList *ml = keys[code];
if (!cmd) {
ModList *prev = 0;
for (;ml;ml = ml->next) {
if (ml->mod == mod)
break;
prev = ml;
}
if (ml) {
if (prev)
prev->next = ml->next; else
keys[code] = ml->next;
delete ml;
XUngrabKey(dpy,code,mod,root);
}
return;
}
ModList *ml2 = ml;
for (;ml2;ml2 = ml2->next) {
if (ml2->mod == mod)
break;
}
if (!ml2) {
ml2 = new ModList;
ml2->next = ml;
keys[code] = ml2;
}
XGrabKey(dpy,code,mod,root,false,GrabModeAsync,GrabModeAsync);
ml2->mod = mod;
ml2->cmd = cmd;
}
UEHandler::UEHandler() : keys() {
UEH = this;
Current = 0;
CurrentIcon = 0;
MoveWin = 0;
Seltype = 0;
downtime = 0;
NewTarget = 0;
SelEntry = 0;
SelClient = 0;
SelDesktop= 0;
RefClient = 0;
menu = 0;
menuitems = 0;
preg = 0;
dialog = 0;
dreg = 0;
for (int i = 'z'-'a';i+1;--i)
reg[i] = 0;
/* Add Keys {{{*/
AddKey(XK_space, Mod1Mask, "+");
// AddKey(XK_Menu, 0, "+");
AddKey(XK_slash, Mod1Mask, "n");
AddKey(XK_slash, Mod1Mask | ShiftMask, "N");
AddKey(XK_space, Mod1Mask | ShiftMask,"x");
AddKey(XK_space, Mod1Mask | ControlMask, " ");
AddKey(XK_space, Mod1Mask | ControlMask | ShiftMask, "X");
AddKey(XK_F3, Mod1Mask, "S");
AddKey(XK_F3, Mod1Mask | ShiftMask, "I");
AddKey(XK_F4, Mod1Mask, "q");
AddKey(XK_F4, Mod1Mask | ShiftMask, "Q");
AddKey(XK_F5, Mod1Mask, "c");
AddKey(XK_F5, Mod1Mask | ShiftMask, "C");
AddKey(XK_F6, Mod1Mask, "m");
AddKey(XK_F6, Mod1Mask | ShiftMask, "M");
AddKey(XK_F7, Mod1Mask, "D");
AddKey(XK_F7, Mod1Mask | ShiftMask, "i");
AddKey(XK_F8, Mod1Mask, "\"a ");
AddKey(XK_F8, Mod1Mask | ShiftMask, "\"ad");
AddKey(XK_F9, Mod1Mask, "\"b ");
AddKey(XK_F9, Mod1Mask | ShiftMask, "\"bd");
AddKey(XK_F10, Mod1Mask, "\"c ");
AddKey(XK_F10, Mod1Mask | ShiftMask, "\"cd");
AddKey(XK_F11, Mod1Mask, "\"d ");
AddKey(XK_F11, Mod1Mask | ShiftMask, "\"dd");
AddKey(XK_F12, Mod1Mask, "\"e ");
AddKey(XK_F12, Mod1Mask | ShiftMask, "\"ed");
AddKey(XK_Tab, Mod1Mask, "t");
AddKey(XK_Tab, Mod1Mask | ShiftMask, "T");
AddKey(XK_Escape,Mod1Mask,"\x0d"); // ctrl-m
// AddKey(XK_BackSpace,Mod1Mask, "u");
AddKey(XK_Left, Mod1Mask, "h");
AddKey(XK_Right, Mod1Mask, "l");
AddKey(XK_Up, Mod1Mask, "k");
AddKey(XK_Down, Mod1Mask, "j");
AddKey(XK_Page_Up, Mod1Mask, "a");
AddKey(XK_Page_Down, Mod1Mask, "A");
AddKey(XK_Left, Mod1Mask | ShiftMask, "H");
AddKey(XK_Right, Mod1Mask | ShiftMask, "L");
AddKey(XK_Up, Mod1Mask | ShiftMask, "K");
AddKey(XK_Down, Mod1Mask | ShiftMask, "J");
AddKey(XK_Left, Mod1Mask | ControlMask, "\x08");
AddKey(XK_Right, Mod1Mask | ControlMask, "\x0c");
AddKey(XK_Up, Mod1Mask | ControlMask, "\x0b");
AddKey(XK_Down, Mod1Mask | ControlMask, "\x0a");
/*}}}*/
for (int i=HISTSIZE; i;)
TextDialog::hist[--i] = 0;
TextDialog::histpos = 0;
}
UEHandler::~UEHandler(){
XUngrabKey(dpy,AnyKey,AnyModifier,root);
for (KeyMapIter i = keys.begin();i != keys.end();++i) {
ModList *ml = i->second,*ml2;
while (ml) {
ml2 = ml->next;
delete ml;
ml = ml2;
}
}
}
void UEHandler::Press(XButtonEvent &e) {
motion = false;
SelIcon = 0;
Client *c = ct->FindClient(e.window, &SelIcon);
if (c)
SelDesktop = c->DesktopAbove();
if (c && c->parent && (e.window == c->window)) {
if ((e.state & Mod1Mask) && (c->flags & CF_GRABALTCLICK)) {
if (c->flags & CF_GRABBED)
XAllowEvents(dpy, AsyncPointer, CurrentTime);
if (e.state & ControlMask) {
c = c->parent;
SelDesktop = SelDesktop->parent;
}
} else {
if (c->flags & CF_GRABBED) {
if (c->flags & CF_PASSFIRSTCLICK)
XAllowEvents(dpy, ReplayPointer, CurrentTime);
else
XAllowEvents(dpy, AsyncPointer, CurrentTime);
}
if (c->flags & CF_FOCUSONCLICK)
c->GetFocus();
if (!ISDESKTOP(c)) {
if (c->flags & CF_RAISEONCLICK) {
c->Raise(R_PARENT|R_INDIRECT);
}
}
}
}
if (c && !((xdown-e.x_root)/2) && !((ydown-e.y_root)/2) && (e.time-downtime < c->Sc->DCTime)) {
if ((Seltype & T_MOUSEBUTTON) == e.button)
Seltype = T_DC; else
Seltype = T_SDC;
} else Seltype=0;
if (e.state & ShiftMask)
Seltype |= T_SHIFT;
SelClient = c;
if (c) {
xdown=e.x_root;
ydown=e.y_root;
downtime=e.time;
bool tmp=false;
SelEntry=NULL;
Seltype = e.button | c->GetRegionMask(e.x_root,e.y_root,SelEntry) | (Seltype & (T_DC | T_SDC | T_SHIFT));
if (SelEntry && SelClient)
SelClient->ReDrawTBEntry(SelEntry,false);
if ((Seltype & T_SDC) && SelClient) {
if (e.state & ShiftMask)
SelClient->UpdateFlags(FLAG_TOGGLE,CF_AUTOSHADE); else
SelClient->Shade();
}
if (!SelEntry)
SelEntry = SelClient;
switch (Seltype) {
case T_B1 | T_DC | T_F2:
// it's enough now
if (SelEntry)
SelEntry->SendWMDelete(DEL_FORCE);// just kill
break;
case T_B1 | T_BAR | T_DC:
c->Raise(0);
if (SelEntry)
SelEntry->RequestDesktop(e.state & Mod1Mask);
break;
case T_B2 | T_DC:
case T_B2 | T_BAR | T_DC:
{
Client *cc = SelEntry;
if (!cc)
cc = SelClient;
if (NewTarget) {
if (cc && cc!=NewTarget)
NewTarget->target = cc->DesktopAbove(); else
NewTarget->target=NULL;
ct->RootDesktop->GetFocus();
XDefineCursor(dpy, root,ct->RootDesktop->Sc->cursors[CU_STD]);
NewTarget = NULL;
} else {
if (cc) {
NewTarget = cc;
XDefineCursor(dpy, root,ct->RootDesktop->Sc->cursors[CU_NEWTARGET]);
}
}
}
break;
case T_B3 | T_DC:
case T_B3 | T_BAR | T_DC:
c->Lower(L_BOTTOM);
break;
case T_B1 | T_DC:
c->Raise(0);
c->RequestDesktop(e.state & Mod1Mask);
break;
case T_B3 | T_SHIFT:
tmp = true;
case T_B2 | T_SHIFT:
{
menuitems = new MenuItem[(ct->windows.size()+1)/3];
int i = 0;
ct->RootDesktop->GetWindowList(menuitems,i,tmp,xdown,ydown,0,(char *)(tmp ? "P" : "g"));
if (!i) {
delete menuitems;
menuitems = 0;
break;
}
menu = new Menu(0,menuitems,i,0,0,c->Sc,xdown,ydown,true);
menu->Init();
break;
}
case T_B1:
if (SelClient->parent)
break;
case T_B1 | T_SHIFT:
{
MenuInfo * mi = (MenuInfo *)rman->GetInfo(SE_MENUINFO,"");
if (!mi)
break;
menuitems = mi->menu;
menu = new Menu(0,menuitems,mi->n,0,0,c->Sc,xdown,ydown,true);
menu->Init();
}
}
}
if (SelIcon) {
SetCurrent(0,SelIcon);
xdown=e.x_root;
ydown=e.y_root;
downtime=e.time;
SelEntry=NULL;
Seltype=e.button | (Seltype & (T_DC | T_SDC));
}
}
void UEHandler::Release(XButtonEvent &e) {
if (MoveWin) {
XUnmapWindow(dpy,MoveWin);
XDestroyWindow(dpy,MoveWin);
}
if (menu && menu->Remove(0)) {
delete menu;
menu = 0;
}
if (dialog && e.window == dialog->window) {
int arg=0;
if (e.button == Button2) {
dialog->SetCursor(e.x);
dialog->Paste();
return;
}
if (e.button == Button1)
arg = e.x;
if (e.button == Button3)
arg = 32767;
dialog->SetCursor(arg);
return;
}
if (SelIcon && MoveWin) {
Desktop *d = ct->FindPointerClient()->DesktopAbove();
bool copy = (e.state & ControlMask);
if (copy || d != SelIcon->parent) {
Client *saveclient = SelIcon->cmd ? 0 : SelIcon->client;
Icon *i = new Icon(d,SelIcon,copy);
d->AddIcon(i);
i->Init();
if (!copy) {
SelIcon->parent->RemoveIcon(SelIcon);
if (saveclient)
saveclient->icon = i;
}
}
}
Client *c = SelClient;
if (c) {
if (SelEntry && (Seltype == (Button2 | T_BAR)) && MoveWin) {
Client *tb=0;
Desktop *d = ct->FindPointerClient()->DesktopAbove();
// Try to reparent SelEntry under d
if (d != SelEntry->parent) {
Desktop *dd;
for (dd=d;dd;dd=dd->parent)
if (dd==SelEntry)
break;
if (d && !dd) { //Don't try to reparent a desktop under itself
Desktop *oldparent = SelEntry->parent;
oldparent->GiveAway(SelEntry,true);
if (tb)
d->Take(SelEntry,SelEntry->x,SelEntry->y); else
d->Take(SelEntry,
e.x_root - xdown + SelEntry->x_root - d->x_root - SelEntry->Sc->BW,
e.y_root - ydown + SelEntry->y_root - d->y_root - SelEntry->Sc->BW);
/* movement */
/* absolute position */
oldparent->AutoClose();
}
}
}
Client *t=NULL;
if (RefClient && Seltype != (T_B2 | T_BAR) && Seltype != T_B2)
RefClient = 0;
if ((T_DC | T_SHIFT | Seltype) ==
(T_DC | T_SHIFT | e.button | c->GetRegionMask(e.x_root,e.y_root,t)))
if (SelEntry == (t ? t : SelClient)/* && !SelIcon*/)
switch (Seltype) {
case T_B1:
if (!motion)
c->Raise(0);
break;
case T_B1 | T_BAR | T_SHIFT:
c->UpdateFlags(FLAG_DSET,CF_ABOVE);
case Button1 | T_BAR:
{
if (motion)
break;
Client *i;
for (i=t;i->transfor;i=i->transfor);
i->Raise((t!=c && !t->MaxH && !t->MaxW) ? (R_MOVEMOUSE | R_GOTO) : R_GOTO);
t->GetFocus();
}
break;
case T_B2 | T_BAR | T_SHIFT:
c->UpdateFlags(FLAG_DREMOVE,CF_ABOVE|CF_BELOW);
break;
case T_B2:
case T_B2 | T_BAR:
if (RefClient) {
if (RefClient == SelEntry)
break;
RefClient->LowerBelow(SelEntry);
RefClient = 0;
} else {
RefClient = SelEntry;
}
break;
case T_B3:
if (!motion)
c->Lower(0);
break;
case T_B3 | T_BAR | T_SHIFT:
c->UpdateFlags(FLAG_DSET,CF_BELOW);
case Button3 | T_BAR:
{
if (motion)
break;
Client *i;
for (i=t;i->trans;i=i->trans);
i->Lower(0);
}
break;
case T_B1 | T_F1 | T_DC:
case T_B1 | T_F1:
t->Maximize((e.state & Mod1Mask) ? MAX_VERTFULL : MAX_FULL);
break;
case T_B2 | T_F1:
case T_B2 | T_F1 | T_DC:
t->Maximize((e.state & Mod1Mask) ? MAX_VERTHALF : MAX_HALF);
break;
case T_B3 | T_F1:
if (e.state & Mod1Mask)
t->Maximize(MAX_HORI); else {
int oldw = t->width;
t->MoveResize(S_BOTTOM|S_RIGHT,true,0,0);
setmouse(root, ((t==c) ? (t->width-oldw) : 0) + e.x_root, e.y_root);
}
break;
case Button1 | T_F2:
t->SendWMDelete((e.state & Mod1Mask) ? DEL_RELEASE : DEL_NORMAL);
break;
case Button2 | T_F2:
t->Hide();
if (!t->mapped)
SelClient = 0;
break;
case Button3 | T_F2:
if (t == c) {
c->MoveResize(0,false,e.x_root, e.y_root);
} else {
t->MoveResize(0,true,0,0);
setmouse(root, e.x_root, e.y_root);
}
break;
case Button1 | T_F2 | T_SHIFT:
c->UpdateFlags(FLAG_DTOGGLE,CF_NOCLOSE);
break;
case Button2 | T_F2 | T_SHIFT:
c->UpdateFlags(FLAG_DTOGGLE,CF_NOMINIMIZE);
break;
case Button3 | T_F2 | T_SHIFT:
c->UpdateFlags(FLAG_DTOGGLE,CF_FIXEDSIZE);
break;
}
}
if (SelIcon && !motion) {
switch (Seltype) {
case T_B1:
SelIcon->Execute();
break;
case T_B3:
SelIcon->parent->ShowIcons(true);
break;
}
}
MoveWin = 0;
c = SelClient;
SelClient = 0;
if (c && SelEntry)
c->ReDrawTBEntry(SelEntry,false);
}
void UEHandler::Motion(XMotionEvent &e) {
if (!motion)
if (((e.x_root-xdown)/2) || ((e.y_root-ydown)/2))
motion = true; else
return;
if (SelIcon && Seltype == T_B2) {
if (!MoveWin) {
XSetWindowAttributes pattr;
pattr.override_redirect = True;
pattr.background_pixel = SelIcon->parent->Sc->colors[C_IBG].pixel;
pattr.border_pixel = SelIcon->parent->Sc->colors[C_IBD].pixel;
pattr.event_mask = ButtonReleaseMask|ExposureMask;
pattr.save_under = true;
MoveWin = XCreateWindow(dpy, root, 0, 0, SelIcon->w, SelIcon->h,
1, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask|CWSaveUnder, &pattr);
XMapWindow(dpy,MoveWin);
XCopyArea(dpy,SelIcon->window,MoveWin,SelIcon->parent->Sc->icon_gc,
0,0,SelIcon->w,SelIcon->h,0,0);
}
XMoveWindow(dpy, MoveWin, e.x_root - SelIcon->w/2, e.y_root - SelIcon->h/2);
return;
}
if (SelIcon && (Seltype == T_B1)) {
SelIcon->x += e.x_root - xdown;
SelIcon->y += e.y_root - ydown;
int oldx=SelIcon->x;
int oldy=SelIcon->y;
SelIcon->Move();
xdown=e.x_root+SelIcon->x-oldx;
ydown=e.y_root+SelIcon->y-oldy;
return;
}
if (SelIcon && (Seltype == T_B3) && !menu) {
menu = new Menu(0, IconMenu, IconMenuNum, 0, 0, SelIcon->parent->Sc, xdown,ydown,true);
menu->Init();
//icon menu
return;
}
// new client menu?
if (SelClient /*Why???*/ && SelEntry && !menu && (Seltype == (T_B3 | T_BAR) || Seltype == T_B3)) {
int num;
if (ISDESKTOP(SelEntry)) {
OptionMenuInfo.num = DesktopOptionMenuNum;
num = DesktopMenuNum;
} else {
OptionMenuInfo.num = ClientOptionMenuNum;
num = ClientMenuNum;
}
menu = new Menu(0, ClientMenu, num, SelEntry, 0,SelClient->Sc, xdown,ydown,true);
menu->Init();
}
if (menu) {
menu->Mouse(e.x_root,e.y_root);
return;
}
if (SelClient && Seltype == T_B1)
if (SelClient->parent) {
int mode=0;
if (!(SelClient->flags & CF_FIXEDSIZE)) {
if (SelClient->width > RESIZEMIN) {
if (xdown-SelClient->x_root <= SelClient->width/RESIZERATIO &&
xdown < SelClient->x_root + RESIZEMAX)
mode |= S_LEFT;
if (xdown-SelClient->x_root >= (RESIZERATIO - 1)*SelClient->width/RESIZERATIO &&
xdown >= SelClient->width + SelClient->x_root - RESIZEMAX)
mode |= S_RIGHT;
}
if (SelClient->height > RESIZEMIN) {
if (ydown-SelClient->y_root <= SelClient->height/RESIZERATIO &&
ydown < SelClient->y_root + RESIZEMAX)
mode |= S_TOP;
if (ydown-SelClient->y_root >= (RESIZERATIO - 1)*SelClient->height/RESIZERATIO &&
ydown >= SelClient->height + SelClient->y_root - RESIZEMAX)
mode |= S_BOTTOM;
}
}
if (!mode)
goto move; // ugly
SelClient->MoveResize(mode,false,xdown,ydown);
}
if (SelDesktop && Seltype == T_B2) {
SelDesktop->Goto(3*(e.x_root - xdown),3*(e.y_root - ydown),GOTO_RELATIVE | GOTO_SNAP);
xdown = e.x_root;
ydown = e.y_root;
}
if (SelClient && SelClient->parent && Seltype == (T_BAR | T_B1)) {
move:
if (!man->IgnoreLeave) {
SelClient->x += e.x_root - xdown;
SelClient->y += e.y_root - ydown;
int oldx=SelClient->x;
int oldy=SelClient->y;
SelClient->Snap(0);
xdown=e.x_root+SelClient->x-oldx;
ydown=e.y_root+SelClient->y-oldy;
xdown -= SelClient->x; // make xdown/down relative to window
ydown -= SelClient->y;
man->IgnoreLeave = SelClient->parent->Leave(e.x_root,e.y_root,false);
oldx = SelClient->x;
oldy = SelClient->y;
SelClient->ChangePos(true);
xdown += oldx;//SelClient->x; // make xdown/ydown absolute
ydown += oldy;//SelClient->y;
}
}
Client *c=SelClient;
if (c && SelEntry && SelEntry->parent && Seltype == (T_BAR | T_B2)) {
int x1=c->x_root - 1 + (SelEntry==SelClient ? SelEntry->TBx : SelEntry->TBEx);
int y1=c->y_root - 1 - c->THeight;
if (!MoveWin) {
XSetWindowAttributes pattr;
pattr.override_redirect = True;
pattr.background_pixel = c->Sc->colors[C_TBG].pixel;
pattr.border_pixel = c->Sc->colors[C_BD].pixel;
pattr.event_mask = ButtonReleaseMask|ExposureMask;
pattr.save_under = true;
int w1 = SelEntry==SelClient ? SelEntry->TBy - SelEntry->TBx :
SelEntry->TBEy - SelEntry->TBEx;
int h1=c->THeight-1;
MoveWin = XCreateWindow(dpy, root, x1, y1 + c->THeight, w1, h1,
1, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask|CWSaveUnder, &pattr);
XMapWindow(dpy,MoveWin);
XCopyArea(dpy,c->title,MoveWin,*(c->Sc->title_gc),
SelEntry == SelClient ? SelEntry->TBx : SelEntry->TBEx,0,w1,h1,0,0);
}
XMoveWindow(dpy, MoveWin, x1 + e.x_root-xdown, y1 + e.y_root-ydown);
}
}
void UEHandler::Key(XKeyEvent &e) {
if (Current && !dialog && e.window == man->input &&
ISDESKTOP(Current) && (Current->flags & DF_GRABKEYBOARD)) {
Current->SendEvent((XAnyEvent &)e);
return;
}
if (e.type != KeyPress)
return;
if (menu) {
if (!menu->Key(e)) {
menu->Remove(0);
delete menu;
menu = 0;
}
return;
}
KeyMapIter iter = keys.find(e.keycode);
char *cmd = 0;
if (iter != keys.end()) {
for (ModList *ml=iter->second; ml; ml=ml->next) {
if (e.state == ml->mod) {
cmd = ml->cmd;
break;
}
}
}
if (cmd)
ExecCommand(0,0,cmd,true); else
if (dialog) {
char *line = dialog->Key(e);
if (line) {
delete dialog;
dialog = 0;
ExecCommand(0,0,line,true);
free(line);
return;
}
if (!(e.state & Mod1Mask))
return;
}
}
void UEHandler::SetCurrent(Client *c, Icon *i) {
Client *oldCurrent = Current;
Current = c;
if (oldCurrent)
oldCurrent->Clear();
XSetWindowAttributes attr;
if (CurrentIcon) {
attr.background_pixel = CurrentIcon->parent->Sc->colors[C_IBG].pixel;
XChangeWindowAttributes(dpy, CurrentIcon->window,CWBackPixel,&attr);
XClearWindow(dpy,CurrentIcon->window);
CurrentIcon->ReDraw();
}
CurrentIcon = i;
if (!i)
return;
attr.background_pixel = i->parent->Sc->colors[C_HIBG].pixel;
XChangeWindowAttributes(dpy, i->window,CWBackPixel,&attr);
XClearWindow(dpy,i->window);
i->ReDraw();
}
void UEHandler::ExecTextCommand(Client *ref,char *str, bool recursive) {
char command[20];
char arg1[20];
char arg2[256];
int k = sscanf(str,":%19s %19s %255[^\n]",command,arg1,arg2);
if (!k)
return;
lower(command);
lower(arg1);
if (ref) {
int act = rman->GetFlagAct(command);
if (act) {
if (FLAG_CMIN <= act && act <=FLAG_CMAX)
ref = ref->DesktopAbove();
unsigned long flag = rman->GetFlag(arg1);
ref->UpdateFlags(act,flag);
Desktop *d = ref->DesktopAbove();
if (ref != d && !recursive)
d->UpdateFlags(act,flag & DF_ALL);
}
}
if (!strcmp("bind",command)) {
KeySym ks = 0,ks2;
unsigned int mods = 0;
char *p = arg1;
char *p2;
while (*p) {
p2 = strchr(p, '+');
if (p2)
*p2='\0';
ks2 = XStringToKeysym(p);
if (ks2) {
ks = ks2;
} else {
for (int i=0; ModNames[i]; ++i) {
if (!strcasecmp(ModNames[i],p)) {
mods |= ModValues[i];
break;
}
}
}
if (!p2)
break;
p = p2 + 1;
}
if (ks)
AddKey(ks,mods,(k>2) ? strdup(arg2) : 0);
return;
}
if (!strcmp("exit",command) || !strcmp("quit",command)) {
man->alive = false;
return;
}
}
void UEHandler::ExecCommands(char *str) {
while (ExecCommand(0,0,str,false))
++str;
ExecCommand(0,0,str,true);
}
// This function simplifies much since it is used both to parse user's input
// and to execute internally commands
bool UEHandler::ExecCommand(Client *ref, Icon *i,char *str,bool ExecAll,bool recursive) {
if (!str)
return false;
if (!ref && !i) {
ref = Current;
i = CurrentIcon;
}
switch (str[0]) {
case '+':
if (dialog) {
if (str[1] == '=') {
dialog->SetLine(str + 1);
} else {
delete dialog;
dialog = 0;
}
} else {
dialog = new TextDialog((Current ? Current : CurrentIcon->parent)->Sc);
if (str[1]) {
dialog->SetLine(str + 1);
}
}
return true;
case '/':
if (!ExecAll)
return false;
{
if (preg)
regfree(preg);
preg = new regex_t;
regcomp(preg,str+1,REG_NOSUB|REG_ICASE|REG_EXTENDED);
}
case 'N':
case 'n':
if (!preg)
return true;
{
if (!ref)
ref = ct->Focus ? ct->Focus : ct->RootDesktop;
int dummy = 0;
Client *cc = (str[0]=='n') ? ref->Next(false) : ref->Prev(false);
bool first = true;
for (Client *c=cc;first || c!=cc; c=(str[0]=='n') ? c->Next(false) : c->Prev(false)) {
if (!regexec(preg,c->name ? c->name : c->wmname,0,0,dummy)) {
c->Raise(R_PARENT | R_INDIRECT | R_GOTO | R_MOVEMOUSE);
return true;
}
first = false;
}
return true;
}
case '!':
if (!ExecAll)
return false;
Fork(str+1);
return true;
case '"':
if (!ExecAll)
return false;
{
char r = tolower(str[1]);
if (!('a'<=r && r<='z') && r!='\'')
return true;
if (str[2] == 'd' || str[2] == 'y') {
reg[r-'a'] = (r=='\'') ? ct->RootDesktop : ref;
return true;
}
dreg = (r=='\'') ? ct->RootDesktop : reg[r-'a'];
ExecCommand(dreg,0,str+2,ExecAll);
return true;
}
case '@':
if (!ExecAll)
return false;
{
Action *ac = ((Action *)rman->GetInfo(SE_ACTION,str+1));
if (ac)
ac->Execute();
}
return true;
case '$':
if (!ExecAll)
return false;
{
lower(str);
MenuInfo *mi = ((MenuInfo *)rman->GetInfo(SE_MENUINFO,str+1));
if (mi) {
int x,y;
GetMousePosition(root, x, y);
menu = new Menu(0,mi->menu,mi->n,0,0,ct->RootDesktop->Sc,x,y,true);
menu->Init();
}
}
return true;
case ':':
if (!ExecAll)
return false;
ExecTextCommand(ref,str,recursive);
return true;
case 'R':
if (!ExecAll)
return false;
ref = ct->RootDesktop;
str[0]='r';
break;
#ifdef DEBUG
case '3':
ct->RootDesktop->Remove(R_RESTART);
ct->RootDesktop->Init();
return true;
#endif
};
if (ref)
switch (str[0]) {
case '?':
{
if (menu)
return true;
int num;
if (ISDESKTOP(ref)) {
OptionMenuInfo.num = DesktopOptionMenuNum;
num = DesktopMenuNum;
} else {
OptionMenuInfo.num = ClientOptionMenuNum;
num = ClientMenuNum;
}
GetMousePosition(root,xdown,ydown);
menu = new Menu(0, ClientMenu, num,
SelEntry,0,ref->Sc,xdown,ydown-3,true); //!!!
menu->Init();
return true;
}
case 'y':
case 'd':
dreg = ref;
return true;
case ' ':
ref->Raise(R_INDIRECT);
return true;
case 'P':
ref->Raise(R_INDIRECT | R_PARENT);
return true;
case 'g':
ref->Raise(R_INDIRECT | R_GOTO | R_MOVEMOUSE);
return true;
case 'S':
ref->Shade();
return true;
case 's':
ref->flags ^= CF_STICKY;
return true;
case 'f':
case 0x0d: // Ctrl-M
ref->Maximize(MAX_FULLSCREEN);
return true;
case 'm':
ref->Maximize(MAX_HALF);
return true;
case 'M':
ref->Maximize(MAX_FULL);
return true;
case 'h':
case 'H':
{
Client *c = ref->Prev(true);
if (!c)
return true;
if (ct->MaxWindow == ref)
c->Maximize(MAX_FULLSCREEN);
c->GetFocus();
if (str[0] == 'H')
c->Raise(R_GOTO); else
c->Scroll(true);
return true;
}
case 0x08: // Ctrl-H
ref->MoveLeft();
return true;
case 'l':
case 'L':
{
Client *c = ref->Next(true);
if (!c)
return true;
c->GetFocus();
if (ct->MaxWindow == ref)
c->Maximize(MAX_FULLSCREEN);
if (str[0] == 'L')
c->Raise(R_GOTO); else
c->Scroll(true);
return true;
}
case 0x0c: // Ctrl-L
ref->MoveRight();
return true;
case 'k':
case 'K':
{
Client *c = ref->parent;
if (!c)
return true;
if (ct->MaxWindow == ref)
c->Maximize(MAX_FULLSCREEN);
c->GetFocus();
if (str[0] == 'K')
c->Raise(R_GOTO); else
c->Scroll(true);
return true;
}
case 0x0b: // Ctrl-K
ref->MoveUp();
return true;
case 'j':
case 'J':
{
Desktop *d =ref->DesktopAbove();
Client *c = d->focus ? d->focus : d->firstchild;
if (!c)
return true;
if (ct->MaxWindow == ref)
c->Maximize(MAX_FULLSCREEN);
c->GetFocus();
if (str[0] == 'J')
c->Raise(R_GOTO); else
c->Scroll(true);
return true;
}
case 0xa: // Ctrl-J
ref->MoveDown();
return true;
case 'i':
ref->DesktopAbove()->ShowIcons(true);
return true;
case 'I':
ref->Hide();
return true;
case 'Q':
if (!ISDESKTOP(ref))
ref->SendWMDelete(DEL_FORCE);
return true;
case 'q':
ref->SendWMDelete(DEL_NORMAL);
return true;
case 'e':
case 0x11: // Ctrl-Q
ref->SendWMDelete(DEL_RELEASE);
return true;
case 'c':
ref->MoveResize(0,true,0,0);
return true;
case 'C':
ref->MoveResize(S_RIGHT | S_BOTTOM,true,0,0);
return true;
case 'D':
ref->RequestDesktop(false);
return true;
case 'E':
case 0x04: // Ctrl+D
ref->DesktopAbove()->RequestDesktop(true);
return true;
case 'x':
ref->Lower(0);
return true;
case 'X':
ref->Lower(L_PARENT);
return true;
case 0x18: // Ctrl-X
ref->Lower(L_BOTTOM);
return true;
case 'p':
{
Desktop *d = ref->DesktopAbove();
// Try to reparent dreg under d
if (!dreg || d == dreg->parent)
return true;
Desktop *dd;
for (dd=d;dd;dd=dd->parent)
if (dd==dreg)
break;
if (d && !dd) { //Don't try to reparent a desktop under itself
Desktop *oldparent = dreg->parent;
oldparent->GiveAway(dreg,true);
d->Take(dreg,dreg->x,dreg->y);
oldparent->AutoClose();
}
return true;
}
case '=':
if (!ExecAll)
return false;
{
Desktop *d = ref->DesktopAbove();
if (d->name)
free(d->name);
if (str[1])
d->name = strdup(str+1); else
d->name = 0;
// Surprisingly, this works
(d->focus ? d->focus : d)->UpdateName(false);
d->Clear();
}
return true;
case 'a':
ref->Stacking(true);
return true;
case 'A':
ref->Stacking(false);
return true;
case 't':
{
Client *c = ref->Focus();
c = c->Next(false);
if (c) {
c->Raise(R_MOVEMOUSE | R_GOTO | R_PARENT);
c->GetFocus();
if (ct->MaxWindow == ref)
c->Maximize(MAX_FULLSCREEN);
}
}
return true;
case 'T':
{
Client *c = ref->Focus();
c = c->Prev(false);
if (c) {
c->Raise(R_MOVEMOUSE | R_GOTO | R_PARENT);
c->GetFocus();
}
}
return true;
case 'R':
case 'r':
if (!ExecAll)
return false;
ExecCommand(ref,i,str+1,true);
{
Desktop *d = dynamic_cast<Desktop *>(ref);
if (!d)
return true;
for (Client *c=d->firstchild;c;c=c->next)
ExecCommand(c,i,str,true,true);
}
return true;
case 'o':
ref->MakeHole(true);
return true;
case 'O':
ref->MakeHole(false);
return true;
case '-':
ref = ref->DesktopAbove();
ref->UpdateFlags((ref->mask & DF_GRABKEYBOARD) ? FLAG_CREMOVE : FLAG_CUNSET,CF_FOCUSONENTER);
ref->flags ^= DF_GRABKEYBOARD;
ref->mask |= DF_GRABKEYBOARD;
ref->GetFocus();
return true;
#ifdef DEBUG
case '1':
{
Pixmap pm = XCreatePixmap(dpy,root,200,200,1);
XShapeCombineMask(dpy, ref->frame, ShapeBounding,
0, ref->THeight, pm, YXBanded);
ref->flags |= CF_HASBEENSHAPED;
XFreePixmap(dpy,pm);
}
return true;
case '2':
ref->Remove(R_RESTART);
ref->Init();
return true;
#endif
default:
return false;
}
if (i)
switch (str[0]) {
case '?':
GetMousePosition(root,xdown,ydown);
menu = new Menu(0, IconMenu, IconMenuNum, 0, 0, i->parent->Sc,xdown,ydown-3,true); //!!!
menu->Init();
return true;
case ' ':
i->Execute();
return true;
case 'h':
i->parent->NextIcon(i,NI_LEFT);
return true;
case 'l':
i->parent->NextIcon(i,NI_RIGHT);
return true;
case 'j':
i->parent->NextIcon(i,NI_DOWN);
return true;
case 'k':
i->parent->NextIcon(i,NI_UP);
return true;
case 't':
case 'T':
SetCurrent(ct->Focus ? ct->Focus : ct->RootDesktop,0);
Current->Raise(R_MOVEMOUSE | R_GOTO | R_PARENT);
return true;
case '=':
if (!ExecAll)
return false;
i->ChangeName(str+1);
return true;
case 'q':
if (i->client && !i->cmd)
i->client->Map(); else
i->parent->RemoveIcon(i);
return true;
case '+':
if (!ExecAll)
return false;
if (i->cmd)
free(i->cmd); else
if (i->client)
return true;
i->cmd = strdup(str+1);
return true;
}
return false;
}
void UEHandler::RemoveClientReferences(Client *c) {
if (NewTarget == c) {
XDefineCursor(dpy, root,ct->RootDesktop->Sc->cursors[CU_STD]);
NewTarget = NULL;
}
if (SelEntry == c)
SelEntry = 0;
if (SelClient == c)
SelClient = 0;
if (SelDesktop == c)
SelDesktop = 0;
if (Current == c)
Current = ct->RootDesktop;
if (RefClient == c)
RefClient = 0;
for (int i = 'z'-'a';i+1;--i)
if (c == reg[i])
reg[i] = 0;
}
syntax highlighted by Code2HTML, v. 0.9.1