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 ¬ify) {
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