1 /*
2 * Copyright �� 2005 Novell, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Novell, Inc. not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior permission.
11 * Novell, Inc. makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *
15 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author: David Reveman <davidr@novell.com>
24 */
25
26 #include <X11/Xlib.h>
27 #include <X11/Xatom.h>
28 #include <X11/Xproto.h>
29 #include <X11/extensions/shape.h>
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <assert.h>
37 #include <math.h>
38
39 #include <boost/bind.hpp>
40
41 #include <core/icon.h>
42 #include <core/atoms.h>
43 #include "core/windowconstrainment.h"
44 #include "privatewindow.h"
45 #include "privatescreen.h"
46 #include "privatestackdebugger.h"
47
48 #include <boost/scoped_array.hpp>
49
50 template class WrapableInterface<CompWindow, WindowInterface>;
51
52 PluginClassStorage::Indices windowPluginClassIndices (0);
53
54 unsigned int
55 CompWindow::allocPluginClassIndex ()
56 {
57 unsigned int i = PluginClassStorage::allocatePluginClassIndex (windowPluginClassIndices);
58
59 foreach (CompWindow *w, screen->windows ())
60 if (windowPluginClassIndices.size () != w->pluginClasses.size ())
61 w->pluginClasses.resize (windowPluginClassIndices.size ());
62
63 return i;
64 }
65
66 void
67 CompWindow::freePluginClassIndex (unsigned int index)
68 {
69 PluginClassStorage::freePluginClassIndex (windowPluginClassIndices, index);
70
71 foreach (CompWindow *w, ::screen->windows ())
72 if (windowPluginClassIndices.size () != w->pluginClasses.size ())
73 w->pluginClasses.resize (windowPluginClassIndices.size ());
74 }
75
76 inline bool
77 PrivateWindow::isInvisible() const
78 {
79 return attrib.map_state != IsViewable ||
80 attrib.x + geometry.width () + output.right <= 0 ||
81 attrib.y + geometry.height () + output.bottom <= 0 ||
82 attrib.x - output.left >= (int) screen->width () ||
83 attrib.y - output.top >= (int) screen->height ();
84 }
85
86 bool
87 PrivateWindow::isAncestorTo (CompWindow *transient,
88 CompWindow *ancestor)
89 {
90 if (transient->priv->transientFor)
91 {
92 if (transient->priv->transientFor == ancestor->priv->id)
93 return true;
94
95 transient = screen->findWindow (transient->priv->transientFor);
96 if (transient)
97 return isAncestorTo (transient, ancestor);
98 }
99
100 return false;
101 }
102
103 void
104 PrivateWindow::recalcNormalHints ()
105 {
106 int maxSize;
107
108 /* FIXME to max Texture size */
109 maxSize = MAXSHORT;
110 maxSize -= serverGeometry.border () * 2;
111
112 sizeHints.x = serverGeometry.x ();
113 sizeHints.y = serverGeometry.y ();
114 sizeHints.width = serverGeometry.width ();
115 sizeHints.height = serverGeometry.height ();
116
117 if (!(sizeHints.flags & PBaseSize))
118 {
119 if (sizeHints.flags & PMinSize)
120 {
121 sizeHints.base_width = sizeHints.min_width;
122 sizeHints.base_height = sizeHints.min_height;
123 }
124 else
125 {
126 sizeHints.base_width = 0;
127 sizeHints.base_height = 0;
128 }
129
130 sizeHints.flags |= PBaseSize;
131 }
132
133 if (!(sizeHints.flags & PMinSize))
134 {
135 sizeHints.min_width = sizeHints.base_width;
136 sizeHints.min_height = sizeHints.base_height;
137 sizeHints.flags |= PMinSize;
138 }
139
140 if (!(sizeHints.flags & PMaxSize))
141 {
142 sizeHints.max_width = 65535;
143 sizeHints.max_height = 65535;
144 sizeHints.flags |= PMaxSize;
145 }
146
147 if (sizeHints.max_width < sizeHints.min_width)
148 sizeHints.max_width = sizeHints.min_width;
149
150 if (sizeHints.max_height < sizeHints.min_height)
151 sizeHints.max_height = sizeHints.min_height;
152
153 if (sizeHints.min_width < 1)
154 sizeHints.min_width = 1;
155
156 if (sizeHints.max_width < 1)
157 sizeHints.max_width = 1;
158
159 if (sizeHints.min_height < 1)
160 sizeHints.min_height = 1;
161
162 if (sizeHints.max_height < 1)
163 sizeHints.max_height = 1;
164
165 if (sizeHints.max_width > maxSize)
166 sizeHints.max_width = maxSize;
167
168 if (sizeHints.max_height > maxSize)
169 sizeHints.max_height = maxSize;
170
171 if (sizeHints.min_width > maxSize)
172 sizeHints.min_width = maxSize;
173
174 if (sizeHints.min_height > maxSize)
175 sizeHints.min_height = maxSize;
176
177 if (sizeHints.base_width > maxSize)
178 sizeHints.base_width = maxSize;
179
180 if (sizeHints.base_height > maxSize)
181 sizeHints.base_height = maxSize;
182
183 if (sizeHints.flags & PResizeInc)
184 {
185 if (sizeHints.width_inc == 0)
186 sizeHints.width_inc = 1;
187
188 if (sizeHints.height_inc == 0)
189 sizeHints.height_inc = 1;
190 }
191 else
192 {
193 sizeHints.width_inc = 1;
194 sizeHints.height_inc = 1;
195 sizeHints.flags |= PResizeInc;
196 }
197
198 if (sizeHints.flags & PAspect)
199 {
200 /* don't divide by 0 */
201 if (sizeHints.min_aspect.y < 1)
202 sizeHints.min_aspect.y = 1;
203
204 if (sizeHints.max_aspect.y < 1)
205 sizeHints.max_aspect.y = 1;
206 }
207 else
208 {
209 sizeHints.min_aspect.x = 1;
210 sizeHints.min_aspect.y = 65535;
211 sizeHints.max_aspect.x = 65535;
212 sizeHints.max_aspect.y = 1;
213 sizeHints.flags |= PAspect;
214 }
215
216 if (!(sizeHints.flags & PWinGravity))
217 {
218 sizeHints.win_gravity = NorthWestGravity;
219 sizeHints.flags |= PWinGravity;
220 }
221 }
222
223 void
224 PrivateWindow::updateNormalHints ()
225 {
226 Status status;
227 long supplied;
228
229 status = XGetWMNormalHints (screen->dpy (), priv->id,
230 &priv->sizeHints, &supplied);
231
232 if (!status)
233 priv->sizeHints.flags = 0;
234
235 priv->recalcNormalHints ();
236 }
237
238 void
239 PrivateWindow::updateWmHints ()
240 {
241 XWMHints *newHints;
242 long dFlags = 0;
243 bool iconChanged = false;
244
245 if (hints)
246 dFlags = hints->flags;
247
248 inputHint = true;
249
250 newHints = XGetWMHints (screen->dpy (), id);
251 if (newHints)
252 {
253 dFlags ^= newHints->flags;
254
255 if (newHints->flags & InputHint)
256 inputHint = newHints->input;
257
258 if (hints)
259 {
260 if ((newHints->flags & IconPixmapHint) &&
261 (hints->icon_pixmap != newHints->icon_pixmap))
262 {
263 iconChanged = true;
264 }
265 else if ((newHints->flags & IconMaskHint) &&
266 (hints->icon_mask != newHints->icon_mask))
267 {
268 iconChanged = true;
269 }
270 }
271 }
272
273 iconChanged |= (dFlags & (IconPixmapHint | IconMaskHint));
274
275 if (iconChanged)
276 freeIcons ();
277
278 if (hints)
279 XFree (hints);
280
281 hints = newHints;
282 }
283
284 void
285 PrivateWindow::updateClassHints ()
286 {
287 XClassHint classHint;
288 int status;
289
290 if (priv->resName)
291 {
292 free (priv->resName);
293 priv->resName = NULL;
294 }
295
296 if (priv->resClass)
297 {
298 free (priv->resClass);
299 priv->resClass = NULL;
300 }
301
302 status = XGetClassHint (screen->dpy (),
303 priv->id, &classHint);
304 if (status)
305 {
306 if (classHint.res_name)
307 {
308 priv->resName = strdup (classHint.res_name);
309 XFree (classHint.res_name);
310 }
311
312 if (classHint.res_class)
313 {
314 priv->resClass = strdup (classHint.res_class);
315 XFree (classHint.res_class);
316 }
317 }
318 }
319
320 void
321 PrivateWindow::updateTransientHint ()
322 {
323 Window transientFor;
324 Status status;
325
326 priv->transientFor = None;
327
328 status = XGetTransientForHint (screen->dpy (),
329 priv->id, &transientFor);
330
331 if (status)
332 {
333 CompWindow *ancestor;
334
335 ancestor = screen->findWindow (transientFor);
336 if (!ancestor)
337 return;
338
339 /* protect against circular transient dependencies */
340 if (transientFor == priv->id ||
341 PrivateWindow::isAncestorTo (ancestor, window))
342 return;
343
344 priv->transientFor = transientFor;
345 }
346 }
347
348 void
349 PrivateWindow::updateIconGeometry ()
350 {
351 Atom actual;
352 int result, format;
353 unsigned long n, left;
354 unsigned char *data;
355
356 priv->iconGeometry.setGeometry (0, 0, 0, 0);
357
358 result = XGetWindowProperty (screen->dpy (), priv->id,
359 Atoms::wmIconGeometry,
360 0L, 1024L, False, XA_CARDINAL,
361 &actual, &format, &n, &left, &data);
362
363 if (result == Success && data)
364 {
365 if (n == 4)
366 {
367 unsigned long *geometry = (unsigned long *) data;
368
369 priv->iconGeometry.setX (geometry[0]);
370 priv->iconGeometry.setY (geometry[1]);
371 priv->iconGeometry.setWidth (geometry[2]);
372 priv->iconGeometry.setHeight (geometry[3]);
373 }
374
375 XFree (data);
376 }
377 }
378
379 Window
380 PrivateWindow::getClientLeaderOfAncestor ()
381 {
382 if (transientFor)
383 {
384 CompWindow *w = screen->findWindow (transientFor);
385 if (w)
386 {
387 if (w->priv->clientLeader)
388 return w->priv->clientLeader;
389
390 return w->priv->getClientLeaderOfAncestor ();
391 }
392 }
393
394 return None;
395 }
396
397 Window
398 PrivateWindow::getClientLeader ()
399 {
400 Atom actual;
401 int result, format;
402 unsigned long n, left;
403 unsigned char *data;
404
405 result = XGetWindowProperty (screen->dpy (), priv->id,
406 Atoms::wmClientLeader,
407 0L, 1L, False, XA_WINDOW, &actual, &format,
408 &n, &left, &data);
409
410 if (result == Success && data)
411 {
412 Window win = None;
413
414 if (n)
415 memcpy (&win, data, sizeof (Window));
416
417 XFree ((void *) data);
418
419 if (win)
420 return win;
421 }
422
423 return priv->getClientLeaderOfAncestor ();
424 }
425
426 char *
427 PrivateWindow::getStartupId ()
428 {
429 Atom actual;
430 int result, format;
431 unsigned long n, left;
432 unsigned char *data;
433
434 result = XGetWindowProperty (screen->dpy (), priv->id,
435 Atoms::startupId,
436 0L, 1024L, False,
437 Atoms::utf8String,
438 &actual, &format,
439 &n, &left, &data);
440
441 if (result == Success && data)
442 {
443 char *id = NULL;
444
445 if (n)
446 id = strdup ((char *) data);
447 XFree ((void *) data);
448
449 return id;
450 }
451
452 return NULL;
453 }
454
455 void
456 PrivateWindow::setFullscreenMonitors (CompFullscreenMonitorSet *monitors)
457 {
458 bool hadFsMonitors = fullscreenMonitorsSet;
459 unsigned int outputs = screen->outputDevs ().size ();
460
461 fullscreenMonitorsSet = false;
462
463 if (monitors &&
464 (unsigned int) monitors->left < outputs &&
465 (unsigned int) monitors->right < outputs &&
466 (unsigned int) monitors->top < outputs &&
467 (unsigned int) monitors->bottom < outputs)
468 {
469 CompRect fsRect (screen->outputDevs ()[monitors->left].x1 (),
470 screen->outputDevs ()[monitors->top].y1 (),
471 screen->outputDevs ()[monitors->right].x2 (),
472 screen->outputDevs ()[monitors->bottom].y2 ());
473
474 if (fsRect.x1 () < fsRect.x2 () && fsRect.y1 () < fsRect.y2 ())
475 {
476 fullscreenMonitorsSet = true;
477 fullscreenMonitorRect = fsRect;
478 }
479 }
480
481 if (fullscreenMonitorsSet)
482 {
483 long data[4];
484
485 data[0] = monitors->top;
486 data[1] = monitors->bottom;
487 data[2] = monitors->left;
488 data[3] = monitors->right;
489
490 XChangeProperty (screen->dpy (), id, Atoms::wmFullscreenMonitors,
491 XA_CARDINAL, 32, PropModeReplace,
492 (unsigned char *) data, 4);
493 }
494 else if (hadFsMonitors)
495 {
496 XDeleteProperty (screen->dpy (), id, Atoms::wmFullscreenMonitors);
497 }
498
499 if (state & CompWindowStateFullscreenMask)
500 if (fullscreenMonitorsSet || hadFsMonitors)
501 window->updateAttributes (CompStackingUpdateModeNone);
502 }
503
504 void
505 CompWindow::changeState (unsigned int newState)
506 {
507 if (priv->state == newState)
508 return;
509
510 unsigned int oldState = priv->state;
511 priv->state = newState;
512
513 recalcType ();
514 recalcActions ();
515
516 if (priv->managed)
517 screen->setWindowState (priv->state, priv->id);
518
519 stateChangeNotify (oldState);
520 screen->matchPropertyChanged (this);
521 }
522
523 static void
524 setWindowActions (CompScreen *s,
525 unsigned int actions,
526 Window id)
527 {
528 Atom data[32];
529 int i = 0;
530
531 if (actions & CompWindowActionMoveMask)
532 data[i++] = Atoms::winActionMove;
533 if (actions & CompWindowActionResizeMask)
534 data[i++] = Atoms::winActionResize;
535 if (actions & CompWindowActionStickMask)
536 data[i++] = Atoms::winActionStick;
537 if (actions & CompWindowActionMinimizeMask)
538 data[i++] = Atoms::winActionMinimize;
539 if (actions & CompWindowActionMaximizeHorzMask)
540 data[i++] = Atoms::winActionMaximizeHorz;
541 if (actions & CompWindowActionMaximizeVertMask)
542 data[i++] = Atoms::winActionMaximizeVert;
543 if (actions & CompWindowActionFullscreenMask)
544 data[i++] = Atoms::winActionFullscreen;
545 if (actions & CompWindowActionCloseMask)
546 data[i++] = Atoms::winActionClose;
547 if (actions & CompWindowActionShadeMask)
548 data[i++] = Atoms::winActionShade;
549 if (actions & CompWindowActionChangeDesktopMask)
550 data[i++] = Atoms::winActionChangeDesktop;
551 if (actions & CompWindowActionAboveMask)
552 data[i++] = Atoms::winActionAbove;
553 if (actions & CompWindowActionBelowMask)
554 data[i++] = Atoms::winActionBelow;
555
556 XChangeProperty (s->dpy (), id, Atoms::wmAllowedActions,
557 XA_ATOM, 32, PropModeReplace,
558 (unsigned char *) data, i);
559 }
560
561 void
562 CompWindow::recalcActions ()
563 {
564 unsigned int actions = 0;
565 unsigned int setActions, clearActions;
566
567 switch (priv->type) {
568 case CompWindowTypeFullscreenMask:
569 case CompWindowTypeNormalMask:
570 actions =
571 CompWindowActionMaximizeHorzMask |
572 CompWindowActionMaximizeVertMask |
573 CompWindowActionFullscreenMask |
574 CompWindowActionMoveMask |
575 CompWindowActionResizeMask |
576 CompWindowActionStickMask |
577 CompWindowActionMinimizeMask |
578 CompWindowActionCloseMask |
579 CompWindowActionChangeDesktopMask;
580 break;
581 case CompWindowTypeUtilMask:
582 case CompWindowTypeMenuMask:
583 case CompWindowTypeToolbarMask:
584 actions =
585 CompWindowActionMoveMask |
586 CompWindowActionResizeMask |
587 CompWindowActionStickMask |
588 CompWindowActionCloseMask |
589 CompWindowActionChangeDesktopMask;
590 break;
591 case CompWindowTypeDialogMask:
592 case CompWindowTypeModalDialogMask:
593 actions =
594 CompWindowActionMaximizeHorzMask |
595 CompWindowActionMaximizeVertMask |
596 CompWindowActionMoveMask |
597 CompWindowActionResizeMask |
598 CompWindowActionStickMask |
599 CompWindowActionCloseMask |
600 CompWindowActionChangeDesktopMask;
601
602 /* allow minimization for dialog windows if they
603 a) are not a transient (transients can be minimized
604 with their parent)
605 b) don't have the skip taskbar hint set (as those
606 have no target to be minimized to)
607 */
608 if (!priv->transientFor &&
609 !(priv->state & CompWindowStateSkipTaskbarMask))
610 {
611 actions |= CompWindowActionMinimizeMask;
612 }
613 default:
614 break;
615 }
616
617 if (priv->serverInput.top)
618 actions |= CompWindowActionShadeMask;
619
620 actions |= (CompWindowActionAboveMask | CompWindowActionBelowMask);
621
622 switch (priv->wmType) {
623 case CompWindowTypeNormalMask:
624 actions |= CompWindowActionFullscreenMask |
625 CompWindowActionMinimizeMask;
626 default:
627 break;
628 }
629
630 if (priv->sizeHints.min_width == priv->sizeHints.max_width &&
631 priv->sizeHints.min_height == priv->sizeHints.max_height)
632 actions &= ~(CompWindowActionResizeMask |
633 CompWindowActionMaximizeHorzMask |
634 CompWindowActionMaximizeVertMask |
635 CompWindowActionFullscreenMask);
636
637 /* Don't allow maximization or fullscreen
638 * of windows which are too big to fit
639 * the screen */
640 bool foundVert = false;
641 bool foundHorz = false;
642 bool foundFull = false;
643
644 foreach (CompOutput &o, screen->outputDevs ())
645 {
646 if (o.width () >= (priv->sizeHints.min_width + priv->border.left + priv->border.right))
647 foundHorz = true;
648 if (o.height () >= (priv->sizeHints.min_height + priv->border.top + priv->border.bottom))
649 foundVert = true;
650
651 /* Fullscreen windows don't need to fit borders... */
652 if (o.width () >= priv->sizeHints.min_width &&
653 o.height () >= priv->sizeHints.min_height)
654 foundFull = true;
655 }
656
657 if (!foundHorz)
658 actions &= ~CompWindowActionMaximizeHorzMask;
659
660 if (!foundVert)
661 actions &= ~CompWindowActionMaximizeVertMask;
662
663 if (!foundFull)
664 actions &= ~CompWindowActionFullscreenMask;
665
666 if (!(priv->mwmFunc & MwmFuncAll))
667 {
668 if (!(priv->mwmFunc & MwmFuncResize))
669 actions &= ~(CompWindowActionResizeMask |
670 CompWindowActionMaximizeHorzMask |
671 CompWindowActionMaximizeVertMask |
672 CompWindowActionFullscreenMask);
673
674 if (!(priv->mwmFunc & MwmFuncMove))
675 actions &= ~(CompWindowActionMoveMask |
676 CompWindowActionMaximizeHorzMask |
677 CompWindowActionMaximizeVertMask |
678 CompWindowActionFullscreenMask);
679
680 if (!(priv->mwmFunc & MwmFuncIconify))
681 actions &= ~CompWindowActionMinimizeMask;
682
683 if (!(priv->mwmFunc & MwmFuncClose))
684 actions &= ~CompWindowActionCloseMask;
685 }
686
687 getAllowedActions (setActions, clearActions);
688 actions &= ~clearActions;
689 actions |= setActions;
690
691 if (actions != priv->actions)
692 {
693 priv->actions = actions;
694 setWindowActions (screen, actions, priv->id);
695 }
696 }
697
698 void
699 CompWindow::getAllowedActions (unsigned int &setActions,
700 unsigned int &clearActions)
701 {
702 WRAPABLE_HND_FUNCTN (getAllowedActions, setActions, clearActions)
703
704 setActions = 0;
705 clearActions = 0;
706 }
707
708 unsigned int
709 CompWindow::constrainWindowState (unsigned int state,
710 unsigned int actions)
711 {
712 if (!(actions & CompWindowActionMaximizeHorzMask))
713 state &= ~CompWindowStateMaximizedHorzMask;
714
715 if (!(actions & CompWindowActionMaximizeVertMask))
716 state &= ~CompWindowStateMaximizedVertMask;
717
718 if (!(actions & CompWindowActionShadeMask))
719 state &= ~CompWindowStateShadedMask;
720
721 if (!(actions & CompWindowActionFullscreenMask))
722 state &= ~CompWindowStateFullscreenMask;
723
724 return state;
725 }
726
727 unsigned int
728 PrivateWindow::windowTypeFromString (const char *str)
729 {
730 if (strcasecmp (str, "desktop") == 0)
731 return CompWindowTypeDesktopMask;
732 else if (strcasecmp (str, "dock") == 0)
733 return CompWindowTypeDockMask;
734 else if (strcasecmp (str, "toolbar") == 0)
735 return CompWindowTypeToolbarMask;
736 else if (strcasecmp (str, "menu") == 0)
737 return CompWindowTypeMenuMask;
738 else if (strcasecmp (str, "utility") == 0)
739 return CompWindowTypeUtilMask;
740 else if (strcasecmp (str, "splash") == 0)
741 return CompWindowTypeSplashMask;
742 else if (strcasecmp (str, "dialog") == 0)
743 return CompWindowTypeDialogMask;
744 else if (strcasecmp (str, "normal") == 0)
745 return CompWindowTypeNormalMask;
746 else if (strcasecmp (str, "dropdownmenu") == 0)
747 return CompWindowTypeDropdownMenuMask;
748 else if (strcasecmp (str, "popupmenu") == 0)
749 return CompWindowTypePopupMenuMask;
750 else if (strcasecmp (str, "tooltip") == 0)
751 return CompWindowTypeTooltipMask;
752 else if (strcasecmp (str, "notification") == 0)
753 return CompWindowTypeNotificationMask;
754 else if (strcasecmp (str, "combo") == 0)
755 return CompWindowTypeComboMask;
756 else if (strcasecmp (str, "dnd") == 0)
757 return CompWindowTypeDndMask;
758 else if (strcasecmp (str, "modaldialog") == 0)
759 return CompWindowTypeModalDialogMask;
760 else if (strcasecmp (str, "fullscreen") == 0)
761 return CompWindowTypeFullscreenMask;
762 else if (strcasecmp (str, "unknown") == 0)
763 return CompWindowTypeUnknownMask;
764 else if (strcasecmp (str, "any") == 0)
765 return ~0;
766
767 return 0;
768 }
769
770 void
771 CompWindow::recalcType ()
772 {
773 unsigned int type;
774
775 type = priv->wmType;
776
777 if (!overrideRedirect () && priv->wmType == CompWindowTypeUnknownMask)
778 type = CompWindowTypeNormalMask;
779
780 if (priv->state & CompWindowStateFullscreenMask)
781 type = CompWindowTypeFullscreenMask;
782
783 if (type == CompWindowTypeNormalMask)
784 {
785 if (priv->transientFor)
786 type = CompWindowTypeDialogMask;
787 }
788
789 if (type == CompWindowTypeDockMask &&
790 (priv->state & CompWindowStateBelowMask))
791 {
792 type = CompWindowTypeNormalMask;
793 }
794
795 if ((type & (CompWindowTypeNormalMask | CompWindowTypeDialogMask)) &&
796 (priv->state & CompWindowStateModalMask))
797 {
798 type = CompWindowTypeModalDialogMask;
799 }
800
801 priv->type = type;
802 }
803
804
805 void
806 PrivateWindow::updateFrameWindow ()
807 {
808 if (!serverFrame)
809 return;
810
811 XWindowChanges xwc = XWINDOWCHANGES_INIT;
812 unsigned int valueMask = CWX | CWY | CWWidth | CWHeight;
813
814 xwc.x = serverGeometry.x ();
815 xwc.y = serverGeometry.y ();
816 xwc.width = serverGeometry.width ();
817 xwc.height = serverGeometry.height ();
818 xwc.border_width = serverGeometry.border ();
819
820 window->configureXWindow (valueMask, &xwc);
821 window->windowNotify (CompWindowNotifyFrameUpdate);
822 window->recalcActions ();
823 }
824
825
826
827 void
828 CompWindow::updateWindowOutputExtents ()
829 {
830 CompWindowExtents output (priv->output);
831
832 getOutputExtents (output);
833
834 if (output.left != priv->output.left ||
835 output.right != priv->output.right ||
836 output.top != priv->output.top ||
837 output.bottom != priv->output.bottom)
838 {
839 priv->output = output;
840
841 resizeNotify (0, 0, 0, 0);
842 }
843 }
844
845 void
846 CompWindow::getOutputExtents (CompWindowExtents& output)
847 {
848 WRAPABLE_HND_FUNCTN (getOutputExtents, output)
849
850 output.left = 0;
851 output.right = 0;
852 output.top = 0;
853 output.bottom = 0;
854 }
855
856 CompRegion
857 PrivateWindow::rectsToRegion (unsigned int n, XRectangle *rects)
858 {
859 CompRegion ret;
860 int x1, x2, y1, y2;
861 const CompWindow::Geometry &geom = attrib.override_redirect ? priv->geometry : priv->serverGeometry;
862
863 for (unsigned int i = 0; i < n; i++)
864 {
865 x1 = rects[i].x + geom.border ();
866 y1 = rects[i].y + geom.border ();
867 x2 = x1 + rects[i].width;
868 y2 = y1 + rects[i].height;
869
870 if (x1 < 0)
871 x1 = 0;
872 if (y1 < 0)
873 y1 = 0;
874 if (x2 > geom.width ())
875 x2 = geom.width ();
876 if (y2 > geom.height ())
877 y2 = geom.height ();
878
879 if (y1 < y2 && x1 < x2)
880 {
881 x1 += geom.x ();
882 y1 += geom.y ();
883 x2 += geom.x ();
884 y2 += geom.y ();
885
886 ret += CompRect (x1, y1, x2 - x1, y2 - y1);
887 }
888 }
889
890 return ret;
891 }
892
893 /* TODO: This function should be able to check the XShape event
894 * kind and only get/set shape rectangles for either ShapeInput
895 * or ShapeBounding, but not both at the same time
896 */
897
898 void
899 PrivateWindow::updateRegion ()
900 {
901 XRectangle r, *boundingShapeRects = NULL;
902 XRectangle *inputShapeRects = NULL;
903 int nBounding = 0, nInput = 0;
904 const CompWindow::Geometry &geom = attrib.override_redirect ? priv->geometry : priv->serverGeometry;
905
906 priv->region = priv->inputRegion = emptyRegion;
907
908 bool useXShape = screen->XShape ();
909 if (useXShape)
910 {
911 int order;
912
913 /* We should update the server here */
914 XSync (screen->dpy (), false);
915
916 boundingShapeRects = XShapeGetRectangles (screen->dpy (), priv->id,
917 ShapeBounding, &nBounding, &order);
918 inputShapeRects = XShapeGetRectangles (screen->dpy (), priv->id,
919 ShapeInput, &nInput, &order);
920 }
921
922 r.x = -geom.border ();
923 r.y = -geom.border ();
924 r.width = geom.widthIncBorders ();
925 r.height = geom.heightIncBorders ();
926
927 if (nBounding < 1)
928 {
929 boundingShapeRects = &r;
930 nBounding = 1;
931 }
932
933 if (!useXShape)
934 {
935 inputShapeRects = &r;
936 nInput = 1;
937 }
938
939 priv->region += rectsToRegion (nBounding, boundingShapeRects);
940 priv->inputRegion += rectsToRegion (nInput, inputShapeRects);
941
942 if (boundingShapeRects && boundingShapeRects != &r)
943 XFree (boundingShapeRects);
944 if (inputShapeRects && inputShapeRects != &r)
945 XFree (inputShapeRects);
946
947 window->updateFrameRegion ();
948 }
949
950 bool
951 CompWindow::updateStruts ()
952 {
953 Atom actual;
954 int result, format;
955 unsigned long n, left;
956 unsigned char *data;
957 bool hasOld, hasNew;
958 CompStruts oldStrut, newStrut;
959
960 if (priv->struts)
961 {
962 hasOld = true;
963
964 oldStrut.left = priv->struts->left;
965 oldStrut.right = priv->struts->right;
966 oldStrut.top = priv->struts->top;
967 oldStrut.bottom = priv->struts->bottom;
968 }
969 else
970 {
971 hasOld = false;
972 }
973
974 hasNew = false;
975
976 newStrut.left.x = 0;
977 newStrut.left.y = 0;
978 newStrut.left.width = 0;
979 newStrut.left.height = screen->height ();
980
981 newStrut.right.x = screen->width ();
982 newStrut.right.y = 0;
983 newStrut.right.width = 0;
984 newStrut.right.height = screen->height ();
985
986 newStrut.top.x = 0;
987 newStrut.top.y = 0;
988 newStrut.top.width = screen->width ();
989 newStrut.top.height = 0;
990
991 newStrut.bottom.x = 0;
992 newStrut.bottom.y = screen->height ();
993 newStrut.bottom.width = screen->width ();
994 newStrut.bottom.height = 0;
995
996 result = XGetWindowProperty (screen->dpy (), priv->id,
997 Atoms::wmStrutPartial,
998 0L, 12L, false, XA_CARDINAL, &actual, &format,
999 &n, &left, &data);
1000
1001 if (result == Success && data)
1002 {
1003 unsigned long *struts = (unsigned long *) data;
1004
1005 if (n == 12)
1006 {
1007 hasNew = true;
1008
1009 newStrut.left.y = struts[4];
1010 newStrut.left.width = struts[0];
1011 newStrut.left.height = struts[5] - newStrut.left.y + 1;
1012
1013 newStrut.right.width = struts[1];
1014 newStrut.right.x = screen->width () - newStrut.right.width;
1015 newStrut.right.y = struts[6];
1016 newStrut.right.height = struts[7] - newStrut.right.y + 1;
1017
1018 newStrut.top.x = struts[8];
1019 newStrut.top.width = struts[9] - newStrut.top.x + 1;
1020 newStrut.top.height = struts[2];
1021
1022 newStrut.bottom.x = struts[10];
1023 newStrut.bottom.width = struts[11] - newStrut.bottom.x + 1;
1024 newStrut.bottom.height = struts[3];
1025 newStrut.bottom.y = screen->height () - newStrut.bottom.height;
1026 }
1027
1028 XFree (data);
1029 }
1030
1031 if (!hasNew)
1032 {
1033 result = XGetWindowProperty (screen->dpy (), priv->id,
1034 Atoms::wmStrut,
1035 0L, 4L, false, XA_CARDINAL,
1036 &actual, &format, &n, &left, &data);
1037
1038 if (result == Success && data)
1039 {
1040 unsigned long *struts = (unsigned long *) data;
1041
1042 if (n == 4)
1043 {
1044 hasNew = true;
1045
1046 newStrut.left.x = 0;
1047 newStrut.left.width = struts[0];
1048
1049 newStrut.right.width = struts[1];
1050 newStrut.right.x = screen->width () - newStrut.right.width;
1051
1052 newStrut.top.y = 0;
1053 newStrut.top.height = struts[2];
1054
1055 newStrut.bottom.height = struts[3];
1056 newStrut.bottom.y = screen->height () - newStrut.bottom.height;
1057 }
1058
1059 XFree (data);
1060 }
1061 }
1062
1063 if (hasNew)
1064 {
1065 int strutX1, strutY1, strutX2, strutY2;
1066 int x1, y1, x2, y2;
1067
1068 /* applications expect us to clip struts to xinerama edges */
1069 for (unsigned int i = 0;
1070 i < screen->screenInfo ().size (); i++)
1071 {
1072 x1 = screen->screenInfo ()[i].x_org;
1073 y1 = screen->screenInfo ()[i].y_org;
1074 x2 = x1 + screen->screenInfo ()[i].width;
1075 y2 = y1 + screen->screenInfo ()[i].height;
1076
1077 strutX1 = newStrut.left.x;
1078 strutX2 = strutX1 + newStrut.left.width;
1079 strutY1 = newStrut.left.y;
1080 strutY2 = strutY1 + newStrut.left.height;
1081
1082 if (strutX2 > x1 && strutX2 <= x2 &&
1083 strutY1 < y2 && strutY2 > y1)
1084 {
1085 newStrut.left.x = x1;
1086 newStrut.left.width = strutX2 - x1;
1087 }
1088
1089 strutX1 = newStrut.right.x;
1090 strutX2 = strutX1 + newStrut.right.width;
1091 strutY1 = newStrut.right.y;
1092 strutY2 = strutY1 + newStrut.right.height;
1093
1094 if (strutX1 > x1 && strutX1 <= x2 &&
1095 strutY1 < y2 && strutY2 > y1)
1096 {
1097 newStrut.right.x = strutX1;
1098 newStrut.right.width = x2 - strutX1;
1099 }
1100
1101 strutX1 = newStrut.top.x;
1102 strutX2 = strutX1 + newStrut.top.width;
1103 strutY1 = newStrut.top.y;
1104 strutY2 = strutY1 + newStrut.top.height;
1105
1106 if (strutX1 < x2 && strutX2 > x1 &&
1107 strutY2 > y1 && strutY2 <= y2)
1108 {
1109 newStrut.top.y = y1;
1110 newStrut.top.height = strutY2 - y1;
1111 }
1112
1113 strutX1 = newStrut.bottom.x;
1114 strutX2 = strutX1 + newStrut.bottom.width;
1115 strutY1 = newStrut.bottom.y;
1116 strutY2 = strutY1 + newStrut.bottom.height;
1117
1118 if (strutX1 < x2 && strutX2 > x1 &&
1119 strutY1 > y1 && strutY1 <= y2)
1120 {
1121 newStrut.bottom.y = strutY1;
1122 newStrut.bottom.height = y2 - strutY1;
1123 }
1124 }
1125 }
1126
1127 if (hasOld != hasNew ||
1128 (hasNew && hasOld &&
1129 memcmp (&newStrut, &oldStrut, sizeof (CompStruts))))
1130 {
1131 if (hasNew)
1132 {
1133 if (!priv->struts)
1134 {
1135 priv->struts = (CompStruts *) malloc (sizeof (CompStruts));
1136 if (!priv->struts)
1137 return false;
1138 }
1139
1140 *priv->struts = newStrut;
1141 }
1142 else
1143 {
1144 free (priv->struts);
1145 priv->struts = NULL;
1146 }
1147
1148 return true;
1149 }
1150
1151 return false;
1152 }
1153
1154 void
1155 CompWindow::incrementDestroyReference ()
1156 {
1157 priv->destroyRefCnt++;
1158 }
1159
1160 void
1161 CompWindow::destroy ()
1162 {
1163 if (priv->id)
1164 {
1165 CompWindow *oldServerNext, *oldServerPrev, *oldNext, *oldPrev;
1166 StackDebugger *dbg = StackDebugger::Default ();
1167
1168 windowNotify (CompWindowNotifyBeforeDestroy);
1169
1170 /* Don't allow frame windows to block input */
1171 if (priv->serverFrame)
1172 XUnmapWindow (screen->dpy (), priv->serverFrame);
1173
1174 if (priv->wrapper)
1175 XUnmapWindow (screen->dpy (), priv->wrapper);
1176
1177 oldServerNext = serverNext;
1178 oldServerPrev = serverPrev;
1179 oldNext = next;
1180 oldPrev = prev;
1181
1182 /* This is where things get tricky ... it is possible
1183 * to receive a ConfigureNotify relative to a frame window
1184 * for a destroyed window in case we process a ConfigureRequest
1185 * for the destroyed window and then a DestroyNotify for it directly
1186 * afterwards. In that case, we will receive the ConfigureNotify
1187 * for the XConfigureWindow request we made relative to that frame
1188 * window. Because of this, we must keep the frame window in the stack
1189 * as a new toplevel window so that the ConfigureNotify will be processed
1190 * properly until it too receives a DestroyNotify */
1191
1192 if (priv->serverFrame)
1193 {
1194 XWindowAttributes attrib;
1195
1196 /* It's possible that the frame window was already destroyed because
1197 * the client was unreparented before it was destroyed (eg
1198 * UnmapNotify before DestroyNotify). In that case the frame window
1199 * is going to be an invalid window but since we haven't received
1200 * a DestroyNotify for it yet, it is possible that restacking
1201 * operations could occurr relative to it so we need to hold it
1202 * in the stack for now. Ensure that it is marked override redirect */
1203 XGetWindowAttributes (screen->dpy (), priv->serverFrame, &attrib);
1204
1205 /* Put the frame window "above" the client window
1206 * in the stack */
1207 PrivateWindow::createCompWindow (priv->id, priv->id, attrib, priv->serverFrame);
1208 }
1209
1210 /* Immediately unhook the window once destroyed
1211 * as the stacking order will be invalid if we don't
1212 * and will continue to be invalid for the period
1213 * that we keep it around in the stack. Instead, push
1214 * it to another stack and keep the next and prev members
1215 * in tact, letting plugins sort out where those windows
1216 * might be in case they need to use them relative to
1217 * other windows */
1218
1219 screen->unhookWindow (this);
1220 screen->unhookServerWindow (this);
1221
1222 /* We must immediately insert the window into the debugging
1223 * stack */
1224 if (dbg)
1225 dbg->removeServerWindow (id ());
1226
1227 /* Unhooking the window will also NULL the next/prev
1228 * linked list links but we don't want that so don't
1229 * do that */
1230
1231 next = oldNext;
1232 prev = oldPrev;
1233 serverNext = oldServerNext;
1234 serverPrev = oldServerPrev;
1235
1236 screen->addToDestroyedWindows (this);
1237
1238 /* We must set the xid of this window
1239 * to zero as it no longer references
1240 * a valid window */
1241 priv->mapNum = 0;
1242 priv->id = 0;
1243 priv->frame = 0;
1244 priv->serverFrame = 0;
1245 priv->managed = false;
1246 }
1247
1248 priv->destroyRefCnt--;
1249 if (priv->destroyRefCnt)
1250 return;
1251
1252 if (!priv->destroyed)
1253 {
1254 if (!priv->serverFrame)
1255 {
1256 StackDebugger *dbg = StackDebugger::Default ();
1257
1258 if (dbg)
1259 dbg->addDestroyedFrame (priv->serverId);
1260 }
1261
1262 priv->destroyed = true;
1263 screen->incrementPendingDestroys();
1264 }
1265
1266 }
1267
1268 void
1269 CompWindow::sendConfigureNotify ()
1270 {
1271 XConfigureEvent xev;
1272
1273 xev.type = ConfigureNotify;
1274 xev.event = priv->id;
1275 xev.window = priv->id;
1276
1277 xev.x = priv->serverGeometry.x ();
1278 xev.y = priv->serverGeometry.y ();
1279 xev.width = priv->serverGeometry.width ();
1280 xev.height = priv->serverGeometry.height ();
1281 xev.border_width = priv->serverGeometry.border ();
1282 xev.override_redirect = priv->attrib.override_redirect;
1283
1284 /* These used to be based on the actual sibling of the window
1285 * (eg, obtained using XQueryTree), but they are now zeroed out.
1286 *
1287 * The ICCCM is a big unclear on what these should be - it
1288 * requires that after the client attempts to configure a window
1289 * we should issue a synthetic ConfigureNotify to work around
1290 * the change of co-ordinates due to reparenting:
1291 *
1292 * "A client will receive a synthetic ConfigureNotify event
1293 * following the change that describes the new geometry of the window"
1294 *
1295 * However there is an acknowledgement on stacking order:
1296 *
1297 * "Not changing the size, location, border width,
1298 * or stacking order of the window at all."
1299 *
1300 * Since there isn't any advice as to what to set the above and
1301 * detail members, the only choices are to either grab the server
1302 * and query it for the sibling to the window's parent, or to just
1303 * set them to zero.
1304 *
1305 * An evaluation of other window managers showed that they just set
1306 * these fields to zero. This is probably a safe option and justifies
1307 * the potential performance tradeoff that we get out of not having
1308 * to grab the server, query window attributes and children and
1309 * translate co-ordinates every time a window is moved
1310 */
1311
1312 xev.above = None;
1313
1314 XSendEvent (screen->dpy (), priv->id, false,
1315 StructureNotifyMask, (XEvent *) &xev);
1316 }
1317
1318 void
1319 CompWindow::map ()
1320 {
1321 windowNotify (CompWindowNotifyBeforeMap);
1322
1323 /* Previously not viewable */
1324 if (!isViewable ())
1325 {
1326 if (priv->pendingMaps > 0)
1327 priv->pendingMaps = 0;
1328
1329 priv->mapNum = screen->nextMapNum();
1330
1331 if (priv->struts)
1332 screen->updateWorkarea ();
1333
1334 if (windowClass () == InputOnly)
1335 return;
1336
1337 priv->unmapRefCnt = 1;
1338
1339 priv->attrib.map_state = IsViewable;
1340
1341 if (!overrideRedirect ())
1342 screen->setWmState (NormalState, priv->id);
1343
1344 priv->invisible = priv->isInvisible ();
1345 priv->alive = true;
1346
1347 priv->lastPong = screen->lastPing ();
1348
1349 priv->updateRegion ();
1350 priv->updateSize ();
1351
1352 screen->updateClientList ();
1353
1354 if (priv->type & CompWindowTypeDesktopMask)
1355 screen->incrementDesktopWindowCount();
1356
1357 if (priv->protocols & CompWindowProtocolSyncRequestMask)
1358 {
1359 sendSyncRequest ();
1360 sendConfigureNotify ();
1361 }
1362
1363 if (!overrideRedirect ())
1364 {
1365 /* been shaded */
1366 if (priv->shaded)
1367 {
1368 priv->shaded = false;
1369 priv->updateFrameWindow ();
1370 }
1371 }
1372 }
1373
1374 windowNotify (CompWindowNotifyMap);
1375 /* Send a resizeNotify to plugins to indicate
1376 * that the map is complete */
1377 resizeNotify (0, 0, 0, 0);
1378 }
1379
1380 void
1381 CompWindow::incrementUnmapReference ()
1382 {
1383 priv->unmapRefCnt++;
1384 }
1385
1386 void
1387 CompWindow::unmap ()
1388 {
1389 if (priv->mapNum)
1390 priv->mapNum = 0;
1391
1392 windowNotify (CompWindowNotifyBeforeUnmap);
1393
1394 /* Even though we're still keeping the backing
1395 * pixmap of the window around, it's safe to
1396 * unmap the frame window since there's no use
1397 * for it at this point anyways and it just blocks
1398 * input, but keep it around if shaded */
1399
1400 XUnmapWindow (screen->dpy (), priv->wrapper);
1401
1402 if (!priv->shaded)
1403 XUnmapWindow (screen->dpy (), priv->serverFrame);
1404
1405 priv->unmapRefCnt--;
1406 if (priv->unmapRefCnt > 0)
1407 return;
1408
1409 if (priv->unmanaging)
1410 {
1411 XWindowChanges xwc = XWINDOWCHANGES_INIT;
1412 unsigned int xwcm;
1413 int gravity = priv->sizeHints.win_gravity;
1414
1415 /* revert gravity adjustment made at MapNotify time */
1416 xwc.x = priv->serverGeometry.x ();
1417 xwc.y = priv->serverGeometry.y ();
1418 xwc.width = 0;
1419 xwc.height = 0;
1420
1421 xwcm = priv->adjustConfigureRequestForGravity (&xwc,
1422 CWX | CWY,
1423 gravity,
1424 -1);
1425 if (xwcm)
1426 configureXWindow (xwcm, &xwc);
1427
1428 priv->unmanaging = false;
1429 }
1430
1431 if (priv->serverFrame && !priv->shaded)
1432 priv->unreparent ();
1433
1434 if (priv->struts)
1435 screen->updateWorkarea ();
1436
1437 if (priv->attrib.map_state != IsViewable)
1438 return;
1439
1440 if (priv->type == CompWindowTypeDesktopMask)
1441 screen->decrementDesktopWindowCount();
1442
1443 priv->attrib.map_state = IsUnmapped;
1444 priv->invisible = true;
1445
1446 if (priv->shaded)
1447 priv->updateFrameWindow ();
1448
1449 screen->updateClientList ();
1450
1451 windowNotify (CompWindowNotifyUnmap);
1452 }
1453
1454 void
1455 PrivateWindow::withdraw ()
1456 {
1457 if (!attrib.override_redirect)
1458 screen->setWmState (WithdrawnState, id);
1459
1460 placed = false;
1461 unmanaging = managed;
1462 managed = false;
1463 }
1464
1465 bool
1466 PrivateWindow::restack (Window aboveId)
1467 {
1468 if (aboveId && (aboveId == id || aboveId == serverFrame))
1469 // Don't try to raise a window above itself
1470 return false;
1471 else if (window->prev)
1472 {
1473 if (aboveId && (aboveId == window->prev->id () ||
1474 aboveId == window->prev->priv->frame))
1475 return false;
1476 }
1477 else if (aboveId == None && !window->next)
1478 return false;
1479
1480 if (aboveId && !screen->findTopLevelWindow (aboveId, true))
1481 {
1482 return false;
1483 }
1484
1485 screen->unhookWindow (window);
1486 screen->insertWindow (window, aboveId);
1487
1488 /* Update the server side window list for
1489 * override redirect windows immediately
1490 * since there is no opportunity to update
1491 * the server side list when we configure them
1492 * since we never get a ConfigureRequest for those */
1493 if (attrib.override_redirect != 0)
1494 {
1495 StackDebugger *dbg = StackDebugger::Default ();
1496
1497 screen->unhookServerWindow (window);
1498 screen->insertServerWindow (window, aboveId);
1499
1500 if (dbg)
1501 dbg->overrideRedirectRestack (window->id (), aboveId);
1502 }
1503
1504 screen->updateClientList ();
1505
1506 window->windowNotify (CompWindowNotifyRestack);
1507
1508 return true;
1509 }
1510
1511 bool
CID 12473 - PASS_BY_VALUE
Passing parameter attr of type XWindowAttributes (size 136 bytes) by value.
1512 CompWindow::resize (XWindowAttributes attr)
1513 {
1514 return resize (Geometry (attr.x, attr.y, attr.width, attr.height,
1515 attr.border_width));
1516 }
1517
1518 bool
1519 CompWindow::resize (int x,
1520 int y,
1521 int width,
1522 int height,
1523 int border)
1524 {
1525 return resize (Geometry (x, y, width, height, border));
1526 }
1527
1528 bool
1529 PrivateWindow::resize (const CompWindow::Geometry &gm)
1530 {
1531 /* Input extents are now the last thing sent
1532 * from the server. This might not work in some
1533 * cases though because setWindowFrameExtents may
1534 * be called more than once in an event processing
1535 * cycle so every set of input extents up until the
1536 * last one will be invalid. The real solution
1537 * here is to handle ConfigureNotify events on
1538 * frame windows and client windows separately */
1539
1540 priv->input = priv->serverInput;
1541
1542 if (priv->geometry.width () != gm.width () ||
1543 priv->geometry.height () != gm.height () ||
1544 priv->geometry.border () != gm.border ())
1545 {
1546 int dx, dy, dwidth, dheight;
1547
1548 dx = gm.x () - priv->geometry.x ();
1549 dy = gm.y () - priv->geometry.y ();
1550 dwidth = gm.width () - priv->geometry.width ();
1551 dheight = gm.height () - priv->geometry.height ();
1552
1553 priv->geometry.set (gm.x (), gm.y (),
1554 gm.width (), gm.height (),
1555 gm.border ());
1556
1557 priv->invisible = priv->isInvisible ();
1558
1559 if (priv->attrib.override_redirect)
1560 {
1561 priv->serverGeometry = priv->geometry;
1562 priv->serverFrameGeometry = priv->frameGeometry;
1563
1564 if (priv->mapNum)
1565 priv->updateRegion ();
1566
1567 window->resizeNotify (dx, dy, dwidth, dheight);
1568 }
1569 }
1570 else if (priv->geometry.x () != gm.x () || priv->geometry.y () != gm.y ())
1571 {
1572 move (gm.x () - priv->geometry.x (),
1573 gm.y () - priv->geometry.y (), true);
1574 }
1575
1576 return true;
1577 }
1578
1579 bool
1580 PrivateWindow::resize (const XWindowAttributes &attr)
1581 {
1582 return resize (CompWindow::Geometry (attr.x, attr.y, attr.width, attr.height,
1583 attr.border_width));
1584 }
1585
1586 bool
1587 PrivateWindow::resize (int x,
1588 int y,
1589 int width,
1590 int height,
1591 int border)
1592 {
1593 return resize (CompWindow::Geometry (x, y, width, height, border));
1594 }
1595
1596 bool
1597 CompWindow::resize (CompWindow::Geometry gm)
1598 {
1599 XWindowChanges xwc = XWINDOWCHANGES_INIT;
1600 unsigned int valueMask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1601
1602 xwc.x = gm.x ();
1603 xwc.y = gm.y ();
1604 xwc.width = gm.width ();
1605 xwc.height = gm.height ();
1606 xwc.border_width = gm.border ();
1607
1608 configureXWindow (valueMask, &xwc);
1609
1610 return true;
1611 }
1612
1613 static void
1614 syncValueIncrement (XSyncValue *value)
1615 {
1616 XSyncValue one;
1617 int overflow;
1618
1619 XSyncIntToValue (&one, 1);
1620 XSyncValueAdd (value, *value, one, &overflow);
1621 }
1622
1623 bool
1624 PrivateWindow::initializeSyncCounter ()
1625 {
1626 XSyncAlarmAttributes values;
1627 Atom actual;
1628 int result, format;
1629 unsigned long n, left;
1630 unsigned char *data;
1631
1632 if (syncCounter)
1633 return syncAlarm != None;
1634
1635 if (!(protocols & CompWindowProtocolSyncRequestMask))
1636 return false;
1637
1638 result = XGetWindowProperty (screen->dpy (), id,
1639 Atoms::wmSyncRequestCounter,
1640 0L, 1L, false, XA_CARDINAL, &actual, &format,
1641 &n, &left, &data);
1642
1643 if (result == Success && n && data)
1644 {
1645 unsigned long *counter = (unsigned long *) data;
1646
1647 syncCounter = *counter;
1648
1649 XFree (data);
1650
1651 XSyncIntsToValue (&syncValue, (unsigned int) rand (), 0);
1652 XSyncSetCounter (screen->dpy (),
1653 syncCounter,
1654 syncValue);
1655
1656 syncValueIncrement (&syncValue);
1657
1658 values.events = true;
1659
1660 values.trigger.counter = syncCounter;
1661 values.trigger.wait_value = syncValue;
1662
1663 values.trigger.value_type = XSyncAbsolute;
1664 values.trigger.test_type = XSyncPositiveComparison;
1665
1666 XSyncIntToValue (&values.delta, 1);
1667
1668 values.events = true;
1669
1670 CompScreenImpl::checkForError (screen->dpy ());
1671
1672 /* Note that by default, the alarm increments the trigger value
1673 * when it fires until the condition (counter.value < trigger.value)
1674 * is false again.
1675 */
1676 syncAlarm = XSyncCreateAlarm (screen->dpy (),
1677 XSyncCACounter |
1678 XSyncCAValue |
1679 XSyncCAValueType |
1680 XSyncCATestType |
1681 XSyncCADelta |
1682 XSyncCAEvents,
1683 &values);
1684
1685 if (CompScreenImpl::checkForError (screen->dpy ()))
1686 return true;
1687
1688 XSyncDestroyAlarm (screen->dpy (), syncAlarm);
1689 syncAlarm = None;
1690 }
1691 else if (result == Success && data)
1692 {
1693 XFree (data);
1694 }
1695
1696 return false;
1697 }
1698
1699 void
1700 CompWindow::sendSyncRequest ()
1701 {
1702 if (priv->syncWait)
1703 return;
1704
1705 if (!priv->initializeSyncCounter ())
1706 return;
1707
1708 XClientMessageEvent xev;
1709
1710 xev.type = ClientMessage;
1711 xev.window = priv->id;
1712 xev.message_type = Atoms::wmProtocols;
1713 xev.format = 32;
1714 xev.data.l[0] = Atoms::wmSyncRequest;
1715 xev.data.l[1] = CurrentTime;
1716 xev.data.l[2] = XSyncValueLow32 (priv->syncValue);
1717 xev.data.l[3] = XSyncValueHigh32 (priv->syncValue);
1718 xev.data.l[4] = 0;
1719
1720 syncValueIncrement (&priv->syncValue);
1721
1722 XSendEvent (screen->dpy (), priv->id, false, 0, (XEvent *) &xev);
1723
1724 priv->syncWait = true;
1725 priv->syncGeometry = priv->serverGeometry;
1726
1727 if (!priv->syncWaitTimer.active ())
1728 priv->syncWaitTimer.start ();
1729 }
1730
1731 void
1732 PrivateWindow::configure (XConfigureEvent *ce)
1733 {
1734 if (priv->frame)
1735 return;
1736
1737 unsigned int valueMask = 0;
1738
1739 /* remove configure event from pending configures */
1740 if (priv->geometry.x () != ce->x)
1741 valueMask |= CWX;
1742
1743 if (priv->geometry.y () != ce->y)
1744 valueMask |= CWY;
1745
1746 if (priv->geometry.width () != ce->width)
1747 valueMask |= CWWidth;
1748
1749 if (priv->geometry.height () != ce->height)
1750 valueMask |= CWHeight;
1751
1752 if (priv->geometry.border () != ce->border_width)
1753 valueMask |= CWBorderWidth;
1754
1755 if (window->prev)
1756 {
1757 if (ROOTPARENT (window->prev) != ce->above)
1758 valueMask |= CWSibling | CWStackMode;
1759 }
1760 else
1761 {
1762 if (ce->above != 0)
1763 valueMask |= CWSibling | CWStackMode;
1764 }
1765
1766 priv->attrib.override_redirect = ce->override_redirect;
1767
1768 priv->frameGeometry.set (ce->x, ce->y, ce->width,
1769 ce->height, ce->border_width);
1770
1771 if (priv->syncWait)
1772 priv->syncGeometry.set (ce->x, ce->y, ce->width, ce->height,
1773 ce->border_width);
1774 else
1775 {
1776 resize (ce->x, ce->y, ce->width, ce->height, ce->border_width);
1777 }
1778
1779 if (ce->event == screen->root ())
1780 priv->restack (ce->above);
1781 }
1782
1783 void
1784 PrivateWindow::configureFrame (XConfigureEvent *ce)
1785 {
1786 if (!priv->frame)
1787 return;
1788
1789 int x, y, width, height;
1790 CompWindow *above;
1791 unsigned int valueMask = 0;
1792
1793 /* remove configure event from pending configures */
1794 if (priv->frameGeometry.x () != ce->x)
1795 valueMask |= CWX;
1796
1797 if (priv->frameGeometry.y () != ce->y)
1798 valueMask |= CWY;
1799
1800 if (priv->frameGeometry.width () != ce->width)
1801 valueMask |= CWWidth;
1802
1803 if (priv->frameGeometry.height () != ce->height)
1804 valueMask |= CWHeight;
1805
1806 if (priv->frameGeometry.border () != ce->border_width)
1807 valueMask |= CWBorderWidth;
1808
1809 if (window->prev)
1810 {
1811 if (ROOTPARENT (window->prev) != ce->above)
1812 valueMask |= CWSibling | CWStackMode;
1813 }
1814 else
1815 {
1816 if (ce->above != 0)
1817 valueMask |= CWSibling | CWStackMode;
1818 }
1819
1820 if (!pendingConfigures.match ((XEvent *) ce))
1821 {
1822 compLogMessage ("core", CompLogLevelWarn, "unhandled ConfigureNotify on 0x%x!", serverFrame);
1823 compLogMessage ("core", CompLogLevelWarn, "this should never happen. you should "\
1824 "probably file a bug about this.");
1825 #ifdef DEBUG
1826 abort ();
1827 #else
1828 pendingConfigures.clear ();
1829 #endif
1830 }
1831
1832 /* subtract the input extents last sent to the
1833 * server to calculate the client size and then
1834 * re-sync the input extents and extents last
1835 * sent to server on resize () */
1836
1837 x = ce->x + priv->serverInput.left;
1838 y = ce->y + priv->serverInput.top;
1839 width = ce->width - priv->serverGeometry.border () * 2 - priv->serverInput.left - priv->serverInput.right;
1840
1841 /* Don't use the server side frame geometry
1842 * to determine the geometry of shaded
1843 * windows since we didn't resize them
1844 * on configureXWindow */
1845 if (priv->shaded)
1846 height = priv->serverGeometry.heightIncBorders () - priv->serverInput.top - priv->serverInput.bottom;
1847 else
1848 height = ce->height + priv->serverGeometry.border () * 2 - priv->serverInput.top - priv->serverInput.bottom;
1849
1850 /* set the frame geometry */
1851 priv->frameGeometry.set (ce->x, ce->y, ce->width, ce->height, ce->border_width);
1852
1853
1854 if (priv->syncWait)
1855 priv->syncGeometry.set (x, y, width, height, ce->border_width);
1856 else
1857 resize (x, y, width, height, ce->border_width);
1858
1859 if (priv->restack (ce->above))
1860 priv->updatePassiveButtonGrabs ();
1861
1862 above = screen->findWindow (ce->above);
1863
1864 if (above)
1865 above->priv->updatePassiveButtonGrabs ();
1866 }
1867
1868 void
1869 PrivateWindow::circulate (XCirculateEvent *ce)
1870 {
1871 Window newAboveId;
1872
1873 if (ce->place == PlaceOnTop)
1874 {
1875 CompWindow *newAbove = screen->getTopWindow ();
1876 newAboveId = newAbove ? newAbove->id () : None;
1877 }
1878 else
1879 newAboveId = 0;
1880
1881 priv->restack (newAboveId);
1882 }
1883
1884 void
1885 CompWindow::move (int dx,
1886 int dy,
1887 bool immediate)
1888 {
1889 if (dx || dy)
1890 {
1891 XWindowChanges xwc = XWINDOWCHANGES_INIT;
1892 unsigned int valueMask = CWX | CWY;
1893
1894 xwc.x = priv->serverGeometry.x () + dx;
1895 xwc.y = priv->serverGeometry.y () + dy;
1896
1897 priv->nextMoveImmediate = immediate;
1898
1899 configureXWindow (valueMask, &xwc);
1900 }
1901 }
1902
1903 void
1904 PrivateWindow::move (int dx,
1905 int dy,
1906 bool immediate)
1907 {
1908 if (dx || dy)
1909 {
1910 priv->geometry.setX (priv->geometry.x () + dx);
1911 priv->geometry.setY (priv->geometry.y () + dy);
1912 priv->frameGeometry.setX (priv->frameGeometry.x () + dx);
1913 priv->frameGeometry.setY (priv->frameGeometry.y () + dy);
1914
1915 if (priv->attrib.override_redirect)
1916 {
1917 priv->serverGeometry = priv->geometry;
1918 priv->serverFrameGeometry = priv->frameGeometry;
1919 priv->region.translate (dx, dy);
1920 priv->inputRegion.translate (dx, dy);
1921 if (!priv->frameRegion.isEmpty ())
1922 priv->frameRegion.translate (dx, dy);
1923
1924 priv->invisible = priv->isInvisible ();
1925
1926 window->moveNotify (dx, dy, true);
1927 }
1928 }
1929 }
1930
1931 bool
1932 compiz::X11::PendingEventQueue::pending ()
1933 {
1934 return !mEvents.empty ();
1935 }
1936
1937 void
1938 compiz::X11::PendingEventQueue::add (PendingEvent::Ptr p)
1939 {
1940 compLogMessage ("core", CompLogLevelDebug, "pending request:");
1941 p->dump ();
1942
1943 mEvents.push_back (p);
1944 }
1945
1946 bool
1947 compiz::X11::PendingEventQueue::removeIfMatching (const PendingEvent::Ptr &p, XEvent *event)
1948 {
1949 if (p->match (event))
1950 {
1951 compLogMessage ("core", CompLogLevelDebug, "received event:");
1952 p->dump ();
1953 return true;
1954 }
1955
1956 return false;
1957 }
1958
1959 void
1960 compiz::X11::PendingEvent::dump ()
1961 {
1962 compLogMessage ("core", CompLogLevelDebug, "- event serial: %i", mSerial);
1963 compLogMessage ("core", CompLogLevelDebug, "- event window 0x%x", mWindow);
1964 }
1965
1966 void
1967 compiz::X11::PendingConfigureEvent::dump ()
1968 {
1969 compiz::X11::PendingEvent::dump ();
1970
1971 compLogMessage ("core", CompLogLevelDebug, "- x: %i y: %i width: %i height: %i "\
1972 "border: %i, sibling: 0x%x",
1973 mXwc.x, mXwc.y, mXwc.width, mXwc.height, mXwc.border_width, mXwc.sibling);
1974 }
1975
1976 bool
1977 compiz::X11::PendingEventQueue::match (XEvent *event)
1978 {
1979 unsigned int lastSize = mEvents.size ();
1980
1981 mEvents.erase (std::remove_if (mEvents.begin (), mEvents.end (),
1982 boost::bind (&compiz::X11::PendingEventQueue::removeIfMatching, this, _1, event)), mEvents.end ());
1983
1984 return lastSize != mEvents.size ();
1985 }
1986
1987 bool
1988 compiz::X11::PendingEventQueue::forEachIf (boost::function<bool (compiz::X11::PendingEvent::Ptr)> f)
1989 {
1990 foreach (compiz::X11::PendingEvent::Ptr p, mEvents)
1991 {
1992 if (f (p))
1993 return true;
1994 }
1995
1996 return false;
1997 }
1998
1999 void
2000 compiz::X11::PendingEventQueue::dump ()
2001 {
2002 foreach (compiz::X11::PendingEvent::Ptr p, mEvents)
2003 p->dump ();
2004 }
2005
2006 compiz::X11::PendingEventQueue::PendingEventQueue (Display *d)
2007 {
2008 /* mClearCheckTimeout.setTimes (0, 0)
2009 *
2010 * XXX: For whatever reason, calling setTimes (0, 0) here causes
2011 * the destructor of the timer object to be called twice later on
2012 * in execution and the stack gets smashed. This could be a
2013 * compiler bug, but requires further investigation */
2014 }
2015
2016 compiz::X11::PendingEventQueue::~PendingEventQueue ()
2017 {
2018 }
2019
2020 Window
2021 compiz::X11::PendingEvent::getEventWindow (XEvent *event)
2022 {
2023 return event->xany.window;
2024 }
2025
2026 bool
2027 compiz::X11::PendingEvent::match (XEvent *event)
2028 {
2029 if (event->xany.serial != mSerial)
2030 return false;
2031 if (getEventWindow (event)!= mWindow)
2032 return false;
2033
2034 return true;
2035 }
2036
2037 compiz::X11::PendingEvent::PendingEvent (Display *d, Window w) :
2038 mSerial (XNextRequest (d)),
2039 mWindow (w)
2040 {
2041 }
2042
2043 compiz::X11::PendingEvent::~PendingEvent ()
2044 {
2045 }
2046
2047 Window
2048 compiz::X11::PendingConfigureEvent::getEventWindow (XEvent *event)
2049 {
2050 return event->xconfigure.window;
2051 }
2052
2053 bool
2054 compiz::X11::PendingConfigureEvent::matchVM (unsigned int valueMask)
2055 {
2056 unsigned int result = mValueMask != 0 ? valueMask & mValueMask : 1;
2057
2058 return result != 0;
2059 }
2060
2061 bool
2062 compiz::X11::PendingConfigureEvent::matchRequest (XWindowChanges &xwc, unsigned int valueMask)
2063 {
2064 if (matchVM (valueMask))
2065 {
2066 if (valueMask & CWX)
2067 if (xwc.x != mXwc.x)
2068 return false;
2069
2070 if (valueMask & CWY)
2071 if (xwc.y != mXwc.y)
2072 return false;
2073
2074 if (valueMask & CWWidth)
2075 if (xwc.width != mXwc.width)
2076 return false;
2077
2078 if (valueMask & CWHeight)
2079 if (xwc.height != mXwc.height)
2080 return false;
2081
2082 if (valueMask & CWBorderWidth)
2083 if (xwc.border_width != mXwc.border_width)
2084 return false;
2085
2086 if (valueMask & (CWStackMode | CWSibling))
2087 if (xwc.sibling != mXwc.sibling)
2088 return false;
2089
2090 return true;
2091 }
2092
2093 return false;
2094 }
2095
2096 bool
2097 compiz::X11::PendingConfigureEvent::match (XEvent *event)
2098 {
2099 XConfigureEvent *ce = (XConfigureEvent *) event;
2100 bool matched = true;
2101
2102 if (!compiz::X11::PendingEvent::match (event))
2103 return false;
2104
2105 XWindowChanges xwc = XWINDOWCHANGES_INIT;
2106
2107 xwc.x = ce->x;
2108 xwc.y = ce->y;
2109 xwc.width = ce->width;
2110 xwc.height = ce->height;
2111 xwc.border_width = ce->border_width;
2112 xwc.sibling = ce->above;
2113
2114 matched = matchRequest (xwc, mValueMask);
2115
2116 /* Remove events from the queue
2117 * even if they didn't match what
2118 * we expected them to be, but still
2119 * complain about it */
2120 if (!matched)
2121 {
2122 compLogMessage ("core", CompLogLevelWarn, "no exact match for ConfigureNotify on 0x%x!", mWindow);
2123 compLogMessage ("core", CompLogLevelWarn, "expected the following changes:");
2124 if (mValueMask & CWX)
2125 compLogMessage ("core", CompLogLevelWarn, "x: %i", mXwc.x);
2126 if (mValueMask & CWY)
2127 compLogMessage ("core", CompLogLevelWarn, "y: %i", mXwc.y);
2128 if (mValueMask & CWWidth)
2129 compLogMessage ("core", CompLogLevelWarn, "width: %i", mXwc.width);
2130 if (mValueMask & CWHeight)
2131 compLogMessage ("core", CompLogLevelWarn, "height: %i", mXwc.height);
2132 if (mValueMask & CWBorderWidth)
2133 compLogMessage ("core", CompLogLevelWarn, "border: %i", mXwc.border_width);
2134 if (mValueMask & (CWStackMode | CWSibling))
2135 compLogMessage ("core", CompLogLevelWarn, "sibling: 0x%x", mXwc.sibling);
2136
2137 compLogMessage ("core", CompLogLevelWarn, "instead got:");
2138 compLogMessage ("core", CompLogLevelWarn, "x: %i", ce->x);
2139 compLogMessage ("core", CompLogLevelWarn, "y: %i", ce->y);
2140 compLogMessage ("core", CompLogLevelWarn, "width: %i", ce->width);
2141 compLogMessage ("core", CompLogLevelWarn, "height: %i", ce->height);
2142 compLogMessage ("core", CompLogLevelWarn, "above: %i", ce->above);
2143 compLogMessage ("core", CompLogLevelWarn, "this should never happen. you should "\
2144 "probably file a bug about this.");
2145 }
2146
2147 return true;
2148 }
2149
2150 compiz::X11::PendingConfigureEvent::PendingConfigureEvent (Display *d,
2151 Window w,
2152 unsigned int valueMask,
2153 XWindowChanges *xwc) :
2154 compiz::X11::PendingEvent::PendingEvent (d, w),
2155 mValueMask (valueMask),
2156 mXwc (*xwc)
2157 {
2158 }
2159
2160 compiz::X11::PendingConfigureEvent::~PendingConfigureEvent ()
2161 {
2162 }
2163
2164 void
2165 CompWindow::syncPosition ()
2166 {
2167 }
2168
2169 bool
2170 CompWindow::focus ()
2171 {
2172 WRAPABLE_HND_FUNCTN_RETURN (bool, focus)
2173
2174 if (overrideRedirect ())
2175 return false;
2176
2177 if (!priv->managed || priv->unmanaging)
2178 return false;
2179
2180 if (!onCurrentDesktop ())
2181 return false;
2182
2183 if (priv->destroyed)
2184 return false;
2185
2186 if (!priv->shaded && (priv->state & CompWindowStateHiddenMask))
2187 return false;
2188
2189 if (priv->serverGeometry.x () + priv->serverGeometry.width () <= 0 ||
2190 priv->serverGeometry.y () + priv->serverGeometry.height () <= 0 ||
2191 priv->serverGeometry.x () >= (int) screen->width ()||
2192 priv->serverGeometry.y () >= (int) screen->height ())
2193 return false;
2194
2195 return true;
2196 }
2197
2198 bool
2199 CompWindow::place (CompPoint &pos)
2200 {
2201 WRAPABLE_HND_FUNCTN_RETURN (bool, place, pos)
2202 return false;
2203 }
2204
2205 void
2206 CompWindow::validateResizeRequest (unsigned int &mask,
2207 XWindowChanges *xwc,
2208 unsigned int source)
2209 {
2210 WRAPABLE_HND_FUNCTN (validateResizeRequest, mask, xwc, source)
2211
2212 if (!(priv->type & (CompWindowTypeDockMask |
2213 CompWindowTypeFullscreenMask |
2214 CompWindowTypeUnknownMask)))
2215 {
2216 if (mask & CWY)
2217 {
2218 int min, max;
2219
2220 min = screen->workArea ().y () + priv->input.top;
2221 max = screen->workArea ().bottom ();
2222
2223 if (priv->state & CompWindowStateStickyMask &&
2224 (xwc->y < min || xwc->y > max))
2225 {
2226 xwc->y = priv->serverGeometry.y ();
2227 }
2228 else
2229 {
2230 min -= screen->vp ().y () * screen->height ();
2231 max += (screen->vpSize ().height () - screen->vp ().y () - 1) *
2232 screen->height ();
2233
2234 if (xwc->y < min)
2235 xwc->y = min;
2236 else if (xwc->y > max)
2237 xwc->y = max;
2238 }
2239 }
2240
2241 if (mask & CWX)
2242 {
2243 int min, max;
2244
2245 min = screen->workArea ().x () + priv->input.left;
2246 max = screen->workArea ().right ();
2247
2248 if (priv->state & CompWindowStateStickyMask &&
2249 (xwc->x < min || xwc->x > max))
2250 {
2251 xwc->x = priv->serverGeometry.x ();
2252 }
2253 else
2254 {
2255 min -= screen->vp ().x () * screen->width ();
2256 max += (screen->vpSize ().width () - screen->vp ().x () - 1) *
2257 screen->width ();
2258
2259 if (xwc->x < min)
2260 xwc->x = min;
2261 else if (xwc->x > max)
2262 xwc->x = max;
2263 }
2264 }
2265 }
2266 }
2267
2268 void
2269 CompWindow::resizeNotify (int dx,
2270 int dy,
2271 int dwidth,
2272 int dheight)
2273 WRAPABLE_HND_FUNCTN (resizeNotify, dx, dy, dwidth, dheight)
2274
2275 void
2276 CompWindow::moveNotify (int dx,
2277 int dy,
2278 bool immediate)
2279 WRAPABLE_HND_FUNCTN (moveNotify, dx, dy, immediate)
2280
2281 void
2282 CompWindow::windowNotify (CompWindowNotify n)
2283 WRAPABLE_HND_FUNCTN (windowNotify, n)
2284
2285 void
2286 CompWindow::grabNotify (int x,
2287 int y,
2288 unsigned int state,
2289 unsigned int mask)
2290 {
2291 WRAPABLE_HND_FUNCTN (grabNotify, x, y, state, mask)
2292 priv->grabbed = true;
2293 }
2294
2295 void
2296 CompWindow::ungrabNotify ()
2297 {
2298 WRAPABLE_HND_FUNCTN (ungrabNotify)
2299 priv->grabbed = false;
2300 }
2301
2302 void
2303 CompWindow::stateChangeNotify (unsigned int lastState)
2304 {
2305 WRAPABLE_HND_FUNCTN (stateChangeNotify, lastState);
2306
2307 /* if being made sticky */
2308 if (!(lastState & CompWindowStateStickyMask) &&
2309 (priv->state & CompWindowStateStickyMask))
2310 {
2311 CompPoint vp; /* index of the window's vp */
2312
2313 /* Find which viewport the window falls in,
2314 and check if it's the current viewport */
2315 vp = defaultViewport ();
2316 if (screen->vp () != vp)
2317 {
2318 unsigned int valueMask = CWX | CWY;
2319 XWindowChanges xwc = XWINDOWCHANGES_INIT;
2320
2321 xwc.x = serverGeometry ().x () + (screen->vp ().x () - vp.x ()) * screen->width ();
2322 xwc.y = serverGeometry ().y () + (screen->vp ().y () - vp.y ()) * screen->height ();
2323
2324 configureXWindow (valueMask, &xwc);
2325 }
2326 }
2327 }
2328
2329
2330 bool
2331 PrivateWindow::isGroupTransient (Window clientLeader)
2332 {
2333 if (!clientLeader)
2334 return false;
2335
2336 if (transientFor == None || transientFor == screen->root ())
2337 {
2338 if (type & (CompWindowTypeUtilMask |
2339 CompWindowTypeToolbarMask |
2340 CompWindowTypeMenuMask |
2341 CompWindowTypeDialogMask |
2342 CompWindowTypeModalDialogMask))
2343 {
2344 if (this->clientLeader == clientLeader)
2345 return true;
2346 }
2347 }
2348
2349 return false;
2350 }
2351
2352 CompWindow *
2353 PrivateWindow::getModalTransient ()
2354 {
2355 CompWindow *w, *modalTransient;
2356
2357 modalTransient = window;
2358
2359 for (w = screen->windows ().back (); w; w = w->prev)
2360 {
2361 if (w == modalTransient || w->priv->mapNum == 0)
2362 continue;
2363
2364 if (w->priv->transientFor == modalTransient->priv->id)
2365 {
2366 if (w->priv->state & CompWindowStateModalMask)
2367 {
2368 modalTransient = w;
2369 w = screen->windows ().back ();
2370 }
2371 }
2372 }
2373
2374 if (modalTransient == window)
2375 {
2376 /* don't look for group transients with modal state if current window
2377 has modal state */
2378 if (state & CompWindowStateModalMask)
2379 return NULL;
2380
2381 for (w = screen->windows ().back (); w; w = w->prev)
2382 {
2383 if (w == modalTransient || w->priv->mapNum == 0)
2384 continue;
2385
2386 if (isAncestorTo (modalTransient, w))
2387 continue;
2388
2389 if (w->priv->isGroupTransient (modalTransient->priv->clientLeader))
2390 {
2391 if (w->priv->state & CompWindowStateModalMask)
2392 {
2393 modalTransient = w;
2394 w = w->priv->getModalTransient ();
2395 if (w)
2396 modalTransient = w;
2397
2398 break;
2399 }
2400 }
2401 }
2402 }
2403
2404 if (modalTransient == window)
2405 modalTransient = NULL;
2406
2407 return modalTransient;
2408 }
2409
2410 void
2411 CompWindow::moveInputFocusTo ()
2412 {
2413 CompScreen *s = screen;
2414 CompWindow *modalTransient;
2415
2416 modalTransient = priv->getModalTransient ();
2417 if (modalTransient)
2418 return modalTransient->moveInputFocusTo ();
2419
2420 /* If the window is still hidden but not shaded
2421 * it probably meant that a plugin overloaded
2422 * CompWindow::focus to allow the focus to go
2423 * to this window, so only move the input focus
2424 * to the frame if the window is shaded */
2425 if (shaded ())
2426 {
2427 XSetInputFocus (s->dpy (), priv->serverFrame,
2428 RevertToPointerRoot, CurrentTime);
2429 XChangeProperty (s->dpy (), s->root (), Atoms::winActive,
2430 XA_WINDOW, 32, PropModeReplace,
2431 (unsigned char *) &priv->id, 1);
2432
2433 screen->setNextActiveWindow(priv->serverFrame);
2434 }
2435 else
2436 {
2437 bool setFocus = false;
2438
2439 if (priv->inputHint)
2440 {
2441 XSetInputFocus (s->dpy (), priv->id, RevertToPointerRoot,
2442 CurrentTime);
2443 setFocus = true;
2444 }
2445
2446 if (priv->protocols & CompWindowProtocolTakeFocusMask)
2447 {
2448 XEvent ev;
2449
2450 ev.type = ClientMessage;
2451 ev.xclient.window = priv->id;
2452 ev.xclient.message_type = Atoms::wmProtocols;
2453 ev.xclient.format = 32;
2454 ev.xclient.data.l[0] = Atoms::wmTakeFocus;
2455 ev.xclient.data.l[1] = s->getCurrentTime ();
2456 ev.xclient.data.l[2] = 0;
2457 ev.xclient.data.l[3] = 0;
2458 ev.xclient.data.l[4] = 0;
2459
2460 XSendEvent (s->dpy (), priv->id, false, NoEventMask, &ev);
2461
2462 setFocus = true;
2463 }
2464
2465 if (setFocus)
2466 screen->setNextActiveWindow(priv->id);
2467
2468 if (!setFocus && !modalTransient)
2469 {
2470 CompWindow *ancestor;
2471
2472 /* move input to closest ancestor */
2473 for (ancestor = s->windows ().front (); ancestor;
2474 ancestor = ancestor->next)
2475 {
2476 if (PrivateWindow::isAncestorTo (this, ancestor))
2477 {
2478 ancestor->moveInputFocusTo ();
2479 break;
2480 }
2481 }
2482 }
2483 }
2484 }
2485
2486 void
2487 CompWindow::moveInputFocusToOtherWindow ()
2488 {
2489 if (priv->id == screen->activeWindow () ||
2490 priv->id == screen->getNextActiveWindow())
2491 {
2492 CompWindow *nextActive = screen->findWindow (screen->getNextActiveWindow());
2493
2494 /* Window pending focus */
2495 if (priv->id != screen->getNextActiveWindow() &&
2496 nextActive &&
2497 nextActive->focus ())
2498 {
2499 nextActive->moveInputFocusTo ();
2500 }
2501 else if (priv->transientFor && priv->transientFor != screen->root ())
2502 {
2503 CompWindow *ancestor;
2504 ancestor = screen->findWindow (priv->transientFor);
2505 if (ancestor &&
2506 ancestor->focus () &&
2507 !(ancestor->priv->type & (CompWindowTypeDesktopMask |
2508 CompWindowTypeDockMask)))
2509 {
2510 ancestor->moveInputFocusTo ();
2511 }
2512 else
2513 screen->focusDefaultWindow ();
2514 }
2515 else if (priv->type & (CompWindowTypeDialogMask |
2516 CompWindowTypeModalDialogMask))
2517 {
2518 CompWindow *a, *focus = NULL;
2519
2520 for (a = screen->windows ().back (); a; a = a->prev)
2521 {
2522 if (a->priv->clientLeader == priv->clientLeader)
2523 {
2524 if (a->focus ())
2525 {
2526 if (focus)
2527 {
2528 if (a->priv->type & (CompWindowTypeNormalMask |
2529 CompWindowTypeDialogMask |
2530 CompWindowTypeModalDialogMask))
2531 {
2532 if (priv->compareWindowActiveness (focus, a) < 0)
2533 focus = a;
2534 }
2535 }
2536 else
2537 focus = a;
2538 }
2539 }
2540 }
2541
2542 if (focus && !(focus->priv->type & (CompWindowTypeDesktopMask |
2543 CompWindowTypeDockMask)))
2544 {
2545 focus->moveInputFocusTo ();
2546 }
2547 else
2548 screen->focusDefaultWindow ();
2549 }
2550 else
2551 screen->focusDefaultWindow ();
2552 }
2553 }
2554
2555
2556 bool
2557 PrivateWindow::stackLayerCheck (CompWindow *w,
2558 Window clientLeader,
2559 CompWindow *below)
2560 {
2561 if (isAncestorTo (w, below))
2562 return true;
2563
2564 if (isAncestorTo (below, w))
2565 return false;
2566
2567 if (clientLeader && below->priv->clientLeader == clientLeader)
2568 if (below->priv->isGroupTransient (clientLeader))
2569 return false;
2570
2571 if (w->priv->state & CompWindowStateAboveMask)
2572 {
2573 return true;
2574 }
2575 else if (w->priv->state & CompWindowStateBelowMask)
2576 {
2577 if (below->priv->state & CompWindowStateBelowMask)
2578 return true;
2579 }
2580 else if (!(below->priv->state & CompWindowStateAboveMask))
2581 {
2582 return true;
2583 }
2584
2585 return false;
2586 }
2587
2588 bool
2589 PrivateWindow::avoidStackingRelativeTo (CompWindow *w)
2590 {
2591 if (w->overrideRedirect ())
2592 return true;
2593
2594 if (w->destroyed ())
2595 return true;
2596
2597 bool allowRelativeToUnmmaped = w->priv->receivedMapRequestAndAwaitingMap ||
2598 w->priv->shaded ||
2599 w->priv->pendingMaps;
2600
2601 if (!allowRelativeToUnmmaped)
2602 {
2603 if (!w->isViewable () || !w->isMapped ())
2604 return true;
2605 }
2606
2607 return false;
2608 }
2609
2610 /* goes through the stack, top-down until we find a window we should
2611 stack above, normal windows can be stacked above fullscreen windows
2612 (and fullscreen windows over others in their layer) if aboveFs is true. */
2613 CompWindow *
2614 PrivateWindow::findSiblingBelow (CompWindow *w,
2615 bool aboveFs)
2616 {
2617 CompWindow *below;
2618 CompWindow *t = screen->findWindow (w->transientFor ());
2619 Window clientLeader = w->priv->clientLeader;
2620 unsigned int type = w->priv->type;
2621 unsigned int belowMask;
2622
2623 if (aboveFs)
2624 belowMask = CompWindowTypeDockMask;
2625 else
2626 belowMask = CompWindowTypeDockMask | CompWindowTypeFullscreenMask;
2627
2628 /* normal stacking of fullscreen windows with below state */
2629 if ((type & CompWindowTypeFullscreenMask) &&
2630 (w->priv->state & CompWindowStateBelowMask))
2631 type = CompWindowTypeNormalMask;
2632
2633 while (t && type != CompWindowTypeDockMask)
2634 {
2635 /* dock stacking of transients for docks */
2636 if (t->type () & CompWindowTypeDockMask)
2637 type = CompWindowTypeDockMask;
2638
2639 t = screen->findWindow (t->transientFor ());
2640 }
2641
2642 if (w->priv->transientFor || w->priv->isGroupTransient (clientLeader))
2643 clientLeader = None;
2644
2645 for (below = screen->serverWindows ().back (); below;
2646 below = below->serverPrev)
2647 {
2648 if (below == w || avoidStackingRelativeTo (below))
2649 continue;
2650
2651 /* always above desktop windows */
2652 if (below->priv->type & CompWindowTypeDesktopMask)
2653 return below;
2654
2655 switch (type) {
2656 case CompWindowTypeDesktopMask:
2657 /* desktop window layer */
2658 break;
2659 case CompWindowTypeFullscreenMask:
2660 if (aboveFs)
2661 return below;
2662 /* otherwise fall-through */
2663 case CompWindowTypeDockMask:
2664 /* fullscreen and dock layer */
2665 if (below->priv->type & (CompWindowTypeFullscreenMask |
2666 CompWindowTypeDockMask))
2667 {
2668 if (stackLayerCheck (w, clientLeader, below))
2669 return below;
2670 }
2671 else
2672 {
2673 return below;
2674 }
2675 break;
2676 default:
2677 {
2678 bool allowedRelativeToLayer = !(below->priv->type & belowMask);
2679
2680 if (aboveFs && below->priv->type & CompWindowTypeFullscreenMask)
2681 if (!below->focus ())
2682 break;
2683
2684 t = screen->findWindow (below->transientFor ());
2685
2686 while (t && allowedRelativeToLayer)
2687 {
2688 /* dock stacking of transients for docks */
2689 allowedRelativeToLayer = !(t->priv->type & belowMask);
2690
2691 t = screen->findWindow (t->transientFor ());
2692 }
2693
2694 /* fullscreen and normal layer */
2695 if (allowedRelativeToLayer)
2696 {
2697 if (stackLayerCheck (w, clientLeader, below))
2698 return below;
2699 }
2700 break;
2701 }
2702 }
2703 }
2704
2705 return NULL;
2706 }
2707
2708 /* goes through the stack, top-down and returns the lowest window we
2709 can stack above. */
2710 CompWindow *
2711 PrivateWindow::findLowestSiblingBelow (CompWindow *w)
2712 {
2713 CompWindow *below, *lowest = screen->serverWindows ().back ();
2714 CompWindow *t = screen->findWindow (w->transientFor ());
2715 Window clientLeader = w->priv->clientLeader;
2716 unsigned int type = w->priv->type;
2717
2718 /* normal stacking fullscreen windows with below state */
2719 if ((type & CompWindowTypeFullscreenMask) &&
2720 (w->priv->state & CompWindowStateBelowMask))
2721 type = CompWindowTypeNormalMask;
2722
2723 while (t && type != CompWindowTypeDockMask)
2724 {
2725 /* dock stacking of transients for docks */
2726 if (t->type () & CompWindowTypeDockMask)
2727 type = CompWindowTypeDockMask;
2728
2729 t = screen->findWindow (t->transientFor ());
2730 }
2731
2732
2733 if (w->priv->transientFor || w->priv->isGroupTransient (clientLeader))
2734 clientLeader = None;
2735
2736 for (below = screen->serverWindows ().back (); below;
2737 below = below->serverPrev)
2738 {
2739 if (below == w || avoidStackingRelativeTo (below))
2740 continue;
2741
2742 /* always above desktop windows */
2743 if (below->priv->type & CompWindowTypeDesktopMask)
2744 return below;
2745
2746 switch (type) {
2747 case CompWindowTypeDesktopMask:
2748 /* desktop window layer - desktop windows always should be
2749 stacked at the bottom; no other window should be below them */
2750 return NULL;
2751 break;
2752 case CompWindowTypeFullscreenMask:
2753 case CompWindowTypeDockMask:
2754 /* fullscreen and dock layer */
2755 if (below->priv->type & (CompWindowTypeFullscreenMask |
2756 CompWindowTypeDockMask))
2757 {
2758 if (!stackLayerCheck (below, clientLeader, w))
2759 return lowest;
2760 }
2761 else
2762 {
2763 return lowest;
2764 }
2765 break;
2766 default:
2767 {
2768 bool allowedRelativeToLayer = !(below->priv->type & CompWindowTypeDockMask);
2769
2770 t = screen->findWindow (below->transientFor ());
2771
2772 while (t && allowedRelativeToLayer)
2773 {
2774 /* dock stacking of transients for docks */
2775 allowedRelativeToLayer = !(t->priv->type & CompWindowTypeDockMask);
2776
2777 t = screen->findWindow (t->transientFor ());
2778 }
2779
2780 /* fullscreen and normal layer */
2781 if (allowedRelativeToLayer)
2782 {
2783 if (!stackLayerCheck (below, clientLeader, w))
2784 return lowest;
2785 }
2786 break;
2787 }
2788 }
2789
2790 lowest = below;
2791 }
2792
2793 return lowest;
2794 }
2795
2796 bool
2797 PrivateWindow::validSiblingBelow (CompWindow *w,
2798 CompWindow *sibling)
2799 {
2800 CompWindow *t = screen->findWindow (w->transientFor ());
2801 Window clientLeader = w->priv->clientLeader;
2802 unsigned int type = w->priv->type;
2803
2804 /* normal stacking fullscreen windows with below state */
2805 if ((type & CompWindowTypeFullscreenMask) &&
2806 (w->priv->state & CompWindowStateBelowMask))
2807 type = CompWindowTypeNormalMask;
2808
2809 while (t && type != CompWindowTypeDockMask)
2810 {
2811 /* dock stacking of transients for docks */
2812 if (t->type () & CompWindowTypeDockMask)
2813 type = CompWindowTypeDockMask;
2814
2815 t = screen->findWindow (t->transientFor ());
2816 }
2817
2818
2819 if (w->priv->transientFor || w->priv->isGroupTransient (clientLeader))
2820 clientLeader = None;
2821
2822 if (sibling == w || avoidStackingRelativeTo (sibling))
2823 return false;
2824
2825 /* always above desktop windows */
2826 if (sibling->priv->type & CompWindowTypeDesktopMask)
2827 return true;
2828
2829 switch (type) {
2830 case CompWindowTypeDesktopMask:
2831 /* desktop window layer */
2832 break;
2833 case CompWindowTypeFullscreenMask:
2834 case CompWindowTypeDockMask:
2835 /* fullscreen and dock layer */
2836 if (sibling->priv->type & (CompWindowTypeFullscreenMask |
2837 CompWindowTypeDockMask))
2838 {
2839 if (stackLayerCheck (w, clientLeader, sibling))
2840 return true;
2841 }
2842 else
2843 {
2844 return true;
2845 }
2846 break;
2847 default:
2848 {
2849 bool allowedRelativeToLayer = !(sibling->priv->type & CompWindowTypeDockMask);
2850
2851 t = screen->findWindow (sibling->transientFor ());
2852
2853 while (t && allowedRelativeToLayer)
2854 {
2855 /* dock stacking of transients for docks */
2856 allowedRelativeToLayer = !(t->priv->type & CompWindowTypeDockMask);
2857
2858 t = screen->findWindow (t->transientFor ());
2859 }
2860
2861 /* fullscreen and normal layer */
2862 if (allowedRelativeToLayer)
2863 {
2864 if (stackLayerCheck (w, clientLeader, sibling))
2865 return true;
2866 }
2867 break;
2868 }
2869 }
2870
2871 return false;
2872 }
2873
2874 void
2875 PrivateWindow::saveGeometry (int mask)
2876 {
2877 /* only save geometry if window has been placed */
2878 if (!placed)
2879 return;
2880
2881 int m = mask & ~saveMask;
2882
2883 if (m & CWX)
2884 saveWc.x = serverGeometry.x ();
2885
2886 if (m & CWY)
2887 saveWc.y = serverGeometry.y ();
2888
2889 if (m & CWWidth)
2890 saveWc.width = serverGeometry.width ();
2891
2892 if (m & CWHeight)
2893 saveWc.height = serverGeometry.height ();
2894
2895 if (m & CWBorderWidth)
2896 saveWc.border_width = serverGeometry.border ();
2897
2898 saveMask |= m;
2899 }
2900
2901 int
2902 PrivateWindow::restoreGeometry (XWindowChanges *xwc,
2903 int mask)
2904 {
2905 int m = mask & saveMask;
2906
2907 if (m & CWX)
2908 xwc->x = saveWc.x;
2909
2910 if (m & CWY)
2911 xwc->y = saveWc.y;
2912
2913 if (m & CWWidth)
2914 {
2915 xwc->width = saveWc.width;
2916
2917 /* This is not perfect but it works OK for now. If the saved width is
2918 the same as the current width then make it a little be smaller so
2919 the user can see that it changed and it also makes sure that
2920 windowResizeNotify is called and plugins are notified. */
2921 if (xwc->width == (int) serverGeometry.width ())
2922 {
2923 xwc->width -= 10;
2924 if (m & CWX)
2925 xwc->x += 5;
2926 }
2927 }
2928
2929 if (m & CWHeight)
2930 {
2931 xwc->height = saveWc.height;
2932
2933 /* As above, if the saved height is the same as the current height
2934 then make it a little be smaller. */
2935 if (xwc->height == (int) serverGeometry.height ())
2936 {
2937 xwc->height -= 10;
2938 if (m & CWY)
2939 xwc->y += 5;
2940 }
2941 }
2942
2943 if (m & CWBorderWidth)
2944 xwc->border_width = saveWc.border_width;
2945
2946 saveMask &= ~mask;
2947
2948 return m;
2949 }
2950
2951 static bool isPendingRestack (compiz::X11::PendingEvent::Ptr p)
2952 {
2953 compiz::X11::PendingConfigureEvent::Ptr pc = boost::shared_static_cast <compiz::X11::PendingConfigureEvent> (p);
2954
2955 return pc->matchVM (CWStackMode | CWSibling);
2956 }
2957
2958 static bool isExistingRequest (compiz::X11::PendingEvent::Ptr p, XWindowChanges &xwc, unsigned int valueMask)
2959 {
2960 compiz::X11::PendingConfigureEvent::Ptr pc = boost::shared_static_cast <compiz::X11::PendingConfigureEvent> (p);
2961
2962 return pc->matchRequest (xwc, valueMask);
2963 }
2964
2965 void
2966 PrivateWindow::reconfigureXWindow (unsigned int valueMask,
2967 XWindowChanges *xwc)
2968 {
2969 if (id == screen->root ())
2970 {
2971 compLogMessage ("core", CompLogLevelWarn, "attempted to reconfigure root window");
2972 return;
2973 }
2974
2975 unsigned int frameValueMask = 0;
2976
2977 /* Remove redundant bits */
2978
2979 xwc->x = valueMask & CWX ? xwc->x : serverGeometry.x ();
2980 xwc->y = valueMask & CWY ? xwc->y : serverGeometry.y ();
2981 xwc->width = valueMask & CWWidth ? xwc->width : serverGeometry.width ();
2982 xwc->height = valueMask & CWHeight ? xwc->height : serverGeometry.height ();
2983 xwc->border_width = valueMask & CWBorderWidth ? xwc->border_width : serverGeometry.border ();
2984
2985 /* Don't allow anything that might generate a BadValue */
2986 if (valueMask & CWWidth && !xwc->width)
2987 {
2988 compLogMessage ("core", CompLogLevelWarn, "Attempted to set < 1 width on a window");
2989 xwc->width = 1;
2990 }
2991
2992 if (valueMask & CWHeight && !xwc->height)
2993 {
2994 compLogMessage ("core", CompLogLevelWarn, "Attempted to set < 1 height on a window");
2995 xwc->height = 1;
2996 }
2997
2998 int dx = valueMask & CWX ? xwc->x - serverGeometry.x () : 0;
2999 int dy = valueMask & CWY ? xwc->y - serverGeometry.y () : 0;
3000 int dwidth = valueMask & CWWidth ? xwc->width - serverGeometry.width () : 0;
3001 int dheight = valueMask & CWHeight ? xwc->height - serverGeometry.height () : 0;
3002
3003 /* FIXME: This is a total fallacy for the reparenting case
3004 * at least since the client doesn't actually move here, it only
3005 * moves within the frame */
3006 if (valueMask & CWX && serverGeometry.x () == xwc->x)
3007 valueMask &= ~(CWX);
3008
3009 if (valueMask & CWY && serverGeometry.y () == xwc->y)
3010 valueMask &= ~(CWY);
3011
3012 if (valueMask & CWWidth && serverGeometry.width () == xwc->width)
3013 valueMask &= ~(CWWidth);
3014
3015 if (valueMask & CWHeight && serverGeometry.height () == xwc->height)
3016 valueMask &= ~(CWHeight);
3017
3018 if (valueMask & CWBorderWidth && serverGeometry.border () == xwc->border_width)
3019 valueMask &= ~(CWBorderWidth);
3020
3021 if (valueMask & CWSibling && window->serverPrev)
3022 {
3023 /* check if the sibling is also pending a restack,
3024 * if not, then setting this bit is useless */
3025 if (ROOTPARENT (window->serverPrev) == xwc->sibling)
3026 {
3027 bool matchingRequest = priv->pendingConfigures.forEachIf (boost::bind (isExistingRequest, _1, *xwc, valueMask));
3028 bool restackPending = window->serverPrev->priv->pendingConfigures.forEachIf (boost::bind (isPendingRestack, _1));
3029 bool remove = matchingRequest;
3030
3031 if (!remove)
3032 remove = !restackPending;
3033
3034 if (remove)
3035 valueMask &= ~(CWSibling | CWStackMode);
3036 }
3037 }
3038
3039 if (valueMask & CWBorderWidth)
3040 serverGeometry.setBorder (xwc->border_width);
3041
3042 if (valueMask & CWX)
3043 serverGeometry.setX (xwc->x);
3044
3045 if (valueMask & CWY)
3046 serverGeometry.setY (xwc->y);
3047
3048 if (valueMask & CWWidth)
3049 serverGeometry.setWidth (xwc->width);
3050
3051 if (valueMask & CWHeight)
3052 serverGeometry.setHeight (xwc->height);
3053
3054 /* Update the server side window list on raise, lower and restack functions.
3055 * This function should only recieve stack_mode == Above
3056 * but warn incase something else does get through, to make the cause
3057 * of any potential misbehaviour obvious. */
3058 if (valueMask & (CWSibling | CWStackMode))
3059 {
3060 if (xwc->stack_mode == Above)
3061 {
3062 if (xwc->sibling)
3063 {
3064 screen->unhookServerWindow (window);
3065 screen->insertServerWindow (window, xwc->sibling);
3066 }
3067 }
3068 else
3069 compLogMessage ("core", CompLogLevelWarn, "restack_mode not Above");
3070 }
3071
3072 frameValueMask = CWX | CWY | CWWidth | CWHeight | (valueMask & (CWStackMode | CWSibling));
3073
3074 if (serverFrameGeometry.x () == xwc->x - serverGeometry.border () - serverInput.left)
3075 frameValueMask &= ~(CWX);
3076
3077 if (serverFrameGeometry.y () == xwc->y - serverGeometry.border () - serverInput.top)
3078 frameValueMask &= ~(CWY);
3079
3080 if (serverFrameGeometry.width () == xwc->width + serverGeometry.border () * 2
3081 + serverInput.left + serverInput.right)
3082 frameValueMask &= ~(CWWidth);
3083
3084 /* shaded windows are not allowed to have their frame window
3085 * height changed (but are allowed to have their client height
3086 * changed */
3087
3088 if (shaded)
3089 {
3090 if (serverFrameGeometry.height () == serverGeometry.border () * 2
3091 + serverInput.top + serverInput.bottom)
3092 frameValueMask &= ~(CWHeight);
3093 }
3094 else
3095 {
3096 if (serverFrameGeometry.height () == xwc->height + serverGeometry.border () * 2
3097 + serverInput.top + serverInput.bottom)
3098 frameValueMask &= ~(CWHeight);
3099 }
3100
3101
3102 if (valueMask & CWStackMode &&
3103 ((xwc->stack_mode != TopIf) && (xwc->stack_mode != BottomIf) && (xwc->stack_mode != Opposite) &&
3104 (xwc->stack_mode != Above) && (xwc->stack_mode != Below)))
3105 {
3106 compLogMessage ("core", CompLogLevelWarn, "Invalid stack mode %i", xwc->stack_mode);
3107 valueMask &= ~(CWStackMode | CWSibling);
3108 }
3109
3110 /* Don't allow anything that might cause a BadMatch error */
3111
3112 if (valueMask & CWSibling && !(valueMask & CWStackMode))
3113 {
3114 compLogMessage ("core", CompLogLevelWarn, "Didn't specify a CWStackMode for CWSibling");
3115 valueMask &= ~CWSibling;
3116 }
3117
3118 if (valueMask & CWSibling && xwc->sibling == (serverFrame ? serverFrame : id))
3119 {
3120 compLogMessage ("core", CompLogLevelWarn, "Can't restack a window relative to itself");
3121 valueMask &= ~CWSibling;
3122 }
3123
3124 if (valueMask & CWBorderWidth && attrib.c_class == InputOnly)
3125 {
3126 compLogMessage ("core", CompLogLevelWarn, "Cannot set border_width of an input_only window");
3127 valueMask &= ~CWBorderWidth;
3128 }
3129
3130 if (valueMask & CWSibling)
3131 {
3132 CompWindow *sibling = screen->findTopLevelWindow (xwc->sibling);
3133
3134 if (!sibling)
3135 {
3136 compLogMessage ("core", CompLogLevelWarn, "Attempted to restack relative to 0x%x which is "\
3137 "not a child of the root window or a window compiz owns", static_cast <unsigned int> (xwc->sibling));
3138 valueMask &= ~(CWSibling | CWStackMode);
3139 }
3140 else if (sibling->frame () && xwc->sibling != sibling->frame ())
3141 {
3142 compLogMessage ("core", CompLogLevelWarn, "Attempted to restack relative to 0x%x which is "\
3143 "not a child of the root window", static_cast <unsigned int> (xwc->sibling));
3144 valueMask &= ~(CWSibling | CWStackMode);
3145 }
3146 }
3147
3148 /* Can't set the border width of frame windows */
3149 frameValueMask &= ~(CWBorderWidth);
3150
3151 if (frameValueMask & CWX)
3152 serverFrameGeometry.setX (xwc->x - serverGeometry.border () - serverInput.left);
3153
3154 if (frameValueMask & CWY)
3155 serverFrameGeometry.setY (xwc->y -serverGeometry.border () - serverInput.top);
3156
3157 if (frameValueMask & CWWidth)
3158 serverFrameGeometry.setWidth (xwc->width + serverGeometry.border () * 2
3159 + serverInput.left + serverInput.right);
3160
3161 if (shaded)
3162 {
3163 if (frameValueMask & CWHeight)
3164 serverFrameGeometry.setHeight (serverGeometry.border () * 2
3165 + serverInput.top + serverInput.bottom);
3166 }
3167 else
3168 {
3169 if (frameValueMask & CWHeight)
3170 serverFrameGeometry.setHeight (xwc->height + serverGeometry.border () * 2
3171 + serverInput.top + serverInput.bottom);
3172 }
3173
3174 if (serverFrame)
3175 {
3176 if (frameValueMask)
3177 {
3178 XWindowChanges wc = *xwc;
3179
3180 wc.x = serverFrameGeometry.x ();
3181 wc.y = serverFrameGeometry.y ();
3182 wc.width = serverFrameGeometry.width ();
3183 wc.height = serverFrameGeometry.height ();
3184
3185 compiz::X11::PendingEvent::Ptr pc =
3186 boost::shared_static_cast<compiz::X11::PendingEvent> (compiz::X11::PendingConfigureEvent::Ptr (
3187 new compiz::X11::PendingConfigureEvent (
3188 screen->dpy (), priv->serverFrame, frameValueMask, &wc)));
3189
3190 pendingConfigures.add (pc);
3191
3192 XConfigureWindow (screen->dpy (), serverFrame, frameValueMask, &wc);
3193 }
3194
3195 valueMask &= ~(CWSibling | CWStackMode);
3196
3197 /* If the frame has changed position (eg, serverInput.top
3198 * or serverInput.left have changed) then we also need to
3199 * update the client and wrapper position */
3200 if (!(valueMask & CWX))
3201 valueMask |= frameValueMask & CWX;
3202 if (!(valueMask & CWY))
3203 valueMask |= frameValueMask & CWY;
3204
3205 if (valueMask)
3206 {
3207 xwc->x = serverInput.left;
3208 xwc->y = serverInput.top;
3209 XConfigureWindow (screen->dpy (), wrapper, valueMask, xwc);
3210
3211 xwc->x = 0;
3212 xwc->y = 0;
3213 }
3214 }
3215
3216 if (valueMask)
3217 XConfigureWindow (screen->dpy (), id, valueMask, xwc);
3218
3219 /* Send the synthetic configure notify
3220 * after the real configure notify arrives
3221 * (ICCCM s4.1.5) */
3222 if (serverFrame)
3223 window->sendConfigureNotify ();
3224
3225 /* When updating plugins we care about
3226 * the absolute position */
3227 if (dx)
3228 valueMask |= CWX;
3229 if (dy)
3230 valueMask |= CWY;
3231 if (dwidth)
3232 valueMask |= CWWidth;
3233 if (dheight)
3234 valueMask |= CWHeight;
3235
3236 if (!attrib.override_redirect)
3237 {
3238 if (valueMask & (CWWidth | CWHeight))
3239 {
3240 updateRegion ();
3241 window->resizeNotify (dx, dy, dwidth, dheight);
3242 }
3243 else if (valueMask & (CWX | CWY))
3244 {
3245 region.translate (dx, dy);
3246 inputRegion.translate (dx, dy);
3247 if (!frameRegion.isEmpty ())
3248 frameRegion.translate (dx, dy);
3249
3250 if (dx || dy)
3251 {
3252 window->moveNotify (dx, dy, priv->nextMoveImmediate);
3253 priv->nextMoveImmediate = true;
3254 }
3255 }
3256 }
3257 }
3258
3259 bool
3260 PrivateWindow::stackDocks (CompWindow *w,
3261 CompWindowList &updateList,
3262 XWindowChanges *xwc,
3263 unsigned int *mask)
3264 {
3265 CompWindow *firstFullscreenWindow = NULL;
3266 CompWindow *belowDocks = NULL;
3267
3268 foreach (CompWindow *dw, screen->serverWindows ())
3269 {
3270 /* fullscreen window found */
3271 if (firstFullscreenWindow)
3272 {
3273 /* If there is another toplevel window above the fullscreen one
3274 * then we need to stack above that */
3275 if ((dw->priv->managed && !dw->priv->unmanaging) &&
3276 !(dw->priv->state & CompWindowStateHiddenMask) &&
3277 !PrivateWindow::isAncestorTo (w, dw) &&
3278 !(dw->type () & (CompWindowTypeFullscreenMask |
3279 CompWindowTypeDockMask)) &&
3280 !dw->overrideRedirect () &&
3281 dw->isViewable ())
3282 {
3283 belowDocks = dw;
3284 }
3285 }
3286 else if (dw->type () & CompWindowTypeFullscreenMask)
3287 {
3288 /* First fullscreen window found when checking up the stack
3289 * now go back down to find a suitable candidate client
3290 * window to put the docks above */
3291 firstFullscreenWindow = dw;
3292 for (CompWindow *dww = dw->serverPrev; dww; dww = dww->serverPrev)
3293 {
3294 if ((dw->priv->managed && !dw->priv->unmanaging) &&
3295 !(dw->priv->state & CompWindowStateHiddenMask) &&
3296 !(dww->type () & (CompWindowTypeFullscreenMask |
3297 CompWindowTypeDockMask)) &&
3298 !dww->overrideRedirect () &&
3299 dww->isViewable ())
3300 {
3301 belowDocks = dww;
3302 break;
3303 }
3304 }
3305 }
3306 }
3307
3308 if (belowDocks)
3309 {
3310 *mask = CWSibling | CWStackMode;
3311 xwc->sibling = ROOTPARENT (belowDocks);
3312
3313 /* Collect all dock windows first */
3314 foreach (CompWindow *dw, screen->serverWindows ())
3315 if (dw->priv->type & CompWindowTypeDockMask)
3316 updateList.push_front (dw);
3317
3318 return true;
3319 }
3320
3321 return false;
3322 }
3323
3324 bool
3325 PrivateWindow::stackTransients (CompWindow *w,
3326 CompWindow *avoid,
3327 XWindowChanges *xwc,
3328 CompWindowList &updateList)
3329 {
3330 CompWindow *t;
3331 Window clientLeader = w->priv->clientLeader;
3332
3333 if (w->priv->transientFor || w->priv->isGroupTransient (clientLeader))
3334 clientLeader = None;
3335
3336 for (t = screen->serverWindows ().back (); t; t = t->serverPrev)
3337 {
3338 if (t == w || t == avoid)
3339 continue;
3340
3341 if (t->priv->transientFor == w->priv->id ||
3342 t->priv->isGroupTransient (clientLeader))
3343 {
3344 if (!stackTransients (t, avoid, xwc, updateList))
3345 return false;
3346
3347 if (xwc->sibling == t->priv->id ||
3348 (t->priv->serverFrame && xwc->sibling == t->priv->serverFrame))
3349 return false;
3350
3351 if (t->priv->mapNum || t->priv->pendingMaps)
3352 updateList.push_back (t);
3353 }
3354 }
3355
3356 return true;
3357 }
3358
3359 void
3360 PrivateWindow::stackAncestors (CompWindow *w,
3361 XWindowChanges *xwc,
3362 CompWindowList &updateList)
3363 {
3364 CompWindow *transient = NULL;
3365
3366 if (w->priv->transientFor)
3367 transient = screen->findWindow (w->priv->transientFor);
3368
3369 if (transient &&
3370 xwc->sibling != transient->priv->id &&
3371 (!transient->priv->serverFrame || xwc->sibling != transient->priv->serverFrame))
3372 {
3373 CompWindow *ancestor;
3374
3375 ancestor = screen->findWindow (w->priv->transientFor);
3376 if (ancestor)
3377 {
3378 if (!stackTransients (ancestor, w, xwc, updateList))
3379 return;
3380
3381 if (ancestor->priv->type & CompWindowTypeDesktopMask)
3382 return;
3383
3384 if (ancestor->priv->type & CompWindowTypeDockMask)
3385 if (!(w->priv->type & CompWindowTypeDockMask))
3386 return;
3387
3388 if (ancestor->priv->mapNum || ancestor->priv->pendingMaps)
3389 updateList.push_back (ancestor);
3390
3391 stackAncestors (ancestor, xwc, updateList);
3392 }
3393 }
3394 else if (w->priv->isGroupTransient (w->priv->clientLeader))
3395 {
3396 CompWindow *a;
3397
3398 for (a = screen->serverWindows ().back (); a; a = a->serverPrev)
3399 {
3400 if (a->priv->clientLeader == w->priv->clientLeader &&
3401 a->priv->transientFor == None &&
3402 !a->priv->isGroupTransient (w->priv->clientLeader))
3403 {
3404 if (xwc->sibling == a->priv->id ||
3405 (a->priv->serverFrame && xwc->sibling == a->priv->serverFrame))
3406 break;
3407
3408 if (!stackTransients (a, w, xwc, updateList))
3409 break;
3410
3411 if (a->priv->type & CompWindowTypeDesktopMask)
3412 continue;
3413
3414 if (a->priv->type & CompWindowTypeDockMask)
3415 if (!(w->priv->type & CompWindowTypeDockMask))
3416 break;
3417
3418 if (a->priv->mapNum || a->priv->pendingMaps)
3419 updateList.push_back (a);
3420 }
3421 }
3422 }
3423 }
3424
3425 void
3426 CompWindow::configureXWindow (unsigned int valueMask,
3427 XWindowChanges *xwc)
3428 {
3429 if (priv->managed && (valueMask & (CWSibling | CWStackMode)))
3430 {
3431 CompWindowList transients;
3432 CompWindowList ancestors;
3433 CompWindowList docks;
3434
3435 /* Since the window list is being reordered in reconfigureXWindow
3436 the list of windows which need to be restacked must be stored
3437 first. The windows are stacked in the opposite order than they
3438 were previously stacked, in order that they are above xwc->sibling
3439 so that when compiz gets the ConfigureNotify event it doesn't
3440 have to restack all the windows again. */
3441
3442 /* transient children above */
3443 if (PrivateWindow::stackTransients (this, NULL, xwc, transients))
3444 {
3445 /* ancestors, siblings and sibling transients below */
3446 PrivateWindow::stackAncestors (this, xwc, ancestors);
3447
3448 for (CompWindowList::reverse_iterator w = ancestors.rbegin ();
3449 w != ancestors.rend (); w++)
3450 {
3451 (*w)->priv->reconfigureXWindow (CWSibling | CWStackMode, xwc);
3452 xwc->sibling = ROOTPARENT (*w);
3453 }
3454
3455 this->priv->reconfigureXWindow (valueMask, xwc);
3456 xwc->sibling = ROOTPARENT (this);
3457
3458 for (CompWindowList::reverse_iterator w = transients.rbegin ();
3459 w != transients.rend (); w++)
3460 {
3461 (*w)->priv->reconfigureXWindow (CWSibling | CWStackMode, xwc);
3462 xwc->sibling = ROOTPARENT (*w);
3463 }
3464
3465 if (PrivateWindow::stackDocks (this, docks, xwc, &valueMask))
3466 {
3467 Window sibling = xwc->sibling;
3468 xwc->stack_mode = Above;
3469
3470 /* Then update the dock windows */
3471 foreach (CompWindow *dw, docks)
3472 {
3473 xwc->sibling = sibling;
3474 dw->priv->reconfigureXWindow (valueMask, xwc);
3475 }
3476 }
3477 }
3478 }
3479 else if (priv->id)
3480 {
3481 priv->reconfigureXWindow (valueMask, xwc);
3482 }
3483 }
3484
3485 int
3486 PrivateWindow::addWindowSizeChanges (XWindowChanges *xwc,
3487 CompWindow::Geometry old)
3488 {
3489 CompRect workArea;
3490 int mask = 0;
3491 int x, y;
3492 CompOutput *output;
3493 CompPoint viewport;
3494
3495 screen->viewportForGeometry (old, viewport);
3496
3497 x = (viewport.x () - screen->vp ().x ()) * screen->width ();
3498 y = (viewport.y () - screen->vp ().y ()) * screen->height ();
3499
3500 /* Try to select and output device that the window is on first
3501 * and make sure if we are fullscreening or maximizing that the
3502 * window is actually able to fit on this output ... otherwise
3503 * we're going to have to use another output device which sucks
3504 * but at least the user will be able to see all of the window */
3505 output = &screen->outputDevs ().at (screen->outputDeviceForGeometry (old));
3506
3507 /*
3508 * output is now the correct output for the given geometry.
3509 * There used to be a lot more logic here to handle the rare special
3510 * case of maximizing a window whose hints say it is too large to fit
3511 * the output and choose a different one. However that logic was a bad
3512 * idea because:
3513 * (1) It's confusing to the user to auto-magically move a window
3514 * between monitors when they didn't ask for it. So don't.
3515 * (2) In the worst case where the window can't go small enough to fit
3516 * the output, they can simply move it with Alt+drag, Alt+F7 or
3517 * expo.
3518 * Not moving the window at all is much less annoying than moving it when
3519 * the user never asked to.
3520 */
3521
3522 workArea = output->workArea ();
3523
3524 if (type & CompWindowTypeFullscreenMask)
3525 {
3526 saveGeometry (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
3527
3528 if (fullscreenMonitorsSet)
3529 {
3530 xwc->x = x + fullscreenMonitorRect.x ();
3531 xwc->y = y + fullscreenMonitorRect.y ();
3532 xwc->width = fullscreenMonitorRect.width ();
3533 xwc->height = fullscreenMonitorRect.height ();
3534 }
3535 else
3536 {
3537 xwc->x = x + output->x ();
3538 xwc->y = y + output->y ();
3539 xwc->width = output->width ();
3540 xwc->height = output->height ();
3541 }
3542
3543 xwc->border_width = 0;
3544
3545 mask |= CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
3546 }
3547 else
3548 {
3549 mask |= restoreGeometry (xwc, CWBorderWidth);
3550 if (state & CompWindowStateMaximizedVertMask)
3551 {
3552 saveGeometry (CWY | CWHeight);
3553
3554 xwc->height = workArea.height () - border.top -
3555 border.bottom - old.border () * 2;
3556
3557 mask |= CWHeight;
3558 }
3559 else
3560 {
3561 mask |= restoreGeometry (xwc, CWY | CWHeight);
3562 }
3563
3564 if (state & CompWindowStateMaximizedHorzMask)
3565 {
3566 saveGeometry (CWX | CWWidth);
3567
3568 xwc->width = workArea.width () - border.left -
3569 border.right - old.border () * 2;
3570
3571 mask |= CWWidth;
3572 }
3573 else
3574 {
3575 mask |= restoreGeometry (xwc, CWX | CWWidth);
3576 }
3577
3578 /* constrain window width if smaller than minimum width */
3579 if (!(mask & CWWidth) && (int) old.width () < sizeHints.min_width)
3580 {
3581 xwc->width = sizeHints.min_width;
3582 mask |= CWWidth;
3583 }
3584
3585 /* constrain window width if greater than maximum width */
3586 if (!(mask & CWWidth) && (int) old.width () > sizeHints.max_width)
3587 {
3588 xwc->width = sizeHints.max_width;
3589 mask |= CWWidth;
3590 }
3591
3592 /* constrain window height if smaller than minimum height */
3593 if (!(mask & CWHeight) && (int) old.height () < sizeHints.min_height)
3594 {
3595 xwc->height = sizeHints.min_height;
3596 mask |= CWHeight;
3597 }
3598
3599 /* constrain window height if greater than maximum height */
3600 if (!(mask & CWHeight) && (int) old.height () > sizeHints.max_height)
3601 {
3602 xwc->height = sizeHints.max_height;
3603 mask |= CWHeight;
3604 }
3605
3606 if (mask & (CWWidth | CWHeight))
3607 {
3608 int width, height, max;
3609
3610 width = (mask & CWWidth) ? xwc->width : old.width ();
3611 height = (mask & CWHeight) ? xwc->height : old.height ();
3612
3613 xwc->width = old.width ();
3614 xwc->height = old.height ();
3615
3616 window->constrainNewWindowSize (width, height, &width, &height);
3617
3618 if (width != (int) old.width ())
3619 {
3620 mask |= CWWidth;
3621 xwc->width = width;
3622 }
3623 else
3624 mask &= ~CWWidth;
3625
3626 if (height != (int) old.height ())
3627 {
3628 mask |= CWHeight;
3629 xwc->height = height;
3630 }
3631 else
3632 mask &= ~CWHeight;
3633
3634 if (state & CompWindowStateMaximizedVertMask)
3635 {
3636 /* If the window is still offscreen, then we need to constrain it
3637 * by the gravity value (so that the corner that the gravity specifies
3638 * is 'anchored' to that edge of the workarea) */
3639
3640 xwc->y = y + workArea.y () + border.top;
3641 mask |= CWY;
3642
3643 switch (priv->sizeHints.win_gravity)
3644 {
3645 case SouthWestGravity:
3646 case SouthEastGravity:
3647 case SouthGravity:
3648 /* Shift the window so that the bottom meets the top of the bottom */
3649 height = xwc->height + old.border () * 2;
3650
3651 max = y + workArea.bottom ();
3652 if (xwc->y + xwc->height + border.bottom > max)
3653 {
3654 xwc->y = max - height - border.bottom;
3655 mask |= CWY;
3656 }
3657 break;
3658 /* For EastGravity, WestGravity and CenterGravity we default to the top
3659 * of the window since the user should at least be able to close it
3660 * (but not for SouthGravity, SouthWestGravity and SouthEastGravity since
3661 * that indicates that the application has requested positioning in that area
3662 */
3663 case EastGravity:
3664 case WestGravity:
3665 case CenterGravity:
3666 case NorthWestGravity:
3667 case NorthEastGravity:
3668 case NorthGravity:
3669 default:
3670 /* Shift the window so that the top meets the top of the screen */
3671 break;
3672 }
3673 }
3674
3675 if (state & CompWindowStateMaximizedHorzMask)
3676 {
3677 xwc->x = x + workArea.x () + border.left;
3678 mask |= CWX;
3679
3680 switch (priv->sizeHints.win_gravity)
3681 {
3682 case NorthEastGravity:
3683 case SouthEastGravity:
3684 case EastGravity:
3685 width = xwc->width + old.border () * 2;
3686
3687 max = x + workArea.right ();
3688
3689 if (old.x () + (int) old.width () + border.right > max)
3690 {
3691 xwc->x = max - width - border.right;
3692 mask |= CWX;
3693 }
3694 else if (old.x () + width + border.right > max)
3695 {
3696 xwc->x = x + workArea.x () +
3697 (workArea.width () - border.left - width -
3698 border.right) / 2 + border.left;
3699 mask |= CWX;
3700 }
3701 /* For NorthGravity, SouthGravity and CenterGravity we default to the top
3702 * of the window since the user should at least be able to close it
3703 * (but not for SouthGravity, SouthWestGravity and SouthEastGravity since
3704 * that indicates that the application has requested positioning in that area
3705 */
3706 case NorthGravity:
3707 case SouthGravity:
3708 case CenterGravity:
3709 case NorthWestGravity:
3710 case SouthWestGravity:
3711 case WestGravity:
3712 default:
3713 break;
3714 }
3715 }
3716 }
3717 }
3718
3719 if ((mask & CWX) && (xwc->x == old.x ()))
3720 mask &= ~CWX;
3721
3722 if ((mask & CWY) && (xwc->y == old.y ()))
3723 mask &= ~CWY;
3724
3725 if ((mask & CWWidth) && (xwc->width == (int) old.width ()))
3726 mask &= ~CWWidth;
3727
3728 if ((mask & CWHeight) && (xwc->height == (int) old.height ()))
3729 mask &= ~CWHeight;
3730
3731 return mask;
3732 }
3733
3734 unsigned int
3735 PrivateWindow::adjustConfigureRequestForGravity (XWindowChanges *xwc,
3736 unsigned int xwcm,
3737 int gravity,
3738 int direction)
3739 {
3740 int newX, newY;
3741 unsigned int mask = 0;
3742
3743 newX = xwc->x;
3744 newY = xwc->y;
3745
3746 if (xwcm & (CWX | CWWidth))
3747 {
3748 switch (gravity) {
3749 case NorthWestGravity:
3750 case WestGravity:
3751 case SouthWestGravity:
3752 if (xwcm & CWX)
3753 newX += priv->border.left * direction;
3754 break;
3755
3756 case NorthGravity:
3757 case CenterGravity:
3758 case SouthGravity:
3759 if (xwcm & CWX)
3760 newX -= (xwc->width / 2 - priv->border.left +
3761 (priv->border.left + priv->border.right) / 2) * direction;
3762 else
3763 newX -= (xwc->width - priv->serverGeometry.width ()) * direction;
3764 break;
3765
3766 case NorthEastGravity:
3767 case EastGravity:
3768 case SouthEastGravity:
3769 if (xwcm & CWX)
3770 newX -= xwc->width + priv->border.right * direction;
3771 else
3772 newX -= (xwc->width - priv->serverGeometry.width ()) * direction;
3773 break;
3774
3775 case StaticGravity:
3776 default:
3777 break;
3778 }
3779 }
3780
3781 if (xwcm & (CWY | CWHeight))
3782 {
3783 switch (gravity) {
3784 case NorthWestGravity:
3785 case NorthGravity:
3786 case NorthEastGravity:
3787 if (xwcm & CWY)
3788 newY = xwc->y + priv->border.top * direction;
3789 break;
3790
3791 case WestGravity:
3792 case CenterGravity:
3793 case EastGravity:
3794 if (xwcm & CWY)
3795 newY -= (xwc->height / 2 - priv->border.top +
3796 (priv->border.top + priv->border.bottom) / 2) * direction;
3797 else
3798 newY -= ((xwc->height - priv->serverGeometry.height ()) / 2) * direction;
3799 break;
3800
3801 case SouthWestGravity:
3802 case SouthGravity:
3803 case SouthEastGravity:
3804 if (xwcm & CWY)
3805 newY -= xwc->height + priv->border.bottom * direction;
3806 else
3807 newY -= (xwc->height - priv->serverGeometry.height ()) * direction;
3808 break;
3809
3810 case StaticGravity:
3811 default:
3812 break;
3813 }
3814 }
3815
3816 if (newX != xwc->x)
3817 {
3818 xwc->x += (newX - xwc->x);
3819 mask |= CWX;
3820 }
3821
3822 if (newY != xwc->y)
3823 {
3824 xwc->y += (newY - xwc->y);
3825 mask |= CWY;
3826 }
3827
3828 return mask;
3829 }
3830
3831 void
3832 CompWindow::moveResize (XWindowChanges *xwc,
3833 unsigned int xwcm,
3834 int gravity,
3835 unsigned int source)
3836 {
3837 bool placed = false;
3838
3839 xwcm &= (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
3840
3841 if (xwcm & (CWX | CWY))
3842 if (priv->sizeHints.flags & (USPosition | PPosition))
3843 placed = true;
3844
3845 if (gravity == 0)
3846 gravity = priv->sizeHints.win_gravity;
3847
3848 if (!(xwcm & CWX))
3849 xwc->x = priv->serverGeometry.x ();
3850 if (!(xwcm & CWY))
3851 xwc->y = priv->serverGeometry.y ();
3852 if (!(xwcm & CWWidth))
3853 xwc->width = priv->serverGeometry.width ();
3854 if (!(xwcm & CWHeight))
3855 xwc->height = priv->serverGeometry.height ();
3856
3857 if (xwcm & (CWWidth | CWHeight))
3858 {
3859 int width, height;
3860
3861 if (constrainNewWindowSize (xwc->width, xwc->height, &width, &height))
3862 {
3863 if (width != xwc->width)
3864 xwcm |= CWWidth;
3865
3866 if (height != xwc->height)
3867 xwcm |= CWHeight;
3868
3869 xwc->width = width;
3870 xwc->height = height;
3871 }
3872 }
3873
3874 xwcm |= priv->adjustConfigureRequestForGravity (xwc, xwcm, gravity, 1);
3875
3876 validateResizeRequest (xwcm, xwc, source);
3877
3878 /* when horizontally maximized only allow width changes added by
3879 addWindowSizeChanges */
3880 if (priv->state & CompWindowStateMaximizedHorzMask)
3881 xwcm &= ~CWWidth;
3882
3883 /* when vertically maximized only allow height changes added by
3884 addWindowSizeChanges */
3885 if (priv->state & CompWindowStateMaximizedVertMask)
3886 xwcm &= ~CWHeight;
3887
3888 xwcm |= priv->addWindowSizeChanges (xwc, Geometry (xwc->x, xwc->y,
3889 xwc->width, xwc->height,
3890 xwc->border_width));
3891
3892 /* check if the new coordinates are useful and valid (different
3893 to current size); if not, we have to clear them to make sure
3894 we send a synthetic ConfigureNotify event if all coordinates
3895 match the server coordinates */
3896 if (xwc->x == priv->serverGeometry.x ())
3897 xwcm &= ~CWX;
3898
3899 if (xwc->y == priv->serverGeometry.y ())
3900 xwcm &= ~CWY;
3901
3902 if (xwc->width == (int) priv->serverGeometry.width ())
3903 xwcm &= ~CWWidth;
3904
3905 if (xwc->height == (int) priv->serverGeometry.height ())
3906 xwcm &= ~CWHeight;
3907
3908 if (xwc->border_width == (int) priv->serverGeometry.border ())
3909 xwcm &= ~CWBorderWidth;
3910
3911 /* update saved window coordinates - if CWX or CWY is set for fullscreen
3912 or maximized windows after addWindowSizeChanges, it should be pretty
3913 safe to assume that the saved coordinates should be updated too, e.g.
3914 because the window was moved to another viewport by some client */
3915 if ((xwcm & CWX) && (priv->saveMask & CWX))
3916 priv->saveWc.x += (xwc->x - priv->serverGeometry.x ());
3917
3918 if ((xwcm & CWY) && (priv->saveMask & CWY))
3919 priv->saveWc.y += (xwc->y - priv->serverGeometry.y ());
3920
3921 if (priv->mapNum && (xwcm & (CWWidth | CWHeight)))
3922 sendSyncRequest ();
3923
3924 if (xwcm)
3925 configureXWindow (xwcm, xwc);
3926 else
3927 {
3928 /* we have to send a configure notify on ConfigureRequest events if
3929 we decide not to do anything according to ICCCM 4.1.5 */
3930 sendConfigureNotify ();
3931 }
3932
3933 if (placed)
3934 priv->placed = true;
3935 }
3936
3937 void
3938 PrivateWindow::updateSize ()
3939 {
3940 if (window->overrideRedirect () || !managed)
3941 return;
3942
3943 XWindowChanges xwc = XWINDOWCHANGES_INIT;
3944
3945 int mask = priv->addWindowSizeChanges (&xwc, priv->serverGeometry);
3946 if (mask)
3947 {
3948 if (priv->mapNum && (mask & (CWWidth | CWHeight)))
3949 window->sendSyncRequest ();
3950
3951 window->configureXWindow (mask, &xwc);
3952 }
3953 }
3954
3955 int
3956 PrivateWindow::addWindowStackChanges (XWindowChanges *xwc,
3957 CompWindow *sibling)
3958 {
3959 int mask = 0;
3960
3961 if (!sibling || sibling->priv->id != id)
3962 {
3963 /* Alow requests to go on top of serverPrev
3964 * if serverPrev was recently restacked */
3965 if (window->serverPrev)
3966 {
3967 if (!sibling && id)
3968 {
3969 XWindowChanges lxwc = XWINDOWCHANGES_INIT;
3970 unsigned int valueMask = CWStackMode;
3971
3972 lxwc.stack_mode = Below;
3973
3974 if (serverFrame)
3975 {
3976 compiz::X11::PendingEvent::Ptr pc =
3977 boost::shared_static_cast<compiz::X11::PendingEvent> (compiz::X11::PendingConfigureEvent::Ptr (
3978 new compiz::X11::PendingConfigureEvent (
3979 screen->dpy (), serverFrame, valueMask, &lxwc)));
3980
3981 pendingConfigures.add (pc);
3982 }
3983
3984 /* Below with no sibling puts the window at the bottom
3985 * of the stack */
3986 XConfigureWindow (screen->dpy (), ROOTPARENT (window), valueMask, &lxwc);
3987
3988 /* Update the list of windows last sent to the server */
3989 screen->unhookServerWindow (window);
3990 screen->insertServerWindow (window, 0);
3991 }
3992 else if (sibling)
3993 {
3994 bool matchingRequest = priv->pendingConfigures.forEachIf (boost::bind (isExistingRequest, _1, *xwc, (CWStackMode | CWSibling)));
3995 bool restackPending = window->serverPrev->priv->pendingConfigures.forEachIf (boost::bind (isPendingRestack, _1));
3996 bool processAnyways = restackPending;
3997
3998 if (matchingRequest)
3999 processAnyways = false;
4000
4001 if (sibling->priv->id != window->serverPrev->priv->id ||
4002 processAnyways)
4003 {
4004 mask |= CWSibling | CWStackMode;
4005
4006 xwc->stack_mode = Above;
4007 xwc->sibling = ROOTPARENT (sibling);
4008 }
4009 }
4010 }
4011 else if (sibling)
4012 {
4013 mask |= CWSibling | CWStackMode;
4014
4015 xwc->stack_mode = Above;
4016 xwc->sibling = ROOTPARENT (sibling);
4017 }
4018 }
4019
4020 return mask;
4021 }
4022
4023 void
4024 CompWindow::raise ()
4025 {
4026 XWindowChanges xwc = XWINDOWCHANGES_INIT;
4027 int mask;
4028 bool aboveFs = false;
4029
4030 /* an active fullscreen window should be raised over all other
4031 windows in its layer */
4032 if (priv->type & CompWindowTypeFullscreenMask)
4033 if (priv->id == screen->activeWindow ())
4034 aboveFs = true;
4035
4036 for (CompWindow *pw = serverPrev; pw; pw = pw->serverPrev)
4037 {
4038 if (pw->priv->type & CompWindowTypeFullscreenMask)
4039 {
4040 if (priv->id == screen->activeWindow ())
4041 aboveFs = true;
4042
4043 break;
4044 }
4045 }
4046
4047 mask = priv->addWindowStackChanges (&xwc,
4048 PrivateWindow::findSiblingBelow (this, aboveFs));
4049
4050 if (mask)
4051 configureXWindow (mask, &xwc);
4052 }
4053
4054 CompWindow *
4055 CompScreenImpl::focusTopMostWindow ()
4056 {
4057 using ::compiz::private_screen::WindowManager;
4058
4059 CompWindow *focus = NULL;
4060 WindowManager::reverse_iterator it = windowManager.rbegin ();
4061
4062 for (; it != windowManager.rend (); it++)
4063 {
4064 CompWindow *w = *it;
4065
4066 if (w->type () & CompWindowTypeDockMask)
4067 continue;
4068
4069 if (w->focus ())
4070 {
4071 focus = w;
4072 break;
4073 }
4074 }
4075
4076 if (focus)
4077 {
4078 if (focus->id () != privateScreen.orphanData.activeWindow)
4079 focus->moveInputFocusTo ();
4080 }
4081 else
4082 XSetInputFocus (privateScreen.dpy, privateScreen.rootWindow(), RevertToPointerRoot,
4083 CurrentTime);
4084 return focus;
4085 }
4086
4087
4088 void
4089 CompWindow::lower ()
4090 {
4091 XWindowChanges xwc = XWINDOWCHANGES_INIT;
4092 int mask;
4093
4094 mask = priv->addWindowStackChanges (&xwc,
4095 PrivateWindow::findLowestSiblingBelow (this));
4096 if (mask)
4097 configureXWindow (mask, &xwc);
4098
4099 /* when lowering a window, focus the topmost window if
4100 the click-to-focus option is on */
4101 if ((screen->getCoreOptions().optionGetClickToFocus ()))
4102 {
4103 CompWindow *focusedWindow = screen->focusTopMostWindow ();
4104
4105 /* if the newly focused window is a desktop window,
4106 give the focus back to w */
4107 if (focusedWindow &&
4108 focusedWindow->type () & CompWindowTypeDesktopMask)
4109 {
4110 moveInputFocusTo ();
4111 }
4112 }
4113 }
4114
4115 void
4116 CompWindow::restackAbove (CompWindow *sibling)
4117 {
4118 for (; sibling; sibling = sibling->serverNext)
4119 if (PrivateWindow::validSiblingBelow (this, sibling))
4120 break;
4121
4122 if (sibling)
4123 {
4124 XWindowChanges xwc = XWINDOWCHANGES_INIT;
4125 int mask;
4126
4127 mask = priv->addWindowStackChanges (&xwc, sibling);
4128 if (mask)
4129 configureXWindow (mask, &xwc);
4130 }
4131 }
4132
4133 /* finds the highest window under sibling we can stack above */
4134 CompWindow *
4135 PrivateWindow::findValidStackSiblingBelow (CompWindow *w,
4136 CompWindow *sibling)
4137 {
4138 CompWindow *lowest, *last, *p;
4139
4140 /* check whether we're allowed to stack under a sibling by finding
4141 * the above 'sibling' and checking whether or not we're allowed
4142 * to stack under that - if not, then there is no valid sibling
4143 * underneath it */
4144
4145 for (p = sibling; p; p = p->serverNext)
4146 {
4147 if (!avoidStackingRelativeTo (p))
4148 {
4149 if (!validSiblingBelow (p, w))
4150 return NULL;
4151 break;
4152 }
4153 }
4154
4155 /* get lowest sibling we're allowed to stack above */
4156 lowest = last = findLowestSiblingBelow (w);
4157
4158 /* walk from bottom up */
4159 for (p = screen->serverWindows ().front (); p; p = p->serverNext)
4160 {
4161 /* stop walking when we reach the sibling we should try to stack
4162 below */
4163 if (p == sibling)
4164 return lowest;
4165
4166 /* skip windows that we should avoid */
4167 if (w == p || avoidStackingRelativeTo (p))
4168 continue;
4169
4170 if (validSiblingBelow (w, p))
4171 {
4172 /* update lowest as we find windows below sibling that we're
4173 allowed to stack above. last window must be equal to the
4174 lowest as we shouldn't update lowest if we passed an
4175 invalid window */
4176 if (last == lowest)
4177 lowest = p;
4178 }
4179
4180 /* update last pointer */
4181 last = p;
4182 }
4183
4184 return lowest;
4185 }
4186
4187 void
4188 CompWindow::restackBelow (CompWindow *sibling)
4189 {
4190 XWindowChanges xwc = XWINDOWCHANGES_INIT;
4191 unsigned int mask;
4192
4193 mask = priv->addWindowStackChanges (&xwc,
4194 PrivateWindow::findValidStackSiblingBelow (this, sibling));
4195
4196 if (mask)
4197 configureXWindow (mask, &xwc);
4198 }
4199
4200 void
4201 CompWindow::updateAttributes (CompStackingUpdateMode stackingMode)
4202 {
4203 if (overrideRedirect () || !priv->managed)
4204 return;
4205
4206 XWindowChanges xwc = XWINDOWCHANGES_INIT;
4207 int mask = 0;
4208
4209 if (priv->state & CompWindowStateShadedMask && !priv->shaded)
4210 {
4211 windowNotify (CompWindowNotifyShade);
4212
4213 priv->hide ();
4214 }
4215 else if (priv->shaded)
4216 {
4217 windowNotify (CompWindowNotifyUnshade);
4218
4219 priv->show ();
4220 }
4221
4222 if (stackingMode != CompStackingUpdateModeNone)
4223 {
4224 bool aboveFs;
4225 CompWindow *sibling;
4226
4227 aboveFs = (stackingMode == CompStackingUpdateModeAboveFullscreen);
4228 if (priv->type & CompWindowTypeFullscreenMask)
4229 {
4230 /* put active or soon-to-be-active fullscreen windows over
4231 all others in their layer */
4232 if (priv->id == screen->activeWindow () ||
4233 priv->id == screen->getNextActiveWindow())
4234 {
4235 aboveFs = true;
4236 }
4237 }
4238
4239 /* put windows that are just mapped, over fullscreen windows */
4240 if (stackingMode == CompStackingUpdateModeInitialMap)
4241 aboveFs = true;
4242
4243 sibling = PrivateWindow::findSiblingBelow (this, aboveFs);
4244
4245 if (sibling &&
4246 (stackingMode == CompStackingUpdateModeInitialMapDeniedFocus))
4247 {
4248 CompWindow *p;
4249
4250 for (p = sibling; p; p = p->serverPrev)
4251 if (p->priv->id == screen->activeWindow ())
4252 break;
4253
4254 /* window is above active window so we should lower it,
4255 * assuing that is allowed (if, for example, our window has
4256 * the "above" state, then lowering beneath the active
4257 * window may not be allowed). */
4258 if (p && PrivateWindow::validSiblingBelow (p, this))
4259 {
4260 p = PrivateWindow::findValidStackSiblingBelow (this, p);
4261
4262 /* if we found a valid sibling under the active window, it's
4263 our new sibling we want to stack above */
4264 if (p)
4265 sibling = p;
4266 }
4267 }
4268
4269 /* If sibling is NULL, then this window will go on the bottom
4270 * of the stack */
4271 mask |= priv->addWindowStackChanges (&xwc, sibling);
4272 }
4273
4274 mask |= priv->addWindowSizeChanges (&xwc, priv->serverGeometry);
4275
4276 if (priv->mapNum && (mask & (CWWidth | CWHeight)))
4277 sendSyncRequest ();
4278
4279 if (mask)
4280 configureXWindow (mask, &xwc);
4281 }
4282
4283 void
4284 PrivateWindow::ensureWindowVisibility ()
4285 {
4286 if (struts || attrib.override_redirect)
4287 return;
4288
4289 if (type & (CompWindowTypeDockMask |
4290 CompWindowTypeFullscreenMask |
4291 CompWindowTypeUnknownMask))
4292 return;
4293
4294 int x1 = screen->workArea ().x () - screen->width () * screen->vp ().x ();
4295 int y1 = screen->workArea ().y () - screen->height () * screen->vp ().y ();
4296 int x2 = x1 + screen->workArea ().width () + screen->vpSize ().width () *
4297 screen->width ();
4298 int y2 = y1 + screen->workArea ().height () + screen->vpSize ().height () *
4299 screen->height ();
4300
4301 int dx = 0;
4302 int width = serverGeometry.widthIncBorders ();
4303
4304 if (serverGeometry.x () - serverInput.left >= x2)
4305 dx = (x2 - 25) - serverGeometry.x ();
4306 else if (serverGeometry.x () + width + serverInput.right <= x1)
4307 dx = (x1 + 25) - (serverGeometry.x () + width);
4308
4309 int dy = 0;
4310 int height = serverGeometry.heightIncBorders ();
4311
4312 if (serverGeometry.y () - serverInput.top >= y2)
4313 dy = (y2 - 25) - serverGeometry.y ();
4314 else if (serverGeometry.y () + height + serverInput.bottom <= y1)
4315 dy = (y1 + 25) - (serverGeometry.y () + height);
4316
4317 if (dx || dy)
4318 {
4319 XWindowChanges xwc = XWINDOWCHANGES_INIT;
4320
4321 xwc.x = serverGeometry.x () + dx;
4322 xwc.y = serverGeometry.y () + dy;
4323
4324 window->configureXWindow (CWX | CWY, &xwc);
4325 }
4326 }
4327
4328 void
4329 PrivateWindow::reveal ()
4330 {
4331 if (window->minimized ())
4332 window->unminimize ();
4333 }
4334
4335 void
4336 PrivateWindow::revealAncestors (CompWindow *w,
4337 CompWindow *transient)
4338 {
4339 if (isAncestorTo (transient, w))
4340 {
4341 screen->forEachWindow (boost::bind (revealAncestors, _1, w));
4342 w->priv->reveal ();
4343 }
4344 }
4345
4346 void
4347 CompWindow::activate ()
4348 {
4349 WRAPABLE_HND_FUNCTN (activate)
4350
4351 screen->setCurrentDesktop (priv->desktop);
4352
4353 screen->forEachWindow (
4354 boost::bind (PrivateWindow::revealAncestors, _1, this));
4355 priv->reveal ();
4356
4357 screen->leaveShowDesktopMode (this);
4358
4359 if (priv->state & CompWindowStateHiddenMask)
4360 {
4361 priv->state &= ~CompWindowStateShadedMask;
4362 if (priv->shaded)
4363 priv->show ();
4364 }
4365
4366 if (priv->state & CompWindowStateHiddenMask)
4367 return;
4368
4369 if (!onCurrentDesktop ())
4370 return;
4371
4372 priv->ensureWindowVisibility ();
4373 updateAttributes (CompStackingUpdateModeAboveFullscreen);
4374 moveInputFocusTo ();
4375 }
4376
4377
4378 #define PVertResizeInc (1 << 0)
4379 #define PHorzResizeInc (1 << 1)
4380
4381 bool
4382 CompWindow::constrainNewWindowSize (int width,
4383 int height,
4384 int *newWidth,
4385 int *newHeight)
4386 {
4387 CompSize size (width, height);
4388 long ignoredHints = 0;
4389 long ignoredResizeHints = 0;
4390
4391 if (screen->getCoreOptions().optionGetIgnoreHintsWhenMaximized ())
4392 {
4393 ignoredHints |= PAspect;
4394
4395 if (priv->state & CompWindowStateMaximizedHorzMask)
4396 ignoredResizeHints |= PHorzResizeInc;
4397
4398 if (priv->state & CompWindowStateMaximizedVertMask)
4399 ignoredResizeHints |= PVertResizeInc;
4400 }
4401
4402 CompSize ret = compiz::window::constrainment::constrainToHints (priv->sizeHints,
4403 size,
4404 ignoredHints, ignoredResizeHints);
4405
4406 *newWidth = ret.width ();
4407 *newHeight = ret.height ();
4408
4409 return ret != size;
4410 }
4411
4412 void
4413 CompWindow::hide ()
4414 {
4415 priv->hidden = true;
4416 priv->hide ();
4417 }
4418
4419 void
4420 CompWindow::show ()
4421 {
4422 priv->hidden = false;
4423 priv->show ();
4424 }
4425
4426 void
4427 PrivateWindow::hide ()
4428 {
4429 if (!managed)
4430 return;
4431
4432 bool onDesktop = window->onCurrentDesktop ();
4433
4434 if (!window->minimized () && !inShowDesktopMode &&
4435 !hidden && onDesktop)
4436 {
4437 if (state & CompWindowStateShadedMask)
4438 {
4439 shaded = true;
4440 }
4441 else
4442 {
4443 return;
4444 }
4445 }
4446 else
4447 {
4448 shaded = false;
4449
4450 if ((state & CompWindowStateShadedMask) && serverFrame)
4451 XUnmapWindow (screen->dpy (), serverFrame);
4452 }
4453
4454 if (!pendingMaps && !window->isViewable ())
4455 return;
4456
4457 window->windowNotify (CompWindowNotifyHide);
4458
4459 pendingUnmaps++;
4460
4461 if (serverFrame && !shaded)
4462 XUnmapWindow (screen->dpy (), serverFrame);
4463
4464 XUnmapWindow (screen->dpy (), id);
4465
4466 if (window->minimized () || inShowDesktopMode || hidden || shaded)
4467 window->changeState (state | CompWindowStateHiddenMask);
4468
4469 if (shaded && id == screen->activeWindow ())
4470 window->moveInputFocusTo ();
4471 }
4472
4473 void
4474 PrivateWindow::show ()
4475 {
4476 if (!managed)
4477 return;
4478
4479 bool onDesktop = window->onCurrentDesktop ();
4480
4481 if (minimized || inShowDesktopMode ||
4482 hidden || !onDesktop)
4483 {
4484 /* no longer hidden but not on current desktop */
4485 if (!minimized && !inShowDesktopMode && !hidden)
4486 window->changeState (state & ~CompWindowStateHiddenMask);
4487
4488 return;
4489 }
4490
4491 /* transition from minimized to shaded */
4492 if (state & CompWindowStateShadedMask)
4493 {
4494 shaded = true;
4495
4496 if (serverFrame)
4497 XMapWindow (screen->dpy (), serverFrame);
4498
4499 updateFrameWindow ();
4500
4501 return;
4502 }
4503
4504 window->windowNotify (CompWindowNotifyShow);
4505
4506 pendingMaps++;
4507
4508 if (serverFrame)
4509 {
4510 XMapWindow (screen->dpy (), serverFrame);
4511 XMapWindow (screen->dpy (), wrapper);
4512 }
4513
4514 XMapWindow (screen->dpy (), id);
4515
4516 window->changeState (state & ~CompWindowStateHiddenMask);
4517 screen->setWindowState (state, id);
4518 }
4519
4520 void
4521 PrivateWindow::minimizeTransients (CompWindow *w,
4522 CompWindow *ancestor)
4523 {
4524 if (w->priv->transientFor == ancestor->priv->id ||
4525 w->priv->isGroupTransient (ancestor->priv->clientLeader))
4526 {
4527 w->minimize ();
4528 }
4529 }
4530
4531 void
4532 CompWindow::minimize ()
4533 {
4534 WRAPABLE_HND_FUNCTN (minimize);
4535
4536 if (!priv->managed)
4537 return;
4538
4539 if (!priv->minimized)
4540 {
4541 windowNotify (CompWindowNotifyMinimize);
4542
4543 priv->minimized = true;
4544
4545 screen->forEachWindow (
4546 boost::bind (PrivateWindow::minimizeTransients, _1, this));
4547
4548 priv->hide ();
4549 }
4550 }
4551
4552 void
4553 PrivateWindow::unminimizeTransients (CompWindow *w,
4554 CompWindow *ancestor)
4555 {
4556 if (w->priv->transientFor == ancestor->priv->id ||
4557 w->priv->isGroupTransient (ancestor->priv->clientLeader))
4558 w->unminimize ();
4559 }
4560
4561 void
4562 CompWindow::unminimize ()
4563 {
4564 WRAPABLE_HND_FUNCTN (unminimize);
4565 if (priv->minimized)
4566 {
4567 windowNotify (CompWindowNotifyUnminimize);
4568
4569 priv->minimized = false;
4570
4571 priv->show ();
4572
4573 screen->forEachWindow (
4574 boost::bind (PrivateWindow::unminimizeTransients, _1, this));
4575 }
4576 }
4577
4578 void
4579 CompWindow::maximize (unsigned int state)
4580 {
4581 if (overrideRedirect ())
4582 return;
4583
4584 state = constrainWindowState (state, priv->actions);
4585
4586 state &= MAXIMIZE_STATE;
4587
4588 if (state == (priv->state & MAXIMIZE_STATE))
4589 return;
4590
4591 state |= (priv->state & ~MAXIMIZE_STATE);
4592
4593 changeState (state);
4594 updateAttributes (CompStackingUpdateModeNone);
4595 }
4596
4597 bool
4598 PrivateWindow::getUserTime (Time& time)
4599 {
4600 Atom actual;
4601 int result, format;
4602 unsigned long n, left;
4603 unsigned char *data;
4604 bool retval = false;
4605
4606 result = XGetWindowProperty (screen->dpy (), priv->id,
4607 Atoms::wmUserTime,
4608 0L, 1L, False, XA_CARDINAL, &actual, &format,
4609 &n, &left, &data);
4610
4611 if (result == Success && data)
4612 {
4613 if (n)
4614 {
4615 CARD32 value;
4616
4617 memcpy (&value, data, sizeof (CARD32));
4618 retval = true;
4619 time = (Time) value;
4620 }
4621
4622 XFree ((void *) data);
4623 }
4624
4625 return retval;
4626 }
4627
4628 void
4629 PrivateWindow::setUserTime (Time time)
4630 {
4631 CARD32 value = (CARD32) time;
4632
4633 XChangeProperty (screen->dpy (), priv->id,
4634 Atoms::wmUserTime,
4635 XA_CARDINAL, 32, PropModeReplace,
4636 (unsigned char *) &value, 1);
4637 }
4638
4639 /*
4640 * Macros from metacity
4641 *
4642 * Xserver time can wraparound, thus comparing two timestamps needs to
4643 * take this into account. Here's a little macro to help out. If no
4644 * wraparound has occurred, this is equivalent to
4645 * time1 < time2
4646 * Of course, the rest of the ugliness of this macro comes from
4647 * accounting for the fact that wraparound can occur and the fact that
4648 * a timestamp of 0 must be special-cased since it means older than
4649 * anything else.
4650 *
4651 * Note that this is NOT an equivalent for time1 <= time2; if that's
4652 * what you need then you'll need to swap the order of the arguments
4653 * and negate the result.
4654 */
4655 #define XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) \
4656 ( (( (time1) < (time2) ) && \
4657 ( (time2) - (time1) < ((unsigned long) -1) / 2 )) || \
4658 (( (time1) > (time2) ) && \
4659 ( (time1) - (time2) > ((unsigned long) -1) / 2 )) \
4660 )
4661 #define XSERVER_TIME_IS_BEFORE(time1, time2) \
4662 ( (time1) == 0 || \
4663 (XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS (time1, time2) && \
4664 (time2) != 0) \
4665 )
4666
4667 bool
4668 PrivateWindow::getUsageTimestamp (Time& timestamp)
4669 {
4670 if (getUserTime (timestamp))
4671 return true;
4672
4673 if (initialTimestampSet)
4674 {
4675 timestamp = initialTimestamp;
4676 return true;
4677 }
4678
4679 return false;
4680 }
4681
4682 bool
4683 PrivateWindow::isWindowFocusAllowed (Time timestamp)
4684 {
4685 CompScreen *s = screen;
4686 CompWindow *active;
4687 Time wUserTime, aUserTime;
4688 bool gotTimestamp = false;
4689 int level;
4690 CompPoint dvp;
4691
4692 level = s->getCoreOptions().optionGetFocusPreventionLevel ();
4693
4694 if (level == CoreOptions::FocusPreventionLevelOff)
4695 return true;
4696
4697 if (timestamp)
4698 {
4699 /* the caller passed a timestamp, so use that
4700 instead of the window's user time */
4701 wUserTime = timestamp;
4702 gotTimestamp = true;
4703 }
4704 else
4705 {
4706 gotTimestamp = getUsageTimestamp (wUserTime);
4707 }
4708
4709 /* if we got no timestamp for the window, try to get at least a timestamp
4710 for its transient parent, if any */
4711 if (!gotTimestamp && transientFor)
4712 {
4713 CompWindow *parent;
4714
4715 parent = screen->findWindow (transientFor);
4716 if (parent)
4717 gotTimestamp = parent->priv->getUsageTimestamp (wUserTime);
4718 }
4719
4720 if (gotTimestamp && !wUserTime)
4721 {
4722 /* window explicitly requested no focus */
4723 return false;
4724 }
4725
4726 /* allow focus for excluded windows */
4727 CompMatch &match = s->getCoreOptions().optionGetFocusPreventionMatch ();
4728 if (!match.evaluate (window))
4729 return true;
4730
4731 if (level == CoreOptions::FocusPreventionLevelVeryHigh)
4732 return false;
4733
4734 active = s->findWindow (s->activeWindow ());
4735
4736 /* no active window */
4737 if (!active || (active->type () & CompWindowTypeDesktopMask))
4738 return true;
4739
4740 /* active window belongs to same application */
4741 if (window->clientLeader () == active->clientLeader ())
4742 return true;
4743
4744 if (level == CoreOptions::FocusPreventionLevelHigh)
4745 return false;
4746
4747 /* not in current viewport or desktop */
4748 if (!window->onCurrentDesktop ())
4749 return false;
4750
4751 dvp = window->defaultViewport ();
4752 if (dvp.x () != s->vp ().x () || dvp.y () != s->vp ().y ())
4753 return false;
4754
4755 if (!gotTimestamp)
4756 {
4757 /* unsure as we have nothing to compare - allow focus in low level,
4758 don't allow in normal level */
4759 if (level == CoreOptions::FocusPreventionLevelNormal)
4760 return false;
4761
4762 return true;
4763 }
4764
4765 /* can't get user time for active window */
4766 if (!active->priv->getUserTime (aUserTime))
4767 return true;
4768
4769 if (XSERVER_TIME_IS_BEFORE (wUserTime, aUserTime))
4770 return false;
4771
4772 return true;
4773 }
4774
4775 bool
4776 PrivateWindow::allowWindowFocus (unsigned int noFocusMask,
4777 Time timestamp)
4778 {
4779 bool retval;
4780
4781 if (priv->id == screen->activeWindow ())
4782 return true;
4783
4784 /* do not focus windows of these types */
4785 if (priv->type & noFocusMask)
4786 return false;
4787
4788 /* window doesn't take focus */
4789 if (!priv->inputHint &&
4790 !(priv->protocols & CompWindowProtocolTakeFocusMask))
4791 {
4792 return false;
4793 }
4794
4795 retval = priv->isWindowFocusAllowed (timestamp);
4796 if (!retval)
4797 {
4798 /* add demands attention state if focus was prevented */
4799 window->changeState (priv->state | CompWindowStateDemandsAttentionMask);
4800 }
4801
4802 return retval;
4803 }
4804
4805 CompPoint
4806 CompWindow::defaultViewport () const
4807 {
4808 CompPoint viewport;
4809
4810 if (priv->serverGeometry.x () < (int) screen->width () &&
4811 priv->serverGeometry.x () + priv->serverGeometry.width () > 0 &&
4812 priv->serverGeometry.y () < (int) screen->height () &&
4813 priv->serverGeometry.y ()+ priv->serverGeometry.height () > 0)
4814 {
4815 return screen->vp ();
4816 }
4817
4818 screen->viewportForGeometry (priv->serverGeometry, viewport);
4819
4820 return viewport;
4821 }
4822
4823 const CompPoint &
4824 CompWindow::initialViewport () const
4825 {
4826 return priv->initialViewport;
4827 }
4828
4829 void
4830 PrivateWindow::readIconHint ()
4831 {
4832 XImage *image, *maskImage = NULL;
4833 Display *dpy = screen->dpy ();
4834 unsigned int width, height, dummy;
4835 unsigned int i, j, k;
4836 int iDummy;
4837 Window wDummy;
4838 CompIcon *icon;
4839 CARD32 *p;
4840
4841 if (!XGetGeometry (dpy, hints->icon_pixmap, &wDummy, &iDummy,
4842 &iDummy, &width, &height, &dummy, &dummy))
4843 return;
4844
4845 image = XGetImage (dpy, hints->icon_pixmap, 0, 0, width, height,
4846 AllPlanes, ZPixmap);
4847 if (!image)
4848 return;
4849
4850 boost::scoped_array<XColor> colors(new XColor[width * height]);
4851 if (!colors)
4852 {
4853 XDestroyImage (image);
4854 return;
4855 }
4856
4857 k = 0;
4858 for (j = 0; j < height; j++)
4859 for (i = 0; i < width; i++)
4860 colors[k++].pixel = XGetPixel (image, i, j);
4861
4862 for (i = 0; i < k; i += 256)
4863 XQueryColors (dpy, screen->colormap(),
4864 &colors[i], MIN (k - i, 256));
4865
4866 XDestroyImage (image);
4867
4868 icon = new CompIcon (width, height);
4869 if (!icon)
4870 {
4871 return;
4872 }
4873
4874 if (hints->flags & IconMaskHint)
4875 maskImage = XGetImage (dpy, hints->icon_mask, 0, 0,
4876 width, height, AllPlanes, ZPixmap);
4877
4878 k = 0;
4879 p = (CARD32 *) icon->data ();
4880
4881 for (j = 0; j < height; j++)
4882 {
4883 for (i = 0; i < width; i++)
4884 {
4885 if (maskImage && !XGetPixel (maskImage, i, j))
4886 *p++ = 0;
4887 else if (image->depth == 1) /* white : black */
4888 *p++ = colors[k].pixel ? 0xffffffff : 0xff000000;
4889 else
4890 *p++ = 0xff000000 | /* alpha */
4891 (((colors[k].red >> 8) & 0xff) << 16) | /* red */
4892 (((colors[k].green >> 8) & 0xff) << 8) | /* green */
4893 ((colors[k].blue >> 8) & 0xff); /* blue */
4894
4895 k++;
4896 }
4897 }
4898
4899 if (maskImage)
4900 XDestroyImage (maskImage);
4901
4902 icons.push_back (icon);
4903 }
4904
4905 /* returns icon with dimensions as close as possible to width and height
4906 but never greater. */
4907 CompIcon *
4908 CompWindow::getIcon (int width,
4909 int height)
4910 {
4911 CompIcon *icon;
4912 int wh, diff, oldDiff;
4913 unsigned int i;
4914
4915 /* need to fetch icon property */
4916 if (priv->icons.size () == 0 && !priv->noIcons)
4917 {
4918 Atom actual;
4919 int result, format;
4920 unsigned long n, left;
4921 unsigned char *data;
4922
4923 result = XGetWindowProperty (screen->dpy (), priv->id, Atoms::wmIcon,
4924 0L, 65536L, false, XA_CARDINAL,
4925 &actual, &format, &n, &left, &data);
4926
4927 if (result == Success && data)
4928 {
4929 CARD32 *p;
4930 CARD32 alpha, red, green, blue;
4931 unsigned long iw, ih;
4932
4933 for (i = 0; i + 2 < n; i += iw * ih + 2)
4934 {
4935 unsigned long *idata = (unsigned long *) data;
4936
4937 iw = idata[i];
4938 ih = idata[i + 1];
4939
4940 /* iw * ih may be larger than the value range of unsigned
4941 * long, so better do some checking for extremely weird
4942 * icon sizes first */
4943 if (iw > 2048 || ih > 2048 || iw * ih + 2 > n - i)
4944 break;
4945
4946 if (iw && ih)
4947 {
4948 icon = new CompIcon (iw, ih);
4949 if (!icon)
4950 continue;
4951
4952 priv->icons.push_back (icon);
4953
4954 p = (CARD32 *) (icon->data ());
4955
4956 /* EWMH doesn't say if icon data is premultiplied or
4957 not but most applications seem to assume data should
4958 be unpremultiplied. */
4959 for (unsigned long j = 0; j < iw * ih; j++)
4960 {
4961 alpha = (idata[i + j + 2] >> 24) & 0xff;
4962 red = (idata[i + j + 2] >> 16) & 0xff;
4963 green = (idata[i + j + 2] >> 8) & 0xff;
4964 blue = (idata[i + j + 2] >> 0) & 0xff;
4965
4966 red = (red * alpha) >> 8;
4967 green = (green * alpha) >> 8;
4968 blue = (blue * alpha) >> 8;
4969
4970 p[j] =
4971 (alpha << 24) |
4972 (red << 16) |
4973 (green << 8) |
4974 (blue << 0);
4975 }
4976 }
4977 }
4978
4979 XFree (data);
4980 }
4981 else if (priv->hints && (priv->hints->flags & IconPixmapHint))
4982 {
4983 priv->readIconHint ();
4984 }
4985
4986 /* don't fetch property again */
4987 if (priv->icons.size () == 0)
4988 priv->noIcons = true;
4989 }
4990
4991 /* no icons available for this window */
4992 if (priv->noIcons)
4993 return NULL;
4994
4995 icon = NULL;
4996 wh = width + height;
4997
4998 for (i = 0; i < priv->icons.size (); i++)
4999 {
5000 const CompSize iconSize = *priv->icons[i];
5001
5002 if ((int) iconSize.width () > width ||
5003 (int) iconSize.height () > height)
5004 continue;
5005
5006 if (icon)
5007 {
5008 diff = wh - (iconSize.width () + iconSize.height ());
5009 oldDiff = wh - (icon->width () + icon->height ());
5010
5011 if (diff < oldDiff)
5012 icon = priv->icons[i];
5013 }
5014 else
5015 icon = priv->icons[i];
5016 }
5017
5018 return icon;
5019 }
5020
5021 const CompRect&
5022 CompWindow::iconGeometry () const
5023 {
5024 return priv->iconGeometry;
5025 }
5026
5027 void
5028 PrivateWindow::freeIcons ()
5029 {
5030 for (unsigned int i = 0; i < priv->icons.size (); i++)
5031 delete priv->icons[i];
5032
5033 priv->icons.resize (0);
5034 priv->noIcons = false;
5035 }
5036
5037 int
5038 CompWindow::outputDevice () const
5039 {
5040 return screen->outputDeviceForGeometry (priv->serverGeometry);
5041 }
5042
5043 bool
5044 CompWindow::onCurrentDesktop () const
5045 {
5046 if (priv->desktop == 0xffffffff ||
5047 priv->desktop == screen->currentDesktop ())
5048 {
5049 return true;
5050 }
5051
5052 return false;
5053 }
5054
5055 void
5056 CompWindow::setDesktop (unsigned int desktop)
5057 {
5058 if (desktop != 0xffffffff)
5059 {
5060 if (priv->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
5061 return;
5062
5063 if (desktop >= screen->nDesktop ())
5064 return;
5065 }
5066
5067 if (desktop == priv->desktop)
5068 return;
5069
5070 priv->desktop = desktop;
5071
5072 if (desktop == 0xffffffff || desktop == screen->currentDesktop ())
5073 priv->show ();
5074 else
5075 priv->hide ();
5076
5077 screen->setWindowProp (priv->id, Atoms::winDesktop, priv->desktop);
5078 }
5079
5080 /* The compareWindowActiveness function compares the two windows 'w1'
5081 and 'w2'. It returns an integer less than, equal to, or greater
5082 than zero if 'w1' is found, respectively, to activated longer time
5083 ago than, to be activated at the same time, or be activated more
5084 recently than 'w2'. */
5085 int
5086 PrivateWindow::compareWindowActiveness (CompWindow *w1,
5087 CompWindow *w2)
5088 {
5089 CompActiveWindowHistory *history = screen->currentHistory ();
5090 int i;
5091
5092 /* check current window history first */
5093 for (i = 0; i < ACTIVE_WINDOW_HISTORY_SIZE; i++)
5094 {
5095 if (history->id[i] == w1->priv->id)
5096 return 1;
5097
5098 if (history->id[i] == w2->priv->id)
5099 return -1;
5100
5101 if (!history->id[i])
5102 break;
5103 }
5104
5105 return w1->priv->activeNum - w2->priv->activeNum;
5106 }
5107
5108 bool
5109 CompWindow::onAllViewports () const
5110 {
5111 if (overrideRedirect ())
5112 return true;
5113
5114 if (!priv->managed && !isViewable ())
5115 return true;
5116
5117 if (priv->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
5118 return true;
5119
5120 if (priv->state & CompWindowStateStickyMask)
5121 return true;
5122
5123 return false;
5124 }
5125
5126 CompPoint
5127 CompWindow::getMovementForOffset (const CompPoint &offset) const
5128 {
5129 CompScreen *s = screen;
5130 int m, vWidth, vHeight;
5131 int offX = offset.x (), offY = offset.y ();
5132 CompPoint rv;
5133
5134 vWidth = s->width () * s->vpSize ().width ();
5135 vHeight = s->height () * s->vpSize ().height ();
5136
5137 offX %= vWidth;
5138 offY %= vHeight;
5139
5140 /* x */
5141 if (s->vpSize ().width () == 1)
5142 {
5143 rv.setX (offX);
5144 }
5145 else
5146 {
5147 m = priv->serverGeometry.x () + offX;
5148 if (m - priv->serverInput.left < (int) s->width () - vWidth)
5149 rv.setX (offX + vWidth);
5150 else if (m + priv->serverGeometry.width () + priv->serverInput.right > vWidth)
5151 rv.setX (offX - vWidth);
5152 else
5153 rv.setX (offX);
5154 }
5155
5156 if (s->vpSize ().height () == 1)
5157 {
5158 rv.setY (offY);
5159 }
5160 else
5161 {
5162 m = priv->serverGeometry.y () + offY;
5163 if (m - priv->serverInput.top < (int) s->height () - vHeight)
5164 rv.setY (offY + vHeight);
5165 else if (m + priv->serverGeometry.height () + priv->serverInput.bottom > vHeight)
5166 rv.setY (offY - vHeight);
5167 else
5168 rv.setY (offY);
5169 }
5170
5171 return rv;
5172 }
5173
5174 void
5175 WindowInterface::getOutputExtents (CompWindowExtents& output)
5176 WRAPABLE_DEF (getOutputExtents, output)
5177
5178 void
5179 WindowInterface::getAllowedActions (unsigned int &setActions,
5180 unsigned int &clearActions)
5181 WRAPABLE_DEF (getAllowedActions, setActions, clearActions)
5182
5183 bool
5184 WindowInterface::focus ()
5185 WRAPABLE_DEF (focus)
5186
5187 void
5188 WindowInterface::activate ()
5189 WRAPABLE_DEF (activate)
5190
5191 bool
5192 WindowInterface::place (CompPoint &pos)
5193 WRAPABLE_DEF (place, pos)
5194
5195 void
5196 WindowInterface::validateResizeRequest (unsigned int &mask,
5197 XWindowChanges *xwc,
5198 unsigned int source)
5199 WRAPABLE_DEF (validateResizeRequest, mask, xwc, source)
5200
5201 void
5202 WindowInterface::resizeNotify (int dx,
5203 int dy,
5204 int dwidth,
5205 int dheight)
5206 WRAPABLE_DEF (resizeNotify, dx, dy, dwidth, dheight)
5207
5208 void
5209 WindowInterface::moveNotify (int dx,
5210 int dy,
5211 bool immediate)
5212 WRAPABLE_DEF (moveNotify, dx, dy, immediate)
5213
5214 void
5215 WindowInterface::windowNotify (CompWindowNotify n)
5216 WRAPABLE_DEF (windowNotify, n)
5217
5218 void
5219 WindowInterface::grabNotify (int x,
5220 int y,
5221 unsigned int state,
5222 unsigned int mask)
5223 WRAPABLE_DEF (grabNotify, x, y, state, mask)
5224
5225 void
5226 WindowInterface::ungrabNotify ()
5227 WRAPABLE_DEF (ungrabNotify)
5228
5229 void
5230 WindowInterface::stateChangeNotify (unsigned int lastState)
5231 WRAPABLE_DEF (stateChangeNotify, lastState)
5232
5233 void
5234 WindowInterface::updateFrameRegion (CompRegion ®ion)
5235 WRAPABLE_DEF (updateFrameRegion, region)
5236
5237 void
5238 WindowInterface::minimize ()
5239 WRAPABLE_DEF (minimize);
5240
5241 void
5242 WindowInterface::unminimize ()
5243 WRAPABLE_DEF (unminimize);
5244
5245 bool
5246 WindowInterface::minimized () const
5247 WRAPABLE_DEF (minimized);
5248
5249 bool
5250 WindowInterface::alpha () const
5251 WRAPABLE_DEF (alpha);
5252
5253 bool
5254 WindowInterface::isFocussable () const
5255 WRAPABLE_DEF (isFocussable);
5256
5257 bool
5258 WindowInterface::managed () const
5259 WRAPABLE_DEF (managed);
5260
5261 bool
5262 WindowInterface::focused () const
5263 WRAPABLE_DEF (focused);
5264
5265 Window
5266 CompWindow::id () const
5267 {
5268 return priv->id;
5269 }
5270
5271 unsigned int
5272 CompWindow::type () const
5273 {
5274 return priv->type;
5275 }
5276
5277 unsigned int &
5278 CompWindow::state () const
5279 {
5280 return priv->state;
5281 }
5282
5283 unsigned int
5284 CompWindow::actions () const
5285 {
5286 return priv->actions;
5287 }
5288
5289 unsigned int &
5290 CompWindow::protocols () const
5291 {
5292 return priv->protocols;
5293 }
5294
5295 void
5296 CompWindow::close (Time serverTime)
5297 {
5298 if (serverTime == 0)
5299 serverTime = screen->getCurrentTime ();
5300
5301 if (priv->alive)
5302 {
5303 if (priv->protocols & CompWindowProtocolDeleteMask)
5304 {
5305 XEvent ev;
5306
5307 ev.type = ClientMessage;
5308 ev.xclient.window = priv->id;
5309 ev.xclient.message_type = Atoms::wmProtocols;
5310 ev.xclient.format = 32;
5311 ev.xclient.data.l[0] = Atoms::wmDeleteWindow;
5312 ev.xclient.data.l[1] = serverTime;
5313 ev.xclient.data.l[2] = 0;
5314 ev.xclient.data.l[3] = 0;
5315 ev.xclient.data.l[4] = 0;
5316
5317 XSendEvent (screen->dpy (), priv->id, false, NoEventMask, &ev);
5318 }
5319 else
5320 {
5321 XKillClient (screen->dpy (), priv->id);
5322 }
5323
5324 priv->closeRequests++;
5325 }
5326 else
5327 {
5328 screen->toolkitAction (Atoms::toolkitActionForceQuitDialog,
5329 serverTime, priv->id, true, 0, 0);
5330 }
5331
5332 priv->lastCloseRequestTime = serverTime;
5333 }
5334
5335 bool
5336 PrivateWindow::handlePingTimeout (unsigned int lastPing)
5337 {
5338 if (!window->isViewable ())
5339 return false;
5340
5341 if (!(priv->type & CompWindowTypeNormalMask))
5342 return false;
5343
5344 if (priv->protocols & CompWindowProtocolPingMask)
5345 {
5346 if (priv->transientFor)
5347 return false;
5348
5349 if (priv->lastPong < lastPing)
5350 {
5351 if (priv->alive)
5352 {
5353 priv->alive = false;
5354
5355 window->windowNotify (CompWindowNotifyAliveChanged);
5356
5357 if (priv->closeRequests)
5358 {
5359 screen->toolkitAction (Atoms::toolkitActionForceQuitDialog,
5360 priv->lastCloseRequestTime,
5361 priv->id, true, 0, 0);
5362
5363 priv->closeRequests = 0;
5364 }
5365 }
5366 }
5367
5368 return true;
5369 }
5370 return false;
5371 }
5372
5373 void
5374 PrivateWindow::handlePing (int lastPing)
5375 {
5376 if (!priv->alive)
5377 {
5378 priv->alive = true;
5379
5380 window->windowNotify (CompWindowNotifyAliveChanged);
5381
5382 if (priv->lastCloseRequestTime)
5383 {
5384 screen->toolkitAction (Atoms::toolkitActionForceQuitDialog,
5385 priv->lastCloseRequestTime,
5386 priv->id, false, 0, 0);
5387
5388 priv->lastCloseRequestTime = 0;
5389 }
5390 }
5391 priv->lastPong = lastPing;
5392 }
5393
5394 void
5395 PrivateWindow::processMap ()
5396 {
5397 bool allowFocus;
5398 bool initiallyMinimized;
5399 CompStackingUpdateMode stackingMode;
5400
5401 priv->initialViewport = screen->vp ();
5402
5403 priv->initialTimestampSet = false;
5404
5405 screen->applyStartupProperties (window);
5406
5407 initiallyMinimized = (priv->hints &&
5408 priv->hints->initial_state == IconicState &&
5409 !window->minimized ());
5410
5411 if (!serverFrame && !initiallyMinimized)
5412 reparent ();
5413
5414 priv->managed = true;
5415
5416 if (!initiallyMinimized && !(priv->state & CompWindowStateHiddenMask))
5417 receivedMapRequestAndAwaitingMap = true;
5418
5419 if (!priv->placed)
5420 {
5421 int gravity = priv->sizeHints.win_gravity;
5422 XWindowChanges xwc = XWINDOWCHANGES_INIT;
5423 unsigned int xwcm;
5424
5425 /* adjust for gravity, but only for frame size */
5426 xwc.x = priv->serverGeometry.x ();
5427 xwc.y = priv->serverGeometry.y ();
5428 xwc.width = 0;
5429 xwc.height = 0;
5430
5431 xwcm = adjustConfigureRequestForGravity (&xwc, CWX | CWY, gravity, 1);
5432
5433 xwc.width = priv->serverGeometry.width ();
5434 xwc.height = priv->serverGeometry.height ();
5435
5436 window->validateResizeRequest (xwcm, &xwc, ClientTypeApplication);
5437
5438 CompPoint pos (xwc.x, xwc.y);
5439 if (window->place (pos))
5440 {
5441 xwc.x = pos.x ();
5442 xwc.y = pos.y ();
5443 xwcm |= CWX | CWY;
5444 }
5445
5446 if (xwcm)
5447 window->configureXWindow (xwcm, &xwc);
5448
5449 priv->placed = true;
5450 }
5451
5452 allowFocus = allowWindowFocus (NO_FOCUS_MASK, 0);
5453
5454 if (!allowFocus && (priv->type & ~NO_FOCUS_MASK))
5455 stackingMode = CompStackingUpdateModeInitialMapDeniedFocus;
5456 else
5457 stackingMode = CompStackingUpdateModeInitialMap;
5458
5459 window->updateAttributes (stackingMode);
5460
5461 if (window->minimized () && !initiallyMinimized)
5462 window->unminimize ();
5463
5464 screen->leaveShowDesktopMode (window);
5465
5466 if (!initiallyMinimized)
5467 {
5468 if (allowFocus && !window->onCurrentDesktop ())
5469 screen->setCurrentDesktop (priv->desktop);
5470
5471 if (!(priv->state & CompWindowStateHiddenMask))
5472 {
5473 show ();
5474 receivedMapRequestAndAwaitingMap = false;
5475 }
5476
5477 if (allowFocus)
5478 {
5479 window->moveInputFocusTo ();
5480 if (!window->onCurrentDesktop ())
5481 screen->setCurrentDesktop (priv->desktop);
5482 }
5483 }
5484 else
5485 {
5486 window->minimize ();
5487 window->changeState (window->state () | CompWindowStateHiddenMask);
5488 }
5489
5490 screen->updateClientList ();
5491 }
5492
5493 /*
5494 * PrivateWindow::updatePassiveButtonGrabs
5495 *
5496 * Updates the passive button grabs for a window. When
5497 * one of the specified button + modifier combinations
5498 * for this window is activated, compiz will be given
5499 * an active grab for the window (which we can turn off
5500 * by calling XAllowEvents later in ::handleEvent)
5501 *
5502 * NOTE: ICCCM says that we are only allowed to grab
5503 * windows that we actually own as a client, so only
5504 * grab the frame window. Additionally, although there
5505 * isn't anything in the ICCCM that says we cannot
5506 * grab every button, some clients do not interpret
5507 * EnterNotify and LeaveNotify events caused by the
5508 * activation of the grab correctly, so ungrab button
5509 * and modifier combinations that we do not need on
5510 * active windows (but in reality we shouldn't be grabbing
5511 * for buttons that we don't actually need at that point
5512 * anyways)
5513 */
5514
5515 void
5516 PrivateWindow::updatePassiveButtonGrabs ()
5517 {
5518 if (!priv->frame)
5519 return;
5520
5521 bool onlyActions = (priv->id == screen->activeWindow() ||
5522 !screen->getCoreOptions().optionGetClickToFocus ());
5523
5524 /* Ungrab everything */
5525 XUngrabButton (screen->dpy(), AnyButton, AnyModifier, frame);
5526
5527 /* We don't need the full grab in the following cases:
5528 * - This window has the focus and either
5529 * - it is raised or
5530 * - we don't want click raise
5531 */
5532
5533 if (onlyActions)
5534 {
5535 if (screen->getCoreOptions().optionGetRaiseOnClick ())
5536 {
5537 CompWindow *highestSibling =
5538 PrivateWindow::findSiblingBelow (window, true);
5539
5540 /* Check if this window is permitted to be raised */
5541 for (CompWindow *above = window->serverNext;
5542 above != NULL; above = above->serverNext)
5543 {
5544 if (highestSibling == above)
5545 {
5546 onlyActions = false;
5547 break;
5548 }
5549 }
5550 }
5551 }
5552
5553 if (onlyActions)
5554 {
5555 screen->updatePassiveButtonGrabs(serverFrame);
5556 }
5557 else
5558 {
5559 /* Grab everything */
5560 XGrabButton (screen->dpy(),
5561 AnyButton,
5562 AnyModifier,
5563 serverFrame, false,
5564 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
5565 GrabModeSync,
5566 GrabModeAsync,
5567 None,
5568 None);
5569 }
5570 }
5571
5572
5573 const CompRegion &
5574 CompWindow::region () const
5575 {
5576 return priv->region;
5577 }
5578
5579 const CompRegion &
5580 CompWindow::frameRegion () const
5581 {
5582 return priv->frameRegion;
5583 }
5584
5585 bool
5586 CompWindow::inShowDesktopMode () const
5587 {
5588 return priv->inShowDesktopMode;
5589 }
5590
5591 void
5592 CompWindow::setShowDesktopMode (bool value)
5593 {
5594 priv->inShowDesktopMode = value;
5595 }
5596
5597 bool
5598 CompWindow::managed () const
5599 {
5600 WRAPABLE_HND_FUNCTN_RETURN (bool, managed);
5601 return priv->managed;
5602 }
5603
5604 bool
5605 CompWindow::focused () const
5606 {
5607 WRAPABLE_HND_FUNCTN_RETURN (bool, focused);
5608 return screen->activeWindow () == id ();
5609 }
5610
5611 bool
5612 CompWindow::grabbed () const
5613 {
5614 return priv->grabbed;
5615 }
5616
5617 int
5618 CompWindow::pendingMaps () const
5619 {
5620 return priv->pendingMaps;
5621 }
5622
5623 unsigned int &
5624 CompWindow::wmType () const
5625 {
5626 return priv->wmType;
5627 }
5628
5629 unsigned int
5630 CompWindow::activeNum () const
5631 {
5632 return priv->activeNum;
5633 }
5634
5635 Window
5636 CompWindow::frame () const
5637 {
5638 return priv->serverFrame;
5639 }
5640
5641 CompString
5642 CompWindow::resName () const
5643 {
5644 if (priv->resName)
5645 return priv->resName;
5646
5647 return CompString ();
5648 }
5649
5650 int
5651 CompWindow::mapNum () const
5652 {
5653 return priv->mapNum;
5654 }
5655
5656 const CompStruts *
5657 CompWindow::struts () const
5658 {
5659 return priv->struts;
5660 }
5661
5662 int &
5663 CompWindow::saveMask () const
5664 {
5665 return priv->saveMask;
5666 }
5667
5668 XWindowChanges &
5669 CompWindow::saveWc ()
5670 {
5671 return priv->saveWc;
5672 }
5673
5674 void
5675 CompWindow::moveToViewportPosition (int x,
5676 int y,
5677 bool sync)
5678 {
5679 int tx, ty;
5680 int vWidth = screen->width () * screen->vpSize ().width ();
5681 int vHeight = screen->height () * screen->vpSize ().height ();
5682
5683 if (screen->vpSize ().width () != 1)
5684 {
5685 x += screen->vp ().x () * screen->width ();
5686 x = compiz::core::screen::wraparound_mod (x, vWidth);
5687 x -= screen->vp ().x () * screen->width ();
5688 }
5689
5690 if (screen->vpSize ().height () != 1)
5691 {
5692 y += screen->vp ().y () * screen->height ();
5693 y = compiz::core::screen::wraparound_mod (y, vHeight);
5694 y -= screen->vp ().y () * screen->height ();
5695 }
5696
5697 tx = x - priv->serverGeometry.x ();
5698 ty = y - priv->serverGeometry.y ();
5699
5700 if (tx || ty)
5701 {
5702 unsigned int valueMask = CWX | CWY;
5703 XWindowChanges xwc = XWINDOWCHANGES_INIT;
5704
5705 if (!priv->managed)
5706 return;
5707
5708 if (priv->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
5709 return;
5710
5711 if (priv->state & CompWindowStateStickyMask)
5712 return;
5713
5714 int wx = tx;
5715 int wy = ty;
5716 int m;
5717
5718 if (screen->vpSize ().width ()!= 1)
5719 {
5720 m = priv->serverGeometry.x () + tx;
5721
5722 if (m - priv->output.left < (int) screen->width () - vWidth)
5723 wx = tx + vWidth;
5724 else if (m + priv->serverGeometry.width () + priv->output.right > vWidth)
5725 wx = tx - vWidth;
5726 }
5727
5728 if (screen->vpSize ().height () != 1)
5729 {
5730 m = priv->serverGeometry.y () + ty;
5731
5732 if (m - priv->output.top < (int) screen->height () - vHeight)
5733 wy = ty + vHeight;
5734 else if (m + priv->serverGeometry.height () + priv->output.bottom > vHeight)
5735 wy = ty - vHeight;
5736 }
5737
5738 if (priv->saveMask & CWX)
5739 priv->saveWc.x += wx;
5740
5741 if (priv->saveMask & CWY)
5742 priv->saveWc.y += wy;
5743
5744 xwc.x = serverGeometry ().x () + wx;
5745 xwc.y = serverGeometry ().y () + wy;
5746
5747 configureXWindow (valueMask, &xwc);
5748 }
5749 }
5750
5751 const char *
5752 CompWindow::startupId () const
5753 {
5754 return priv->startupId;
5755 }
5756
5757 void
5758 PrivateWindow::applyStartupProperties (CompStartupSequence *s)
5759 {
5760 int workspace;
5761
5762 priv->initialViewport.setX (s->viewportX);
5763 priv->initialViewport.setY (s->viewportY);
5764
5765 workspace = sn_startup_sequence_get_workspace (s->sequence);
5766 if (workspace >= 0)
5767 window->setDesktop (workspace);
5768
5769 priv->initialTimestamp =
5770 sn_startup_sequence_get_timestamp (s->sequence);
5771 priv->initialTimestampSet = true;
5772 }
5773
5774 unsigned int
5775 CompWindow::desktop () const
5776 {
5777 return priv->desktop;
5778 }
5779
5780 Window
5781 CompWindow::clientLeader (bool checkAncestor) const
5782 {
5783 if (priv->clientLeader)
5784 return priv->clientLeader;
5785
5786 if (checkAncestor)
5787 return priv->getClientLeaderOfAncestor ();
5788
5789 return None;
5790 }
5791
5792 Window
5793 CompWindow::transientFor () const
5794 {
5795 return priv->transientFor;
5796 }
5797
5798 int
5799 CompWindow::pendingUnmaps () const
5800 {
5801 return priv->pendingUnmaps;
5802 }
5803
5804 bool
5805 CompWindow::minimized () const
5806 {
5807 WRAPABLE_HND_FUNCTN_RETURN (bool, minimized);
5808 return priv->minimized;
5809 }
5810
5811 bool
5812 CompWindow::placed () const
5813 {
5814 return priv->placed;
5815 }
5816
5817 bool
5818 CompWindow::shaded () const
5819 {
5820 return priv->shaded;
5821 }
5822
5823 const CompWindowExtents &
5824 CompWindow::border () const
5825 {
5826 return priv->border;
5827 }
5828
5829 const CompWindowExtents &
5830 CompWindow::input () const
5831 {
5832 return priv->serverInput;
5833 }
5834
5835 const CompWindowExtents &
5836 CompWindow::output () const
5837 {
5838 return priv->output;
5839 }
5840
5841 XSizeHints &
5842 CompWindow::sizeHints () const
5843 {
5844 return priv->sizeHints;
5845 }
5846
5847 void
5848 PrivateWindow::updateMwmHints ()
5849 {
5850 screen->getMwmHints (priv->id, &priv->mwmFunc, &priv->mwmDecor);
5851 window->recalcActions ();
5852 }
5853
5854 void
5855 PrivateWindow::updateStartupId ()
5856 {
5857 char *oldId = startupId;
5858 bool newId = true;
5859
5860 startupId = getStartupId ();
5861
5862 if (oldId && startupId)
5863 {
5864 if (strcmp (startupId, oldId) == 0)
5865 newId = false;
5866 }
5867
5868 if (managed && startupId && newId)
5869 {
5870 Time timestamp = 0;
5871 CompPoint vp, svp;
5872 CompSize size;
5873 int x, y;
5874
5875 initialTimestampSet = false;
5876 screen->applyStartupProperties (window);
5877
5878 if (initialTimestampSet)
5879 timestamp = initialTimestamp;
5880
5881 /* as the viewport can't be transmitted via startup
5882 notification, assume the client changing the ID
5883 wanted to activate the window on the current viewport */
5884
5885 vp = window->defaultViewport ();
5886 svp = screen->vp ();
5887 size = *screen;
5888
5889 x = window->serverGeometry ().x () + (svp.x () - vp.x ()) * size.width ();
5890 y = window->serverGeometry ().y () + (svp.y () - vp.y ()) * size.height ();
5891 window->moveToViewportPosition (x, y, true);
5892
5893 if (allowWindowFocus (0, timestamp))
5894 window->activate ();
5895 }
5896
5897 if (oldId)
5898 free (oldId);
5899 }
5900
5901 bool
5902 CompWindow::destroyed () const
5903 {
5904 return priv->destroyed;
5905 }
5906
5907 bool
5908 CompWindow::invisible () const
5909 {
5910 return priv->invisible;
5911 }
5912
5913 XSyncAlarm
5914 CompWindow::syncAlarm () const
5915 {
5916 return priv->syncAlarm;
5917 }
5918
5919 CompWindow *
5920 PrivateWindow::createCompWindow (Window aboveId, Window aboveServerId, XWindowAttributes &wa, Window id)
5921 {
5922 PrivateWindow* priv(new PrivateWindow ());
5923 priv->id = id;
5924 priv->serverId = id;
5925
5926 CompWindow *fw = new CompWindow (aboveId, aboveServerId, wa, priv);
5927
5928 return fw;
5929 }
5930
5931
5932 CompWindow::CompWindow (Window aboveId,
5933 Window aboveServerId,
5934 XWindowAttributes &wa,
5935 PrivateWindow *priv) :
5936 PluginClassStorage (windowPluginClassIndices),
5937 priv (priv)
5938 {
5939 StackDebugger *dbg = StackDebugger::Default ();
5940
5941 // TODO: Reparent first!
5942
5943 priv->window = this;
5944
5945 screen->insertWindow (this, aboveId);
5946 screen->insertServerWindow (this, aboveServerId);
5947
5948 /* We must immediately insert the window into the debugging
5949 * stack */
5950 if (dbg)
5951 dbg->overrideRedirectRestack (priv->id, aboveId);
5952
5953 priv->attrib = wa;
5954 priv->serverGeometry.set (priv->attrib.x, priv->attrib.y,
5955 priv->attrib.width, priv->attrib.height,
5956 priv->attrib.border_width);
5957 priv->serverFrameGeometry = priv->frameGeometry = priv->syncGeometry
5958 = priv->geometry = priv->serverGeometry;
5959
5960 priv->sizeHints.flags = 0;
5961
5962 priv->recalcNormalHints ();
5963
5964 priv->transientFor = None;
5965 priv->clientLeader = None;
5966
5967 XSelectInput (screen->dpy (), priv->id,
5968 wa.your_event_mask |
5969 PropertyChangeMask |
5970 EnterWindowMask |
5971 FocusChangeMask);
5972
5973 priv->alpha = (priv->attrib.depth == 32);
5974 priv->lastPong = screen->lastPing ();
5975
5976 if (screen->XShape ())
5977 XShapeSelectInput (screen->dpy (), priv->id, ShapeNotifyMask);
5978
5979 if (priv->attrib.c_class != InputOnly)
5980 {
5981 priv->region = CompRegion (priv->serverGeometry);
5982 priv->inputRegion = priv->region;
5983
5984 /* need to check for DisplayModal state on all windows */
5985 priv->state = screen->getWindowState (priv->id);
5986
5987 priv->updateClassHints ();
5988 }
5989 else
5990 {
5991 priv->attrib.map_state = IsUnmapped;
5992 }
5993
5994 priv->wmType = screen->getWindowType (priv->id);
5995 priv->protocols = screen->getProtocols (priv->id);
5996
5997 if (!overrideRedirect ())
5998 {
5999 priv->updateNormalHints ();
6000 updateStruts ();
6001 priv->updateWmHints ();
6002 priv->updateTransientHint ();
6003
6004 priv->clientLeader = priv->getClientLeader ();
6005 priv->startupId = priv->getStartupId ();
6006
6007 recalcType ();
6008
6009 screen->getMwmHints (priv->id, &priv->mwmFunc, &priv->mwmDecor);
6010
6011 if (!(priv->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask)))
6012 {
6013 priv->desktop = screen->getWindowProp (priv->id, Atoms::winDesktop,
6014 priv->desktop);
6015 if (priv->desktop != 0xffffffff)
6016 {
6017 if (priv->desktop >= screen->nDesktop ())
6018 priv->desktop = screen->currentDesktop ();
6019 }
6020 }
6021 }
6022 else
6023 {
6024 recalcType ();
6025 }
6026
6027 if (priv->attrib.map_state == IsViewable)
6028 {
6029 priv->placed = true;
6030
6031 if (!overrideRedirect ())
6032 {
6033 // needs to happen right after maprequest
6034 if (!priv->serverFrame)
6035 priv->reparent ();
6036 priv->managed = true;
6037
6038 if (screen->getWmState (priv->id) == IconicState)
6039 {
6040 if (priv->state & CompWindowStateShadedMask)
6041 priv->shaded = true;
6042 else
6043 priv->minimized = true;
6044 }
6045 else
6046 {
6047 if (priv->wmType & (CompWindowTypeDockMask |
6048 CompWindowTypeDesktopMask))
6049 {
6050 setDesktop (0xffffffff);
6051 }
6052 else
6053 {
6054 if (priv->desktop != 0xffffffff)
6055 priv->desktop = screen->currentDesktop ();
6056
6057 screen->setWindowProp (priv->id, Atoms::winDesktop,
6058 priv->desktop);
6059 }
6060 }
6061 }
6062
6063 priv->attrib.map_state = IsUnmapped;
6064 priv->pendingMaps++;
6065
6066 map ();
6067
6068 updateAttributes (CompStackingUpdateModeNormal);
6069
6070 if (priv->minimized || priv->inShowDesktopMode ||
6071 priv->hidden || priv->shaded)
6072 {
6073 priv->state |= CompWindowStateHiddenMask;
6074
6075 priv->pendingUnmaps++;
6076
6077 if (priv->serverFrame && !priv->shaded)
6078 XUnmapWindow (screen->dpy (), priv->serverFrame);
6079
6080 XUnmapWindow (screen->dpy (), priv->id);
6081
6082 screen->setWindowState (priv->state, priv->id);
6083 }
6084 }
6085 else if (!overrideRedirect ())
6086 {
6087 if (screen->getWmState (priv->id) == IconicState)
6088 {
6089 // before everything else in maprequest
6090 if (!priv->serverFrame)
6091 priv->reparent ();
6092 priv->managed = true;
6093 priv->placed = true;
6094
6095 if (priv->state & CompWindowStateHiddenMask)
6096 {
6097 if (priv->state & CompWindowStateShadedMask)
6098 priv->shaded = true;
6099 else
6100 priv->minimized = true;
6101 }
6102 }
6103 }
6104
6105 /* TODO: bailout properly when objectInitPlugins fails */
6106 bool init_succeeded = CompPlugin::windowInitPlugins (this);
6107 assert (init_succeeded);
6108 if (!init_succeeded)
6109 return;
6110
6111 recalcActions ();
6112 priv->updateIconGeometry ();
6113
6114 if (priv->shaded)
6115 priv->updateFrameWindow ();
6116
6117 if (priv->attrib.map_state == IsViewable)
6118 {
6119 priv->invisible = priv->isInvisible ();
6120 }
6121 }
6122
6123 CompWindow::~CompWindow ()
6124 {
6125 if (priv->serverFrame)
6126 priv->unreparent ();
6127
6128 /* Update the references of other windows
6129 * pending destroy if this was a sibling
6130 * of one of those */
6131
6132 screen->destroyedWindows().remove (this);
6133
6134 foreach (CompWindow *dw, screen->destroyedWindows())
6135 {
6136 if (dw->next == this)
6137 dw->next = this->next;
6138 if (dw->prev == this)
6139 dw->prev = this->prev;
6140
6141 if (dw->serverNext == this)
6142 dw->serverNext = this->serverNext;
6143 if (dw->serverPrev == this)
6144 dw->serverPrev = this->serverPrev;
6145 }
6146
6147 if (!priv->destroyed)
6148 {
6149 StackDebugger *dbg = StackDebugger::Default ();
6150
6151 screen->unhookWindow (this);
6152 screen->unhookServerWindow (this);
6153
6154 /* We must immediately insert the window into the debugging
6155 * stack */
6156 if (dbg)
6157 dbg->removeServerWindow (id ());
6158
6159 /* restore saved geometry and map if hidden */
6160 if (!priv->attrib.override_redirect)
6161 {
6162 if (priv->saveMask)
6163 XConfigureWindow (screen->dpy (), priv->id,
6164 priv->saveMask, &priv->saveWc);
6165
6166 if (!priv->hidden)
6167 {
6168 if (priv->state & CompWindowStateHiddenMask)
6169 XMapWindow (screen->dpy (), priv->id);
6170 }
6171 }
6172
6173 if (screen->XShape ())
6174 XShapeSelectInput (screen->dpy (), priv->id, NoEventMask);
6175
6176 if (screen->grabWindowIsNot(priv->id))
6177 XSelectInput (screen->dpy (), priv->id, NoEventMask);
6178
6179 XUngrabButton (screen->dpy (), AnyButton, AnyModifier, priv->id);
6180 }
6181
6182
6183 if (priv->attrib.map_state == IsViewable)
6184 {
6185 if (priv->type == CompWindowTypeDesktopMask)
6186 screen->decrementDesktopWindowCount();
6187
6188 if (priv->destroyed && priv->struts)
6189 screen->updateWorkarea ();
6190 }
6191
6192 if (priv->destroyed)
6193 screen->updateClientList ();
6194
6195 CompPlugin::windowFiniPlugins (this);
6196
6197 delete priv;
6198 }
6199
6200 PrivateWindow::PrivateWindow () :
6201 priv (this),
6202 refcnt (1),
6203 id (None),
6204 serverFrame (None),
6205 frame (None),
6206 wrapper (None),
6207 mapNum (0),
6208 activeNum (0),
6209 transientFor (None),
6210 clientLeader (None),
6211 hints (NULL),
6212 inputHint (true),
6213 alpha (false),
6214 region (),
6215 wmType (0),
6216 type (CompWindowTypeUnknownMask),
6217 state (0),
6218 actions (0),
6219 protocols (0),
6220 mwmDecor (MwmDecorAll),
6221 mwmFunc (MwmFuncAll),
6222 invisible (true),
6223 destroyed (false),
6224 managed (false),
6225 unmanaging (false),
6226 destroyRefCnt (1),
6227 unmapRefCnt (1),
6228
6229 initialViewport (0, 0),
6230
6231 initialTimestamp (0),
6232 initialTimestampSet (false),
6233
6234 fullscreenMonitorsSet (false),
6235
6236 placed (false),
6237 minimized (false),
6238 inShowDesktopMode (false),
6239 shaded (false),
6240 hidden (false),
6241 grabbed (false),
6242
6243 desktop (0),
6244
6245 pendingUnmaps (0),
6246 pendingMaps (0),
6247 pendingConfigures (screen->dpy ()),
6248 receivedMapRequestAndAwaitingMap (false),
6249
6250 startupId (0),
6251 resName (0),
6252 resClass (0),
6253
6254 group (0),
6255
6256 lastPong (0),
6257 alive (true),
6258
6259 struts (0),
6260
6261 icons (0),
6262 noIcons (false),
6263
6264 saveMask (0),
6265 syncCounter (0),
6266 syncAlarm (None),
6267 syncWaitTimer (),
6268
6269 syncWait (false),
6270 closeRequests (false),
6271 lastCloseRequestTime (0)
6272 {
6273 input.left = 0;
6274 input.right = 0;
6275 input.top = 0;
6276 input.bottom = 0;
6277
6278 serverInput.left = 0;
6279 serverInput.right = 0;
6280 serverInput.top = 0;
6281 serverInput.bottom = 0;
6282
6283 border.top = 0;
6284 border.bottom = 0;
6285 border.left = 0;
6286 border.right = 0;
6287
6288 output.left = 0;
6289 output.right = 0;
6290 output.top = 0;
6291 output.bottom = 0;
6292
6293 syncWaitTimer.setTimes (1000, 1200);
6294 syncWaitTimer.setCallback (boost::bind (&PrivateWindow::handleSyncAlarm,
6295 this));
6296 }
6297
6298 PrivateWindow::~PrivateWindow ()
6299 {
6300 if (syncAlarm)
6301 XSyncDestroyAlarm (screen->dpy (), syncAlarm);
6302
6303 syncWaitTimer.stop ();
6304
6305 if (serverFrame)
6306 XDestroyWindow (screen->dpy (), serverFrame);
6307 else if (frame)
6308 XDestroyWindow (screen->dpy (), frame);
6309
6310 if (struts)
6311 free (struts);
6312
6313 if (hints)
6314 XFree (hints);
6315
6316 if (icons.size ())
6317 freeIcons ();
6318
6319 if (startupId)
6320 free (startupId);
6321
6322 if (resName)
6323 free (resName);
6324
6325 if (resClass)
6326 free (resClass);
6327 }
6328
6329 bool
6330 CompWindow::syncWait () const
6331 {
6332 return priv->syncWait;
6333 }
6334
6335 bool
6336 CompWindow::alpha () const
6337 {
6338 WRAPABLE_HND_FUNCTN_RETURN (bool, alpha);
6339
6340 return priv->alpha;
6341 }
6342
6343 bool
6344 CompWindow::overrideRedirect () const
6345 {
6346 return priv->attrib.override_redirect;
6347 }
6348
6349 void
6350 PrivateWindow::setOverrideRedirect (bool overrideRedirect)
6351 {
6352 if (overrideRedirect == window->overrideRedirect ())
6353 return;
6354
6355 priv->attrib.override_redirect = overrideRedirect ? 1 : 0;
6356 window->recalcType ();
6357 window->recalcActions ();
6358
6359 screen->matchPropertyChanged (window);
6360 }
6361
6362 bool
6363 CompWindow::isMapped () const
6364 {
6365 return priv->mapNum > 0;
6366 }
6367
6368 bool
6369 CompWindow::isViewable () const
6370 {
6371 return (priv->attrib.map_state == IsViewable);
6372 }
6373
6374 bool
6375 CompWindow::isFocussable () const
6376 {
6377 WRAPABLE_HND_FUNCTN_RETURN (bool, isFocussable);
6378
6379 if (priv->inputHint)
6380 return true;
6381
6382 if (priv->protocols & CompWindowProtocolTakeFocusMask)
6383 return true;
6384
6385 return false;
6386 }
6387
6388 int
6389 CompWindow::windowClass () const
6390 {
6391 return priv->attrib.c_class;
6392 }
6393
6394 unsigned int
6395 CompWindow::depth () const
6396 {
6397 return priv->attrib.depth;
6398 }
6399
6400 bool
6401 CompWindow::alive () const
6402 {
6403 return priv->alive;
6404 }
6405
6406 unsigned int
6407 CompWindow::mwmDecor () const
6408 {
6409 return priv->mwmDecor;
6410 }
6411
6412 unsigned int
6413 CompWindow::mwmFunc () const
6414 {
6415 return priv->mwmFunc;
6416 }
6417
6418 /* TODO: This function should be able to check the XShape event
6419 * kind and only get/set shape rectangles for either ShapeInput
6420 * or ShapeBounding, but not both at the same time
6421 */
6422
6423 void
6424 CompWindow::updateFrameRegion ()
6425 {
6426 if (priv->serverFrame)
6427 {
6428 CompRect r;
6429 int x, y;
6430
6431 priv->frameRegion = emptyRegion;
6432
6433 updateFrameRegion (priv->frameRegion);
6434
6435 if (!shaded ())
6436 {
6437 r = priv->region.boundingRect ();
6438 priv->frameRegion -= r;
6439
6440 r.setGeometry (r.x1 () - priv->serverInput.left,
6441 r.y1 () - priv->serverInput.top,
6442 r.width () + priv->serverInput.right + priv->serverInput.left,
6443 r.height () + priv->serverInput.bottom + priv->serverInput.top);
6444
6445 priv->frameRegion &= CompRegion (r);
6446 }
6447
6448 x = priv->serverGeometry.x () - priv->serverInput.left;
6449 y = priv->serverGeometry.y () - priv->serverInput.top;
6450
6451 XShapeCombineRegion (screen->dpy (), priv->serverFrame,
6452 ShapeBounding, -x, -y,
6453 priv->frameRegion.united (priv->region).handle (),
6454 ShapeSet);
6455
6456 XShapeCombineRegion (screen->dpy (), priv->serverFrame,
6457 ShapeInput, -x, -y,
6458 priv->frameRegion.united (priv->inputRegion).handle (),
6459 ShapeSet);
6460 }
6461 }
6462
6463 void
6464 CompWindow::setWindowFrameExtents (const CompWindowExtents *b,
6465 const CompWindowExtents *i)
6466 {
6467 /* override redirect windows can't have frame extents */
6468 if (priv->attrib.override_redirect)
6469 return;
6470
6471 /* Input extents are used for frame size,
6472 * Border extents used for placement.
6473 */
6474
6475 if (!i)
6476 i = b;
6477
6478 if (priv->serverInput.left != i->left ||
6479 priv->serverInput.right != i->right ||
6480 priv->serverInput.top != i->top ||
6481 priv->serverInput.bottom != i->bottom ||
6482 priv->border.left != b->left ||
6483 priv->border.right != b->right ||
6484 priv->border.top != b->top ||
6485 priv->border.bottom != b->bottom)
6486 {
6487 priv->serverInput = *i;
6488 priv->border = *b;
6489
6490 recalcActions ();
6491
6492 priv->updateSize ();
6493 priv->updateFrameWindow ();
6494
6495 /* Always send a moveNotify
6496 * whenever the frame extents update
6497 * so that plugins can re-position appropriately */
6498 moveNotify (0, 0, true);
6499 }
6500
6501 /* Use b for _NET_WM_FRAME_EXTENTS here because
6502 * that is the representation of the actual decoration
6503 * around the window that the user sees and should
6504 * be used for placement and such */
6505
6506 /* Also update frame extents regardless of whether or not
6507 * the frame extents actually changed, eg, a plugin could
6508 * suggest that a window has no frame extents and that it
6509 * might later get frame extents - this is mandatory if we
6510 * say that we support it, so set them
6511 * additionaly some applications might request frame extents
6512 * and we must respond by setting the property - ideally
6513 * this should only ever be done when some plugin actually
6514 * need to change the frame extents or the applications
6515 * requested it */
6516
6517 unsigned long data[4];
6518
6519 data[0] = b->left;
6520 data[1] = b->right;
6521 data[2] = b->top;
6522 data[3] = b->bottom;
6523
6524 XChangeProperty (screen->dpy (), priv->id,
6525 Atoms::frameExtents,
6526 XA_CARDINAL, 32, PropModeReplace,
6527 (unsigned char *) data, 4);
6528 }
6529
6530 bool
6531 CompWindow::hasUnmapReference () const
6532 {
6533 return (priv && priv->unmapRefCnt > 1);
6534 }
6535
6536 void
6537 CompWindow::updateFrameRegion (CompRegion& region)
6538 WRAPABLE_HND_FUNCTN (updateFrameRegion, region)
6539
6540 bool
6541 PrivateWindow::reparent ()
6542 {
6543 XSetWindowAttributes attr;
6544 XWindowAttributes wa;
6545 XWindowChanges xwc = XWINDOWCHANGES_INIT;
6546 int mask;
6547 unsigned int nchildren;
6548 Window *children, root_return, parent_return;
6549 Display *dpy = screen->dpy ();
6550 Visual *visual = DefaultVisual (screen->dpy (),
6551 screen->screenNum ());
6552 Colormap cmap = DefaultColormap (screen->dpy (),
6553 screen->screenNum ());
6554
6555 if (serverFrame)
6556 return false;
6557
6558 XSync (dpy, false);
6559 XGrabServer (dpy);
6560
6561 if (!XGetWindowAttributes (dpy, id, &wa))
6562 {
6563 XUngrabServer (dpy);
6564 XSync (dpy, false);
6565 return false;
6566 }
6567
6568 if (wa.override_redirect)
6569 return false;
6570
6571 /* Don't ever reparent windows which have ended up
6572 * reparented themselves on the server side but not
6573 * on the client side */
6574
6575 XQueryTree (dpy, id, &root_return, &parent_return, &children, &nchildren);
6576
6577 if (parent_return != root_return)
6578 {
6579 XFree (children);
6580 XUngrabServer (dpy);
6581 XSync (dpy, false);
6582 return false;
6583 }
6584
6585 XFree (children);
6586
6587 XQueryTree (dpy, root_return, &root_return, &parent_return, &children, &nchildren);
6588
6589 XChangeSaveSet (dpy, id, SetModeInsert);
6590
6591 /* Force border width to 0 */
6592 xwc.border_width = 0;
6593 XConfigureWindow (dpy, id, CWBorderWidth, &xwc);
6594
6595 priv->serverGeometry.setBorder (0);
6596
6597 mask = CWBorderPixel | CWColormap | CWBackPixmap | CWOverrideRedirect;
6598
6599 if (wa.depth == 32)
6600 {
6601 cmap = wa.colormap;
6602 visual = wa.visual;
6603 }
6604
6605 attr.background_pixmap = None;
6606 attr.border_pixel = 0;
6607 attr.colormap = cmap;
6608 attr.override_redirect = true;
6609
6610 /* We need to know when the frame window is created
6611 * but that's all */
6612 XSelectInput (dpy, screen->root (), SubstructureNotifyMask);
6613
6614 /* Awaiting a new frame to be given to us */
6615 frame = None;
6616 serverFrame = XCreateWindow (dpy, screen->root (), 0, 0,
6617 wa.width, wa.height, 0, wa.depth,
6618 InputOutput, visual, mask, &attr);
6619
6620 /* Do not get any events from here on */
6621 XSelectInput (dpy, screen->root (), NoEventMask);
6622
6623 wrapper = XCreateWindow (dpy, serverFrame, 0, 0,
6624 wa.width, wa.height, 0, wa.depth,
6625 InputOutput, visual, mask, &attr);
6626
6627 xwc.stack_mode = Above;
6628
6629 /* Look for the client in the current server side stacking
6630 * order and put the frame above what the client is above
6631 */
6632 if (nchildren &&
6633 children[0] == id)
6634 {
6635 /* client at the bottom */
6636 xwc.stack_mode = Below;
6637 xwc.sibling = id;
6638 }
6639 else
6640 {
6641 for (unsigned int i = 0; i < nchildren; i++)
6642 {
6643 if (i < nchildren - 1)
6644 {
6645 if (children[i + 1] == id)
6646 {
6647 xwc.sibling = children[i];
6648 break;
6649 }
6650 }
6651 else /* client on top */
6652 xwc.sibling = children[i];
6653 }
6654 }
6655
6656 XFree (children);
6657
6658 /* Make sure the frame is underneath the client */
6659 XConfigureWindow (dpy, serverFrame, CWSibling | CWStackMode, &xwc);
6660
6661 /* Wait for the restacking to finish */
6662 XSync (dpy, false);
6663
6664 /* Always need to have the wrapper window mapped */
6665 XMapWindow (dpy, wrapper);
6666
6667 /* Reparent the client into the wrapper window */
6668 XReparentWindow (dpy, id, wrapper, 0, 0);
6669
6670 /* Restore events */
6671 attr.event_mask = wa.your_event_mask;
6672
6673 /* We don't care about client events on the frame, and listening for them
6674 * will probably end up fighting the client anyways, so disable them */
6675
6676 attr.do_not_propagate_mask = KeyPressMask | KeyReleaseMask |
6677 ButtonPressMask | ButtonReleaseMask |
6678 EnterWindowMask | LeaveWindowMask |
6679 PointerMotionMask | PointerMotionHintMask |
6680 Button1MotionMask | Button2MotionMask |
6681 Button3MotionMask | Button4MotionMask |
6682 Button5MotionMask | ButtonMotionMask |
6683 KeymapStateMask | ExposureMask |
6684 VisibilityChangeMask | StructureNotifyMask |
6685 ResizeRedirectMask | SubstructureNotifyMask |
6686 SubstructureRedirectMask | FocusChangeMask |
6687 PropertyChangeMask | ColormapChangeMask |
6688 OwnerGrabButtonMask;
6689
6690 XChangeWindowAttributes (dpy, id, CWEventMask | CWDontPropagate, &attr);
6691
6692 if (wa.map_state == IsViewable || shaded)
6693 XMapWindow (dpy, serverFrame);
6694
6695 attr.event_mask = SubstructureRedirectMask |
6696 SubstructureNotifyMask | EnterWindowMask |
6697 LeaveWindowMask;
6698
6699 serverFrameGeometry = serverGeometry;
6700
6701 XMoveResizeWindow (dpy, serverFrame, serverFrameGeometry.x (), serverFrameGeometry.y (),
6702 serverFrameGeometry.width (), serverFrameGeometry.height ());
6703
6704 XChangeWindowAttributes (dpy, serverFrame, CWEventMask, &attr);
6705 XChangeWindowAttributes (dpy, wrapper, CWEventMask, &attr);
6706
6707 XSelectInput (dpy, screen->root (),
6708 SubstructureRedirectMask |
6709 SubstructureNotifyMask |
6710 StructureNotifyMask |
6711 PropertyChangeMask |
6712 LeaveWindowMask |
6713 EnterWindowMask |
6714 KeyPressMask |
6715 KeyReleaseMask |
6716 ButtonPressMask |
6717 ButtonReleaseMask |
6718 FocusChangeMask |
6719 ExposureMask);
6720
6721 XUngrabServer (dpy);
6722 XSync (dpy, false);
6723
6724 window->windowNotify (CompWindowNotifyReparent);
6725
6726 return true;
6727 }
6728
6729 void
6730 PrivateWindow::unreparent ()
6731 {
6732 Display *dpy = screen->dpy ();
6733 XEvent e;
6734 bool alive = true;
6735 XWindowChanges xwc = XWINDOWCHANGES_INIT;
6736 unsigned int nchildren;
6737 Window *children = NULL, root_return, parent_return;
6738 XWindowAttributes wa;
6739 StackDebugger *dbg = StackDebugger::Default ();
6740
6741 if (!serverFrame)
6742 return;
6743
6744 XSync (dpy, false);
6745
6746 if (XCheckTypedWindowEvent (dpy, id, DestroyNotify, &e))
6747 {
6748 XPutBackEvent (dpy, &e);
6749 alive = false;
6750 }
6751 else
6752 {
6753 if (!XGetWindowAttributes (dpy, id, &wa))
6754 alive = false;
6755 }
6756
6757 /* Also don't reparent back into root windows that have ended up
6758 * reparented into other windows (and as such we are unmanaging them) */
6759
6760 if (alive)
6761 {
6762 XQueryTree (dpy, id, &root_return, &parent_return, &children, &nchildren);
6763
6764 if (parent_return != wrapper)
6765 alive = false;
6766 }
6767
6768 if ((!destroyed) && alive)
6769 {
6770 XGrabServer (dpy);
6771
6772 XChangeSaveSet (dpy, id, SetModeDelete);
6773 XSelectInput (dpy, serverFrame, NoEventMask);
6774 XSelectInput (dpy, wrapper, NoEventMask);
6775 XSelectInput (dpy, id, NoEventMask);
6776 XSelectInput (dpy, screen->root (), NoEventMask);
6777 XReparentWindow (dpy, id, screen->root (), 0, 0);
6778
6779 /* Wait for the reparent to finish */
6780 XSync (dpy, false);
6781
6782 xwc.x = serverGeometry.xMinusBorder ();
6783 xwc.y = serverGeometry.yMinusBorder ();
6784 xwc.width = serverGeometry.widthIncBorders ();
6785 xwc.height = serverGeometry.heightIncBorders ();
6786
6787 XConfigureWindow (dpy, serverFrame, CWX | CWY | CWWidth | CWHeight, &xwc);
6788
6789
6790 xwc.stack_mode = Below;
6791 xwc.sibling = serverFrame;
6792 XConfigureWindow (dpy, id, CWSibling | CWStackMode, &xwc);
6793
6794 /* Wait for the window to be restacked */
6795 XSync (dpy, false);
6796
6797 XUnmapWindow (dpy, serverFrame);
6798
6799 XSelectInput (dpy, id, wa.your_event_mask);
6800
6801 XSelectInput (dpy, screen->root (),
6802 SubstructureRedirectMask |
6803 SubstructureNotifyMask |
6804 StructureNotifyMask |
6805 PropertyChangeMask |
6806 LeaveWindowMask |
6807 EnterWindowMask |
6808 KeyPressMask |
6809 KeyReleaseMask |
6810 ButtonPressMask |
6811 ButtonReleaseMask |
6812 FocusChangeMask |
6813 ExposureMask);
6814
6815 XUngrabServer (dpy);
6816 XSync (dpy, false);
6817
6818 XMoveWindow (dpy, id, serverGeometry.x (), serverGeometry.y ());
6819 }
6820
6821 if (children)
6822 XFree (children);
6823
6824 if (dbg)
6825 dbg->addDestroyedFrame (serverFrame);
6826
6827 /* This is where things get tricky ... it is possible
6828 * to receive a ConfigureNotify relative to a frame window
6829 * for a destroyed window in case we process a ConfigureRequest
6830 * for the destroyed window and then a DestroyNotify for it directly
6831 * afterwards. In that case, we will receive the ConfigureNotify
6832 * for the XConfigureWindow request we made relative to that frame
6833 * window. Because of this, we must keep the frame window in the stack
6834 * as a new toplevel window so that the ConfigureNotify will be processed
6835 * properly until it too receives a DestroyNotify */
6836
6837 if (serverFrame)
6838 {
6839 XWindowAttributes attrib;
6840
6841 /* It's possible that the frame window was already destroyed because
6842 * the client was unreparented before it was destroyed (eg
6843 * UnmapNotify before DestroyNotify). In that case the frame window
6844 * is going to be an invalid window but since we haven't received
6845 * a DestroyNotify for it yet, it is possible that restacking
6846 * operations could occurr relative to it so we need to hold it
6847 * in the stack for now. Ensure that it is marked override redirect */
6848 XGetWindowAttributes (screen->dpy (), serverFrame, &attrib);
6849
6850 /* Put the frame window "above" the client window
6851 * in the stack */
6852 PrivateWindow::createCompWindow (id, id, attrib, serverFrame);
6853 }
6854
6855 /* Issue a DestroyNotify */
6856 XDestroyWindow (screen->dpy (), serverFrame);
6857 XDestroyWindow (screen->dpy (), wrapper);
6858
6859 /* This window is no longer "managed" in the
6860 * reparenting sense so clear its pending event
6861 * queue ... though maybe in future it would
6862 * be better to bookeep these events too and
6863 * handle the ReparentNotify */
6864 pendingConfigures.clear ();
6865
6866 frame = None;
6867 wrapper = None;
6868 serverFrame = None;
6869
6870 // Finally, (i.e. after updating state) notify the change
6871 window->windowNotify (CompWindowNotifyUnreparent);
6872 }