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