1 /* treewm - an X11 window manager.
2  * Copyright (c) 2001-2003 Thomas J�ger <TheHunter2000 at web dot de>
3  * This code is released under the terms of the GNU GPL.
4  * See the included file LICENSE for details.
5  *
6  * changed<05.02.2003> by Rudolf Polzer: sscanf format string bug (was
7  *   [^\\0], now is a[^\n]
8  */
9 
10 #include "uehandler.h"
11 #include "clienttree.h"
12 #include "icon.h"
13 #include "sceme.h"
14 #include "action.h"
15 #include "menu.h"
16 #include "menuinfo.h"
17 #include "textdialog.h"
18 #include <X11/keysym.h>
19 #include <ctype.h>
20 
21 MenuItem OptionMenu[] = {
22   {"Shaded","shaded",0,0},
23   {"AutoShade","autoshade",0,0},
24   {"Fixed Size","fixedsize",0,0},
25   {"Don't Close","noclose",0,0},
26   {"Don't Minimize","nominimize",0,0},
27   {"Sticky","sticky",0,0},
28   {"Title","hastitle",0,0},
29   {"TaskBar Entry","hastbentry",0,0},
30   {"Grab Alt Clicks","grabaltclick",0,0},
31   {"Pass First Click","passfirstclick",0,0},
32   {"TitleBar Buttons","titlebarbuttons",0,0},
33   {"Snap","snap",0,0},
34   {"Always on Top","above",0,0},
35   {"Below","below",0,0},
36   {"Hide Mouse","hidemouse",0,0},
37   {"Raise On Click","raiseonclick",0,0},
38   {"Raise On Enter","raiseonenter",0,0},
39   {"Focus On Click","focusonclick",0,0},
40   {"Focus On Enter","focusonenter",0,0},
41   {"Buttons","Buttons",0,0},
42   {"TaskBar Buttons","taskbarbuttons",0,0},
43   {"Simultaneous Input","grabkeyboard",0,0},
44   {"Autoscroll","autoscroll",0,0},
45   {"Tile","tile",0,0},
46   {"Autoresize","autoresize",0,0}
47 };
48 
49 const int ClientOptionMenuNum = 19;
50 const int DesktopOptionMenuNum = 25;
51 
52 SubMenuInfo OptionMenuInfo = {(MenuItem *)OptionMenu, DesktopOptionMenuNum};
53 
54 #define OMI &OptionMenuInfo
55 
56 MenuItem ClientMenu[] = {
57   {"Menu","?",0,0,0},
58   {"Raise"," ",0,0,0},
59   {"Lower","x",0,0,0},
60   {"Create Desktop","D",0,0,0},
61   {"Iconify","I",0,0,0},
62   {"Shade","S",0,0,0},
63   {"(Un)Stick","s",0,0,0},
64   {"Maximize Full","M",0,0,0},
65   {"Maximize Half","m",0,0,0},
66   {"Maximize to Fullscreen","f",0,0,0},
67   {"Move","c",0,0,0},
68   {"Resize","C",0,0,0},
69   {"Move Up","a",0,0,0},
70   {"Move Down","A",0,0,0},
71 #ifdef SHAPE
72   {"Add Hole","o",0,0,0},
73   {"Delete Holes","O",0,0,0},
74 #endif
75   {"Destroy!","Q",0,0,0},
76   {"Close","q",0,0,0},
77   {"Set Option",":set ",0,0,OMI},
78   {"Unset Option",":unset ",0,0,OMI},
79   {"Toggle Option",":toggle ",0,0,OMI},
80   {"Remove Option",":remove ",0,0,OMI},
81 /* ----Desktop---- */
82   {"Toggle (only me)",":dtoggle ",0,0,OMI},
83   {"Remove (only me)",":dremove ",0,0,OMI},
84   {"Toggle (children)",":ctoggle ",0,0,OMI},
85   {"Remove (children)",":cremove ",0,0,OMI},
86   {"Show/Hide Icons","i",0,0,0},
87   {"Release Windows","e",0,0,0},
88   {"Rename","+=",0,0,0}
89 };
90 
91 MenuItem IconMenu[] = {
92   {"Menu","?",0,0,0},
93   {"Execute"," ",0,0,0},
94   {"Rename","+=",0,0,0},
95   {"Remove","q",0,0,0}
96 };
97 const int IconMenuNum = 4;
98 
99 #ifdef SHAPE
100 const int ClientMenuNum = 22;
101 const int DesktopMenuNum = 29;
102 #else
103 const int ClientMenuNum = 20;
104 const int DesktopMenuNum = 27;
105 #endif
106 
107 
108 const char *ModNames[] = {
109 	"Shift", "Lock", "Control", "Alt",
110 	"Mod1", "Mod2", "Mod3", "Mod4", "Mod5",
111 	"Button1", "Button2", "Button3", "Button4", "Button5", "AnyButton",
112 	0
113 };
114 
115 unsigned int ModValues[] = {
116 	ShiftMask, LockMask, ControlMask, Mod1Mask,
117 	Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask,
118 	Button1, Button2, Button3, Button4, Button5, AnyButton
119 };
120 
121 
122 UEHandler *UEH;
123 
124 
125 
AddKey(KeySym key,unsigned int mod,char * cmd)126 void UEHandler::AddKey(KeySym key,unsigned int mod,char *cmd) {
127   KeySym code = XKeysymToKeycode(dpy,key);
128   ModList *ml = keys[code];
129   if (!cmd) {
130     ModList *prev = 0;
131     for (;ml;ml = ml->next) {
132       if (ml->mod == mod)
133         break;
134       prev = ml;
135     }
136     if (ml) {
137       if (prev)
138         prev->next = ml->next; else
139         keys[code] = ml->next;
140       delete ml;
141       XUngrabKey(dpy,code,mod,root);
142     }
143     return;
144   }
145   ModList *ml2 = ml;
146   for (;ml2;ml2 = ml2->next) {
147     if (ml2->mod == mod)
148       break;
149   }
150   if (!ml2) {
151     ml2 = new ModList;
152     ml2->next = ml;
153     keys[code] = ml2;
154   }
155   XGrabKey(dpy,code,mod,root,false,GrabModeAsync,GrabModeAsync);
156   ml2->mod = mod;
157   ml2->cmd = cmd;
158 }
159 
160 
161 
UEHandler()162 UEHandler::UEHandler() : keys() {
163   UEH = this;
164   Current = 0;
165   CurrentIcon = 0;
166   MoveWin   = 0;
167   Seltype   = 0;
168   downtime  = 0;
169   NewTarget = 0;
170   SelEntry  = 0;
171   SelClient = 0;
172   SelDesktop= 0;
173   RefClient = 0;
174   menu      = 0;
175   menuitems = 0;
176   preg      = 0;
177   dialog    = 0;
178   dreg = 0;
179   for (int i = 'z'-'a';i+1;--i)
180     reg[i] = 0;
181   /* Add Keys {{{*/
182   AddKey(XK_space, Mod1Mask, "+");
183 //  AddKey(XK_Menu, 0, "+");
184 
185   AddKey(XK_slash, Mod1Mask, "n");
186   AddKey(XK_slash, Mod1Mask | ShiftMask, "N");
187   AddKey(XK_space, Mod1Mask | ShiftMask,"x");
188   AddKey(XK_space, Mod1Mask | ControlMask, " ");
189   AddKey(XK_space, Mod1Mask | ControlMask | ShiftMask, "X");
190   AddKey(XK_F3,    Mod1Mask, "S");
191   AddKey(XK_F3,    Mod1Mask | ShiftMask, "I");
192   AddKey(XK_F4,    Mod1Mask, "q");
193   AddKey(XK_F4,    Mod1Mask | ShiftMask, "Q");
194   AddKey(XK_F5,    Mod1Mask, "c");
195   AddKey(XK_F5,    Mod1Mask | ShiftMask, "C");
196   AddKey(XK_F6,    Mod1Mask, "m");
197   AddKey(XK_F6,    Mod1Mask | ShiftMask, "M");
198   AddKey(XK_F7,    Mod1Mask, "D");
199   AddKey(XK_F7,    Mod1Mask | ShiftMask, "i");
200   AddKey(XK_F8,    Mod1Mask, "\"a ");
201   AddKey(XK_F8,    Mod1Mask | ShiftMask, "\"ad");
202   AddKey(XK_F9,    Mod1Mask, "\"b ");
203   AddKey(XK_F9,    Mod1Mask | ShiftMask, "\"bd");
204   AddKey(XK_F10,   Mod1Mask, "\"c ");
205   AddKey(XK_F10,   Mod1Mask | ShiftMask, "\"cd");
206   AddKey(XK_F11,   Mod1Mask, "\"d ");
207   AddKey(XK_F11,   Mod1Mask | ShiftMask, "\"dd");
208   AddKey(XK_F12,   Mod1Mask, "\"e ");
209   AddKey(XK_F12,   Mod1Mask | ShiftMask, "\"ed");
210 
211   AddKey(XK_Tab,   Mod1Mask, "t");
212   AddKey(XK_Tab,   Mod1Mask | ShiftMask, "T");
213   AddKey(XK_Escape,Mod1Mask,"\x0d"); // ctrl-m
214 //  AddKey(XK_BackSpace,Mod1Mask, "u");
215 
216   AddKey(XK_Left,  Mod1Mask, "h");
217   AddKey(XK_Right, Mod1Mask, "l");
218   AddKey(XK_Up,    Mod1Mask, "k");
219   AddKey(XK_Down,  Mod1Mask, "j");
220   AddKey(XK_Page_Up,    Mod1Mask, "a");
221   AddKey(XK_Page_Down,  Mod1Mask, "A");
222   AddKey(XK_Left,  Mod1Mask | ShiftMask, "H");
223   AddKey(XK_Right, Mod1Mask | ShiftMask, "L");
224   AddKey(XK_Up,    Mod1Mask | ShiftMask, "K");
225   AddKey(XK_Down,  Mod1Mask | ShiftMask, "J");
226   AddKey(XK_Left,  Mod1Mask | ControlMask, "\x08");
227   AddKey(XK_Right, Mod1Mask | ControlMask, "\x0c");
228   AddKey(XK_Up,    Mod1Mask | ControlMask, "\x0b");
229   AddKey(XK_Down,  Mod1Mask | ControlMask, "\x0a");
230 /*}}}*/
231   for (int i=HISTSIZE; i;)
232     TextDialog::hist[--i] = 0;
233   TextDialog::histpos = 0;
234 
235 }
~UEHandler()236 UEHandler::~UEHandler(){
237   XUngrabKey(dpy,AnyKey,AnyModifier,root);
238   for (KeyMapIter i = keys.begin();i != keys.end();++i) {
239     ModList *ml = i->second,*ml2;
240     while (ml) {
241       ml2 = ml->next;
242       delete ml;
243       ml = ml2;
244     }
245   }
246 }
247 
248 
249 
Press(XButtonEvent & e)250 void UEHandler::Press(XButtonEvent &e) {
251   motion = false;
252   SelIcon = 0;
253   Client *c = ct->FindClient(e.window, &SelIcon);
254   if (c)
255     SelDesktop = c->DesktopAbove();
256   if (c && c->parent && (e.window == c->window)) {
257     if ((e.state & Mod1Mask) && (c->flags & CF_GRABALTCLICK)) {
258       if (c->flags & CF_GRABBED)
259         XAllowEvents(dpy, AsyncPointer, CurrentTime);
260       if (e.state & ControlMask) {
261         c = c->parent;
262         SelDesktop = SelDesktop->parent;
263       }
264     } else {
265       if (c->flags & CF_GRABBED) {
266         if (c->flags & CF_PASSFIRSTCLICK)
267           XAllowEvents(dpy, ReplayPointer, CurrentTime);
268         else
269           XAllowEvents(dpy, AsyncPointer, CurrentTime);
270       }
271       if (c->flags & CF_FOCUSONCLICK)
272         c->GetFocus();
273       if (!ISDESKTOP(c)) {
274         if (c->flags & CF_RAISEONCLICK) {
275           c->Raise(R_PARENT|R_INDIRECT);
276         }
277       }
278     }
279   }
280 
281   if (c && !((xdown-e.x_root)/2) && !((ydown-e.y_root)/2) && (e.time-downtime < c->Sc->DCTime)) {
282     if ((Seltype & T_MOUSEBUTTON) == e.button)
283       Seltype = T_DC; else
284       Seltype = T_SDC;
285   } else Seltype=0;
286   if (e.state & ShiftMask)
287     Seltype |= T_SHIFT;
288 
289 
290   SelClient = c;
291   if (c) {
292 
293     xdown=e.x_root;
294     ydown=e.y_root;
295     downtime=e.time;
296 
297     bool tmp=false;
298 
299     SelEntry=NULL;
300     Seltype = e.button | c->GetRegionMask(e.x_root,e.y_root,SelEntry) | (Seltype & (T_DC | T_SDC | T_SHIFT));
301     if (SelEntry && SelClient)
302       SelClient->ReDrawTBEntry(SelEntry,false);
303     if ((Seltype & T_SDC) && SelClient) {
304       if (e.state & ShiftMask)
305         SelClient->UpdateFlags(FLAG_TOGGLE,CF_AUTOSHADE); else
306         SelClient->Shade();
307     }
308     if (!SelEntry)
309       SelEntry = SelClient;
310     switch (Seltype) {
311       case T_B1 | T_DC | T_F2:
312         // it's enough now
313         if (SelEntry)
314           SelEntry->SendWMDelete(DEL_FORCE);// just kill
315         break;
316       case T_B1 | T_BAR | T_DC:
317         c->Raise(0);
318         if (SelEntry)
319           SelEntry->RequestDesktop(e.state & Mod1Mask);
320         break;
321       case T_B2 | T_DC:
322       case T_B2 | T_BAR | T_DC:
323       {
324         Client *cc = SelEntry;
325         if (!cc)
326           cc = SelClient;
327         if (NewTarget) {
328           if (cc && cc!=NewTarget)
329             NewTarget->target = cc->DesktopAbove(); else
330             NewTarget->target=NULL;
331           ct->RootDesktop->GetFocus();
332           XDefineCursor(dpy, root,ct->RootDesktop->Sc->cursors[CU_STD]);
333           NewTarget = NULL;
334         } else {
335           if (cc) {
336             NewTarget = cc;
337             XDefineCursor(dpy, root,ct->RootDesktop->Sc->cursors[CU_NEWTARGET]);
338           }
339         }
340       }
341       break;
342       case T_B3 | T_DC:
343       case T_B3 | T_BAR | T_DC:
344         c->Lower(L_BOTTOM);
345         break;
346       case T_B1 | T_DC:
347         c->Raise(0);
348         c->RequestDesktop(e.state & Mod1Mask);
349         break;
350       case T_B3 | T_SHIFT:
351         tmp = true;
352       case T_B2 | T_SHIFT:
353       {
354         menuitems = new MenuItem[(ct->windows.size()+1)/3];
355         int i = 0;
356         ct->RootDesktop->GetWindowList(menuitems,i,tmp,xdown,ydown,0,(char *)(tmp ? "P" : "g"));
357         if (!i) {
358           delete menuitems;
359           menuitems = 0;
360           break;
361         }
362         menu = new Menu(0,menuitems,i,0,0,c->Sc,xdown,ydown,true);
363         menu->Init();
364         break;
365       }
366       case T_B1:
367         if (SelClient->parent)
368           break;
369       case T_B1 | T_SHIFT:
370       {
371         MenuInfo * mi = (MenuInfo *)rman->GetInfo(SE_MENUINFO,"");
372         if (!mi)
373           break;
374         menuitems = mi->menu;
375         menu = new Menu(0,menuitems,mi->n,0,0,c->Sc,xdown,ydown,true);
376         menu->Init();
377       }
378     }
379   }
380   if (SelIcon) {
381     SetCurrent(0,SelIcon);
382     xdown=e.x_root;
383     ydown=e.y_root;
384     downtime=e.time;
385     SelEntry=NULL;
386     Seltype=e.button | (Seltype & (T_DC | T_SDC));
387   }
388 }
389 
390 
Release(XButtonEvent & e)391 void UEHandler::Release(XButtonEvent &e) {
392   if (MoveWin) {
393     XUnmapWindow(dpy,MoveWin);
394     XDestroyWindow(dpy,MoveWin);
395   }
396 
397   if (menu && menu->Remove(0)) {
398     delete menu;
399     menu = 0;
400   }
401 
402   if (dialog && e.window == dialog->window) {
403     int arg=0;
404     if (e.button == Button2) {
405       dialog->SetCursor(e.x);
406       dialog->Paste();
407       return;
408     }
409     if (e.button == Button1)
410       arg = e.x;
411     if (e.button == Button3)
412       arg = 32767;
413     dialog->SetCursor(arg);
414     return;
415   }
416 
417   if (SelIcon && MoveWin) {
418     Desktop *d = ct->FindPointerClient()->DesktopAbove();
419     bool copy = (e.state & ControlMask);
420     if (copy  || d != SelIcon->parent) {
421       Client *saveclient = SelIcon->cmd ? 0 : SelIcon->client;
422       Icon *i = new Icon(d,SelIcon,copy);
423       d->AddIcon(i);
424       i->Init();
425       if (!copy) {
426         SelIcon->parent->RemoveIcon(SelIcon);
427         if (saveclient)
428           saveclient->icon = i;
429       }
430     }
431 
432   }
433 
434   Client *c = SelClient;
435   if (c) {
436     if (SelEntry && (Seltype == (Button2 | T_BAR)) && MoveWin) {
437       Client *tb=0;
438       Desktop *d = ct->FindPointerClient()->DesktopAbove();
439 
440       // Try to reparent SelEntry under d
441       if (d != SelEntry->parent) {
442         Desktop *dd;
443         for (dd=d;dd;dd=dd->parent)
444           if (dd==SelEntry)
445             break;
446         if (d && !dd) { //Don't try to reparent a desktop under itself
447           Desktop *oldparent = SelEntry->parent;
448           oldparent->GiveAway(SelEntry,true);
449           if (tb)
450             d->Take(SelEntry,SelEntry->x,SelEntry->y); else
451             d->Take(SelEntry,
452               e.x_root - xdown + SelEntry->x_root  -  d->x_root - SelEntry->Sc->BW,
453               e.y_root - ydown + SelEntry->y_root  -  d->y_root - SelEntry->Sc->BW);
454               /*  movement  */
455               /*         absolute position        */
456             oldparent->AutoClose();
457         }
458       }
459     }
460     Client *t=NULL;
461     if (RefClient && Seltype != (T_B2 | T_BAR) && Seltype != T_B2)
462       RefClient = 0;
463     if ((T_DC | T_SHIFT | Seltype) ==
464         (T_DC | T_SHIFT | e.button | c->GetRegionMask(e.x_root,e.y_root,t)))
465       if (SelEntry == (t ? t : SelClient)/* && !SelIcon*/)
466         switch (Seltype) {
467           case T_B1:
468             if (!motion)
469               c->Raise(0);
470             break;
471           case T_B1 | T_BAR | T_SHIFT:
472             c->UpdateFlags(FLAG_DSET,CF_ABOVE);
473           case Button1 | T_BAR:
474             {
475               if (motion)
476                 break;
477               Client *i;
478               for (i=t;i->transfor;i=i->transfor);
479               i->Raise((t!=c && !t->MaxH && !t->MaxW) ? (R_MOVEMOUSE | R_GOTO) : R_GOTO);
480               t->GetFocus();
481             }
482             break;
483           case T_B2 | T_BAR | T_SHIFT:
484             c->UpdateFlags(FLAG_DREMOVE,CF_ABOVE|CF_BELOW);
485             break;
486           case T_B2:
487           case T_B2 | T_BAR:
488             if (RefClient) {
489               if (RefClient == SelEntry)
490                 break;
491               RefClient->LowerBelow(SelEntry);
492               RefClient = 0;
493 
494             } else {
495               RefClient = SelEntry;
496             }
497             break;
498           case T_B3:
499             if (!motion)
500               c->Lower(0);
501             break;
502           case T_B3 | T_BAR | T_SHIFT:
503             c->UpdateFlags(FLAG_DSET,CF_BELOW);
504           case Button3 | T_BAR:
505             {
506               if (motion)
507                 break;
508               Client *i;
509               for (i=t;i->trans;i=i->trans);
510               i->Lower(0);
511             }
512             break;
513           case T_B1 | T_F1 | T_DC:
514           case T_B1 | T_F1:
515             t->Maximize((e.state & Mod1Mask) ? MAX_VERTFULL : MAX_FULL);
516             break;
517           case T_B2 | T_F1:
518           case T_B2 | T_F1 | T_DC:
519             t->Maximize((e.state & Mod1Mask) ? MAX_VERTHALF : MAX_HALF);
520             break;
521           case T_B3 | T_F1:
522 	    if (e.state & Mod1Mask)
523 	      t->Maximize(MAX_HORI); else {
524               int oldw = t->width;
525               t->MoveResize(S_BOTTOM|S_RIGHT,true,0,0);
526               setmouse(root, ((t==c) ? (t->width-oldw) : 0) + e.x_root, e.y_root);
527             }
528             break;
529           case Button1 | T_F2:
530             t->SendWMDelete((e.state & Mod1Mask) ? DEL_RELEASE : DEL_NORMAL);
531             break;
532           case Button2 | T_F2:
533             t->Hide();
534             if (!t->mapped)
535               SelClient = 0;
536             break;
537           case Button3 | T_F2:
538             if (t == c) {
539               c->MoveResize(0,false,e.x_root, e.y_root);
540             } else {
541               t->MoveResize(0,true,0,0);
542               setmouse(root, e.x_root, e.y_root);
543             }
544             break;
545           case Button1 | T_F2 | T_SHIFT:
546             c->UpdateFlags(FLAG_DTOGGLE,CF_NOCLOSE);
547             break;
548           case Button2 | T_F2 | T_SHIFT:
549             c->UpdateFlags(FLAG_DTOGGLE,CF_NOMINIMIZE);
550             break;
551           case Button3 | T_F2 | T_SHIFT:
552             c->UpdateFlags(FLAG_DTOGGLE,CF_FIXEDSIZE);
553             break;
554 
555         }
556 
557   }
558 
559   if (SelIcon && !motion) {
560     switch (Seltype) {
561       case T_B1:
562         SelIcon->Execute();
563         break;
564       case T_B3:
565         SelIcon->parent->ShowIcons(true);
566         break;
567     }
568   }
569 
570   MoveWin = 0;
571   c = SelClient;
572   SelClient = 0;
573 
574   if (c && SelEntry)
575     c->ReDrawTBEntry(SelEntry,false);
576 
577 }
578 
579 
580 
Motion(XMotionEvent & e)581 void UEHandler::Motion(XMotionEvent &e) {
582   if (!motion)
583     if (((e.x_root-xdown)/2) || ((e.y_root-ydown)/2))
584       motion = true; else
585       return;
586 
587 
588   if (SelIcon && Seltype == T_B2) {
589     if (!MoveWin) {
590       XSetWindowAttributes pattr;
591       pattr.override_redirect = True;
592       pattr.background_pixel = SelIcon->parent->Sc->colors[C_IBG].pixel;
593       pattr.border_pixel = SelIcon->parent->Sc->colors[C_IBD].pixel;
594       pattr.event_mask = ButtonReleaseMask|ExposureMask;
595       pattr.save_under = true;
596       MoveWin = XCreateWindow(dpy, root, 0, 0, SelIcon->w, SelIcon->h,
597           1, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
598           CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask|CWSaveUnder, &pattr);
599 
600       XMapWindow(dpy,MoveWin);
601 
602       XCopyArea(dpy,SelIcon->window,MoveWin,SelIcon->parent->Sc->icon_gc,
603         0,0,SelIcon->w,SelIcon->h,0,0);
604 
605     }
606     XMoveWindow(dpy, MoveWin, e.x_root - SelIcon->w/2, e.y_root - SelIcon->h/2);
607     return;
608   }
609 
610   if (SelIcon && (Seltype == T_B1)) {
611     SelIcon->x += e.x_root - xdown;
612     SelIcon->y += e.y_root - ydown;
613     int oldx=SelIcon->x;
614     int oldy=SelIcon->y;
615     SelIcon->Move();
616     xdown=e.x_root+SelIcon->x-oldx;
617     ydown=e.y_root+SelIcon->y-oldy;
618     return;
619   }
620 
621   if (SelIcon && (Seltype == T_B3) && !menu) {
622     menu = new Menu(0, IconMenu, IconMenuNum, 0, 0, SelIcon->parent->Sc, xdown,ydown,true);
623     menu->Init();
624     //icon menu
625     return;
626   }
627 
628 // new client menu?
629   if (SelClient /*Why???*/ && SelEntry && !menu && (Seltype == (T_B3 | T_BAR) || Seltype == T_B3)) {
630     int num;
631     if (ISDESKTOP(SelEntry)) {
632       OptionMenuInfo.num = DesktopOptionMenuNum;
633       num = DesktopMenuNum;
634     } else {
635       OptionMenuInfo.num = ClientOptionMenuNum;
636       num = ClientMenuNum;
637     }
638     menu = new Menu(0, ClientMenu, num, SelEntry, 0,SelClient->Sc, xdown,ydown,true);
639     menu->Init();
640   }
641 
642   if (menu) {
643     menu->Mouse(e.x_root,e.y_root);
644     return;
645   }
646 
647   if (SelClient && Seltype == T_B1)
648     if (SelClient->parent) {
649       int mode=0;
650       if (!(SelClient->flags & CF_FIXEDSIZE)) {
651         if (SelClient->width > RESIZEMIN) {
652           if (xdown-SelClient->x_root <= SelClient->width/RESIZERATIO &&
653               xdown < SelClient->x_root + RESIZEMAX)
654             mode |= S_LEFT;
655           if (xdown-SelClient->x_root >= (RESIZERATIO - 1)*SelClient->width/RESIZERATIO &&
656               xdown >= SelClient->width + SelClient->x_root - RESIZEMAX)
657             mode |= S_RIGHT;
658         }
659         if (SelClient->height > RESIZEMIN) {
660           if (ydown-SelClient->y_root <= SelClient->height/RESIZERATIO &&
661               ydown < SelClient->y_root + RESIZEMAX)
662             mode |= S_TOP;
663           if (ydown-SelClient->y_root >= (RESIZERATIO - 1)*SelClient->height/RESIZERATIO &&
664               ydown >= SelClient->height + SelClient->y_root - RESIZEMAX)
665             mode |= S_BOTTOM;
666         }
667       }
668       if (!mode)
669         goto move; // ugly
670       SelClient->MoveResize(mode,false,xdown,ydown);
671     }
672 
673   if (SelDesktop && Seltype == T_B2) {
674     SelDesktop->Goto(3*(e.x_root - xdown),3*(e.y_root - ydown),GOTO_RELATIVE | GOTO_SNAP);
675     xdown = e.x_root;
676     ydown = e.y_root;
677   }
678 
679 
680   if (SelClient && SelClient->parent && Seltype == (T_BAR | T_B1)) {
681     move:
682     if (!man->IgnoreLeave) {
683       SelClient->x += e.x_root - xdown;
684       SelClient->y += e.y_root - ydown;
685       int oldx=SelClient->x;
686       int oldy=SelClient->y;
687       SelClient->Snap(0);
688       xdown=e.x_root+SelClient->x-oldx;
689       ydown=e.y_root+SelClient->y-oldy;
690       xdown -= SelClient->x; // make xdown/down relative to window
691       ydown -= SelClient->y;
692       man->IgnoreLeave = SelClient->parent->Leave(e.x_root,e.y_root,false);
693       oldx = SelClient->x;
694       oldy = SelClient->y;
695       SelClient->ChangePos(true);
696       xdown += oldx;//SelClient->x; // make xdown/ydown absolute
697       ydown += oldy;//SelClient->y;
698     }
699   }
700 
701 
702 
703   Client *c=SelClient;
704 
705   if (c && SelEntry && SelEntry->parent && Seltype == (T_BAR | T_B2)) {
706     int x1=c->x_root - 1 + (SelEntry==SelClient ? SelEntry->TBx : SelEntry->TBEx);
707     int y1=c->y_root - 1 - c->THeight;
708     if (!MoveWin) {
709       XSetWindowAttributes pattr;
710       pattr.override_redirect = True;
711       pattr.background_pixel = c->Sc->colors[C_TBG].pixel;
712       pattr.border_pixel = c->Sc->colors[C_BD].pixel;
713       pattr.event_mask = ButtonReleaseMask|ExposureMask;
714       pattr.save_under = true;
715       int w1 = SelEntry==SelClient ? SelEntry->TBy - SelEntry->TBx :
716                                      SelEntry->TBEy - SelEntry->TBEx;
717       int h1=c->THeight-1;
718       MoveWin = XCreateWindow(dpy, root, x1, y1 + c->THeight, w1, h1,
719           1, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
720           CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask|CWSaveUnder, &pattr);
721 
722       XMapWindow(dpy,MoveWin);
723 
724       XCopyArea(dpy,c->title,MoveWin,*(c->Sc->title_gc),
725         SelEntry == SelClient ? SelEntry->TBx : SelEntry->TBEx,0,w1,h1,0,0);
726 
727     }
728 
729     XMoveWindow(dpy, MoveWin, x1 + e.x_root-xdown, y1 + e.y_root-ydown);
730   }
731 }
732 
733 
Key(XKeyEvent & e)734 void UEHandler::Key(XKeyEvent &e) {
735   if (Current && !dialog && e.window == man->input &&
736       ISDESKTOP(Current) && (Current->flags & DF_GRABKEYBOARD)) {
737     Current->SendEvent((XAnyEvent &)e);
738     return;
739   }
740   if (e.type != KeyPress)
741     return;
742   if (menu) {
743     if (!menu->Key(e)) {
744       menu->Remove(0);
745       delete menu;
746       menu = 0;
747     }
748     return;
749   }
750 
751   KeyMapIter iter = keys.find(e.keycode);
752   char *cmd = 0;
753   if (iter != keys.end()) {
754     for (ModList *ml=iter->second; ml; ml=ml->next) {
755       if (e.state == ml->mod) {
756         cmd = ml->cmd;
757         break;
758       }
759     }
760   }
761 
762   if (cmd)
763     ExecCommand(0,0,cmd,true); else
764       if (dialog) {
765 	char *line = dialog->Key(e);
766 	if (line) {
767 	  delete dialog;
768 	  dialog = 0;
769 	  ExecCommand(0,0,line,true);
770 	  free(line);
771 	  return;
772 	}
773 	if (!(e.state & Mod1Mask))
774 	  return;
775       }
776 
777 }
778 
779 
SetCurrent(Client * c,Icon * i)780 void UEHandler::SetCurrent(Client *c, Icon *i) {
781   Client *oldCurrent = Current;
782   Current = c;
783   if (oldCurrent)
784     oldCurrent->Clear();
785   XSetWindowAttributes attr;
786   if (CurrentIcon) {
787     attr.background_pixel = CurrentIcon->parent->Sc->colors[C_IBG].pixel;
788     XChangeWindowAttributes(dpy, CurrentIcon->window,CWBackPixel,&attr);
789     XClearWindow(dpy,CurrentIcon->window);
790     CurrentIcon->ReDraw();
791   }
792   CurrentIcon = i;
793   if (!i)
794     return;
795   attr.background_pixel = i->parent->Sc->colors[C_HIBG].pixel;
796   XChangeWindowAttributes(dpy, i->window,CWBackPixel,&attr);
797   XClearWindow(dpy,i->window);
798   i->ReDraw();
799 }
800 
801 
ExecTextCommand(Client * ref,char * str,bool recursive)802 void UEHandler::ExecTextCommand(Client *ref,char *str, bool recursive) {
803   char command[20];
804   char arg1[20];
805   char arg2[256];
806   int k = sscanf(str,":%19s %19s %255[^\n]",command,arg1,arg2);
807   if (!k)
808     return;
809   lower(command);
810   lower(arg1);
811   if (ref) {
812     int act = rman->GetFlagAct(command);
813     if (act) {
814       if (FLAG_CMIN <= act && act <=FLAG_CMAX)
815         ref = ref->DesktopAbove();
816       unsigned long flag = rman->GetFlag(arg1);
817       ref->UpdateFlags(act,flag);
818       Desktop *d = ref->DesktopAbove();
819       if (ref != d && !recursive)
820         d->UpdateFlags(act,flag & DF_ALL);
821     }
822   }
823   if (!strcmp("bind",command)) {
824     KeySym ks = 0,ks2;
825     unsigned int mods = 0;
826     char *p = arg1;
827     char *p2;
828     while (*p) {
829       p2 = strchr(p, '+');
830       if (p2)
831         *p2='\0';
832       ks2 = XStringToKeysym(p);
833       if (ks2) {
834         ks = ks2;
835       } else {
836         for (int i=0; ModNames[i]; ++i) {
837           if (!strcasecmp(ModNames[i],p)) {
838             mods |= ModValues[i];
839             break;
840           }
841         }
842       }
843       if (!p2)
844         break;
845       p = p2 + 1;
846     }
847     if (ks)
848       AddKey(ks,mods,(k>2) ? strdup(arg2) : 0);
849 
850 
851     return;
852   }
853 
854   if (!strcmp("exit",command) || !strcmp("quit",command)) {
855     man->alive = false;
856     return;
857   }
858 
859 
860 }
861 
ExecCommands(char * str)862 void UEHandler::ExecCommands(char *str) {
863   while (ExecCommand(0,0,str,false))
864     ++str;
865   ExecCommand(0,0,str,true);
866 
867 }
868 
869 
870 
871 // This function simplifies much since it is used both to parse user's input
872 // and to execute internally commands
873 
ExecCommand(Client * ref,Icon * i,char * str,bool ExecAll,bool recursive)874 bool UEHandler::ExecCommand(Client *ref, Icon *i,char *str,bool ExecAll,bool recursive) {
875   if (!str)
876     return false;
877   if (!ref && !i) {
878     ref = Current;
879     i = CurrentIcon;
880   }
881   switch (str[0]) {
882     case '+':
883       if (dialog) {
884 	if (str[1] == '=') {
885 	  dialog->SetLine(str + 1);
886 	} else {
887 	  delete dialog;
888 	  dialog = 0;
889 	}
890       } else {
891 	dialog = new TextDialog((Current ? Current : CurrentIcon->parent)->Sc);
892 	if (str[1]) {
893 	  dialog->SetLine(str + 1);
894 	}
895       }
896       return true;
897     case '/':
898       if (!ExecAll)
899         return false;
900       {
901         if (preg)
902           regfree(preg);
903         preg = new regex_t;
904         regcomp(preg,str+1,REG_NOSUB|REG_ICASE|REG_EXTENDED);
905       }
906     case 'N':
907     case 'n':
908       if (!preg)
909         return true;
910       {
911         if (!ref)
912           ref = ct->Focus ? ct->Focus : ct->RootDesktop;
913         int dummy = 0;
914         Client *cc = (str[0]=='n') ? ref->Next(false) : ref->Prev(false);
915         bool first = true;
916         for (Client *c=cc;first || c!=cc; c=(str[0]=='n') ? c->Next(false) : c->Prev(false)) {
917           if (!regexec(preg,c->name ? c->name : c->wmname,0,0,dummy)) {
918             c->Raise(R_PARENT | R_INDIRECT | R_GOTO | R_MOVEMOUSE);
919             return true;
920           }
921           first = false;
922         }
923         return true;
924       }
925     case '!':
926       if (!ExecAll)
927         return false;
928       Fork(str+1);
929       return true;
930     case '"':
931       if (!ExecAll)
932         return false;
933       {
934         char r = tolower(str[1]);
935         if (!('a'<=r && r<='z') && r!='\'')
936           return true;
937         if (str[2] == 'd' || str[2] == 'y') {
938           reg[r-'a'] = (r=='\'') ? ct->RootDesktop : ref;
939           return true;
940         }
941         dreg = (r=='\'') ? ct->RootDesktop : reg[r-'a'];
942         ExecCommand(dreg,0,str+2,ExecAll);
943         return true;
944       }
945     case '@':
946       if (!ExecAll)
947         return false;
948       {
949         Action *ac = ((Action *)rman->GetInfo(SE_ACTION,str+1));
950         if (ac)
951           ac->Execute();
952       }
953       return true;
954     case '$':
955       if (!ExecAll)
956         return false;
957       {
958         lower(str);
959         MenuInfo *mi = ((MenuInfo *)rman->GetInfo(SE_MENUINFO,str+1));
960         if (mi) {
961           int x,y;
962           GetMousePosition(root, x, y);
963           menu = new Menu(0,mi->menu,mi->n,0,0,ct->RootDesktop->Sc,x,y,true);
964           menu->Init();
965         }
966       }
967       return true;
968     case ':':
969       if (!ExecAll)
970         return false;
971       ExecTextCommand(ref,str,recursive);
972       return true;
973     case 'R':
974       if (!ExecAll)
975         return false;
976       ref = ct->RootDesktop;
977       str[0]='r';
978       break;
979 #ifdef DEBUG
980     case '3':
981       ct->RootDesktop->Remove(R_RESTART);
982       ct->RootDesktop->Init();
983       return true;
984 #endif
985   };
986   if (ref)
987     switch (str[0]) {
988       case '?':
989 	{
990 	  if (menu)
991 	    return true;
992 	  int num;
993 	  if (ISDESKTOP(ref)) {
994 	    OptionMenuInfo.num = DesktopOptionMenuNum;
995 	    num = DesktopMenuNum;
996 	  } else {
997 	    OptionMenuInfo.num = ClientOptionMenuNum;
998 	    num = ClientMenuNum;
999 	  }
1000 	  GetMousePosition(root,xdown,ydown);
1001 	  menu = new Menu(0, ClientMenu, num,
1002 	      SelEntry,0,ref->Sc,xdown,ydown-3,true); //!!!
1003 	  menu->Init();
1004 	  return true;
1005 	}
1006       case 'y':
1007       case 'd':
1008         dreg = ref;
1009         return true;
1010       case ' ':
1011         ref->Raise(R_INDIRECT);
1012         return true;
1013       case 'P':
1014         ref->Raise(R_INDIRECT | R_PARENT);
1015         return true;
1016       case 'g':
1017         ref->Raise(R_INDIRECT | R_GOTO | R_MOVEMOUSE);
1018         return true;
1019       case 'S':
1020         ref->Shade();
1021         return true;
1022       case 's':
1023         ref->flags ^= CF_STICKY;
1024         return true;
1025       case 'f':
1026       case 0x0d: // Ctrl-M
1027         ref->Maximize(MAX_FULLSCREEN);
1028         return true;
1029       case 'm':
1030         ref->Maximize(MAX_HALF);
1031         return true;
1032       case 'M':
1033         ref->Maximize(MAX_FULL);
1034         return true;
1035       case 'h':
1036       case 'H':
1037         {
1038           Client *c = ref->Prev(true);
1039           if (!c)
1040             return true;
1041 	  if (ct->MaxWindow == ref)
1042 	    c->Maximize(MAX_FULLSCREEN);
1043           c->GetFocus();
1044           if (str[0] == 'H')
1045             c->Raise(R_GOTO); else
1046             c->Scroll(true);
1047           return true;
1048         }
1049       case 0x08: // Ctrl-H
1050         ref->MoveLeft();
1051         return true;
1052       case 'l':
1053       case 'L':
1054         {
1055           Client *c = ref->Next(true);
1056           if (!c)
1057             return true;
1058           c->GetFocus();
1059 	  if (ct->MaxWindow == ref)
1060 	    c->Maximize(MAX_FULLSCREEN);
1061           if (str[0] == 'L')
1062             c->Raise(R_GOTO); else
1063             c->Scroll(true);
1064           return true;
1065         }
1066       case 0x0c: // Ctrl-L
1067         ref->MoveRight();
1068         return true;
1069       case 'k':
1070       case 'K':
1071         {
1072           Client *c = ref->parent;
1073           if (!c)
1074             return true;
1075 	  if (ct->MaxWindow == ref)
1076 	    c->Maximize(MAX_FULLSCREEN);
1077           c->GetFocus();
1078           if (str[0] == 'K')
1079             c->Raise(R_GOTO); else
1080             c->Scroll(true);
1081           return true;
1082         }
1083       case 0x0b: // Ctrl-K
1084         ref->MoveUp();
1085         return true;
1086       case 'j':
1087       case 'J':
1088         {
1089           Desktop *d =ref->DesktopAbove();
1090           Client *c = d->focus ? d->focus : d->firstchild;
1091           if (!c)
1092             return true;
1093 	  if (ct->MaxWindow == ref)
1094 	    c->Maximize(MAX_FULLSCREEN);
1095           c->GetFocus();
1096           if (str[0] == 'J')
1097             c->Raise(R_GOTO); else
1098             c->Scroll(true);
1099           return true;
1100         }
1101       case 0xa: // Ctrl-J
1102         ref->MoveDown();
1103         return true;
1104       case 'i':
1105         ref->DesktopAbove()->ShowIcons(true);
1106         return true;
1107       case 'I':
1108         ref->Hide();
1109         return true;
1110       case 'Q':
1111         if (!ISDESKTOP(ref))
1112           ref->SendWMDelete(DEL_FORCE);
1113         return true;
1114       case 'q':
1115         ref->SendWMDelete(DEL_NORMAL);
1116         return true;
1117       case 'e':
1118       case 0x11: // Ctrl-Q
1119         ref->SendWMDelete(DEL_RELEASE);
1120         return true;
1121       case 'c':
1122         ref->MoveResize(0,true,0,0);
1123         return true;
1124       case 'C':
1125         ref->MoveResize(S_RIGHT | S_BOTTOM,true,0,0);
1126         return true;
1127       case 'D':
1128         ref->RequestDesktop(false);
1129         return true;
1130       case 'E':
1131       case 0x04: // Ctrl+D
1132         ref->DesktopAbove()->RequestDesktop(true);
1133         return true;
1134       case 'x':
1135         ref->Lower(0);
1136         return true;
1137       case 'X':
1138         ref->Lower(L_PARENT);
1139         return true;
1140       case 0x18: // Ctrl-X
1141         ref->Lower(L_BOTTOM);
1142         return true;
1143       case 'p':
1144         {
1145           Desktop *d = ref->DesktopAbove();
1146           // Try to reparent dreg under d
1147           if (!dreg || d == dreg->parent)
1148             return true;
1149           Desktop *dd;
1150           for (dd=d;dd;dd=dd->parent)
1151             if (dd==dreg)
1152               break;
1153           if (d && !dd) { //Don't try to reparent a desktop under itself
1154             Desktop *oldparent = dreg->parent;
1155             oldparent->GiveAway(dreg,true);
1156             d->Take(dreg,dreg->x,dreg->y);
1157             oldparent->AutoClose();
1158           }
1159           return true;
1160         }
1161       case '=':
1162         if (!ExecAll)
1163           return false;
1164         {
1165           Desktop *d = ref->DesktopAbove();
1166           if (d->name)
1167             free(d->name);
1168           if (str[1])
1169             d->name = strdup(str+1); else
1170             d->name = 0;
1171           // Surprisingly, this works
1172           (d->focus ? d->focus : d)->UpdateName(false);
1173           d->Clear();
1174 
1175         }
1176         return true;
1177       case 'a':
1178         ref->Stacking(true);
1179         return true;
1180       case 'A':
1181         ref->Stacking(false);
1182         return true;
1183       case 't':
1184         {
1185           Client *c = ref->Focus();
1186           c = c->Next(false);
1187           if (c) {
1188             c->Raise(R_MOVEMOUSE | R_GOTO | R_PARENT);
1189             c->GetFocus();
1190 	    if (ct->MaxWindow == ref)
1191 	      c->Maximize(MAX_FULLSCREEN);
1192           }
1193         }
1194         return true;
1195       case 'T':
1196         {
1197           Client *c = ref->Focus();
1198           c = c->Prev(false);
1199           if (c) {
1200             c->Raise(R_MOVEMOUSE | R_GOTO | R_PARENT);
1201             c->GetFocus();
1202           }
1203         }
1204         return true;
1205       case 'R':
1206       case 'r':
1207         if (!ExecAll)
1208           return false;
1209         ExecCommand(ref,i,str+1,true);
1210         {
1211           Desktop *d = dynamic_cast<Desktop *>(ref);
1212           if (!d)
1213             return true;
1214           for (Client *c=d->firstchild;c;c=c->next)
1215             ExecCommand(c,i,str,true,true);
1216         }
1217         return true;
1218       case 'o':
1219         ref->MakeHole(true);
1220         return true;
1221       case 'O':
1222         ref->MakeHole(false);
1223         return true;
1224       case '-':
1225         ref = ref->DesktopAbove();
1226         ref->UpdateFlags((ref->mask & DF_GRABKEYBOARD) ? FLAG_CREMOVE : FLAG_CUNSET,CF_FOCUSONENTER);
1227         ref->flags ^= DF_GRABKEYBOARD;
1228         ref->mask |=  DF_GRABKEYBOARD;
1229         ref->GetFocus();
1230         return true;
1231 #ifdef DEBUG
1232       case '1':
1233         {
1234           Pixmap pm = XCreatePixmap(dpy,root,200,200,1);
1235           XShapeCombineMask(dpy, ref->frame, ShapeBounding,
1236             0, ref->THeight, pm, YXBanded);
1237           ref->flags |= CF_HASBEENSHAPED;
1238           XFreePixmap(dpy,pm);
1239         }
1240         return true;
1241       case '2':
1242         ref->Remove(R_RESTART);
1243         ref->Init();
1244         return true;
1245 #endif
1246 
1247       default:
1248         return false;
1249     }
1250 
1251   if (i)
1252     switch (str[0]) {
1253       case '?':
1254 	  GetMousePosition(root,xdown,ydown);
1255 	  menu = new Menu(0, IconMenu, IconMenuNum, 0, 0, i->parent->Sc,xdown,ydown-3,true); //!!!
1256 	  menu->Init();
1257 	  return true;
1258       case ' ':
1259         i->Execute();
1260         return true;
1261       case 'h':
1262         i->parent->NextIcon(i,NI_LEFT);
1263         return true;
1264       case 'l':
1265         i->parent->NextIcon(i,NI_RIGHT);
1266         return true;
1267       case 'j':
1268         i->parent->NextIcon(i,NI_DOWN);
1269         return true;
1270       case 'k':
1271         i->parent->NextIcon(i,NI_UP);
1272         return true;
1273       case 't':
1274       case 'T':
1275         SetCurrent(ct->Focus ? ct->Focus : ct->RootDesktop,0);
1276         Current->Raise(R_MOVEMOUSE | R_GOTO | R_PARENT);
1277         return true;
1278       case '=':
1279         if (!ExecAll)
1280           return false;
1281         i->ChangeName(str+1);
1282         return true;
1283       case 'q':
1284         if (i->client && !i->cmd)
1285           i->client->Map(); else
1286           i->parent->RemoveIcon(i);
1287         return true;
1288       case '+':
1289         if (!ExecAll)
1290           return false;
1291         if (i->cmd)
1292           free(i->cmd); else
1293           if (i->client)
1294             return true;
1295           i->cmd = strdup(str+1);
1296         return true;
1297     }
1298 
1299   return false;
1300 }
1301 
RemoveClientReferences(Client * c)1302 void UEHandler::RemoveClientReferences(Client *c) {
1303   if (NewTarget == c) {
1304     XDefineCursor(dpy, root,ct->RootDesktop->Sc->cursors[CU_STD]);
1305     NewTarget = NULL;
1306   }
1307   if (SelEntry == c)
1308     SelEntry = 0;
1309   if (SelClient == c)
1310     SelClient = 0;
1311   if (SelDesktop == c)
1312     SelDesktop = 0;
1313   if (Current == c)
1314     Current = ct->RootDesktop;
1315   if (RefClient == c)
1316     RefClient = 0;
1317   for (int i = 'z'-'a';i+1;--i)
1318     if (c == reg[i])
1319       reg[i] = 0;
1320 }
1321