1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2013 Marko Macek
5  */
6 #include "config.h"
7 #include "ymenu.h"
8 #include "wmmgr.h"
9 #include "sysdep.h"
10 #include "wmtaskbar.h"
11 #include "wmwinlist.h"
12 #include "wmwinmenu.h"
13 #include "wmswitch.h"
14 #include "wmstatus.h"
15 #include "wmminiicon.h"
16 #include "wmcontainer.h"
17 #include "wmconfig.h"
18 #include "wmframe.h"
19 #include "wmdialog.h"
20 #include "wmsession.h"
21 #include "wmprog.h"
22 #include "wmdock.h"
23 #include "wmapp.h"
24 #include "prefs.h"
25 #include "yprefs.h"
26 #include "yxcontext.h"
27 #include "workspaces.h"
28 #include "ystring.h"
29 #include "intl.h"
30 #include "ywordexp.h"
31 
32 YContext<YFrameClient> clientContext("clientContext", false);
33 YContext<YFrameWindow> frameContext("framesContext", false);
34 
35 YAction layerActionSet[WinLayerCount] = {
36     actionLayerDesktop,
37     actionLayerOne,
38     actionLayerBelow,
39     actionLayerThree,
40     actionLayerNormal,
41     actionLayerFive,
42     actionLayerOnTop,
43     actionLayerSeven,
44     actionLayerDock,
45     actionLayerNine,
46     actionLayerAboveDock,
47     actionLayerEleven,
48     actionLayerMenu,
49     actionLayerThirteen,
50     actionLayerFullscreen,
51     actionLayerAboveAll,
52 };
53 
54 Workspaces workspaces;
55 WorkspacesCount workspaceCount;
56 WorkspacesNames workspaceNames;
57 WorkspacesActive workspaceActionActivate;
58 WorkspacesMoveTo workspaceActionMoveTo;
59 
YWindowManager(IApp * app,YActionListener * wmActionListener,YSMListener * smListener,YWindow * parent,Window win)60 YWindowManager::YWindowManager(
61     IApp *app,
62     YActionListener *wmActionListener,
63     YSMListener *smListener,
64     YWindow *parent,
65     Window win)
66     : YDesktop(parent, win),
67     app(app),
68     wmActionListener(wmActionListener),
69     smActionListener(smListener),
70     fLayout(_NET_WM_ORIENTATION_HORZ, OldMaxWorkspaces, 1, _NET_WM_TOPLEFT)
71 {
72     fWmState = WMState(-1);
73     fShowingDesktop = false;
74     fShuttingDown = false;
75     fActiveWindow = (Window) -1;
76     fFocusWin = nullptr;
77     lockFocusCount = 0;
78     fServerGrabCount = 0;
79     fCascadeX = 0;
80     fCascadeY = 0;
81     fIconColumn = 0;
82     fIconRow = 0;
83 
84     fColormapWindow = nullptr;
85     fActiveWorkspace = WinWorkspaceInvalid;
86     fLastWorkspace = WinWorkspaceInvalid;
87     fArrangeCount = 0;
88     fArrangeInfo = nullptr;
89     rootProxy = nullptr;
90     fWorkArea = nullptr;
91     fWorkAreaWorkspaceCount = 0;
92     fWorkAreaScreenCount = 0;
93     fWorkAreaLock = 0;
94     fWorkAreaUpdate = 0;
95     fRestackLock = 0;
96     fRestackUpdate = 0;
97     fFullscreenEnabled = true;
98     fCreatedUpdated = true;
99     fLayeredUpdated = true;
100     fDefaultKeyboard = 0;
101     fSwitchWindow = nullptr;
102     fDockApp = nullptr;
103 
104     manager = this;
105     desktop = this;
106 
107     setWmState(wmSTARTUP);
108     setStyle(wsManager);
109     setPointer(YWMApp::leftPointer);
110 #ifdef CONFIG_XRANDR
111     if (xrandr.supported) {
112         XRRSelectInput(xapp->display(), handle(),
113                        RRScreenChangeNotifyMask
114                        | RRCrtcChangeNotifyMask
115                        | RROutputChangeNotifyMask
116                        | RROutputPropertyNotifyMask
117                       );
118     }
119 #endif
120 
121     fTopWin = new YWindow();
122     fTopWin->setStyle(wsOverrideRedirect | wsInputOnly);
123     fTopWin->setGeometry(YRect(-1, -1, 1, 1));
124     fTopWin->setTitle("IceTopWin");
125     fTopWin->show();
126 
127     fBottom = new YWindow();
128     fBottom->setStyle(wsOverrideRedirect | wsInputOnly);
129     fBottom->setGeometry(YRect(-1, -1, 1, 1));
130     fBottom->setTitle("IceBottom");
131 
132     if (edgeHorzWorkspaceSwitching) {
133         edges += new EdgeSwitch(this, -1, false);
134         edges += new EdgeSwitch(this, +1, false);
135     }
136     if (edgeVertWorkspaceSwitching) {
137         edges += new EdgeSwitch(this, -1, true);
138         edges += new EdgeSwitch(this, +1, true);
139     }
140 
141     YWindow::setWindowFocus();
142 }
143 
~YWindowManager()144 YWindowManager::~YWindowManager() {
145     if (fWorkArea) {
146         delete [] fWorkArea[0];
147         delete [] fWorkArea;
148     }
149     delete fTopWin;
150     delete fBottom;
151     delete rootProxy;
152     delete fSwitchWindow;
153     delete fDockApp;
154 }
155 
setWmState(WMState newWmState)156 void YWindowManager::setWmState(WMState newWmState) {
157     if (fWmState != newWmState) {
158         MSG(("wmstate %d", newWmState));
159         fWmState = newWmState;
160     }
161 }
162 
grabServer()163 void YWindowManager::grabServer() {
164     if (0 == fServerGrabCount++)
165         XGrabServer(xapp->display());
166 }
167 
ungrabServer()168 void YWindowManager::ungrabServer() {
169     if (0 == --fServerGrabCount)
170         XUngrabServer(xapp->display());
171 }
172 
grabKeys()173 void YWindowManager::grabKeys() {
174     XUngrabKey(xapp->display(), AnyKey, AnyModifier, handle());
175 
176     ///if (taskBar && taskBar->addressBar())
177         GRAB_WMKEY(gKeySysAddressBar);
178     if (quickSwitch) {
179         GRAB_WMKEY(gKeySysSwitchNext);
180         GRAB_WMKEY(gKeySysSwitchLast);
181         GRAB_WMKEY(gKeySysSwitchClass);
182     }
183     GRAB_WMKEY(gKeySysWinNext);
184     GRAB_WMKEY(gKeySysWinPrev);
185     GRAB_WMKEY(gKeySysDialog);
186 
187     GRAB_WMKEY(gKeySysWorkspacePrev);
188     GRAB_WMKEY(gKeySysWorkspaceNext);
189     GRAB_WMKEY(gKeySysWorkspaceLast);
190 
191     GRAB_WMKEY(gKeySysWorkspacePrevTakeWin);
192     GRAB_WMKEY(gKeySysWorkspaceNextTakeWin);
193     GRAB_WMKEY(gKeySysWorkspaceLastTakeWin);
194 
195     GRAB_WMKEY(gKeySysWinMenu);
196     GRAB_WMKEY(gKeySysMenu);
197     GRAB_WMKEY(gKeySysWindowList);
198     GRAB_WMKEY(gKeySysWinListMenu);
199 
200     GRAB_WMKEY(gKeySysWorkspace1);
201     GRAB_WMKEY(gKeySysWorkspace2);
202     GRAB_WMKEY(gKeySysWorkspace3);
203     GRAB_WMKEY(gKeySysWorkspace4);
204     GRAB_WMKEY(gKeySysWorkspace5);
205     GRAB_WMKEY(gKeySysWorkspace6);
206     GRAB_WMKEY(gKeySysWorkspace7);
207     GRAB_WMKEY(gKeySysWorkspace8);
208     GRAB_WMKEY(gKeySysWorkspace9);
209     GRAB_WMKEY(gKeySysWorkspace10);
210     GRAB_WMKEY(gKeySysWorkspace11);
211     GRAB_WMKEY(gKeySysWorkspace12);
212 
213     GRAB_WMKEY(gKeySysWorkspace1TakeWin);
214     GRAB_WMKEY(gKeySysWorkspace2TakeWin);
215     GRAB_WMKEY(gKeySysWorkspace3TakeWin);
216     GRAB_WMKEY(gKeySysWorkspace4TakeWin);
217     GRAB_WMKEY(gKeySysWorkspace5TakeWin);
218     GRAB_WMKEY(gKeySysWorkspace6TakeWin);
219     GRAB_WMKEY(gKeySysWorkspace7TakeWin);
220     GRAB_WMKEY(gKeySysWorkspace8TakeWin);
221     GRAB_WMKEY(gKeySysWorkspace9TakeWin);
222     GRAB_WMKEY(gKeySysWorkspace10TakeWin);
223     GRAB_WMKEY(gKeySysWorkspace11TakeWin);
224     GRAB_WMKEY(gKeySysWorkspace12TakeWin);
225 
226     GRAB_WMKEY(gKeySysTileVertical);
227     GRAB_WMKEY(gKeySysTileHorizontal);
228     GRAB_WMKEY(gKeySysCascade);
229     GRAB_WMKEY(gKeySysArrange);
230     GRAB_WMKEY(gKeySysUndoArrange);
231 
232     if (minimizeToDesktop)
233     GRAB_WMKEY(gKeySysArrangeIcons);
234     GRAB_WMKEY(gKeySysMinimizeAll);
235     GRAB_WMKEY(gKeySysHideAll);
236 
237     GRAB_WMKEY(gKeySysShowDesktop);
238     if (taskBar || showTaskBar) {
239         GRAB_WMKEY(gKeySysCollapseTaskBar);
240         GRAB_WMKEY(gKeyTaskBarSwitchNext);
241         GRAB_WMKEY(gKeyTaskBarSwitchPrev);
242         GRAB_WMKEY(gKeyTaskBarMoveNext);
243         GRAB_WMKEY(gKeyTaskBarMovePrev);
244     }
245 
246     {
247         KProgramIterType k = keyProgs.iterator();
248         while (++k) {
249             grabVKey(k->key(), k->modifiers());
250         }
251     }
252     if (xapp->WinMask && win95keys) {
253         if (xapp->Win_L) {
254             KeyCode keycode = XKeysymToKeycode(xapp->display(), xapp->Win_L);
255             if (keycode != 0)
256                 XGrabKey(xapp->display(), keycode, AnyModifier, desktop->handle(), False,
257                          GrabModeAsync, GrabModeSync);
258         }
259         if (xapp->Win_R) {
260             KeyCode keycode = XKeysymToKeycode(xapp->display(), xapp->Win_R);
261             if (keycode != 0)
262                 XGrabKey(xapp->display(), keycode, AnyModifier, desktop->handle(), False,
263                          GrabModeAsync, GrabModeSync);
264         }
265     }
266 
267     if (useMouseWheel) {
268         grabButton(4, ControlMask | xapp->AltMask);
269         grabButton(5, ControlMask | xapp->AltMask);
270         if (xapp->WinMask) {
271             grabButton(4, xapp->WinMask);
272             grabButton(5, xapp->WinMask);
273         }
274     }
275 
276     for (YFrameWindow *ff = topLayer(); ff; ff = ff->nextLayer()) {
277         ff->grabKeys();
278     }
279 }
280 
YProxyWindow(YWindow * parent)281 YProxyWindow::YProxyWindow(YWindow *parent): YWindow(parent) {
282     setStyle(wsOverrideRedirect);
283     setTitle("IceRootProxy");
284 }
285 
~YProxyWindow()286 YProxyWindow::~YProxyWindow() {
287 }
288 
handleButton(const XButtonEvent &)289 void YProxyWindow::handleButton(const XButtonEvent &/*button*/) {
290 }
291 
setupRootProxy()292 void YWindowManager::setupRootProxy() {
293     if (grabRootWindow) {
294         rootProxy = new YProxyWindow(nullptr);
295     }
296 }
297 
handleTimer(YTimer * timer)298 bool YWindowManager::handleTimer(YTimer* timer) {
299     if (timer == fSwitchDownTimer) {
300         fSwitchDownTimer = null;
301         if (switchWindowVisible() == false) {
302             delete fSwitchWindow;
303             fSwitchWindow = nullptr;
304         }
305     }
306     if (timer == fLayoutTimer) {
307         fLayoutTimer = null;
308         if (taskBar) {
309             taskBar->keyboardUpdate(null);
310         }
311     }
312     return false;
313 }
314 
handlePopDown(YPopupWindow * popup)315 void YWindowManager::handlePopDown(YPopupWindow* popup) {
316     if (popup == fSwitchWindow) {
317         fSwitchDownTimer->setTimer(3000, this, true);
318     }
319 }
320 
getSwitchWindow()321 SwitchWindow* YWindowManager::getSwitchWindow() {
322     if (fSwitchWindow == nullptr && quickSwitch) {
323         fSwitchWindow = new SwitchWindow(desktop, nullptr, quickSwitchVertical);
324     }
325     return fSwitchWindow;
326 }
327 
switchWindowVisible() const328 bool YWindowManager::switchWindowVisible() const {
329     return fSwitchWindow && fSwitchWindow->visible();
330 }
331 
handleSwitchWorkspaceKey(const XKeyEvent & key,KeySym k,unsigned vm)332 bool YWindowManager::handleSwitchWorkspaceKey(const XKeyEvent& key,
333         KeySym k, unsigned vm)
334 {
335     if (IS_WMKEY(k, vm, gKeySysWorkspacePrev)) {
336         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
337         switchToPrevWorkspace(false);
338         return true;
339     }
340     else if (IS_WMKEY(k, vm, gKeySysWorkspaceNext)) {
341         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
342         switchToNextWorkspace(false);
343         return true;
344     }
345     else if (IS_WMKEY(k, vm, gKeySysWorkspaceLast)) {
346         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
347         switchToLastWorkspace(false);
348         return true;
349     }
350     else if (IS_WMKEY(k, vm, gKeySysWorkspace1)) {
351         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
352         switchToWorkspace(0, false);
353         return true;
354     }
355     else if (IS_WMKEY(k, vm, gKeySysWorkspace2)) {
356         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
357         switchToWorkspace(1, false);
358         return true;
359     }
360     else if (IS_WMKEY(k, vm, gKeySysWorkspace3)) {
361         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
362         switchToWorkspace(2, false);
363         return true;
364     }
365     else if (IS_WMKEY(k, vm, gKeySysWorkspace4)) {
366         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
367         switchToWorkspace(3, false);
368         return true;
369     }
370     else if (IS_WMKEY(k, vm, gKeySysWorkspace5)) {
371         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
372         switchToWorkspace(4, false);
373         return true;
374     }
375     else if (IS_WMKEY(k, vm, gKeySysWorkspace6)) {
376         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
377         switchToWorkspace(5, false);
378         return true;
379     }
380     else if (IS_WMKEY(k, vm, gKeySysWorkspace7)) {
381         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
382         switchToWorkspace(6, false);
383         return true;
384     }
385     else if (IS_WMKEY(k, vm, gKeySysWorkspace8)) {
386         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
387         switchToWorkspace(7, false);
388         return true;
389     }
390     else if (IS_WMKEY(k, vm, gKeySysWorkspace9)) {
391         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
392         switchToWorkspace(8, false);
393         return true;
394     }
395     else if (IS_WMKEY(k, vm, gKeySysWorkspace10)) {
396         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
397         switchToWorkspace(9, false);
398         return true;
399     }
400     else if (IS_WMKEY(k, vm, gKeySysWorkspace11)) {
401         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
402         switchToWorkspace(10, false);
403         return true;
404     }
405     else if (IS_WMKEY(k, vm, gKeySysWorkspace12)) {
406         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
407         switchToWorkspace(11, false);
408         return true;
409     }
410     return false;
411 }
412 
handleWMKey(const XKeyEvent & key,KeySym k,unsigned vm)413 bool YWindowManager::handleWMKey(const XKeyEvent &key, KeySym k, unsigned vm) {
414     YFrameWindow *frame = getFocus();
415 
416     KProgramIterType p = keyProgs.iterator();
417     while (++p) {
418         if (!p->isKey(k, vm)) continue;
419 
420         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
421         p->open(key.state);
422         return true;
423     }
424 
425     if (IS_WMKEY(k, vm, gKeySysSwitchNext)) {
426         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
427         if (getSwitchWindow())
428             getSwitchWindow()->begin(true, key.state);
429         return true;
430     }
431     else if (IS_WMKEY(k, vm, gKeySysSwitchLast)) {
432         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
433         if (getSwitchWindow())
434             getSwitchWindow()->begin(false, key.state);
435         return true;
436     }
437     else if (gKeySysSwitchClass.eq(k, vm)) {
438         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
439         if (getSwitchWindow()) {
440             char *prop = frame && frame->client()->adopted()
441                        ? frame->client()->classHint()->resource() : nullptr;
442             getSwitchWindow()->begin(true, key.state, prop);
443         }
444         return true;
445     }
446     else if (IS_WMKEY(k, vm, gKeySysWinNext)) {
447         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
448         if (frame) frame->wmNextWindow();
449         return true;
450     } else if (IS_WMKEY(k, vm, gKeySysWinPrev)) {
451         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
452         if (frame) frame->wmPrevWindow();
453         return true;
454     } else if (IS_WMKEY(k, vm, gKeySysWinMenu)) {
455         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
456         if (frame) frame->popupSystemMenu(this);
457         return true;
458     } else if (IS_WMKEY(k, vm, gKeySysDialog)) {
459         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
460         if (wmapp->getCtrlAltDelete()) {
461             wmapp->getCtrlAltDelete()->activate();
462         }
463         return true;
464     } else if (IS_WMKEY(k, vm, gKeySysWinListMenu)) {
465         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
466         popupWindowListMenu(this);
467         return true;
468     } else if (IS_WMKEY(k, vm, gKeySysMenu)) {
469         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
470         popupStartMenu(this);
471         return true;
472     } else if (IS_WMKEY(k, vm, gKeySysWindowList)) {
473         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
474         wmActionListener->actionPerformed(actionWindowList, 0);
475         return true;
476     }
477     else if (handleSwitchWorkspaceKey(key, k, vm)) {
478         return true;
479     }
480     else if (IS_WMKEY(k, vm, gKeySysWorkspacePrevTakeWin)) {
481         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
482         switchToPrevWorkspace(true);
483         return true;
484     } else if (IS_WMKEY(k, vm, gKeySysWorkspaceNextTakeWin)) {
485         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
486         switchToNextWorkspace(true);
487         return true;
488     } else if (IS_WMKEY(k, vm, gKeySysWorkspaceLastTakeWin)) {
489         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
490         switchToLastWorkspace(true);
491         return true;
492     } else if (IS_WMKEY(k, vm, gKeySysWorkspace1TakeWin)) {
493         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
494         switchToWorkspace(0, true);
495         return true;
496     } else if (IS_WMKEY(k, vm, gKeySysWorkspace2TakeWin)) {
497         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
498         switchToWorkspace(1, true);
499         return true;
500     } else if (IS_WMKEY(k, vm, gKeySysWorkspace3TakeWin)) {
501         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
502         switchToWorkspace(2, true);
503         return true;
504     } else if (IS_WMKEY(k, vm, gKeySysWorkspace4TakeWin)) {
505         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
506         switchToWorkspace(3, true);
507         return true;
508     } else if (IS_WMKEY(k, vm, gKeySysWorkspace5TakeWin)) {
509         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
510         switchToWorkspace(4, true);
511         return true;
512     } else if (IS_WMKEY(k, vm, gKeySysWorkspace6TakeWin)) {
513         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
514         switchToWorkspace(5, true);
515         return true;
516     } else if (IS_WMKEY(k, vm, gKeySysWorkspace7TakeWin)) {
517         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
518         switchToWorkspace(6, true);
519         return true;
520     } else if (IS_WMKEY(k, vm, gKeySysWorkspace8TakeWin)) {
521         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
522         switchToWorkspace(7, true);
523         return true;
524     } else if (IS_WMKEY(k, vm, gKeySysWorkspace9TakeWin)) {
525         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
526         switchToWorkspace(8, true);
527         return true;
528     } else if (IS_WMKEY(k, vm, gKeySysWorkspace10TakeWin)) {
529         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
530         switchToWorkspace(9, true);
531         return true;
532     } else if (IS_WMKEY(k, vm, gKeySysWorkspace11TakeWin)) {
533         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
534         switchToWorkspace(10, true);
535         return true;
536     } else if (IS_WMKEY(k, vm, gKeySysWorkspace12TakeWin)) {
537         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
538         switchToWorkspace(11, true);
539         return true;
540     } else if (IS_WMKEY(k, vm, gKeySysTileVertical)) {
541         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
542         wmActionListener->actionPerformed(actionTileVertical, 0);
543         return true;
544     } else if (IS_WMKEY(k, vm, gKeySysTileHorizontal)) {
545         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
546         wmActionListener->actionPerformed(actionTileHorizontal, 0);
547         return true;
548     } else if (IS_WMKEY(k, vm, gKeySysCascade)) {
549         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
550         wmActionListener->actionPerformed(actionCascade, 0);
551         return true;
552     } else if (IS_WMKEY(k, vm, gKeySysArrange)) {
553         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
554         wmActionListener->actionPerformed(actionArrange, 0);
555         return true;
556     } else if (IS_WMKEY(k, vm, gKeySysUndoArrange)) {
557         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
558         wmActionListener->actionPerformed(actionUndoArrange, 0);
559         return true;
560     } else if (IS_WMKEY(k, vm, gKeySysArrangeIcons)) {
561         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
562         wmActionListener->actionPerformed(actionArrangeIcons, 0);
563         return true;
564     } else if (IS_WMKEY(k, vm, gKeySysMinimizeAll)) {
565         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
566         wmActionListener->actionPerformed(actionMinimizeAll, 0);
567         return true;
568     } else if (IS_WMKEY(k, vm, gKeySysHideAll)) {
569         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
570         wmActionListener->actionPerformed(actionHideAll, 0);
571         return true;
572     } else if (IS_WMKEY(k, vm, gKeySysAddressBar)) {
573         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
574         if (taskBar) {
575             taskBar->showAddressBar();
576             return true;
577         }
578     } else if (IS_WMKEY(k, vm, gKeySysShowDesktop)) {
579         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
580         wmActionListener->actionPerformed(actionShowDesktop, 0);
581         return true;
582     } else if (IS_WMKEY(k, vm, gKeySysCollapseTaskBar)) {
583         XAllowEvents(xapp->display(), AsyncKeyboard, key.time);
584         if (taskBar)
585             taskBar->handleCollapseButton();
586         return true;
587 
588     } else if (IS_WMKEY(k, vm, gKeyTaskBarSwitchPrev)) {
589         if (taskBar)
590             taskBar->switchToPrev();
591         return true;
592     } else if (IS_WMKEY(k, vm, gKeyTaskBarSwitchNext)) {
593         if (taskBar)
594             taskBar->switchToNext();
595         return true;
596     } else if (IS_WMKEY(k, vm, gKeyTaskBarMovePrev)) {
597         if (taskBar)
598             taskBar->movePrev();
599         return true;
600     } else if (IS_WMKEY(k, vm, gKeyTaskBarMoveNext)) {
601         if (taskBar)
602             taskBar->moveNext();
603         return true;
604     }
605     return false;
606 }
607 
handleKey(const XKeyEvent & key)608 bool YWindowManager::handleKey(const XKeyEvent &key) {
609     if (key.type == KeyPress) {
610         KeySym k = keyCodeToKeySym(key.keycode);
611         unsigned int m = KEY_MODMASK(key.state);
612         unsigned int vm = VMod(m);
613 
614         MSG(("down key: %lu, mod: %d", k, m));
615         bool handled = handleWMKey(key, k, vm);
616         if (xapp->WinMask && win95keys) {
617             if (handled) {
618             } else if (k == xapp->Win_L || k == xapp->Win_R) {
619                 /// !!! needs sync grab
620                 XAllowEvents(xapp->display(), SyncKeyboard, key.time);
621             } else { //if (m & xapp->WinMask) {
622                 /// !!! needs sync grab
623                 XAllowEvents(xapp->display(), ReplayKeyboard, key.time);
624             }
625         }
626         return handled;
627     } else if (key.type == KeyRelease) {
628         KeySym k = keyCodeToKeySym(key.keycode);
629         unsigned int m = KEY_MODMASK(key.state);
630 
631         (void)m;
632 #ifdef DEBUG
633         MSG(("up key: %lu, mod: %d", k, m));
634 #endif
635         if (xapp->WinMask && win95keys) {
636             if (k == xapp->Win_L || k == xapp->Win_R) {
637                 /// !!! needs sync grab
638                 XAllowEvents(xapp->display(), ReplayKeyboard, key.time);
639                 return true;
640             }
641         }
642         XAllowEvents(xapp->display(), SyncKeyboard, key.time);
643     }
644     return true;
645 }
646 
handleButton(const XButtonEvent & button)647 void YWindowManager::handleButton(const XButtonEvent &button) {
648     if (rootProxy && button.window == handle() &&
649         notbit(useRootButtons, 1 << (button.button - Button1)) &&
650         !hasbits(button.state, (ControlMask | xapp->AltMask)))
651     {
652         if (button.send_event == False)
653             XUngrabPointer(xapp->display(), CurrentTime);
654         XSendEvent(xapp->display(),
655                    rootProxy->handle(),
656                    False,
657                    SubstructureNotifyMask,
658                    (XEvent *) &button);
659         return ;
660     }
661 
662     if (button.type == ButtonPress) {
663         if (useMouseWheel && inrange(int(button.button), Button4, Button5) &&
664             (xapp->hasControlAlt(button.state) || xapp->hasWinMask(button.state)))
665         {
666             YFrameWindow* frame = getFocus();
667             if (frame) {
668                 if (button.button == Button4)
669                     frame->wmNextWindow();
670                 else if (button.button == Button5)
671                     frame->wmPrevWindow();
672             }
673         }
674         else if (button.button + 10 == rootMenuButton) {
675             if (rootMenu)
676                 rootMenu->popup(nullptr, nullptr, nullptr, button.x, button.y,
677                                 YPopupWindow::pfCanFlipVertical |
678                                 YPopupWindow::pfCanFlipHorizontal |
679                                 YPopupWindow::pfPopupMenu |
680                                 YPopupWindow::pfButtonDown);
681         }
682         else if (button.button + 10 == rootWinMenuButton) {
683             popupWindowListMenu(this, button.x, button.y);
684         }
685         else {
686             // allow buttons to trigger actions from "keys" for #333.
687             KeySym k = button.button - Button1 + XK_Pointer_Button1;
688             unsigned int m = KEY_MODMASK(button.state);
689             unsigned int vm = VMod(m);
690             KProgramIterType p = keyProgs.iterator();
691             while (++p) {
692                 if (p->isKey(k, vm)) {
693                     p->open(m);
694                     break;
695                 }
696             }
697         }
698     }
699     YWindow::handleButton(button);
700 }
701 
handleClick(const XButtonEvent & up,int count)702 void YWindowManager::handleClick(const XButtonEvent &up, int count) {
703     if (count == 1) {
704         if (up.button == rootMenuButton) {
705             if (rootMenu)
706                 rootMenu->popup(nullptr, nullptr, nullptr, up.x, up.y,
707                                 YPopupWindow::pfCanFlipVertical |
708                                 YPopupWindow::pfCanFlipHorizontal |
709                                 YPopupWindow::pfPopupMenu);
710         }
711         else if (up.button == rootWinMenuButton) {
712             popupWindowListMenu(this, up.x, up.y);
713         }
714         else if (up.button == rootWinListButton) {
715             windowList->showFocused(up.x_root, up.y_root);
716         }
717     }
718 }
719 
handleConfigure(const XConfigureEvent & configure)720 void YWindowManager::handleConfigure(const XConfigureEvent &configure) {
721     if (configure.window == handle()) {
722 #ifdef CONFIG_XRANDR
723         updateScreenSize((XEvent *)&configure);
724 #endif
725     }
726 }
727 
handleConfigureRequest(const XConfigureRequestEvent & configureRequest)728 void YWindowManager::handleConfigureRequest(const XConfigureRequestEvent &configureRequest) {
729     YFrameWindow *frame = findFrame(configureRequest.window);
730 
731     if (frame) {
732         MSG(("root configure request -- frame"));
733         frame->configureClient(configureRequest);
734     } else {
735         MSG(("root configure request -- client"));
736 
737         XWindowChanges xwc;
738         xwc.x = configureRequest.x;
739         xwc.y = configureRequest.y;
740         xwc.width = configureRequest.width;
741         xwc.height = configureRequest.height;
742         xwc.border_width = configureRequest.border_width;
743         xwc.stack_mode = configureRequest.detail;
744         xwc.sibling = configureRequest.above;
745         XConfigureWindow(xapp->display(), configureRequest.window,
746                          configureRequest.value_mask, &xwc);
747     }
748 }
749 
handleMapRequest(const XMapRequestEvent & mapRequest)750 void YWindowManager::handleMapRequest(const XMapRequestEvent &mapRequest) {
751     mapClient(mapRequest.window);
752 }
753 
handleUnmapNotify(const XUnmapEvent & unmap)754 void YWindowManager::handleUnmapNotify(const XUnmapEvent &unmap) {
755     if (unmap.send_event) {
756         YFrameWindow* frame = findFrame(unmap.window);
757         if (frame) {
758             if (frame->client()->visible()) {
759                 frame->actionPerformed(actionHide);
760             } else {
761                 frame->client()->handleUnmapNotify(unmap);
762             }
763         }
764     }
765 }
766 
handleClientMessage(const XClientMessageEvent & message)767 void YWindowManager::handleClientMessage(const XClientMessageEvent &message) {
768     if (message.message_type == _XA_NET_CURRENT_DESKTOP) {
769         MSG(("ClientMessage: _NET_CURRENT_DESKTOP => %ld", message.data.l[0]));
770         setWorkspace(message.data.l[0]);
771         return;
772     }
773     if (message.message_type == _XA_NET_NUMBER_OF_DESKTOPS) {
774         MSG(("ClientMessage: _NET_NUMBER_OF_DESKTOPS => %ld", message.data.l[0]));
775         long ws = message.data.l[0];
776         long now = workspaceCount;
777         if (inrange<long>(ws, now + 1, NewMaxWorkspaces)) {
778             appendNewWorkspaces(ws - now);
779         }
780         else if (inrange<long>(ws, 1, now - 1)) {
781             removeLastWorkspaces(now - ws);
782         }
783         return;
784     }
785     if (message.message_type == _XA_NET_SHOWING_DESKTOP) {
786         MSG(("ClientMessage: _NET_SHOWING_DESKTOP => %ld", message.data.l[0]));
787         if (message.data.l[0] == 0 && fShowingDesktop) {
788             undoArrange();
789             setShowingDesktop(false);
790         }
791         else if (message.data.l[0] == True && !fShowingDesktop) {
792             YArrange arrange = getWindowsToArrange(true, true);
793             if (arrange) {
794                 setWindows(arrange, actionMinimizeAll);
795                 arrange.discard();
796             }
797             setShowingDesktop(true);
798         }
799         return;
800     }
801     if (message.message_type == _XA_NET_REQUEST_FRAME_EXTENTS) {
802         Window win = message.window;
803         XWindowAttributes attributes;
804         if (!XGetWindowAttributes(xapp->display(), win, &attributes))
805             return;
806 
807         if (attributes.map_state != IsUnmapped)
808             return;
809 
810         if (attributes.override_redirect)
811             return;
812 
813         YFrameClient* client = new YFrameClient(desktop, nullptr);
814         client->setSize(attributes.width, attributes.height);
815         client->setPosition(attributes.x, attributes.y);
816         client->setBorder(attributes.border_width);
817         int count(0);
818         Atom* atoms = XListProperties(xapp->display(), win, &count);
819         for (int i = 0; i < count; ++i) {
820             Atom type(None);
821             int format(0);
822             unsigned long nitems(0);
823             unsigned long after(0);
824             unsigned char* prop(nullptr);
825             int p = XGetWindowProperty(xapp->display(), win, atoms[i],
826                                        0, 100*1000, False, AnyPropertyType,
827                                        &type, &format, &nitems,
828                                        &after, &prop);
829             if (p == Success && type && format && nitems) {
830                 XChangeProperty(xapp->display(), client->handle(),
831                                 atoms[i], type, format, PropModeReplace,
832                                 prop, nitems);
833             }
834             if (prop && p == Success)
835                 XFree(prop);
836         }
837         if (atoms)
838             XFree(atoms);
839 
840         YFrameWindow* frame = new YFrameWindow(wmActionListener);
841         bool activate = false;
842         bool focus = false;
843         frame->doManage(client, activate, focus);
844         long bx = frame->borderX();
845         long by = frame->borderY();
846         long ty = frame->titleY();
847         delete frame;
848 
849         long extents[] = { bx, bx, by + ty, by, };
850         XChangeProperty(xapp->display(), win,
851                         _XA_NET_FRAME_EXTENTS, XA_CARDINAL,
852                         32, PropModeReplace,
853                         (unsigned char *) extents, int ACOUNT(extents));
854 
855         return;
856     }
857     if (message.message_type == _XA_WM_PROTOCOLS &&
858         message.data.l[0] == (long) _XA_NET_WM_PING) {
859         YFrameWindow* frame = findFrame(message.data.l[2]);
860         if (frame) {
861             frame->client()->recvPing(message);
862         }
863         return;
864     }
865     if (message.message_type == _XA_ICEWM_ACTION) {
866         MSG(("ClientMessage: _ICEWM_ACTION => %ld", message.data.l[1]));
867         WMAction action = WMAction(message.data.l[1]);
868         switch (action) {
869         case ICEWM_ACTION_LOGOUT:
870         case ICEWM_ACTION_CANCEL_LOGOUT:
871         case ICEWM_ACTION_SHUTDOWN:
872         case ICEWM_ACTION_SUSPEND:
873         case ICEWM_ACTION_REBOOT:
874         case ICEWM_ACTION_RESTARTWM:
875         case ICEWM_ACTION_WINDOWLIST:
876         case ICEWM_ACTION_ABOUT:
877         case ICEWM_ACTION_WINOPTIONS:
878         case ICEWM_ACTION_RELOADKEYS:
879             smActionListener->handleSMAction(action);
880             break;
881         }
882     }
883 }
884 
handleFocus(const XFocusChangeEvent & focus)885 void YWindowManager::handleFocus(const XFocusChangeEvent &focus) {
886     if (focus.mode == NotifyNormal) {
887         if (focus.type == FocusIn) {
888             if (focus.detail == NotifyDetailNone) {
889                 if (clickFocus || !strongPointerFocus)
890                     focusLastWindow();
891             }
892         } else if (focus.type == FocusOut) {
893             if (focus.detail != NotifyInferior) {
894                 if (fFocusWin) {
895                     fFocusWin->client()->testDestroyed();
896                 }
897                 /* This causes more problems than it solves, as a change of
898                  * focus has already been processed for some other event.
899                  * This is likely to be an old event and no longer relevant.
900                  * Maybe consider only NotifyDetailNone or NotifyPointerRoot.
901                 switchFocusFrom(fFocusWin);
902                  * Need to test strongPointerFocus. */
903             }
904         }
905     }
906 }
907 
findWindow(const char * resource)908 Window YWindowManager::findWindow(const char *resource) {
909     if (isEmpty(resource))
910         return None;
911 
912     for (YFrameIter iter = focusedReverseIterator(); ++iter; ) {
913         YFrameClient* cli(iter->client());
914         if (cli && cli->adopted() && !cli->destroyed()) {
915             if (cli->classHint()->match(resource)) {
916                 return cli->handle();
917             }
918         }
919     }
920 
921     Window match = None;
922     xsmart<Window> clients;
923     unsigned count = 0;
924     if (xapp->children(handle(), &clients, &count)) {
925         for (unsigned i = 0; match == None && i < count; ++i) {
926             YWindow* ywin = windowContext.find(clients[i]);
927             if (nullptr == ywin) {
928                 match = matchWindow(clients[i], resource);
929             }
930         }
931     }
932 
933     return match;
934 }
935 
findWindow(Window win,char const * resource,int maxdepth)936 Window YWindowManager::findWindow(Window win, char const* resource,
937                                   int maxdepth) {
938     if (isEmpty(resource))
939         return None;
940 
941     Window match = None;
942     xsmart<Window> clients;
943     unsigned count = 0;
944     if (xapp->children(win, &clients, &count)) {
945         for (unsigned i = 0; match == None && i < count; ++i) {
946             if (matchWindow(clients[i], resource))
947                 match = clients[i];
948         }
949     }
950     if (maxdepth) {
951         for (unsigned i = 0; match == None && i < count; ++i) {
952             match = findWindow(clients[i], resource, maxdepth - 1);
953         }
954     }
955 
956     return match;
957 }
958 
matchWindow(Window win,char const * resource)959 bool YWindowManager::matchWindow(Window win, char const* resource) {
960     ClassHint hint;
961     return XGetClassHint(xapp->display(), handle(), &hint)
962         && hint.match(resource);
963 }
964 
findFrame(Window win)965 YFrameWindow *YWindowManager::findFrame(Window win) {
966     return frameContext.find(win);
967 }
968 
findClient(Window win)969 YFrameClient *YWindowManager::findClient(Window win) {
970     return clientContext.find(win);
971 }
972 
setFocus(YFrameWindow * f,bool canWarp,bool reorder)973 void YWindowManager::setFocus(YFrameWindow *f, bool canWarp, bool reorder) {
974     YFrameClient *c = f ? f->client() : nullptr;
975     Window w = None;
976 
977     if (focusLocked())
978         return;
979     MSG(("SET FOCUS f=%p", f));
980 
981     if (f == nullptr) {
982         switchFocusFrom(getFocus());
983     }
984 
985     if (f && f->visible()) {
986         if (c && c->visible() && f->isMapped())
987             w = c->handle();
988         else
989             w = f->handle();
990 
991         if (f->getInputFocusHint())
992             switchFocusTo(f, reorder);
993 
994         f->setWmUrgency(false);
995     }
996 #ifdef DEBUG
997     if (w == desktop->handle()) {
998         MSG(("%lX Focus 0x%lX desktop",
999              xapp->getEventTime("focus1"), w));
1000     } else if (f && w == f->handle()) {
1001         MSG(("%lX Focus 0x%lX frame %s",
1002              xapp->getEventTime("focus1"), w, f->getTitle().c_str()));
1003     } else if (f && c && w == c->handle()) {
1004         MSG(("%lX Focus 0x%lX client %s",
1005              xapp->getEventTime("focus1"), w, f->getTitle().c_str()));
1006     } else {
1007         MSG(("%lX Focus 0x%lX",
1008              xapp->getEventTime("focus1"), w));
1009     }
1010 #endif
1011 
1012     bool focusUnset(fFocusWin == nullptr);
1013     if (w != None) {
1014         if (f->getInputFocusHint()) {
1015             XSetInputFocus(xapp->display(), w, RevertToNone,
1016                            xapp->getEventTime("setFocus"));
1017             focusUnset = false;
1018         }
1019     }
1020     if (w == None || focusUnset) {
1021         XSetInputFocus(xapp->display(), fTopWin->handle(), RevertToNone,
1022                        xapp->getEventTime("setFocus"));
1023         notifyActive(nullptr);
1024     }
1025 
1026     if (c &&
1027         w == c->handle() &&
1028         (c->protocol(YFrameClient::wpTakeFocus) ||
1029          f->frameOption(YFrameWindow::foAppTakesFocus)))
1030     {
1031         c->sendTakeFocus();
1032     }
1033 
1034     if (!pointerColormap)
1035         setColormapWindow(f);
1036 
1037     /// !!! /* warp pointer sucks */
1038     if (f &&
1039         canWarp &&
1040         !clickFocus &&
1041         warpPointer &&
1042         isRunning())
1043     {
1044         XWarpPointer(xapp->display(), None, handle(), 0, 0, 0, 0,
1045                      f->x() + f->borderX(), f->y() + f->borderY() + f->titleY());
1046     }
1047 
1048     if (f) {
1049         WindowOption wo(f->getWindowOption());
1050         if (wo.keyboard != null) {
1051             setKeyboard(wo.keyboard);
1052         } else {
1053             setKeyboard(fDefaultKeyboard);
1054         }
1055     }
1056     else {
1057         setKeyboard(fDefaultKeyboard);
1058     }
1059 
1060     MSG(("SET FOCUS END"));
1061     updateFullscreenLayer();
1062 }
1063 
top(long layer) const1064 YFrameWindow *YWindowManager::top(long layer) const {
1065     PRECONDITION(inrange(layer, 0L, WinLayerCount - 1L));
1066     return fLayers[layer].front();
1067 }
1068 
setTop(long layer,YFrameWindow * top)1069 void YWindowManager::setTop(long layer, YFrameWindow *top) {
1070     if (true || !clientMouseActions) // some programs are buggy
1071         if (fLayers[layer]) {
1072             if (raiseOnClickClient)
1073                 fLayers[layer].front()->container()->grabButtons();
1074         }
1075     fLayers[layer].prepend(top);
1076     fLayeredUpdated = true;
1077     if (true || !clientMouseActions) // some programs are buggy
1078         if (fLayers[layer]) {
1079             if (raiseOnClickClient &&
1080                 !(focusOnClickClient && !fLayers[layer].front()->focused()))
1081                 fLayers[layer].front()->container()->releaseButtons();
1082         }
1083 }
1084 
bottom(long layer) const1085 YFrameWindow *YWindowManager::bottom(long layer) const {
1086     return fLayers[layer].back();
1087 }
1088 
setBottom(long layer,YFrameWindow * bottom)1089 void YWindowManager::setBottom(long layer, YFrameWindow *bottom) {
1090     fLayers[layer].append(bottom);
1091     fLayeredUpdated = true;
1092 }
1093 
installColormap(Colormap cmap)1094 void YWindowManager::installColormap(Colormap cmap) {
1095     if (xapp->hasColormap()) {
1096         //MSG(("installing colormap 0x%lX", cmap));
1097         if (xapp->grabWindow() == nullptr) {
1098             if (cmap == None) {
1099                 XInstallColormap(xapp->display(), xapp->colormap());
1100             } else {
1101                 XInstallColormap(xapp->display(), cmap);
1102             }
1103         }
1104     }
1105 }
1106 
setColormapWindow(YFrameWindow * frame)1107 void YWindowManager::setColormapWindow(YFrameWindow *frame) {
1108     if (fColormapWindow != frame) {
1109         fColormapWindow = frame;
1110 
1111         if (colormapWindow() && colormapWindow()->client())
1112             installColormap(colormapWindow()->client()->colormap());
1113         else
1114             installColormap(None);
1115     }
1116 }
1117 
manageClients()1118 void YWindowManager::manageClients() {
1119     YWindow sheet(this);
1120     sheet.lower();
1121     sheet.setGeometry(geometry());
1122     sheet.show();
1123     xapp->sync();
1124 
1125     setWmState(wmSTARTUP);
1126     lockWorkArea();
1127     lockRestack();
1128     grabServer();
1129     if (fDockApp == nullptr) {
1130         fDockApp = new DockApp;
1131     }
1132 
1133     unsigned count = 0;
1134     xsmart<Window> clients;
1135     if (xapp->children(handle(), &clients, &count)) {
1136         Window ignore = sheet.handle();
1137         for (unsigned i = 0; i < count; i++) {
1138             if (clients[i] != ignore && findClient(clients[i]) == nullptr) {
1139                 YFrameClient* client = allocateClient(clients[i], false);
1140                 if (client) {
1141                     manageClient(client);
1142                     if (client->getFrame() == nullptr) {
1143                         delete client;
1144                     }
1145                 }
1146             }
1147         }
1148     }
1149     sheet.hide();
1150 
1151     setWmState(wmRUNNING);
1152     ungrabServer();
1153     unlockRestack();
1154     unlockWorkArea();
1155 
1156     YProperty prop(this, _XA_NET_ACTIVE_WINDOW, F32, 1, XA_WINDOW);
1157     if (prop && prop[0]) {
1158         YFrameWindow* frame = findFrame(prop[0]);
1159         if (frame && frame->visible() && frame->visibleNow() &&
1160             frame->canFocus() && frame->client()->adopted())
1161         {
1162             setFocus(frame);
1163         }
1164     }
1165     if (getFocus() == nullptr) {
1166         focusTopWindow();
1167     }
1168     for (MiniIcon* icon : MiniIcon::fIcons) {
1169         if (icon->getFrame()->isIconic()) {
1170             icon->lower();
1171             icon->show();
1172         }
1173     }
1174 }
1175 
unmanageClients()1176 void YWindowManager::unmanageClients() {
1177     setWmState(wmSHUTDOWN);
1178     lockWorkArea();
1179     if (taskBar)
1180         taskBar->detachDesktopTray();
1181     setFocus(nullptr);
1182     grabServer();
1183 
1184     for (unsigned int l = 0; l < WinLayerCount; l++) {
1185         while (bottom(l)) {
1186             YFrameWindow* frame = bottom(l);
1187             YFrameClient* client = frame->client();
1188 
1189             frame->unmanage();
1190             delete frame;
1191 
1192             if (client->adopted()) {
1193                 client->show();
1194                 delete client;
1195             }
1196         }
1197     }
1198     delete fDockApp; fDockApp = nullptr;
1199 
1200     XSetInputFocus(xapp->display(), PointerRoot, RevertToNone, CurrentTime);
1201     notifyActive(nullptr);
1202     ungrabServer();
1203     XSync(xapp->display(), True);
1204     unlockWorkArea();
1205 }
1206 
addco(int * v,int & n,int c)1207 static int addco(int *v, int &n, int c) {
1208     int l, r, m;
1209 
1210     /* find */
1211     l = 0;
1212     r = n;
1213     while (l < r) {
1214         m = (l + r) / 2;
1215         if (v[m] == c)
1216             return 0;
1217         else if (v[m] > c)
1218             r = m;
1219         else
1220             l = m + 1;
1221     }
1222     /* insert if not found */
1223     memmove(v + l + 1, v + l, (n - l) * sizeof(int));
1224     v[l] = c;
1225     n++;
1226     return 1;
1227 }
1228 
calcCoverage(bool down,YFrameWindow * frame1,int x,int y,int w,int h)1229 int YWindowManager::calcCoverage(bool down, YFrameWindow *frame1, int x, int y, int w, int h) {
1230     int cover = 0;
1231     int factor = down ? 2 : 1; // try harder not to cover top windows
1232     const YRect rect(x, y, w, h);
1233 
1234     YFrameWindow *frame = nullptr;
1235 
1236     if (down) {
1237         frame = top(frame1->getActiveLayer());
1238     } else {
1239         frame = frame1;
1240     }
1241     for (YFrameWindow * f = frame; f ; f = (down ? f->next() : f->prev())) {
1242         if (f == frame1 || f->isMinimized() || f->isHidden() || !f->isManaged())
1243             continue;
1244 
1245         if (!f->isAllWorkspaces() && f->getWorkspace() != frame1->getWorkspace())
1246             continue;
1247 
1248         cover += rect.overlap(f->geometry()) * factor;
1249 
1250         if (factor > 1)
1251             factor /= 2;
1252     }
1253     //msg("coverage %d %d %d %d = %d", x, y, w, h, cover);
1254     return cover;
1255 }
1256 
tryCover(bool down,YFrameWindow * frame,int x,int y,int w,int h,int & px,int & py,int & cover,int mx,int my,int Mx,int My)1257 void YWindowManager::tryCover(bool down, YFrameWindow *frame,
1258                               int x, int y, int w, int h,
1259                               int& px, int& py, int& cover,
1260                               int mx, int my, int Mx, int My)
1261 {
1262     int ncover;
1263 
1264     if (x < mx)
1265         return ;
1266     if (y < my)
1267         return ;
1268     if (x + w > Mx)
1269         return ;
1270     if (y + h > My)
1271         return ;
1272 
1273     ncover = calcCoverage(down, frame, x, y, w, h);
1274     if (ncover < cover) {
1275         //msg("min: %d %d %d", ncover, x, y);
1276         px = x;
1277         py = y;
1278         cover = ncover;
1279     }
1280 }
1281 
getSmartPlace(bool down,YFrameWindow * frame1,int & x,int & y,int w,int h,int xiscreen)1282 bool YWindowManager::getSmartPlace(bool down, YFrameWindow *frame1, int &x, int &y, int w, int h, int xiscreen) {
1283     int mx, my, Mx, My;
1284     getWorkArea(frame1, &mx, &my, &Mx, &My, xiscreen);
1285 
1286     x = mx;
1287     y = my;
1288 
1289     int cover, px, py;
1290     int *xcoord, *ycoord;
1291     int xcount, ycount;
1292     int n = 0;
1293     YFrameWindow *frame = nullptr;
1294 
1295     if (down) {
1296         frame = top(frame1->getActiveLayer());
1297     } else {
1298         frame = frame1;
1299     }
1300     YFrameWindow *f = nullptr;
1301 
1302     for (f = frame; f; f = (down ? f->next() : f->prev()))
1303         n++;
1304     n = (n + 1) * 2;
1305     xcoord = new int[n];
1306     if (xcoord == nullptr)
1307         return false;
1308     ycoord = new int[n];
1309     if (ycoord == nullptr) {
1310         delete[] xcoord;
1311         return false;
1312     }
1313 
1314     xcount = ycount = 0;
1315     addco(xcoord, xcount, mx);
1316     addco(ycoord, ycount, my);
1317     for (f = frame; f; f = (down ? f->next() : f->prev())) {
1318         if (f == frame1 || f->isMinimized() || f->isHidden() || !f->isManaged() || f->isMaximized())
1319             continue;
1320 
1321         if (!f->isAllWorkspaces() && f->getWorkspace() != frame1->getWorkspace())
1322             continue;
1323 
1324         addco(xcoord, xcount, f->x());
1325         addco(xcoord, xcount, f->x() + f->width());
1326         addco(ycoord, ycount, f->y());
1327         addco(ycoord, ycount, f->y() + f->height());
1328     }
1329     addco(xcoord, xcount, Mx);
1330     addco(ycoord, ycount, My);
1331     assert(xcount <= n);
1332     assert(ycount <= n);
1333 
1334     int xn = 0, yn = 0;
1335     px = x; py = y;
1336     cover = calcCoverage(down, frame1, x, y, w, h);
1337     while (true) {
1338         x = xcoord[xn];
1339         y = ycoord[yn];
1340 
1341         tryCover(down, frame1, x - w, y - h, w, h, px, py, cover, mx, my, Mx, My);
1342         tryCover(down, frame1, x - w, y    , w, h, px, py, cover, mx, my, Mx, My);
1343         tryCover(down, frame1, x    , y - h, w, h, px, py, cover, mx, my, Mx, My);
1344         tryCover(down, frame1, x    , y    , w, h, px, py, cover, mx, my, Mx, My);
1345 
1346         if (cover == 0)
1347             break;
1348 
1349         xn++;
1350         if (xn >= xcount) {
1351             xn = 0;
1352             yn++;
1353             if (yn >= ycount)
1354                 break;
1355         }
1356     }
1357     x = px;
1358     y = py;
1359     delete [] xcoord;
1360     delete [] ycoord;
1361     return true;
1362 }
1363 
smartPlace(YArrange arrange)1364 void YWindowManager::smartPlace(YArrange arrange) {
1365     if (saveArrange(arrange) == false)
1366         return;
1367 
1368     int* screens = new int[arrange.size()];
1369     int k = 0;
1370     for (YFrameWindow* f : arrange) {
1371         screens[k++] = f->getScreen();
1372     }
1373 
1374     int n = getScreenCount();
1375     for (int s = 0; s < n; s++)
1376     {
1377         k = 0;
1378         for (YFrameWindow* f : arrange) {
1379             if (s == screens[k]) {
1380                 int x = f->x();
1381                 int y = f->y();
1382                 if (getSmartPlace(false, f, x, y, f->width(), f->height(), s)) {
1383                     f->setNormalPositionOuter(x, y);
1384                 }
1385             }
1386             k++;
1387         }
1388     }
1389 
1390     delete[] screens;
1391 }
1392 
getCascadePlace(YFrameWindow * frame,int & lastX,int & lastY,int & x,int & y,int w,int h)1393 void YWindowManager::getCascadePlace(YFrameWindow *frame, int &lastX, int &lastY, int &x, int &y, int w, int h) {
1394     int mx, my, Mx, My;
1395     getWorkArea(frame, &mx, &my, &Mx, &My);
1396 
1397     /// !!! make auto placement cleaner and (optionally) smarter
1398     if (lastX < mx) lastX = mx;
1399     if (lastY < my) lastY = my;
1400 
1401     x = lastX;
1402     y = lastY;
1403     y -= min(frame->borderYN(), int(topSideVerticalOffset));
1404 
1405     lastX += wsTitleBar;
1406     lastY += wsTitleBar;
1407     if (int(y + h) >= My) {
1408         y = my;
1409         lastY = wsTitleBar;
1410     }
1411     if (int(x + w) >= Mx) {
1412         x = mx;
1413         lastX = wsTitleBar;
1414     }
1415 }
1416 
cascadePlace(YArrange arrange)1417 void YWindowManager::cascadePlace(YArrange arrange) {
1418     if (saveArrange(arrange) == false)
1419         return;
1420 
1421     int mx, my, Mx, My;
1422     getWorkArea(nullptr, &mx, &my, &Mx, &My);
1423 
1424     int lx = mx;
1425     int ly = my;
1426     for (int i = arrange.size(); --i >= 0; ) {
1427         YFrameWindow* f = arrange[i];
1428         int x;
1429         int y;
1430 
1431         getCascadePlace(f, lx, ly, x, y, f->width(), f->height());
1432         f->setNormalPositionOuter(x, y);
1433     }
1434 }
1435 
setWindows(YArrange arrange,YAction action)1436 void YWindowManager::setWindows(YArrange arrange, YAction action) {
1437     if (saveArrange(arrange) == false)
1438         return;
1439 
1440     lockFocus();
1441     for (YFrameWindow* f : arrange) {
1442         if (action == actionHideAll) {
1443             if (!f->isHidden())
1444                 f->setState(WinStateUnmapped, WinStateHidden);
1445         } else if (action == actionMinimizeAll) {
1446             if (!f->isMinimized())
1447                 f->setState(WinStateUnmapped, WinStateMinimized);
1448         }
1449     }
1450     unlockFocus();
1451     focusTopWindow();
1452 }
1453 
getNewPosition(YFrameWindow * frame,int & x,int & y,int w,int h,int xiscreen)1454 void YWindowManager::getNewPosition(YFrameWindow *frame, int &x, int &y,
1455                                     int w, int h, int xiscreen) {
1456     if (centerTransientsOnOwner && frame->owner()) {
1457         int mx, my, Mx, My;
1458         getWorkArea(frame->owner(), &mx, &my, &Mx, &My);
1459         mx -= frame->borderXN();
1460         my -= frame->borderYN() - topSideVerticalOffset;
1461         YRect o(frame->owner()->geometry());
1462         int cx = max(mx, o.x()) / 2 + min(Mx, o.x() + int(o.width())) / 2 - w / 2;
1463         int cy = max(my, o.y()) / 2 + min(My, o.y() + int(o.height())) / 2 - h / 2;
1464         x = max(mx, min(Mx - w, cx));
1465         y = max(my, min(My - h, cy));
1466     }
1467     else if (smartPlacement) {
1468         getSmartPlace(true, frame, x, y, w, h, xiscreen);
1469     }
1470     else {
1471         getCascadePlace(frame, fCascadeX, fCascadeY, x, y, w, h);
1472     }
1473     if (centerLarge) {
1474         int mx, my, Mx, My;
1475         getWorkArea(frame, &mx, &my, &Mx, &My);
1476         int dw = (Mx - mx) / 2, dh = (My - my) / 2;
1477         if (w > dw && h > dh) {
1478             x = max(mx, mx + dw - w / 2);
1479             y = max(my, my + dh - h / 2);
1480         }
1481     }
1482 }
1483 
placeWindow(YFrameWindow * frame,int x,int y,int cw,int ch,bool newClient,bool & doActivate)1484 void YWindowManager::placeWindow(YFrameWindow *frame,
1485                                  int x, int y, int cw, int ch,
1486                                  bool newClient, bool& doActivate)
1487 {
1488     YFrameClient *client = frame->client();
1489 
1490     int borderWidth = frame->borderXN();
1491     int borderHeight = frame->borderYN();
1492     int borderOffset = newClient * min(borderHeight, int(topSideVerticalOffset));
1493     int titleHeight = frame->titleYN();
1494     int frameWidth = 2 * borderWidth;
1495     int frameHeight = 2 * borderHeight + titleHeight;
1496     int posWidth = cw + frameWidth;
1497     int posHeight = ch + frameHeight;
1498     int posX = x;
1499     int posY = y;
1500 
1501 #ifdef CONFIG_SESSION
1502     if (smapp->haveSessionManager() && findWindowInfo(frame)) {
1503         if (frame->getWorkspace() != activeWorkspace())
1504             doActivate = false;
1505     }
1506     else
1507 #endif
1508     if (newClient) {
1509         WindowOption wo(frame->getWindowOption());
1510 
1511         if (frame->frameOption(YFrameWindow::foClose)) {
1512             frame->wmClose();
1513             doActivate = false;
1514         }
1515 
1516         if (wo.opacity && inrange(wo.opacity, 1, 100)) {
1517             Atom omax = 0xFFFFFFFF;
1518             Atom oper = omax / 100;
1519             Atom orem = omax - oper * 100;
1520             Atom opaq = wo.opacity * oper + wo.opacity * orem / 100;
1521             frame->setNetOpacity(opaq);
1522         }
1523 
1524         if (hasbits(wo.gflags, WidthValue | HeightValue) && wo.gh && wo.gw) {
1525             posWidth = wo.gw + frameWidth;
1526             posHeight = wo.gh + frameHeight;
1527         }
1528 
1529         if (hasbits(wo.gflags, XValue | YValue)) {
1530             posX = wo.gx;
1531             posY = wo.gy;
1532             if (wo.gflags & XNegative)
1533                 posX += desktop->width() - posWidth;
1534             if (wo.gflags & YNegative)
1535                 posY += desktop->height() - posHeight;
1536             goto setGeo; /// FIX
1537         }
1538     }
1539 
1540     if (newClient && client->adopted() && client->sizeHints() &&
1541         (!(client->sizeHints()->flags & (USPosition | PPosition)) ||
1542          ((client->sizeHints()->flags & PPosition)
1543           && frame->frameOption(YFrameWindow::foIgnorePosition)
1544          )))
1545     {
1546         int xiscreen = (fFocusWin ? fFocusWin->getScreen() :
1547                         frame->owner() ? frame->owner()->getScreen() :
1548                         xineramaPrimaryScreen);
1549         getNewPosition(frame, x, y, posWidth, posHeight - borderOffset, xiscreen);
1550         posX = x;
1551         posY = y - borderOffset;
1552     } else {
1553         int gx, gy;
1554         client->gravityOffsets(gx, gy);
1555         if (gx > 0)
1556             posX -= 2 * borderWidth - client->getBorder() - 1;
1557         if (gy > 0)
1558             posY -= 2 * borderHeight + titleHeight - client->getBorder() - 1;
1559         if (gx == 0 && gy == 0 && client->winGravity() == StaticGravity) {
1560             posX -= borderWidth;
1561             posY -= borderHeight + titleHeight;
1562         }
1563         posY -= borderOffset;
1564     }
1565 
1566 setGeo:
1567     MSG(("mapping geometry 1 (%d:%d %dx%d)", posX, posY, posWidth, posHeight));
1568     frame->setNormalGeometryOuter(posX, posY, posWidth, posHeight);
1569 }
1570 
allocateClient(Window win,bool mapClient)1571 YFrameClient* YWindowManager::allocateClient(Window win, bool mapClient) {
1572     YFrameClient* client = findClient(win);
1573     if (client == nullptr) {
1574         XWindowAttributes attributes;
1575 
1576         if (XGetWindowAttributes(xapp->display(), win, &attributes) &&
1577             attributes.override_redirect == false &&
1578             (mapClient || attributes.map_state > IsUnmapped))
1579         {
1580             client = new YFrameClient(nullptr, nullptr, win,
1581                                       attributes.depth,
1582                                       attributes.visual,
1583                                       attributes.colormap);
1584             if (client && client->isKdeTrayWindow()) {
1585                 if (taskBar && taskBar->windowTrayRequestDock(win)) {
1586                     delete client; client = nullptr;
1587                 }
1588             }
1589             if (client && attributes.border_width)
1590                 client->setBorder(attributes.border_width);
1591             if (client && fDockApp && fDockApp->dock(client))
1592                 client = nullptr;
1593         }
1594     }
1595     return client;
1596 }
1597 
allocateFrame(YFrameClient * client)1598 YFrameWindow* YWindowManager::allocateFrame(YFrameClient* client) {
1599     unsigned depth = (client->depth() == 32) ? 32 : xapp->depth();
1600     bool sameDepth = (depth == xapp->depth());
1601     Visual* visual = (sameDepth ? xapp->visual() : client->visual());
1602     Colormap clmap = (sameDepth ? xapp->colormap() : client->colormap());
1603     return new YFrameWindow(wmActionListener, depth, visual, clmap);
1604 }
1605 
manageClient(YFrameClient * client,bool mapClient)1606 void YWindowManager::manageClient(YFrameClient* client, bool mapClient) {
1607     bool canManualPlace = false;
1608     bool doActivate = (mapClient && (isRunning() || client->visible()));
1609     bool requestFocus = true;
1610 
1611     MSG(("manage client 0x%lX", client->handle()));
1612     PRECONDITION(findFrame(client->handle()) == nullptr);
1613 
1614     int cx = client->x();
1615     int cy = client->y();
1616     int cw = client->width();
1617     int ch = client->height();
1618     MSG(("initial geometry 1 (%d:%d %dx%d)", cx, cy, cw, ch));
1619 
1620     YRestackLock restack;
1621     updateFullscreenLayerEnable(false);
1622 
1623     YFrameWindow* frame = allocateFrame(client);
1624     if (frame == nullptr) {
1625         return;
1626     }
1627     MSG(("initial geometry 2 (%d:%d %dx%d)",
1628          client->x(), client->y(), client->width(), client->height()));
1629 
1630     frame->doManage(client, doActivate, requestFocus);
1631     MSG(("initial geometry 3 (%d:%d %dx%d)",
1632          client->x(), client->y(), client->width(), client->height()));
1633     if (frame->client() == nullptr) {
1634         client->setFrame(nullptr);
1635         delete frame;
1636         return;
1637     }
1638 
1639     placeWindow(frame, cx, cy, cw, ch, isRunning(), doActivate);
1640 
1641     if ((limitSize || limitPosition) &&
1642         isRunning() &&
1643         !frame->affectsWorkArea())
1644     {
1645         int posX(frame->x() + frame->borderXN()),
1646             posY(frame->y() + frame->borderYN()),
1647             posWidth(frame->width() - 2 * frame->borderXN()),
1648             posHeight(frame->height() - 2 * frame->borderYN());
1649 
1650         MSG(("mapping geometry 2 (%d:%d %dx%d)", posX, posY, posWidth, posHeight));
1651 
1652         if (limitSize) {
1653             int Mw, Mh;
1654             getWorkAreaSize(frame, &Mw, &Mh);
1655 
1656             posWidth = min(posWidth, Mw);
1657             posHeight = min(posHeight, Mh);
1658 
1659 /// TODO #warning "cleanup the constrainSize code, there is some duplication"
1660             posHeight -= frame->titleYN();
1661             client->constrainSize(posWidth, posHeight, 0);
1662             posHeight += frame->titleYN();
1663         }
1664 
1665         if (limitPosition &&
1666             !(client->sizeHints() &&
1667               (client->sizeHints()->flags & USPosition)))
1668         {
1669             int mx, my, Mx, My;
1670             getWorkArea(frame, &mx, &my, &Mx, &My);
1671 
1672             posX = clamp(posX, mx, Mx - posWidth);
1673             posY = clamp(posY, my, My - posHeight);
1674         }
1675         posHeight -= frame->titleYN();
1676 
1677         MSG(("mapping geometry 3 (%d:%d %dx%d)", posX, posY, posWidth, posHeight));
1678         frame->setNormalGeometryInner(posX, posY, posWidth, posHeight);
1679     }
1680 
1681     if (isRunning()) {
1682         if (frame->frameOption(YFrameWindow::foAllWorkspaces))
1683             frame->setAllWorkspaces();
1684         if (frame->frameOption(YFrameWindow::foFullscreen))
1685             frame->setState(WinStateFullscreen, WinStateFullscreen);
1686         else if (frame->frameOption(YFrameWindow::foMaximizedBoth))
1687             frame->setState(WinStateMaximizedBoth | WinStateFullscreen,
1688                            (WinStateMaximizedBoth & frame->frameOptions()));
1689         if (frame->startMinimized()) {
1690             frame->setState(WinStateUnmapped, WinStateMinimized);
1691             doActivate = false;
1692             requestFocus = false;
1693         }
1694 
1695         if (frame->frameOption(YFrameWindow::foNoFocusOnMap))
1696             requestFocus = false;
1697     }
1698 
1699     frame->setManaged(true);
1700 
1701     if (doActivate && manualPlacement && isRunning() &&
1702         client != windowList &&
1703         client != taskBar &&
1704         !frame->owner() &&
1705         (!client->sizeHints() ||
1706          !(client->sizeHints()->flags & (USPosition | PPosition))))
1707         canManualPlace = true;
1708 
1709     if (doActivate && canManualPlace && !opaqueMove &&
1710         frame->notState(WinStateHidden | WinStateMinimized | WinStateFullscreen)) {
1711         frame->manualPlace();
1712     }
1713     frame->updateState();
1714     frame->updateProperties();
1715     frame->updateTaskBar();
1716     if (frame->affectsWorkArea())
1717         updateWorkArea();
1718     if (isRunning()) {
1719         if (doActivate) {
1720             frame->activateWindow(true);
1721             if (canManualPlace && opaqueMove)
1722                 frame->wmMove();
1723         } else if (requestFocus) {
1724             if (mapInactiveOnTop)
1725                 frame->wmRaise();
1726             frame->setWmUrgency(true);
1727         }
1728         if (switchWindowVisible())
1729             fSwitchWindow->createdFrame(frame);
1730     }
1731     updateFullscreenLayerEnable(true);
1732 }
1733 
mapClient(Window win)1734 void YWindowManager::mapClient(Window win) {
1735     MSG(("mapping window 0x%lX", win));
1736     YFrameWindow* frame = findFrame(win);
1737     if (frame) {
1738         if (frame->isUnmapped())
1739             frame->makeMapped();
1740         if (clickFocus || !strongPointerFocus)
1741             frame->activate(true);/// !!! is this ok
1742     }
1743     else {
1744         grabServer();
1745         lockWorkArea();
1746 
1747         YFrameClient* client = allocateClient(win, true);
1748         if (client) {
1749             manageClient(client, true);
1750             if (client->getFrame() == nullptr) {
1751                 delete client;
1752             }
1753         }
1754 
1755         ungrabServer();
1756         unlockWorkArea();
1757     }
1758 }
1759 
unmanageClient(YFrameClient * client)1760 void YWindowManager::unmanageClient(YFrameClient* client) {
1761     MSG(("unmanaging window 0x%lX", client->handle()));
1762     YFrameWindow* frame = client->getFrame();
1763     if (frame) {
1764         frame->unmanage();
1765         delete frame;
1766     }
1767     if (client->isDocked() && fDockApp) {
1768         fDockApp->undock(client);
1769     }
1770     delete client;
1771 }
1772 
destroyedClient(Window win)1773 void YWindowManager::destroyedClient(Window win) {
1774     YFrameWindow *frame = findFrame(win);
1775 
1776     if (frame) {
1777         frame->hide();
1778         delete frame;
1779     }
1780     else {
1781         YFrameClient* client = findClient(win);
1782         if (client && client->isDocked() && fDockApp) {
1783             fDockApp->undock(client);
1784             delete client;
1785             return;
1786         }
1787         MSG(("destroyed: unknown window: 0x%lX", win));
1788     }
1789 }
1790 
focusTopWindow()1791 void YWindowManager::focusTopWindow() {
1792     if (notRunning() || focusLocked())
1793         return ;
1794     if (!clickFocus && strongPointerFocus) {
1795         XSetInputFocus(xapp->display(), PointerRoot, RevertToNone, CurrentTime);
1796         notifyActive(nullptr);
1797         return ;
1798     }
1799     if (!focusTop(topLayer(WinLayerOnTop)))
1800         focusTop(topLayer());
1801 }
1802 
focusTop(YFrameWindow * f)1803 bool YWindowManager::focusTop(YFrameWindow *f) {
1804     if (!f)
1805         return false;
1806 
1807     f = f->findWindow(YFrameWindow::fwfVisible |
1808                       YFrameWindow::fwfFocusable |
1809                       YFrameWindow::fwfNotHidden |
1810                       YFrameWindow::fwfWorkspace |
1811                       YFrameWindow::fwfSame |
1812                       YFrameWindow::fwfLayers |
1813                       YFrameWindow::fwfCycle);
1814     //msg("found focus %lX", f);
1815     setFocus(f);
1816     return f;
1817 }
1818 
getFrameUnderMouse(long workspace)1819 YFrameWindow *YWindowManager::getFrameUnderMouse(long workspace) {
1820     YFrameWindow* frame;
1821     Window root, xwin = None;
1822     YWindow* ywin;
1823     int ignore;
1824     unsigned ignore2;
1825     if (XQueryPointer(xapp->display(), xapp->root(), &root, &xwin,
1826                       &ignore, &ignore, &ignore, &ignore, &ignore2) &&
1827         xwin != None &&
1828         (ywin = windowContext.find(xwin)) != nullptr &&
1829         !ywin->adopted() &&
1830         (frame = dynamic_cast<YFrameWindow *>(ywin)) != nullptr &&
1831         frame->isManaged() &&
1832         frame->visibleOn(workspace) &&
1833         frame->avoidFocus() == false &&
1834         frame->client()->destroyed() == false &&
1835         frame->client()->visible() &&
1836         frame->client() != taskBar)
1837     {
1838         return frame;
1839     }
1840     return nullptr;
1841 }
1842 
getLastFocus(bool skipAllWorkspaces,long workspace)1843 YFrameWindow *YWindowManager::getLastFocus(bool skipAllWorkspaces, long workspace) {
1844     if (workspace == AllWorkspaces)
1845         workspace = activeWorkspace();
1846 
1847     YFrameWindow *toFocus = nullptr;
1848 
1849     if (toFocus == nullptr) {
1850         toFocus = workspaces[workspace].focused;
1851     }
1852 
1853     if (toFocus != nullptr) {
1854         if (toFocus->isMinimized() ||
1855             toFocus->isHidden() ||
1856             !toFocus->visibleOn(workspace) ||
1857             toFocus->client()->destroyed() ||
1858             toFocus->client() == taskBar ||
1859             toFocus->avoidFocus())
1860         {
1861             toFocus = nullptr;
1862         }
1863     }
1864 
1865     if (toFocus == nullptr && clickFocus == false) {
1866         toFocus = getFrameUnderMouse(workspace);
1867     }
1868 
1869     if (toFocus == nullptr) {
1870         int pass = 0;
1871         if (!skipAllWorkspaces)
1872             pass = 1;
1873         for (; pass < 3; pass++) {
1874             YFrameIter w = focusedReverseIterator();
1875             while (++w) {
1876                 if (!w->client()->adopted())
1877                     continue;
1878                 if (w->isMinimized())
1879                     continue;
1880                 if (w->isHidden())
1881                     continue;
1882                 if (!w->visibleOn(workspace))
1883                     continue;
1884                 if (w->avoidFocus() || pass == 2)
1885                     continue;
1886                 if ((w->isAllWorkspaces() && w != fFocusWin) || pass == 1) {
1887                     if (w->client() != taskBar && toFocus == nullptr)
1888                         toFocus = w;
1889                     continue;
1890                 }
1891                 if (w->client() == taskBar)
1892                     continue;
1893                 toFocus = w;
1894                 goto gotit;
1895             }
1896         }
1897     }
1898 gotit:
1899 #if 0
1900     YFrameWindow *f = toFocus;
1901     YWindow *c = f ? f->client() : 0;
1902     Window w = c ? c->handle() : 0;
1903     if (w == desktop->handle()) {
1904         msg("%lX Focus 0x%lX desktop",
1905             xapp->getEventTime(0), w);
1906     } else if (f && w == f->handle()) {
1907         msg("%lX Focus 0x%lX frame %s",
1908             xapp->getEventTime(0), w, f->getTitle().c_str());
1909     } else if (f && c && w == c->handle()) {
1910         msg("%lX Focus 0x%lX client %s",
1911             xapp->getEventTime(0), w, f->getTitle().c_str());
1912     } else {
1913         msg("%lX Focus 0x%lX",
1914             xapp->getEventTime(0), w);
1915     }
1916 #endif
1917     return toFocus;
1918 }
1919 
focusLastWindow()1920 void YWindowManager::focusLastWindow() {
1921     if (notRunning() || focusLocked())
1922         return;
1923     if (!clickFocus && strongPointerFocus) {
1924         XSetInputFocus(xapp->display(), PointerRoot, RevertToNone, CurrentTime);
1925         notifyActive(nullptr);
1926         return ;
1927     }
1928 
1929     YFrameWindow *toFocus = getLastFocus(false);
1930     if (toFocus == nullptr || toFocus->client() == taskBar) {
1931         focusTopWindow();
1932     } else {
1933         if (raiseOnFocus)
1934             toFocus->wmRaise();
1935         setFocus(toFocus);
1936     }
1937 }
1938 
1939 
topLayer(long layer)1940 YFrameWindow *YWindowManager::topLayer(long layer) {
1941     for (long l = layer; l >= 0; l--)
1942         if (fLayers[l])
1943             return fLayers[l].front();
1944 
1945     return nullptr;
1946 }
1947 
bottomLayer(long layer)1948 YFrameWindow *YWindowManager::bottomLayer(long layer) {
1949     for (long l = layer; l < WinLayerCount; l++)
1950         if (fLayers[l])
1951             return fLayers[l].back();
1952 
1953     return nullptr;
1954 }
1955 
setAbove(YFrameWindow * frame,YFrameWindow * above)1956 bool YWindowManager::setAbove(YFrameWindow* frame, YFrameWindow* above) {
1957     const long layer = frame->getActiveLayer();
1958     if (above != nullptr && layer != above->getActiveLayer()) {
1959         MSG(("ignore z-order change between layers: win=0x%lX (above: 0x%lX) ",
1960                     frame->handle(), above->client()->handle()));
1961         return false;
1962     }
1963 
1964 #ifdef DEBUG
1965     if (debug_z) dumpZorder("before setAbove", frame, above);
1966 #endif
1967     bool change = false;
1968     if (above != frame->next() && above != frame) {
1969         fLayers[layer].remove(frame);
1970         fLayeredUpdated = true;
1971         if (above) {
1972             fLayers[layer].insertBefore(frame, above);
1973         } else {
1974             fLayers[layer].append(frame);
1975         }
1976 #ifdef DEBUG
1977         if (debug_z) dumpZorder("after setAbove", frame, above);
1978 #endif
1979         change = true;
1980     }
1981     return change;
1982 }
1983 
setBelow(YFrameWindow * frame,YFrameWindow * below)1984 bool YWindowManager::setBelow(YFrameWindow* frame, YFrameWindow* below) {
1985     if (below != nullptr &&
1986         frame->getActiveLayer() != below->getActiveLayer())
1987     {
1988         MSG(("ignore z-order change between layers: win=0x%lX (below %ld)",
1989                    frame->handle(), below->client()->handle()));
1990         return false;
1991     }
1992     if (below != frame->prev() && below != frame) {
1993         return setAbove(frame, below ? below->next() : nullptr);
1994     }
1995     return false;
1996 }
1997 
removeLayeredFrame(YFrameWindow * frame)1998 void YWindowManager::removeLayeredFrame(YFrameWindow *frame) {
1999     long layer = frame->getActiveLayer();
2000     PRECONDITION(inrange(layer, 0L, WinLayerCount - 1L));
2001     fLayers[layer].remove(frame);
2002     fLayeredUpdated = true;
2003 }
2004 
updateFullscreenLayerEnable(bool enable)2005 void YWindowManager::updateFullscreenLayerEnable(bool enable) {
2006     fFullscreenEnabled = enable;
2007     updateFullscreenLayer();
2008 }
2009 
updateFullscreenLayer()2010 void YWindowManager::updateFullscreenLayer() { /// HACK !!!
2011     for (YFrameWindow *w = topLayer(); w; w = w->nextLayer()) {
2012         if (w->getActiveLayer() == WinLayerFullscreen || w->isFullscreen()) {
2013             w->updateLayer();
2014         }
2015     }
2016     if (taskBar)
2017         taskBar->updateFullscreen(getFocus() && getFocus()->isFullscreen());
2018 }
2019 
restackWindows()2020 void YWindowManager::restackWindows() {
2021     if (fRestackLock) {
2022         fRestackUpdate++;
2023         return;
2024     }
2025 
2026     YArray<Window> w(12 + focusedCount());
2027 
2028     w.append(fTopWin->handle());
2029 
2030     for (YPopupWindow* p = xapp->popup(); p; p = p->prevPopup())
2031         w.append(p->handle());
2032 
2033     if (taskBar)
2034         w.append(taskBar->edgeTriggerWindow());
2035 
2036     for (auto edge : edges)
2037         w.append(edge->handle());
2038 
2039     if (wmapp->hasCtrlAltDelete()) {
2040         if (wmapp->getCtrlAltDelete()->visible()) {
2041             w.append(wmapp->getCtrlAltDelete()->handle());
2042         }
2043     }
2044 
2045     if (statusMoveSize && statusMoveSize->visible())
2046         w.append(statusMoveSize->handle());
2047     else if (statusWorkspace && statusWorkspace->visible())
2048         w.append(statusWorkspace->handle());
2049 
2050     int top = w.getCount();
2051     int dockAppLayer = fDockApp && fDockApp->created()
2052                      ? fDockApp->layer() : WinLayerInvalid;
2053     for (int layer = WinLayerCount - 1; layer >= 0; --layer) {
2054         if (layer == dockAppLayer)
2055             w.append(fDockApp->handle());
2056         for (YFrameWindow* f = fLayers[layer].front(); f; f = f->next())
2057             w.append(f->handle());
2058     }
2059 
2060     if (quickSwitchRaiseCandidate && switchWindowVisible()) {
2061         YFrameWindow* active = fSwitchWindow->current();
2062         if (active) {
2063             Window handle = active->handle();
2064             if (findRemove(w, handle))
2065                 w.insert(top, handle);
2066         }
2067     }
2068 
2069     w.append(fBottom->handle());
2070     XRestackWindows(xapp->display(), &*w, w.getCount());
2071 
2072     if (taskBar)
2073         taskBar->workspacesRepaint();
2074 }
2075 
getWorkArea(int * mx,int * my,int * Mx,int * My)2076 void YWindowManager::getWorkArea(int *mx, int *my, int *Mx, int *My) {
2077     int s = max(0, min(xineramaPrimaryScreen, getScreenCount() - 1));
2078     if (fWorkArea && 0 < fWorkAreaWorkspaceCount) {
2079         *mx = fWorkArea[0][s].fMinX;
2080         *my = fWorkArea[0][s].fMinY;
2081         *Mx = fWorkArea[0][s].fMaxX;
2082         *My = fWorkArea[0][s].fMaxY;
2083     } else {
2084         unsigned dw, dh;
2085         getScreenGeometry(mx, my, &dw, &dh, s);
2086         *Mx = dw; *My = dh;
2087     }
2088 }
2089 
getWorkArea(const YFrameWindow * frame,int * mx,int * my,int * Mx,int * My,int xiscreen)2090 void YWindowManager::getWorkArea(const YFrameWindow* frame,
2091                                  int *mx, int *my, int *Mx, int *My,
2092                                  int xiscreen)
2093 {
2094     long ws = WinWorkspaceInvalid;
2095 
2096     if (frame) {
2097         if (xiscreen == -1)
2098             xiscreen = frame->getScreen();
2099 
2100         if (frame->inWorkArea() || frame->isIconic()) {
2101             if (frame->isAllWorkspaces())
2102                 ws = activeWorkspace();
2103             else
2104                 ws = frame->getWorkspace();
2105         }
2106     }
2107 
2108     if (inrange(ws, 0L, fWorkAreaWorkspaceCount - 1L) && 0 <= xiscreen) {
2109         *mx = fWorkArea[ws][xiscreen].fMinX;
2110         *my = fWorkArea[ws][xiscreen].fMinY;
2111         *Mx = fWorkArea[ws][xiscreen].fMaxX;
2112         *My = fWorkArea[ws][xiscreen].fMaxY;
2113     } else {
2114         *mx = 0;
2115         *my = 0;
2116         *Mx = width();
2117         *My = height();
2118     }
2119 
2120     if (0 <= xiscreen) {
2121         int dx, dy;
2122         unsigned dw, dh;
2123         getScreenGeometry(&dx, &dy, &dw, &dh, xiscreen);
2124 
2125         if (*mx < dx)
2126             *mx = dx;
2127         if (*my < dy)
2128             *my = dy;
2129         if (*Mx > dx + (int) dw)
2130             *Mx = dx + (int) dw;
2131         if (*My > dy + (int) dh)
2132             *My = dy + (int) dh;
2133     }
2134 }
2135 
getWorkAreaSize(YFrameWindow * frame,int * Mw,int * Mh)2136 void YWindowManager::getWorkAreaSize(YFrameWindow *frame, int *Mw,int *Mh) {
2137     int mx, my, Mx, My;
2138     getWorkArea(frame, &mx, &my, &Mx, &My);
2139     *Mw = Mx - mx;
2140     *Mh = My - my;
2141 }
2142 
updateArea(long workspace,int screen_number,int l,int t,int r,int b)2143 void YWindowManager::updateArea(long workspace, int screen_number,
2144                                 int l, int t, int r, int b)
2145 {
2146     long low = 0, lim = fWorkAreaWorkspaceCount - 1L;
2147     if (inrange(workspace, low, lim)) {
2148         low = lim = workspace;
2149     } else if (workspace != WinWorkspaceInvalid) {
2150         return;
2151     }
2152     for (long ws = low; ws <= lim; ++ws) {
2153         WorkAreaRect *wa = fWorkArea[ws] + screen_number;
2154         if (l > wa->fMinX) wa->fMinX = l;
2155         if (t > wa->fMinY) wa->fMinY = t;
2156         if (r < wa->fMaxX) wa->fMaxX = r;
2157         if (b < wa->fMaxY) wa->fMaxY = b;
2158     }
2159 }
2160 
debugWorkArea(const char * prefix)2161 void YWindowManager::debugWorkArea(const char* prefix) {
2162 #ifdef DEBUG
2163     for (int i = 0; i < fWorkAreaWorkspaceCount; i++) {
2164         for (int j = 0; j < fWorkAreaScreenCount; j++) {
2165             MSG(("%s: workarea w:%d s:%d %d %d %d %d",
2166                 prefix,
2167                 i, j,
2168                 fWorkArea[i][j].fMinX,
2169                 fWorkArea[i][j].fMinY,
2170                 fWorkArea[i][j].fMaxX,
2171                 fWorkArea[i][j].fMaxY));
2172         }
2173     }
2174 #endif
2175 }
2176 
updateWorkArea()2177 void YWindowManager::updateWorkArea() {
2178     if (fWorkAreaLock) {
2179         requestWorkAreaUpdate();
2180     }
2181     else {
2182         bool update = false;
2183         fWorkAreaLock = true;
2184         do {
2185             fWorkAreaUpdate = 0;
2186             update |= updateWorkAreaInner();
2187         } while (fWorkAreaUpdate);
2188         fWorkAreaLock = false;
2189         if (update) {
2190             workAreaUpdated();
2191         }
2192     }
2193 }
2194 
updateWorkAreaInner()2195 bool YWindowManager::updateWorkAreaInner() {
2196     long oldWorkAreaWorkspaceCount = fWorkAreaWorkspaceCount;
2197     int oldWorkAreaScreenCount = fWorkAreaScreenCount;
2198     WorkAreaRect **oldWorkArea = new WorkAreaRect *[::workspaceCount];
2199     if (oldWorkArea == nullptr)
2200         return false;
2201     else {
2202         long areaCount = ::workspaceCount * getScreenCount();
2203         oldWorkArea[0] = new WorkAreaRect[areaCount];
2204         if (oldWorkArea[0] == nullptr) {
2205             delete[] oldWorkArea;
2206             return false;
2207         }
2208         else {
2209             fWorkAreaWorkspaceCount = ::workspaceCount;
2210             fWorkAreaScreenCount = getScreenCount();
2211             swap(fWorkArea, oldWorkArea);
2212         }
2213     }
2214 
2215     for (long i = 0; i < fWorkAreaWorkspaceCount; i++) {
2216         if (i)
2217             fWorkArea[i] = fWorkArea[i - 1] + fWorkAreaScreenCount;
2218         for (int j = 0; j < fWorkAreaScreenCount; j++)
2219             fWorkArea[i][j] = xiInfo[j];
2220     }
2221 
2222     debugWorkArea("before");
2223 
2224     for (YFrameWindow *w = topLayer(); w; w = w->nextLayer()) {
2225         if (w->isUnmapped()) {
2226             continue;
2227         }
2228 
2229         MSG(("workarea window %s: ws:%d s:%d x:%d y:%d w:%d h:%d",
2230             w->getTitle().c_str(), w->getWorkspace(), w->getScreen(),
2231             w->x(), w->y(), w->width(), w->height()));
2232         if (w->haveStruts())
2233         {
2234             int ws = w->getWorkspace();
2235             int s = w->getScreen();
2236             int sx = xiInfo[s].x_org;
2237             int sy = xiInfo[s].y_org;
2238             int sw = xiInfo[s].width;
2239             int sh = xiInfo[s].height;
2240 
2241             int l = sx + w->strutLeft();
2242             int t = sy + w->strutTop();
2243             int r = sx + sw - w->strutRight();
2244             int b = sy + sh - w->strutBottom();
2245 
2246             MSG(("strut %d %d %d %d", w->strutLeft(), w->strutTop(),
2247                                       w->strutRight(), w->strutBottom()));
2248             MSG(("limit %d %d %d %d", l, t, r, b));
2249             updateArea(ws, s, l, t, r, b);
2250         }
2251 
2252         if (w->doNotCover() ||
2253             (limitByDockLayer && w->getActiveLayer() == WinLayerDock))
2254         {
2255             int ws = w->getWorkspace();
2256             int s = w->getScreen();
2257             int sx = xiInfo[s].x_org;
2258             int sy = xiInfo[s].y_org;
2259             int sw = xiInfo[s].width;
2260             int sh = xiInfo[s].height;
2261 
2262             int lowX = sx + sw / 4;
2263             int lowY = sy + sh / 4;
2264             int hiX = sx + 3 * sw / 4;
2265             int hiY = sy + 3 * sh / 4;
2266             bool const isHoriz(w->width() > w->height());
2267 
2268             int l = sx;
2269             int t = sy;
2270             int r = sx + sw;
2271             int b = sy + sh;
2272 
2273             if (isHoriz) {
2274                 if (w->y() + (int) w->height() < lowY) {
2275                     t = w->y() + w->height();
2276                 } else if (w->y() + (int) height() > hiY) {
2277                     b = w->y();
2278                 }
2279             } else {
2280                 if (w->x() + (int) w->width() < lowX) {
2281                     l = w->x() + w->width();
2282                 } else if (w->x() + (int) width() > hiX) {
2283                     r = w->x();
2284                 }
2285             }
2286             MSG(("dock limit %d %d %d %d", l, t, r, b));
2287             updateArea(ws, s, l, t, r, b);
2288         }
2289         debugWorkArea("updated");
2290     }
2291     debugWorkArea("after");
2292 
2293     bool changed = false;
2294     if (oldWorkArea == nullptr ||
2295         oldWorkAreaWorkspaceCount != fWorkAreaWorkspaceCount ||
2296         oldWorkAreaScreenCount != fWorkAreaScreenCount) {
2297         changed = true;
2298     } else {
2299         for (long ws = 0; ws < fWorkAreaWorkspaceCount; ws++) {
2300             for (int s = 0; s < fWorkAreaScreenCount; s++) {
2301                 if (fWorkArea[ws][s] != oldWorkArea[ws][s]) {
2302                     changed = true;
2303                     break;
2304                 }
2305             }
2306             if (changed)
2307                 break;
2308         }
2309     }
2310 
2311     bool resize = false;
2312     if (changed) {
2313         MSG(("announceWorkArea"));
2314         announceWorkArea();
2315         long spaces = min(fWorkAreaWorkspaceCount, oldWorkAreaWorkspaceCount);
2316         int screens = min(fWorkAreaScreenCount, oldWorkAreaScreenCount);
2317         for (YFrameWindow* f = topLayer(); f; f = f->nextLayer()) {
2318             if (f->x() >= 0 && f->y() >= 0 && f->inWorkArea()) {
2319                 int s = f->getScreen();
2320                 int w = f->isAllWorkspaces()
2321                       ? activeWorkspace() : f->getWorkspace();
2322                 if (s < spaces && w < spaces &&
2323                     fWorkArea[w][s].displaced(oldWorkArea[w][s])) {
2324                     int dx =
2325                         (f->x() >= oldWorkArea[w][s].fMinX &&
2326                          f->x() < fWorkArea[w][s].fMinX) ?
2327                         fWorkArea[w][s].fMinX - f->x() :
2328                         (f->x() == oldWorkArea[w][s].fMinX &&
2329                          f->x() > fWorkArea[w][s].fMinX) ?
2330                         fWorkArea[w][s].fMinX - f->x() :
2331                         0;
2332                     int dy =
2333                         (f->y() >= oldWorkArea[w][s].fMinY &&
2334                          f->y() < fWorkArea[w][s].fMinY) ?
2335                         fWorkArea[w][s].fMinY - f->y() :
2336                         (f->y() == oldWorkArea[w][s].fMinY &&
2337                          f->y() > fWorkArea[w][s].fMinY) ?
2338                         fWorkArea[w][s].fMinY - f->y() :
2339                         0;
2340                     if (dx || dy) {
2341                         f->setNormalPositionOuter(f->x() + dx, f->y() + dy);
2342                     }
2343                 }
2344             }
2345         }
2346         if (screens < oldWorkAreaScreenCount) {
2347             resize = true;
2348         }
2349         else {
2350             for (long ws = 0; ws < spaces; ws++) {
2351                 for (int s = 0; s < screens; s++) {
2352                     if (fWorkArea[ws][s].width() < oldWorkArea[ws][s].width())
2353                         resize = true;
2354                     if (fWorkArea[ws][s].height() < oldWorkArea[ws][s].height())
2355                         resize = true;
2356                 }
2357             }
2358         }
2359     }
2360 
2361     if (oldWorkArea) {
2362         delete [] oldWorkArea[0];
2363         delete [] oldWorkArea;
2364     }
2365     if (resize) {
2366         MSG(("resizeWindows"));
2367         resizeWindows();
2368     }
2369     return resize | changed;
2370 }
2371 
announceWorkArea()2372 void YWindowManager::announceWorkArea() {
2373     long nw = workspaceCount();
2374     long *area = new long[nw * 4];
2375     bool isCloned = true;
2376 
2377     /*
2378       NET_WORKAREA behaviour: 0 (single/multimonitor with STRUT information,
2379                                  like metacity),
2380                               1 (always full desktop),
2381                               2 (singlemonitor with STRUT,
2382                                  multimonitor without STRUT)
2383     */
2384 
2385     if (!area)
2386         return;
2387 
2388     if (getScreenCount() > 1 && netWorkAreaBehaviour != 1) {
2389         for (int i = 0; i < getScreenCount(); i++) {
2390             if (xiInfo[i].x_org != 0 || xiInfo[i].y_org != 0) {
2391                 isCloned = false;
2392                 break;
2393             }
2394         }
2395     }
2396 
2397     const YRect desktopArea(desktop->geometry());
2398     for (int ws = 0; ws < nw; ws++) {
2399         YRect r(desktopArea);
2400         if (netWorkAreaBehaviour != 1) {
2401             r = fWorkArea[ws][0];
2402         }
2403 
2404         if (getScreenCount() > 1 && ! isCloned && netWorkAreaBehaviour != 1) {
2405             if (netWorkAreaBehaviour == 0) {
2406                 // STRUTS information is messy and broken for multimonitor,
2407                 // but there is no solution for this problem.
2408                 // So we imitate metacity's behaviour := merge,
2409                 // but limit height of each screen and hope for the best
2410                 for (int i = 1; i < getScreenCount(); i++) {
2411                     r.unionRect(fWorkArea[ws][i].fMinX, fWorkArea[ws][i].fMinY,
2412                                 fWorkArea[ws][i].width(),
2413                                 fWorkArea[ws][0].height());
2414                 }
2415             } else if (netWorkAreaBehaviour == 2) {
2416                 r = desktopArea;
2417             }
2418         }
2419         area[ws * 4 + 0] = r.x();
2420         area[ws * 4 + 1] = r.y();
2421         area[ws * 4 + 2] = r.width();
2422         area[ws * 4 + 3] = r.height();
2423     }
2424 
2425     XChangeProperty(xapp->display(), handle(),
2426                     _XA_NET_WORKAREA,
2427                     XA_CARDINAL,
2428                     32, PropModeReplace,
2429                     (unsigned char *)area, nw * 4);
2430     delete [] area;
2431 }
2432 
resizeWindows()2433 void YWindowManager::resizeWindows() {
2434     for (YFrameWindow * f = topLayer(); f; f = f->nextLayer()) {
2435         if (f->visibleNow() && f->inWorkArea() && !f->client()->destroyed()) {
2436             if (f->isMaximized())
2437                 f->updateDerivedSize(WinStateMaximizedBoth);
2438             f->updateLayout();
2439         }
2440     }
2441 }
2442 
workAreaUpdated()2443 void YWindowManager::workAreaUpdated() {
2444     if (isRunning() && (taskBar || !showTaskBar)) {
2445         for (YFrameIter frame = fCreationOrder.iterator(); ++frame; ) {
2446             if (frame->isIconic()) {
2447                 frame->getMiniIcon()->show();
2448             }
2449         }
2450         if (fDockApp && fDockApp->visible()) {
2451             fDockApp->adapt();
2452         }
2453     }
2454 }
2455 
arrangeIcons()2456 void YWindowManager::arrangeIcons() {
2457     fIconColumn = fIconRow = 0;
2458     for (bool tf : {false, true}) {
2459         for (MiniIcon* icon : MiniIcon::fIcons) {
2460             if (icon->getFrame()->isMinimized() == tf) {
2461                 icon->getFrame()->updateIconPosition();
2462             }
2463         }
2464     }
2465 }
2466 
initWorkspaces()2467 void YWindowManager::initWorkspaces() {
2468     long initialWorkspace = 0;
2469 
2470     readDesktopNames(true, true);
2471     setDesktopCount();
2472     setDesktopGeometry();
2473     setDesktopViewport();
2474     setShowingDesktop();
2475     readCurrentDesktop(initialWorkspace);
2476     readDesktopLayout();
2477     activateWorkspace(initialWorkspace);
2478 }
2479 
activateWorkspace(long workspace)2480 void YWindowManager::activateWorkspace(long workspace) {
2481     if (workspace != fActiveWorkspace) {
2482         lockWorkArea();
2483         lockFocus();
2484 
2485         if (taskBar && fActiveWorkspace != WinWorkspaceInvalid) {
2486             taskBar->setWorkspaceActive(fActiveWorkspace, false);
2487         }
2488         fLastWorkspace = fActiveWorkspace;
2489         fActiveWorkspace = workspace;
2490         if (taskBar) {
2491             taskBar->setWorkspaceActive(fActiveWorkspace, true);
2492         }
2493 
2494         setProperty(_XA_NET_CURRENT_DESKTOP, XA_CARDINAL, fActiveWorkspace);
2495 
2496         resizeWindows();
2497 
2498         YArray<YFrameWindow*> stack;
2499         for (YFrameWindow* w = topLayer(); w; w = w->nextLayer()) {
2500             if (w->visibleNow()) {
2501                 w->updateState();
2502                 w->updateTaskBar();
2503             }
2504             else if (w->getWorkspace() == fLastWorkspace) {
2505                 stack.append(w);
2506             }
2507         }
2508         for (; stack.nonempty(); stack.pop()) {
2509             YFrameWindow* w = stack.last();
2510             w->updateState();
2511             w->updateTaskBar();
2512         }
2513         unlockFocus();
2514 
2515         YFrameWindow *toFocus = getLastFocus(true, workspace);
2516         setFocus(toFocus, false, !switchWindowVisible());
2517         resetColormap(true);
2518 
2519         if (taskBar) {
2520             taskBar->relayout();
2521             taskBar->relayoutNow();
2522         }
2523 
2524         if (workspaceSwitchStatus
2525             && (!showTaskBar || !taskBarShowWorkspaces || taskBarAutoHide
2526                 || (taskBar && taskBar->hidden()))
2527            )
2528             statusWorkspace->begin(workspace);
2529         wmapp->signalGuiEvent(geWorkspaceChange);
2530         unlockWorkArea();
2531     }
2532 }
2533 
appendNewWorkspaces(long extra)2534 void YWindowManager::appendNewWorkspaces(long extra) {
2535     if ( !inrange<long>(extra, 1L, NewMaxWorkspaces - workspaces.count()))
2536         return;
2537 
2538     int ws = workspaces.count();
2539     int target = ws + int(extra);
2540     if (ws < target) {
2541         char buf[32];
2542         do {
2543             if (nonempty(workspaces.spare(ws))) {
2544                 strlcpy(buf, workspaces.spare(ws), sizeof buf);
2545             } else {
2546                 snprintf(buf, sizeof buf, ws < 999 ? "%3d " : "%d", 1 + ws);
2547             }
2548         } while (workspaces.add(buf) && ++ws < target);
2549     }
2550 
2551     updateWorkspaces(true);
2552 }
2553 
removeLastWorkspaces(long minus)2554 void YWindowManager::removeLastWorkspaces(long minus) {
2555     if ( !inrange(minus, 1L, workspaces.count() - 1L))
2556         return;
2557 
2558     const int last = int(workspaces.count() - 1 - minus);
2559 
2560     // switch away from the workspace being removed
2561     bool refocus = (fActiveWorkspace > last);
2562     if (refocus) {
2563         setFocus(nullptr);
2564         activateWorkspace(last);
2565     }
2566 
2567     // move windows away from the workspace being removed
2568     for (bool changed(true); changed; ) {
2569         changed = false;
2570         for (YFrameIter frame(focusedIterator()); ++frame; ) {
2571             if (frame->getWorkspace() > last) {
2572                 frame->setWorkspace(last);
2573                 changed = true;
2574             }
2575         }
2576     }
2577 
2578     for (int i = 1; i <= minus && last + 1 < workspaces.count(); ++i)
2579         workspaces.drop();
2580 
2581     updateWorkspaces(false);
2582 
2583     if (refocus)
2584         focusLastWindow();
2585 }
2586 
updateWorkspaces(bool increase)2587 void YWindowManager::updateWorkspaces(bool increase) {
2588     if (increase) {
2589         setDesktopViewport();
2590         updateWorkArea();
2591         setDesktopCount();
2592     } else {
2593         setDesktopCount();
2594         updateWorkArea();
2595         setDesktopViewport();
2596     }
2597     if (taskBar) {
2598         taskBar->workspacesUpdateButtons();
2599     }
2600     if (windowList) {
2601         windowList->updateWorkspaces();
2602     }
2603     updateMoveMenu();
2604 }
2605 
readCurrentDesktop(long & workspace)2606 bool YWindowManager::readCurrentDesktop(long &workspace) {
2607     YProperty netp(this, _XA_NET_CURRENT_DESKTOP, F32, 1L, XA_CARDINAL);
2608     if (netp) {
2609         workspace = clamp(*netp, 0L, workspaceCount() - 1L);
2610         return true;
2611     }
2612     return false;
2613 }
2614 
setDesktopGeometry()2615 void YWindowManager::setDesktopGeometry() {
2616     Atom data[2] = { desktop->width(), desktop->height() };
2617     MSG(("setting: _NET_DESKTOP_GEOMETRY = (%lu,%lu)", data[0], data[1]));
2618     setProperty(_XA_NET_DESKTOP_GEOMETRY, XA_CARDINAL, data, 2);
2619 }
2620 
setShowingDesktop()2621 void YWindowManager::setShowingDesktop() {
2622     MSG(("setting: _NET_SHOWING_DESKTOP = %d", fShowingDesktop));
2623     setProperty(_XA_NET_SHOWING_DESKTOP, XA_CARDINAL, fShowingDesktop);
2624 }
2625 
setShowingDesktop(bool setting)2626 void YWindowManager::setShowingDesktop(bool setting) {
2627 
2628     if (fShowingDesktop != setting) {
2629         fShowingDesktop = setting;
2630         setShowingDesktop();
2631     }
2632 }
2633 
updateTaskBarNames()2634 void YWindowManager::updateTaskBarNames() {
2635     if (taskBar) {
2636         taskBar->workspacesRelabelButtons();
2637     }
2638 }
2639 
updateMoveMenu()2640 void YWindowManager::updateMoveMenu() {
2641     if (moveMenu) {
2642         moveMenu->removeAll();
2643         moveMenu->updatePopup();
2644     }
2645 }
2646 
readDesktopLayout()2647 bool YWindowManager::readDesktopLayout() {
2648     bool success = false;
2649     YProperty prop(this, _XA_NET_DESKTOP_LAYOUT, F32, 4L, XA_CARDINAL);
2650     if (prop && 3 <= prop.size()) {
2651         int orient = (int) prop[0];
2652         int cols   = (int) prop[1];
2653         int rows   = (int) prop[2];
2654         int corner = (prop.size() == 3) ? _NET_WM_TOPLEFT : (int) prop[3];
2655         if (inrange(orient, 0, 1) &&
2656             inrange(cols, 0, 100) &&
2657             inrange(rows, 0, 100) &&
2658             (rows || cols) &&
2659             inrange(corner, 0, 3))
2660         {
2661             fLayout = (DesktopLayout) { orient, cols, rows, corner, };
2662             success = true;
2663         }
2664     }
2665     MSG(("read: _NET_DESKTOP_LAYOUT(%d): %s (%d, %lu) { %d, %d, %d, %d }",
2666         (int) _XA_NET_DESKTOP_LAYOUT, boolstr(success), prop.bits(), prop.size(),
2667         fLayout.orient, fLayout.columns, fLayout.rows, fLayout.corner));
2668 
2669     return success;
2670 }
2671 
readDesktopNames(bool init,bool net)2672 void YWindowManager::readDesktopNames(bool init, bool net) {
2673     YStringList netList;
2674     bool haveNet = readNetDesktopNames(netList);
2675 
2676     if (init) {
2677         if (haveNet && !strncmp(netList[0], "Workspace", 9))
2678             haveNet = false;
2679         for (int i = 0; i < configWorkspaces.getCount(); ++i)
2680             workspaces.add(configWorkspaces[i]);
2681         if (workspaces.count() < 1)
2682             workspaces + " 1 " + " 2 " + " 3 " + " 4 ";
2683     }
2684 
2685     if (haveNet) {
2686         compareDesktopNames(netList);
2687     }
2688     else {
2689         setDesktopNames();
2690     }
2691 }
2692 
compareDesktopNames(const YStringList & list)2693 bool YWindowManager::compareDesktopNames(const YStringList& list) {
2694     bool changed = false;
2695 
2696     // old strings must persist until after the update
2697     asmart<csmart> oldWorkspaceNames(new csmart[list.count]);
2698 
2699     for (int i = 0; i < list.count; i++) {
2700         if (i >= workspaces.count()) {
2701             workspaces.spare(i, list[i]);
2702         }
2703         else if (strcmp(list[i], workspaces[i])) {
2704             char* name(newstr(list[i]));
2705             swap(name, *workspaces[i]);
2706             oldWorkspaceNames[i] = name;
2707             changed = true;
2708             MSG(("Workspace %d: '%s' -> '%s'", i, name, list[i]));
2709         }
2710     }
2711 
2712     if (changed) {
2713         updateTaskBarNames();
2714         updateMoveMenu();
2715     }
2716 
2717     return changed;
2718 }
2719 
readNetDesktopNames(YStringList & list)2720 bool YWindowManager::readNetDesktopNames(YStringList& list) {
2721     bool success = false;
2722 
2723     MSG(("reading: _NET_DESKTOP_NAMES(%d)",(int)_XA_NET_DESKTOP_NAMES));
2724 
2725     XTextProperty names;
2726     if (XGetTextProperty(xapp->display(), handle(), &names,
2727                          _XA_NET_DESKTOP_NAMES) && names.nitems > 0) {
2728         if (XmbTextPropertyToTextList(xapp->display(), &names,
2729                                       &list.strings, &list.count) == Success) {
2730             if (list.count > 0 && isEmpty(list.last()))
2731                 list.count--;
2732             success = true;
2733         } else {
2734             MSG(("warning: could not convert strings for _NET_DESKTOP_NAMES"));
2735         }
2736         XFree(names.value);
2737     } else {
2738         MSG(("warning: could not read _NET_DESKTOP_NAMES"));
2739     }
2740 
2741     return success;
2742 }
2743 
setNetDesktopNames(long count)2744 void YWindowManager::setNetDesktopNames(long count) {
2745     MSG(("setting: _NET_DESKTOP_NAMES"));
2746     static char terminator[] = { '\0' };
2747     asmart<char *> strings(new char *[count + 1]);
2748     for (long i = 0; i < count; i++) {
2749         strings[i] = i < workspaces.count()
2750                    ? *workspaces[i]
2751                    : const_cast<char *>(workspaces.spare(i));
2752     }
2753     strings[count] = terminator;
2754     XTextProperty names;
2755     if (XmbTextListToTextProperty(xapp->display(), strings, count + 1,
2756                                   XUTF8StringStyle, &names) == Success) {
2757         XSetTextProperty(xapp->display(), handle(), &names,
2758                          _XA_NET_DESKTOP_NAMES);
2759         XFree(names.value);
2760     }
2761 }
2762 
setDesktopNames(long count)2763 void YWindowManager::setDesktopNames(long count) {
2764     MSG(("setting: %ld desktop names", count));
2765     setNetDesktopNames(count);
2766 }
2767 
setDesktopNames()2768 void YWindowManager::setDesktopNames() {
2769     setDesktopNames(workspaceCount());
2770 }
2771 
setDesktopCount()2772 void YWindowManager::setDesktopCount() {
2773     MSG(("setting: _NET_NUMBER_OF_DESKTOPS = %lu", Atom(workspaceCount())));
2774     setProperty(_XA_NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, Atom(workspaceCount()));
2775 }
2776 
setDesktopViewport()2777 void YWindowManager::setDesktopViewport() {
2778     MSG(("setting: _NET_DESKTOP_VIEWPORT"));
2779     const int n = 2 * workspaceCount();
2780     Atom data[n];
2781     for (int i = 0; i < n; ++i)
2782         data[i] = 0;
2783     setProperty(_XA_NET_DESKTOP_VIEWPORT, XA_CARDINAL, data, n);
2784 }
2785 
setWorkspace(int workspace)2786 void YWindowManager::setWorkspace(int workspace) {
2787     if (inrange(workspace, 0, workspaceCount() - 1)) {
2788         activateWorkspace(workspace);
2789     }
2790     else { MSG(("invalid workspace switch %d", workspace)); }
2791 }
2792 
wmCloseSession()2793 void YWindowManager::wmCloseSession() { // ----------------- shutdow started ---
2794     for (YFrameWindow * f(topLayer()); f; f = f->nextLayer())
2795         if (f->client()->adopted()) // not to ourselves?
2796             f->wmClose();
2797 }
2798 
getIconPosition(MiniIcon * iw,int * iconX,int * iconY)2799 void YWindowManager::getIconPosition(MiniIcon* iw, int *iconX, int *iconY) {
2800     if (isStartup() || fWorkAreaUpdate || (showTaskBar && taskBar == nullptr)) {
2801         return;
2802     }
2803 
2804     YFrameWindow* frame = iw->getFrame();
2805 
2806     int mrow, mcol, Mrow, Mcol; /* Minimum and maximum for rows and columns */
2807     int width, height; /* column width and row height */
2808     int drow, dcol; /* row and column directions */
2809     int *iconRow, *iconCol;
2810     const int margin = 4;
2811 
2812     if (miniIconsPlaceHorizontal) {
2813         getWorkArea(frame, &mcol, &mrow, &Mcol, &Mrow);
2814         width = iw->width() + 2 * margin;
2815         height = iw->height() + 2 * margin;
2816         drow = miniIconsBottomToTop ? -1 : +1;
2817         dcol = miniIconsRightToLeft ? -1 : +1;
2818         iconRow = iconY;
2819         iconCol = iconX;
2820     } else {
2821         getWorkArea(frame, &mrow, &mcol, &Mrow, &Mcol);
2822         width = iw->height() + 2 * margin;
2823         height = iw->width() + 2 * margin;
2824         drow = miniIconsRightToLeft ? -1 : +1;
2825         dcol = miniIconsBottomToTop ? -1 : +1;
2826         iconRow = iconX;
2827         iconCol = iconY;
2828     }
2829     if (inrange(*iconX, mcol, Mcol + 1 - (width - 2 * margin)) &&
2830         inrange(*iconY, mrow, Mrow + 1 - (height - 2 * margin)))
2831     {
2832         return;
2833     }
2834 
2835     /* Calculate start row and start column */
2836     int srow = (drow > 0) ? mrow : (Mrow - height);
2837     int scol = (dcol > 0) ? mcol : (Mcol - width);
2838 
2839     if (MiniIcon::fIcons.getCount() < 256) {
2840         int r = srow, c = scol;
2841         int omax = width * height, over = omax;
2842         YRect best(r, c, width, height);
2843         do {
2844             YRect geo(c, r, width, height);
2845             int olap = 0;
2846             for (MiniIcon* icon : MiniIcon::fIcons) {
2847                 if (icon != iw) {
2848                     olap += geo.overlap(icon->geometry());
2849                 }
2850             }
2851             if (olap < over) {
2852                 over = olap;
2853                 best = geo;
2854             }
2855             c += width * dcol;
2856             if (c >= Mcol - width / 2 || c < mcol - width / 2) {
2857                 c = scol;
2858                 r += height * drow;
2859                 if (r >= Mcol - height / 2 || r < mrow - height / 2) {
2860                     break;
2861                 }
2862             }
2863         } while (0 < over);
2864         if (over == 0) {
2865             *iconCol = best.x();
2866             *iconRow = best.y();
2867             if ((drow > 0 ? *iconRow > fIconRow : *iconRow < fIconRow)
2868                 || (fIconRow == *iconRow &&
2869                     (dcol > 0 ? *iconCol > fIconColumn
2870                       : *iconCol < fIconColumn))) {
2871                 fIconRow = *iconRow;
2872                 fIconColumn = *iconCol;
2873             }
2874             return;
2875         }
2876     }
2877 
2878     if ((fIconColumn == 0 && fIconRow == 0) ||
2879         !(inrange(fIconRow, mrow, Mrow - height) &&
2880           inrange(fIconColumn, mcol, Mcol - width)))
2881     {
2882         fIconRow = srow;
2883         fIconColumn = scol;
2884     }
2885 
2886     /* Return values */
2887     *iconRow = fIconRow + margin;
2888     *iconCol = fIconColumn + margin;
2889 
2890     /* Set row and column to new position */
2891     fIconColumn += width * dcol;
2892 
2893     int w2 = width / 2;
2894     if (fIconColumn >= Mcol - w2 || fIconColumn < mcol - w2) {
2895         fIconRow += height * drow;
2896         fIconColumn = scol;
2897         int h2 = height / 2;
2898         if (fIconRow >= Mrow - h2 || fIconRow < mrow - h2)
2899             fIconColumn = fIconRow = 0;
2900     }
2901 }
2902 
windowCount(long workspace)2903 int YWindowManager::windowCount(long workspace) {
2904     int count = 0;
2905 
2906     for (int layer = 0 ; layer < WinLayerCount; layer++) {
2907         for (YFrameWindow *frame = top(layer); frame; frame = frame->next()) {
2908             if (!frame->visibleOn(workspace))
2909                 continue;
2910             if (frame->frameOption(YFrameWindow::foIgnoreWinList))
2911                 continue;
2912             if (workspace != activeWorkspace() &&
2913                 frame->visibleNow())
2914                 continue;
2915             count++;
2916         }
2917     }
2918     return count;
2919 }
2920 
resetColormap(bool active)2921 void YWindowManager::resetColormap(bool active) {
2922     if (active) {
2923         if (colormapWindow() && colormapWindow()->client())
2924             installColormap(colormapWindow()->client()->colormap());
2925     } else {
2926         installColormap(None);
2927     }
2928 }
2929 
handleProperty(const XPropertyEvent & property)2930 void YWindowManager::handleProperty(const XPropertyEvent &property) {
2931     if (property.atom == _XA_ICEWM_HINT) {
2932         YProperty prop(this, _XA_ICEWM_HINT, F8, 8192, _XA_ICEWM_HINT, True);
2933         if (prop) {
2934             unsigned long nitems = prop.size();
2935             unsigned char* propdata = prop.data<unsigned char>();
2936             for (unsigned i = 0; i + 3 < nitems; ) {
2937                 const char* s[3] = { nullptr, nullptr, nullptr, };
2938                 for (int k = 0; k < 3 && i < nitems; ++k) {
2939                     s[k] = i + (const char *) propdata;
2940                     while (i < nitems && propdata[i++]);
2941                 }
2942                 if (s[0] && s[1] && s[2] && propdata[i - 1] == 0) {
2943                     hintOptions->setWinOption(s[0], s[1], s[2]);
2944                 }
2945             }
2946         }
2947     }
2948     else if (property.atom == _XA_NET_DESKTOP_NAMES) {
2949         MSG(("change: net desktop names"));
2950         readDesktopNames(false, true);
2951     }
2952     else if (property.atom == _XA_NET_DESKTOP_LAYOUT) {
2953         readDesktopLayout();
2954     }
2955 }
2956 
updateClientList()2957 void YWindowManager::updateClientList() {
2958     YArray<XID> ids;
2959     if (fLayeredUpdated || fCreatedUpdated) {
2960         ids.setCapacity(fCreationOrder.count());
2961     }
2962 
2963     if (fLayeredUpdated) {
2964         fLayeredUpdated = false;
2965 
2966         for (int i = 0; i < WinLayerCount; ++i) {
2967             if (fLayers[i]) {
2968                 YFrameIter frame = fLayers[i].reverseIterator();
2969                 while (++frame) {
2970                     if (frame->client() && frame->client()->adopted()) {
2971                         ids.append(frame->client()->handle());
2972                     }
2973                 }
2974             }
2975         }
2976 
2977         const int num = ids.getCount();
2978         Atom* data = num ? &*ids : nullptr;
2979         setProperty(_XA_NET_CLIENT_LIST_STACKING, XA_WINDOW, data, num);
2980     }
2981 
2982     if (fCreatedUpdated) {
2983         fCreatedUpdated = false;
2984 
2985         ids.shrink(0);
2986         for (YFrameIter frame = fCreationOrder.iterator(); ++frame; ) {
2987             if (frame->client() && frame->client()->adopted())
2988                 ids.append(frame->client()->handle());
2989         }
2990 
2991         const int num = ids.getCount();
2992         Atom* data = num ? &*ids : nullptr;
2993         setProperty(_XA_NET_CLIENT_LIST, XA_WINDOW, data, num);
2994     }
2995     checkLogout();
2996 }
2997 
updateUserTime(const UserTime & userTime)2998 void YWindowManager::updateUserTime(const UserTime& userTime) {
2999     if (userTime.good() && fLastUserTime < userTime)
3000         fLastUserTime = userTime;
3001 }
3002 
checkLogout()3003 void YWindowManager::checkLogout() {
3004     if (fShuttingDown && !haveClients()) {
3005         fShuttingDown = false; /* Only run the command once */
3006 
3007         if (rebootOrShutdown == Reboot && nonempty(rebootCommand)) {
3008             msg("reboot... (%s)", rebootCommand);
3009             smActionListener->runCommand(rebootCommand);
3010         } else if (rebootOrShutdown == Shutdown && nonempty(shutdownCommand)) {
3011             msg("shutdown ... (%s)", shutdownCommand);
3012             smActionListener->runCommand(shutdownCommand);
3013         } else if (rebootOrShutdown == Logout)
3014             app->exit(0);
3015     }
3016 }
3017 
removeClientFrame(YFrameWindow * frame)3018 void YWindowManager::removeClientFrame(YFrameWindow *frame) {
3019     if (fArrangeInfo) {
3020         for (int i = 0; i < fArrangeCount; i++)
3021             if (fArrangeInfo[i].frame == frame)
3022                 fArrangeInfo[i].frame = nullptr;
3023     }
3024     for (int w = 0; w < workspaceCount(); w++) {
3025         if (workspaces[w].focused == frame) {
3026             workspaces[w].focused = nullptr;
3027         }
3028     }
3029     if (wmState() == wmRUNNING) {
3030         if (frame == getFocus())
3031             focusLastWindow();
3032         if (frame == getFocus())
3033             setFocus(nullptr);
3034         if (colormapWindow() == frame)
3035             setColormapWindow(getFocus());
3036         if (frame->affectsWorkArea())
3037             updateWorkArea();
3038         if (switchWindowVisible())
3039             fSwitchWindow->destroyedFrame(frame);
3040     }
3041 }
3042 
notifyActive(YFrameWindow * frame)3043 void YWindowManager::notifyActive(YFrameWindow *frame) {
3044     Window win = frame ? frame->client()->handle() : None;
3045     if (win != fActiveWindow && (win || wmState() == wmRUNNING)) {
3046         setProperty(_XA_NET_ACTIVE_WINDOW, XA_WINDOW, win);
3047         fActiveWindow = win;
3048     }
3049 }
3050 
switchFocusTo(YFrameWindow * frame,bool reorderFocus)3051 void YWindowManager::switchFocusTo(YFrameWindow *frame, bool reorderFocus) {
3052 
3053     if (frame != fFocusWin) {
3054         if (fFocusWin) {
3055             YFrameWindow* f = fFocusWin;
3056             fFocusWin = nullptr;
3057             f->loseWinFocus();
3058         }
3059         if (frame) {
3060             fFocusWin = frame;
3061             fFocusWin->setWinFocus();
3062         }
3063 
3064         workspaces[activeWorkspace()].focused = frame;
3065     }
3066     if (frame && reorderFocus) {
3067         raiseFocusFrame(frame);
3068     }
3069     notifyActive(frame);
3070     updateClientList();
3071 }
3072 
switchFocusFrom(YFrameWindow * frame)3073 void YWindowManager::switchFocusFrom(YFrameWindow *frame) {
3074     if (fFocusWin == frame && frame) {
3075         fFocusWin = nullptr;
3076         frame->loseWinFocus();
3077     }
3078 }
3079 
popupWindowListMenu(YWindow * owner,int x,int y)3080 void YWindowManager::popupWindowListMenu(YWindow *owner, int x, int y) {
3081     windowListMenu->popup(owner, nullptr, nullptr, x, y,
3082                           YPopupWindow::pfCanFlipVertical |
3083                           YPopupWindow::pfCanFlipHorizontal |
3084                           YPopupWindow::pfPopupMenu);
3085 }
3086 
popupStartMenu(YWindow * owner)3087 void YWindowManager::popupStartMenu(YWindow *owner) { // !! fix
3088     if (showTaskBar && taskBar && taskBarShowStartMenu)
3089         taskBar->popupStartMenu();
3090     else
3091         rootMenu->popup(owner, nullptr, nullptr, 0, 0,
3092                         YPopupWindow::pfCanFlipVertical |
3093                         YPopupWindow::pfCanFlipHorizontal |
3094                         YPopupWindow::pfPopupMenu);
3095 }
3096 
popupWindowListMenu(YWindow * owner)3097 void YWindowManager::popupWindowListMenu(YWindow *owner) {
3098     if (showTaskBar && taskBar && taskBarShowWindowListMenu)
3099         taskBar->popupWindowListMenu();
3100     else
3101         popupWindowListMenu(owner, 0, 0);
3102 }
3103 
switchToWorkspace(long nw,bool takeCurrent)3104 void YWindowManager::switchToWorkspace(long nw, bool takeCurrent) {
3105     if (nw >= 0 && nw < workspaceCount()) {
3106         lockWorkArea();
3107         YFrameWindow *frame = getFocus();
3108         if (takeCurrent && frame && !frame->isAllWorkspaces()) {
3109             lockFocus();
3110             frame->wmOccupyAll();
3111             frame->wmRaise();
3112             activateWorkspace(nw);
3113             frame->wmOccupyWorkspace(nw);
3114             unlockFocus();
3115             frame->wmRaise();
3116             setFocus(frame);
3117         } else {
3118             activateWorkspace(nw);
3119         }
3120         unlockWorkArea();
3121         if (taskBar) {
3122             taskBar->workspacesRepaint();
3123         }
3124     }
3125 }
3126 
switchToPrevWorkspace(bool takeCurrent)3127 void YWindowManager::switchToPrevWorkspace(bool takeCurrent) {
3128     long nw = (activeWorkspace() + workspaceCount() - 1) % workspaceCount();
3129 
3130     switchToWorkspace(nw, takeCurrent);
3131 }
3132 
switchToNextWorkspace(bool takeCurrent)3133 void YWindowManager::switchToNextWorkspace(bool takeCurrent) {
3134     long nw = (activeWorkspace() + 1) % workspaceCount();
3135 
3136     switchToWorkspace(nw, takeCurrent);
3137 }
3138 
switchToLastWorkspace(bool takeCurrent)3139 void YWindowManager::switchToLastWorkspace(bool takeCurrent) {
3140     switchToWorkspace(lastWorkspace(), takeCurrent);
3141 }
3142 
tilePlace(YFrameWindow * w,int tx,int ty,int tw,int th)3143 void YWindowManager::tilePlace(YFrameWindow *w, int tx, int ty, int tw, int th) {
3144     long mask = WinStateUnmapped | WinStateMaximizedBoth;
3145     if (w->hasState(mask)) {
3146         w->setState(mask, 0);
3147     }
3148     int bx = w->borderXN();
3149     int bb = w->borderYN();
3150     int bt = bb + w->titleYN();
3151     int vo = min(bb, int(topSideVerticalOffset));
3152     int cw = tw - bx - bx;
3153     int ch = th - bb - (bt - vo);
3154     w->client()->constrainSize(cw, ch, None);
3155     int ow = cw + bx + bx;
3156     int oh = ch + bb + bt;
3157     w->setNormalGeometryOuter(tx, ty - vo, ow, oh);
3158 }
3159 
tileWindows(YArrange arrange,bool vertical)3160 void YWindowManager::tileWindows(YArrange arrange, bool vertical) {
3161     if (saveArrange(arrange) == false)
3162         return;
3163 
3164     int curWin = 0;
3165     int cols = 1;
3166 
3167     while (cols * cols <= arrange.size())
3168         cols++;
3169     cols--;
3170 
3171     int areaX, areaY, areaW, areaH;
3172 
3173     int mx, my, Mx, My;
3174     getWorkArea(arrange[0], &mx, &my, &Mx, &My);
3175 
3176     if (vertical) { // swap meaning of rows/cols
3177         areaY = mx;
3178         areaX = my;
3179         areaH = Mx - mx;
3180         areaW = My - my;
3181     } else {
3182         areaX = mx;
3183         areaY = my;
3184         areaW = Mx - mx;
3185         areaH = My - my;
3186     }
3187 
3188     int normalRows = arrange.size() / cols;
3189     int normalWidth = areaW / cols;
3190     int windowX = areaX;
3191 
3192     for (int col = 0; col < cols; col++) {
3193         int rows = normalRows;
3194         int windowWidth = normalWidth;
3195         int windowY = areaY;
3196 
3197         if (col >= (cols * (1 + normalRows) - arrange.size()))
3198             rows++;
3199         if (col >= (cols * (1 + normalWidth) - areaW))
3200             windowWidth++;
3201 
3202         int normalHeight = areaH / max(1, rows);
3203 
3204         for (int row = 0; row < rows; row++) {
3205             int windowHeight = normalHeight;
3206 
3207             if (row >= (rows * (1 + normalHeight) - areaH))
3208                 windowHeight++;
3209 
3210             if (vertical) // swap meaning of rows/cols
3211                 tilePlace(arrange[curWin++],
3212                           windowY, windowX, windowHeight, windowWidth);
3213             else
3214                 tilePlace(arrange[curWin++],
3215                           windowX, windowY, windowWidth, windowHeight);
3216 
3217             windowY += windowHeight;
3218         }
3219         windowX += windowWidth;
3220     }
3221 }
3222 
getWindowsToArrange(bool all,bool skipNonMinimizable)3223 YArrange YWindowManager::getWindowsToArrange(bool all, bool skipNonMinimizable)
3224 {
3225     int capacity = focusedCount() + 1;
3226     int indexarr = 0;
3227     YFrameWindow** arrange = new YFrameWindow *[capacity];
3228     if (capacity && arrange) {
3229         for (YFrameWindow *w = topLayer(WinLayerOnTop);
3230              w && w->getActiveLayer() >= WinLayerBelow;
3231              w = w->nextLayer()) {
3232             if (w->owner() == nullptr && // not transient ?
3233                 w->visibleNow() && // visible
3234                 (all || !w->isAllWorkspaces()) && // not on all workspaces
3235                 !w->isUnmapped() &&
3236                 (!skipNonMinimizable || w->canMinimize()) &&
3237                 indexarr + 1 < capacity)
3238             {
3239                 arrange[indexarr] = w;
3240                 indexarr++;
3241             }
3242         }
3243         arrange[indexarr] = nullptr;
3244     }
3245     if (indexarr == 0) {
3246         delete[] arrange;
3247         arrange = nullptr;
3248     }
3249     return YArrange(arrange, indexarr);
3250 }
3251 
saveArrange(YArrange arrange)3252 bool YWindowManager::saveArrange(YArrange arrange) {
3253     if (arrange.size() != fArrangeCount || fArrangeInfo == nullptr) {
3254         delete[] fArrangeInfo;
3255         fArrangeCount = arrange.size();
3256         fArrangeInfo = new WindowPosState[fArrangeCount];
3257     }
3258     if (fArrangeInfo) {
3259         WindowPosState* info = fArrangeInfo;
3260         for (YFrameWindow* frame : arrange) {
3261             info->x = frame->x();
3262             info->y = frame->y();
3263             info->w = frame->width();
3264             info->h = frame->height();
3265             info->state = frame->getState();
3266             info->frame = frame;
3267             info++;
3268         }
3269     }
3270     setShowingDesktop(false);
3271     return 0 < fArrangeCount;
3272 }
3273 
undoArrange()3274 void YWindowManager::undoArrange() {
3275     if (fArrangeInfo && 0 < fArrangeCount) {
3276         lockFocus();
3277         for (int i = 0; i < fArrangeCount; i++) {
3278             WindowPosState info(fArrangeInfo[i]);
3279             YFrameWindow* f = info.frame;
3280             if (f && (f->getState() & WIN_STATE_ALL) != info.state) {
3281                 f->setState(WIN_STATE_ALL, info.state);
3282             }
3283             if (f) {
3284                 f->setNormalGeometryOuter(info.x, info.y, info.w, info.h);
3285             }
3286         }
3287         delete [] fArrangeInfo; fArrangeInfo = nullptr;
3288         fArrangeCount = 0;
3289         unlockFocus();
3290         focusTopWindow();
3291     }
3292     setShowingDesktop(false);
3293 }
3294 
tileWindows(bool vertical)3295 void YWindowManager::tileWindows(bool vertical) {
3296     YArrange arrange = getWindowsToArrange();
3297     if (arrange) {
3298         tileWindows(arrange, vertical);
3299         arrange.discard();
3300     }
3301 }
3302 
arrangeWindows()3303 void YWindowManager::arrangeWindows() {
3304     YArrange arrange = getWindowsToArrange();
3305     if (arrange) {
3306         smartPlace(arrange);
3307         arrange.discard();
3308     }
3309 }
3310 
actionWindows(YAction action)3311 void YWindowManager::actionWindows(YAction action) {
3312     YArrange arrange = getWindowsToArrange();
3313     if (arrange) {
3314         setWindows(arrange, action);
3315         arrange.discard();
3316     }
3317 }
3318 
toggleDesktop()3319 void YWindowManager::toggleDesktop() {
3320     YArrange arrange = getWindowsToArrange(true, true);
3321     if (arrange) {
3322         setWindows(arrange, actionMinimizeAll);
3323         setShowingDesktop(true);
3324         arrange.discard();
3325     } else {
3326         undoArrange();
3327         setShowingDesktop(false);
3328     }
3329 }
3330 
cascadeWindows()3331 void YWindowManager::cascadeWindows() {
3332     YArrange arrange = getWindowsToArrange(true, true);
3333     if (arrange) {
3334         cascadePlace(arrange);
3335         arrange.discard();
3336     }
3337 }
3338 
haveClients()3339 bool YWindowManager::haveClients() {
3340     for (YFrameWindow * f(topLayer()); f ; f = f->nextLayer())
3341         if (f->canClose() && f->client()->adopted())
3342             return true;
3343 
3344     return false;
3345 }
3346 
exitAfterLastClient(bool shuttingDown)3347 void YWindowManager::exitAfterLastClient(bool shuttingDown) {
3348     fShuttingDown = shuttingDown;
3349     checkLogout();
3350 }
3351 
updateKeyboard(int configIndex)3352 void YWindowManager::updateKeyboard(int configIndex) {
3353     setKeyboard(configIndex);
3354 }
3355 
reflectKeyboard(int configIndex,mstring keyboard)3356 void YWindowManager::reflectKeyboard(int configIndex, mstring keyboard) {
3357     fDefaultKeyboard = configIndex;
3358     fCurrentKeyboard = keyboard;
3359 }
3360 
setKeyboard(int configIndex)3361 void YWindowManager::setKeyboard(int configIndex) {
3362     if (inrange(configIndex, 0, configKeyboards.getCount() - 1)) {
3363         fDefaultKeyboard = configIndex;
3364         setKeyboard(configKeyboards[configIndex]);
3365     }
3366 }
3367 
setKeyboard(mstring keyboard)3368 void YWindowManager::setKeyboard(mstring keyboard) {
3369     if (keyboard != null && keyboard != fCurrentKeyboard) {
3370         fCurrentKeyboard = keyboard;
3371         auto program = "setxkbmap";
3372         csmart path(path_lookup(program));
3373         if (path) {
3374             wordexp_t exp = {};
3375             exp.we_offs = 1;
3376             if (wordexp(keyboard, &exp, WRDE_NOCMD | WRDE_DOOFFS) == 0) {
3377                 exp.we_wordv[0] = strdup(program);
3378                 wmapp->runProgram(program, exp.we_wordv);
3379                 wordfree(&exp);
3380             }
3381             if (taskBar) {
3382                 taskBar->keyboardUpdate(keyboard);
3383             }
3384         }
3385         else if (ONCE) {
3386             new YMsgBox(YMsgBox::mbOK,
3387                         _("Missing program setxkbmap"),
3388                         _("For keyboard switching, please install setxkbmap."),
3389                         this, "key");
3390         }
3391     }
3392 }
3393 
getKeyboard()3394 mstring YWindowManager::getKeyboard() {
3395     return fCurrentKeyboard;
3396 }
3397 
kbLayout()3398 void YWindowManager::kbLayout() {
3399     fLayoutTimer->setTimer(100L, this, true);
3400 }
3401 
handleMsgBox(YMsgBox * msgbox,int operation)3402 void YWindowManager::handleMsgBox(YMsgBox *msgbox, int operation) {
3403     msgbox->unmanage();
3404 }
3405 
EdgeSwitch(YWindowManager * manager,int delta,bool vertical)3406 EdgeSwitch::EdgeSwitch(YWindowManager *manager, int delta, bool vertical):
3407     YDndWindow(desktop),
3408     fManager(manager),
3409     fCursor(delta < 0 ? vertical ? YWMApp::scrollUpPointer
3410                                  : YWMApp::scrollLeftPointer
3411                       : vertical ? YWMApp::scrollDownPointer
3412                                  : YWMApp::scrollRightPointer),
3413     fDelta(delta),
3414     fVert(vertical)
3415 {
3416     setStyle(wsOverrideRedirect | wsInputOnly);
3417     setPointer(YWMApp::leftPointer);
3418     setGeometry();
3419     setTitle("IceEdgeSwitch");
3420     setDND(true);
3421     show();
3422 }
3423 
~EdgeSwitch()3424 EdgeSwitch::~EdgeSwitch() {
3425 }
3426 
setGeometry()3427 void EdgeSwitch::setGeometry() {
3428     int x = (!fVert && 0 < fDelta) ? int(desktop->width() - 1) : 0;
3429     int y = (fVert && 0 < fDelta) ? int(desktop->height() - 1) : 0;
3430     unsigned w = !fVert ? 1 : desktop->width();
3431     unsigned h = fVert ? 1 : desktop->height();
3432 
3433     YWindow::setGeometry(YRect(x, y, w, h));
3434 }
3435 
handleCrossing(const XCrossingEvent & crossing)3436 void EdgeSwitch::handleCrossing(const XCrossingEvent &crossing) {
3437     if (crossing.type == EnterNotify && crossing.mode == NotifyNormal) {
3438         fEdgeSwitchTimer->setTimer(edgeSwitchDelay, this, true);
3439         setPointer(fCursor);
3440     } else if (crossing.type == LeaveNotify && crossing.mode == NotifyNormal) {
3441         if (fEdgeSwitchTimer && fEdgeSwitchTimer->getTimerListener() == this) {
3442             fEdgeSwitchTimer = null;
3443             setPointer(YWMApp::leftPointer);
3444         }
3445     }
3446 }
3447 
handleDNDEnter()3448 void EdgeSwitch::handleDNDEnter() {
3449     fEdgeSwitchTimer->setTimer(edgeSwitchDelay, this, true);
3450 }
3451 
handleDNDLeave()3452 void EdgeSwitch::handleDNDLeave() {
3453     if (fEdgeSwitchTimer && fEdgeSwitchTimer->getTimerListener() == this) {
3454         fEdgeSwitchTimer = null;
3455     }
3456 }
3457 
handleTimer(YTimer * t)3458 bool EdgeSwitch::handleTimer(YTimer *t) {
3459     if (t != fEdgeSwitchTimer)
3460         return false;
3461 
3462     int worksps = workspaceCount;
3463     int orient  = fManager->layout().orient;
3464     int columns = min(fManager->layout().columns, worksps);
3465     int rows    = min(fManager->layout().rows, worksps);
3466     int corner  = fManager->layout().corner;
3467 
3468     if (rows == 0)
3469         rows = (worksps + (columns - 1)) / non_zero(columns);
3470     if (columns == 0)
3471         columns = (worksps + (rows - 1)) / non_zero(rows);
3472     if (orient == _NET_WM_ORIENTATION_VERT) {
3473         columns = (worksps + (rows - 1)) / non_zero(rows);
3474     } else {
3475         rows = (worksps + (columns - 1)) / non_zero(columns);
3476     }
3477 
3478     int dx = fVert ? 0 : fDelta;
3479     int dy = fVert ? fDelta : 0;
3480     if (corner == _NET_WM_TOPRIGHT || corner == _NET_WM_BOTTOMRIGHT)
3481         dx = -dx;
3482     if (corner == _NET_WM_BOTTOMLEFT || corner == _NET_WM_BOTTOMRIGHT)
3483         dy = -dy;
3484     if (orient == _NET_WM_ORIENTATION_VERT) {
3485         swap(rows, columns);
3486         swap(dx, dy);
3487     }
3488 
3489     int col = fManager->activeWorkspace() % non_zero(columns);
3490     int row = fManager->activeWorkspace() / non_zero(columns);
3491 
3492     if (dx == 0 && rows == 1) {
3493         swap(dx, dy);
3494     }
3495 
3496     int wsp, end(100);
3497     do {
3498         if (0 == --end) {
3499             tlog("inflp");
3500             goto end;
3501         }
3502 
3503         col = (col + dx + columns) % columns;
3504         row = (row + dy + rows) % rows;
3505         wsp = col + row * columns;
3506     } while (wsp >= worksps);
3507 
3508     fManager->switchToWorkspace(wsp, false);
3509 
3510     if (warpPointerOnEdgeSwitch) {
3511         int dest_x = -fDelta * !fVert * (desktop->width() - 5);
3512         int dest_y = -fDelta * fVert * (desktop->height() - 5);
3513         XWarpPointer(xapp->display(), None, None, 0, 0, 0, 0,
3514                      dest_x, dest_y);
3515     }
3516 
3517     if (edgeContWorkspaceSwitching) {
3518         return true;
3519     }
3520 
3521 end:
3522     setPointer(YWMApp::leftPointer);
3523     return false;
3524 }
3525 
getSwitchScreen()3526 int YWindowManager::getSwitchScreen() {
3527     int s = fFocusWin ? fFocusWin->getScreen() : xineramaPrimaryScreen;
3528     return inrange(s, 0, getScreenCount() - 1) ? s : 0;
3529 }
3530 
doWMAction(WMAction action,bool putback)3531 void YWindowManager::doWMAction(WMAction action, bool putback) {
3532     XClientMessageEvent xev;
3533     memset(&xev, 0, sizeof(xev));
3534 
3535     xev.type = ClientMessage;
3536     xev.window = xapp->root();
3537     xev.message_type = _XA_ICEWM_ACTION;
3538     xev.format = 32;
3539     xev.data.l[0] = CurrentTime;
3540     xev.data.l[1] = action;
3541 
3542     if (putback) {
3543         XEvent event;
3544         event.xclient = xev;
3545         XPutBackEvent(xapp->display(), &event);
3546     }
3547     else {
3548         xapp->send(xev, xapp->root(), SubstructureNotifyMask);
3549     }
3550 }
3551 
3552 #ifdef CONFIG_XRANDR
handleRRScreenChangeNotify(const XRRScreenChangeNotifyEvent & xrrsc)3553 void YWindowManager::handleRRScreenChangeNotify(const XRRScreenChangeNotifyEvent &xrrsc) {
3554     // logRandrScreen((const union _XEvent&) xrrsc);
3555     updateScreenSize((XEvent *)&xrrsc);
3556 }
3557 
handleRRNotify(const XRRNotifyEvent & notify)3558 void YWindowManager::handleRRNotify(const XRRNotifyEvent &notify) {
3559     // logRandrNotify((const union _XEvent&) notify);
3560 }
3561 
updateScreenSize(XEvent * event)3562 void YWindowManager::updateScreenSize(XEvent *event) {
3563     unsigned nw = width();
3564     unsigned nh = height();
3565 
3566     XRRUpdateConfiguration(event);
3567 
3568     if (updateXineramaInfo(nw, nh)) {
3569         MSG(("xrandr: %d %d", nw, nh));
3570         Atom data[2] = { nw, nh };
3571         setProperty(_XA_NET_DESKTOP_GEOMETRY, XA_CARDINAL, data, 2);
3572         setSize(nw, nh);
3573         updateWorkArea();
3574         if (taskBar && pagerShowPreview) {
3575             taskBar->workspacesUpdateButtons();
3576         }
3577         if (taskBar) {
3578             taskBar->relayout();
3579             taskBar->relayoutNow();
3580         }
3581         for (int i = 0; i < edges.getCount(); ++i)
3582             edges[i]->setGeometry();
3583 
3584         /// TODO #warning "make something better"
3585         if (arrangeWindowsOnScreenSizeChange) {
3586             wmActionListener->actionPerformed(actionArrange, 0);
3587         }
3588     }
3589 
3590     refresh();
3591 }
3592 #endif
3593 
refresh()3594 void YWindowManager::refresh() {
3595     if (taskBar) {
3596         taskBar->refresh();
3597     }
3598     for (YFrameIter frame(focusedIterator()); ++frame; ) {
3599         if (frame->visibleNow()) {
3600             frame->refresh();
3601         }
3602     }
3603 }
3604 
appendCreatedFrame(YFrameWindow * f)3605 void YWindowManager::appendCreatedFrame(YFrameWindow *f) {
3606     fCreationOrder.append(f);
3607     fCreatedUpdated = true;
3608 }
3609 
removeCreatedFrame(YFrameWindow * f)3610 void YWindowManager::removeCreatedFrame(YFrameWindow *f) {
3611     fCreationOrder.remove(f);
3612     fCreatedUpdated = true;
3613 }
3614 
insertFocusFrame(YFrameWindow * frame,bool focused)3615 void YWindowManager::insertFocusFrame(YFrameWindow* frame, bool focused) {
3616     if (focused || fFocusedOrder.count() < 1) {
3617         fFocusedOrder.append(frame);
3618     }
3619     else {
3620         fFocusedOrder.insertBefore(frame, fFocusedOrder.back());
3621     }
3622 }
3623 
removeFocusFrame(YFrameWindow * frame)3624 void YWindowManager::removeFocusFrame(YFrameWindow* frame) {
3625     fFocusedOrder.remove(frame);
3626 }
3627 
lowerFocusFrame(YFrameWindow * frame)3628 void YWindowManager::lowerFocusFrame(YFrameWindow* frame) {
3629     if (frame->YFocusedNode::nodePrev()) {
3630         fFocusedOrder.remove(frame);
3631         fFocusedOrder.prepend(frame);
3632     }
3633 }
3634 
raiseFocusFrame(YFrameWindow * frame)3635 void YWindowManager::raiseFocusFrame(YFrameWindow* frame) {
3636     if (frame->YFocusedNode::nodeNext()) {
3637         fFocusedOrder.remove(frame);
3638         fFocusedOrder.append(frame);
3639     }
3640 }
3641 
3642 // vim: set sw=4 ts=4 et:
3643