]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
when a window is uniconified, ensure its nothe 'last focused window' on some other...
[mikachu/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41
42 #ifdef HAVE_STDLIB_H
43    #include <stdlib.h>
44 #endif // HAVE_STDLIB_H
45 }
46
47 #include "i18n.hh"
48 #include "blackbox.hh"
49 #include "Clientmenu.hh"
50 #include "Font.hh"
51 #include "GCCache.hh"
52 #include "Iconmenu.hh"
53 #include "Image.hh"
54 #include "Screen.hh"
55 #include "Toolbar.hh"
56 #include "Util.hh"
57 #include "Window.hh"
58 #include "Windowmenu.hh"
59 #include "Workspace.hh"
60
61 using std::string;
62 using std::abs;
63
64 // change this to change what modifier keys openbox uses for mouse bindings
65 // for example: Mod1Mask | ControlMask
66 //          or: ControlMask| ShiftMask
67 const unsigned int ModMask = Mod1Mask;
68
69 /*
70  * Initializes the class with default values/the window's set initial values.
71  */
72 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
73   // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
74   // sizeof(BlackboxWindow));
75
76 #ifdef    DEBUG
77   fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
78 #endif // DEBUG
79
80   /*
81     set timer to zero... it is initialized properly later, so we check
82     if timer is zero in the destructor, and assume that the window is not
83     fully constructed if timer is zero...
84   */
85   timer = 0;
86   blackbox = b;
87   client.window = w;
88   screen = s;
89   xatom = blackbox->getXAtom();
90
91   if (! validateClient()) {
92     delete this;
93     return;
94   }
95
96   // fetch client size and placement
97   XWindowAttributes wattrib;
98   if (! XGetWindowAttributes(blackbox->getXDisplay(),
99                              client.window, &wattrib) ||
100       ! wattrib.screen || wattrib.override_redirect) {
101 #ifdef    DEBUG
102     fprintf(stderr,
103             "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
104 #endif // DEBUG
105
106     delete this;
107     return;
108   }
109
110 #ifdef DEBUG
111   fprintf(stderr, "0x%lx: initial (%d, %d) w: %d, h: %d\n", client.window,
112           wattrib.x, wattrib.y, wattrib.width, wattrib.height);
113 #endif // DEBUG
114
115   // set the eventmask early in the game so that we make sure we get
116   // all the events we are interested in
117   XSetWindowAttributes attrib_set;
118   attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
119                           StructureNotifyMask;
120   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
121                                      ButtonMotionMask;
122   XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
123                           CWEventMask|CWDontPropagate, &attrib_set);
124
125   flags.moving = flags.resizing = flags.shaded = flags.visible =
126     flags.iconic = flags.focused = flags.stuck = flags.modal =
127     flags.send_focus_message = flags.shaped = flags.skip_taskbar =
128     flags.skip_pager = flags.fullscreen = False;
129   flags.maximized = 0;
130
131   blackbox_attrib.workspace = window_number = BSENTINEL;
132
133   blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack
134     = blackbox_attrib.decoration = 0l;
135   blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
136   blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
137
138   frame.border_w = 1;
139   frame.window = frame.plate = frame.title = frame.handle = None;
140   frame.close_button = frame.iconify_button = frame.maximize_button = None;
141   frame.right_grip = frame.left_grip = None;
142
143   frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
144   frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
145     frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
146     frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
147     frame.fgrip_pixel = 0;
148   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
149   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
150   frame.pbutton = frame.ugrip = frame.fgrip = None;
151
152   decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
153                 Decor_Iconify | Decor_Maximize;
154   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
155
156   client.normal_hint_flags = 0;
157   client.window_group = None;
158   client.transient_for = 0;
159
160   current_state = NormalState;
161
162   /*
163     get the initial size and location of client window (relative to the
164     _root window_). This position is the reference point used with the
165     window's gravity to find the window's initial position.
166   */
167   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
168   client.old_bw = wattrib.border_width;
169
170   windowmenu = 0;
171   lastButtonPressTime = 0;
172
173   timer = new BTimer(blackbox, this);
174   timer->setTimeout(blackbox->getAutoRaiseDelay());
175
176   if (! getBlackboxHints()) {
177     getMWMHints();
178     getNetWMHints();
179   }
180
181   // get size, aspect, minimum/maximum size and other hints set by the
182   // client
183   getWMProtocols();
184   getWMHints();
185   getWMNormalHints();
186
187 #ifdef DEBUG
188   fprintf(stderr, "0x%lx: after hints (%d, %d) w: %d, h: %d\n", client.window,
189           client.rect.x(), client.rect.y(),
190           client.rect.width(), client.rect.height());
191 #endif // DEBUG
192
193   frame.window = createToplevelWindow();
194   frame.plate = createChildWindow(frame.window);
195   associateClientWindow();
196
197   blackbox->saveWindowSearch(frame.window, this);
198   blackbox->saveWindowSearch(frame.plate, this);
199   blackbox->saveWindowSearch(client.window, this);
200
201   // determine if this is a transient window
202   getTransientInfo();
203
204   // determine the window's type, so we can decide its decorations and
205   // functionality, or if we should not manage it at all
206   getWindowType();
207
208   // adjust the window decorations/behavior based on the window type
209   switch (window_type) {
210   case Type_Desktop:
211   case Type_Dock:
212   case Type_Menu:
213   case Type_Toolbar:
214   case Type_Utility:
215   case Type_Splash:
216     // none of these windows are decorated or manipulated by the window manager
217     decorations = 0;
218     functions = 0;
219     blackbox_attrib.workspace = 0;  // we do need to belong to a workspace
220     flags.stuck = True;             // we show up on all workspaces
221     break;
222
223   case Type_Dialog:
224     // dialogs cannot be maximized, and don't display a handle
225     decorations &= ~(Decor_Maximize | Decor_Handle);
226     functions &= ~Func_Maximize;
227     break;
228
229   case Type_Normal:
230     // normal windows retain all of the possible decorations and functionality
231     break;
232   }
233
234   // further adjeust the window's decorations/behavior based on window sizes
235   if ((client.normal_hint_flags & PMinSize) &&
236       (client.normal_hint_flags & PMaxSize) &&
237       client.max_width <= client.min_width &&
238       client.max_height <= client.min_height) {
239     decorations &= ~(Decor_Maximize | Decor_Handle);
240     functions &= ~(Func_Resize | Func_Maximize);
241   }
242   upsize();
243
244 #ifdef DFEBUG
245   fprintf(stderr, "0x%lx: sizes reflect the frame from now on\n",
246           client.window);
247   fprintf(stderr, "0x%lx: after upsize (%d, %d) w: %d, h: %d\n", client.window,
248           frame.rect.x(), frame.rect.y(),
249           frame.rect.width(), frame.rect.height());
250 #endif // DEBUG
251
252   setAllowedActions();
253
254   bool place_window = True;
255   if (blackbox->isStartup() || isTransient() ||
256       client.normal_hint_flags & (PPosition|USPosition)) {
257     applyGravity(frame.rect);
258
259     if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
260       place_window = False;
261   }
262
263 #ifdef DEBUG
264   fprintf(stderr, "0x%lx: after gravity (%d, %d) w: %d, h: %d\n",
265           client.window,
266           frame.rect.x(), frame.rect.y(),
267           frame.rect.width(), frame.rect.height());
268 #endif // DEBUG
269
270   // add the window's strut. note this is done *after* placing the window.
271   screen->addStrut(&client.strut);
272   updateStrut();
273   
274   if (decorations & Decor_Titlebar)
275     createTitlebar();
276
277   if (decorations & Decor_Handle)
278     createHandle();
279
280 #ifdef    SHAPE
281   if (blackbox->hasShapeExtensions() && flags.shaped) {
282     configureShape();
283   }
284 #endif // SHAPE
285
286   windowmenu = new Windowmenu(this);
287
288   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
289     screen->getCurrentWorkspace()->addWindow(this, place_window);
290   else
291     screen->getWorkspace(blackbox_attrib.workspace)->
292       addWindow(this, place_window);
293
294   if (! place_window) {
295     // don't need to call configure if we are letting the workspace
296     // place the window
297     configure(frame.rect.x(), frame.rect.y(),
298               frame.rect.width(), frame.rect.height());
299
300 #ifdef DEBUG
301     fprintf(stderr, "0x%lx: after configure (%d, %d) w: %d, h: %d\n",
302             client.window,
303             frame.rect.x(), frame.rect.y(),
304             frame.rect.width(), frame.rect.height());
305 #endif // DEBUG
306   }
307
308   // preserve the window's initial state on first map, and its current state
309   // across a restart
310   unsigned long initial_state = current_state;
311   if (! getState())
312     current_state = initial_state;
313
314   // get sticky state from our parent window if we've got one
315   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
316       client.transient_for->isStuck() != flags.stuck)
317     stick();
318
319   if (flags.shaded) {
320     flags.shaded = False;
321     initial_state = current_state;
322     shade();
323
324     /*
325       At this point in the life of a window, current_state should only be set
326       to IconicState if the window was an *icon*, not if it was shaded.
327     */
328     if (initial_state != IconicState)
329       current_state = NormalState;
330   }
331
332   if (flags.stuck) {
333     flags.stuck = False;
334     stick();
335   }
336
337   if (flags.maximized && (functions & Func_Maximize))
338     remaximize();
339
340   /*
341     When the window is mapped (and also when its attributes are restored), the
342     current_state that was set here will be used.
343     It is set to Normal if the window is to be mapped or it is set to Iconic
344     if the window is to be iconified.
345     *Note* that for sticky windows, the same rules apply here, they are in
346     fact never set to Iconic since there is no way for us to tell if a sticky
347     window was iconified previously.
348   */
349  
350   positionWindows();
351   decorate();
352   grabButtons();
353
354   XMapSubwindows(blackbox->getXDisplay(), frame.window);
355
356   redrawWindowFrame();
357
358 #ifdef DEBUG
359   fprintf(stderr, "0x%lx: end of constructor (%d, %d) w: %d, h: %d\n",
360           client.window,
361           frame.rect.x(), frame.rect.y(),
362           frame.rect.width(), frame.rect.height());
363 #endif // DEBUG
364 }
365
366
367 BlackboxWindow::~BlackboxWindow(void) {
368 #ifdef    DEBUG
369   fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
370           client.window);
371 #endif // DEBUG
372
373   if (! timer) // window not managed...
374     return;
375
376   screen->removeStrut(&client.strut);
377   screen->updateAvailableArea();
378
379   // We don't need to worry about resizing because resizing always grabs the X
380   // server. This should only ever happen if using opaque moving.
381   if (flags.moving)
382     endMove();
383
384   delete timer;
385
386   delete windowmenu;
387
388   if (client.window_group) {
389     BWindowGroup *group = blackbox->searchGroup(client.window_group);
390     if (group) group->removeWindow(this);
391   }
392
393   // remove ourselves from our transient_for
394   if (isTransient()) {
395     if (client.transient_for != (BlackboxWindow *) ~0ul) {
396       client.transient_for->client.transientList.remove(this);
397     }
398     client.transient_for = (BlackboxWindow*) 0;
399   }
400
401   if (client.transientList.size() > 0) {
402     // reset transient_for for all transients
403     BlackboxWindowList::iterator it, end = client.transientList.end();
404     for (it = client.transientList.begin(); it != end; ++it) {
405       (*it)->client.transient_for = (BlackboxWindow*) 0;
406     }
407   }
408
409   if (frame.title)
410     destroyTitlebar();
411
412   if (frame.handle)
413     destroyHandle();
414
415   if (frame.plate) {
416     blackbox->removeWindowSearch(frame.plate);
417     XDestroyWindow(blackbox->getXDisplay(), frame.plate);
418   }
419
420   if (frame.window) {
421     blackbox->removeWindowSearch(frame.window);
422     XDestroyWindow(blackbox->getXDisplay(), frame.window);
423   }
424
425   blackbox->removeWindowSearch(client.window);
426 }
427
428
429 /*
430  * Creates a new top level window, with a given location, size, and border
431  * width.
432  * Returns: the newly created window
433  */
434 Window BlackboxWindow::createToplevelWindow(void) {
435   XSetWindowAttributes attrib_create;
436   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
437                               CWOverrideRedirect | CWEventMask;
438
439   attrib_create.background_pixmap = None;
440   attrib_create.colormap = screen->getColormap();
441   attrib_create.override_redirect = True;
442   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
443                              ButtonMotionMask | EnterWindowMask;
444
445   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
446                        0, 0, 1, 1, frame.border_w, screen->getDepth(),
447                        InputOutput, screen->getVisual(), create_mask,
448                        &attrib_create);
449 }
450
451
452 /*
453  * Creates a child window, and optionally associates a given cursor with
454  * the new window.
455  */
456 Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) {
457   XSetWindowAttributes attrib_create;
458   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
459                               CWEventMask;
460
461   attrib_create.background_pixmap = None;
462   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
463                              ButtonMotionMask | ExposureMask;
464
465   if (cursor) {
466     create_mask |= CWCursor;
467     attrib_create.cursor = cursor;
468   }
469
470   return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
471                        screen->getDepth(), InputOutput, screen->getVisual(),
472                        create_mask, &attrib_create);
473 }
474
475
476 void BlackboxWindow::associateClientWindow(void) {
477   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
478   getWMName();
479   getWMIconName();
480
481   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
482
483   XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
484
485   XGrabServer(blackbox->getXDisplay());
486
487   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
488                              StructureNotifyMask;
489   XSelectInput(blackbox->getXDisplay(), client.window,
490                event_mask & ~StructureNotifyMask);
491   XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
492   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
493
494   XUngrabServer(blackbox->getXDisplay());
495
496   XRaiseWindow(blackbox->getXDisplay(), frame.plate);
497   XMapSubwindows(blackbox->getXDisplay(), frame.plate);
498
499
500 #ifdef    SHAPE
501   if (blackbox->hasShapeExtensions()) {
502     XShapeSelectInput(blackbox->getXDisplay(), client.window,
503                       ShapeNotifyMask);
504
505     Bool shaped = False;
506     int foo;
507     unsigned int ufoo;
508
509     XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
510                        &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
511                        &ufoo, &ufoo);
512     flags.shaped = shaped;
513   }
514 #endif // SHAPE
515 }
516
517
518 void BlackboxWindow::decorate(void) {
519   BTexture* texture;
520
521   texture = &(screen->getWindowStyle()->b_focus);
522   frame.fbutton = texture->render(frame.button_w, frame.button_w,
523                                   frame.fbutton);
524   if (! frame.fbutton)
525     frame.fbutton_pixel = texture->color().pixel();
526
527   texture = &(screen->getWindowStyle()->b_unfocus);
528   frame.ubutton = texture->render(frame.button_w, frame.button_w,
529                                   frame.ubutton);
530   if (! frame.ubutton)
531     frame.ubutton_pixel = texture->color().pixel();
532
533   texture = &(screen->getWindowStyle()->b_pressed);
534   frame.pbutton = texture->render(frame.button_w, frame.button_w,
535                                   frame.pbutton);
536   if (! frame.pbutton)
537     frame.pbutton_pixel = texture->color().pixel();
538
539   if (decorations & Decor_Titlebar) {
540     texture = &(screen->getWindowStyle()->t_focus);
541     frame.ftitle = texture->render(frame.inside_w, frame.title_h,
542                                    frame.ftitle);
543     if (! frame.ftitle)
544       frame.ftitle_pixel = texture->color().pixel();
545
546     texture = &(screen->getWindowStyle()->t_unfocus);
547     frame.utitle = texture->render(frame.inside_w, frame.title_h,
548                                    frame.utitle);
549     if (! frame.utitle)
550       frame.utitle_pixel = texture->color().pixel();
551
552     XSetWindowBorder(blackbox->getXDisplay(), frame.title,
553                      screen->getBorderColor()->pixel());
554
555     decorateLabel();
556   }
557
558   if (decorations & Decor_Border) {
559     frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel();
560     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel();
561     blackbox_attrib.flags |= AttribDecoration;
562     blackbox_attrib.decoration = DecorNormal;
563   } else {
564     blackbox_attrib.flags |= AttribDecoration;
565     blackbox_attrib.decoration = DecorNone;
566   }
567
568   if (decorations & Decor_Handle) {
569     texture = &(screen->getWindowStyle()->h_focus);
570     frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
571                                     frame.fhandle);
572     if (! frame.fhandle)
573       frame.fhandle_pixel = texture->color().pixel();
574
575     texture = &(screen->getWindowStyle()->h_unfocus);
576     frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
577                                     frame.uhandle);
578     if (! frame.uhandle)
579       frame.uhandle_pixel = texture->color().pixel();
580
581     texture = &(screen->getWindowStyle()->g_focus);
582     frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
583     if (! frame.fgrip)
584       frame.fgrip_pixel = texture->color().pixel();
585
586     texture = &(screen->getWindowStyle()->g_unfocus);
587     frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
588     if (! frame.ugrip)
589       frame.ugrip_pixel = texture->color().pixel();
590
591     XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
592                      screen->getBorderColor()->pixel());
593     XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
594                      screen->getBorderColor()->pixel());
595     XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
596                      screen->getBorderColor()->pixel());
597   }
598
599   XSetWindowBorder(blackbox->getXDisplay(), frame.window,
600                    screen->getBorderColor()->pixel());
601 }
602
603
604 void BlackboxWindow::decorateLabel(void) {
605   BTexture *texture;
606
607   texture = &(screen->getWindowStyle()->l_focus);
608   frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
609   if (! frame.flabel)
610     frame.flabel_pixel = texture->color().pixel();
611
612   texture = &(screen->getWindowStyle()->l_unfocus);
613   frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
614   if (! frame.ulabel)
615     frame.ulabel_pixel = texture->color().pixel();
616 }
617
618
619 void BlackboxWindow::createHandle(void) {
620   frame.handle = createChildWindow(frame.window);
621   blackbox->saveWindowSearch(frame.handle, this);
622
623   frame.left_grip =
624     createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor());
625   blackbox->saveWindowSearch(frame.left_grip, this);
626
627   frame.right_grip =
628     createChildWindow(frame.handle, blackbox->getLowerRightAngleCursor());
629   blackbox->saveWindowSearch(frame.right_grip, this);
630 }
631
632
633 void BlackboxWindow::destroyHandle(void) {
634   if (frame.fhandle)
635     screen->getImageControl()->removeImage(frame.fhandle);
636
637   if (frame.uhandle)
638     screen->getImageControl()->removeImage(frame.uhandle);
639
640   if (frame.fgrip)
641     screen->getImageControl()->removeImage(frame.fgrip);
642
643   if (frame.ugrip)
644     screen->getImageControl()->removeImage(frame.ugrip);
645
646   blackbox->removeWindowSearch(frame.left_grip);
647   blackbox->removeWindowSearch(frame.right_grip);
648
649   XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
650   XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
651   frame.left_grip = frame.right_grip = None;
652
653   blackbox->removeWindowSearch(frame.handle);
654   XDestroyWindow(blackbox->getXDisplay(), frame.handle);
655   frame.handle = None;
656 }
657
658
659 void BlackboxWindow::createTitlebar(void) {
660   frame.title = createChildWindow(frame.window);
661   frame.label = createChildWindow(frame.title);
662   blackbox->saveWindowSearch(frame.title, this);
663   blackbox->saveWindowSearch(frame.label, this);
664
665   if (decorations & Decor_Iconify) createIconifyButton();
666   if (decorations & Decor_Maximize) createMaximizeButton();
667   if (decorations & Decor_Close) createCloseButton();
668 }
669
670
671 void BlackboxWindow::destroyTitlebar(void) {
672   if (frame.close_button)
673     destroyCloseButton();
674
675   if (frame.iconify_button)
676     destroyIconifyButton();
677
678   if (frame.maximize_button)
679     destroyMaximizeButton();
680
681   if (frame.ftitle)
682     screen->getImageControl()->removeImage(frame.ftitle);
683
684   if (frame.utitle)
685     screen->getImageControl()->removeImage(frame.utitle);
686
687   if (frame.flabel)
688     screen->getImageControl()->removeImage(frame.flabel);
689
690   if( frame.ulabel)
691     screen->getImageControl()->removeImage(frame.ulabel);
692
693   if (frame.fbutton)
694     screen->getImageControl()->removeImage(frame.fbutton);
695
696   if (frame.ubutton)
697     screen->getImageControl()->removeImage(frame.ubutton);
698
699   if (frame.pbutton)
700     screen->getImageControl()->removeImage(frame.pbutton);
701
702   blackbox->removeWindowSearch(frame.title);
703   blackbox->removeWindowSearch(frame.label);
704
705   XDestroyWindow(blackbox->getXDisplay(), frame.label);
706   XDestroyWindow(blackbox->getXDisplay(), frame.title);
707   frame.title = frame.label = None;
708 }
709
710
711 void BlackboxWindow::createCloseButton(void) {
712   if (frame.title != None) {
713     frame.close_button = createChildWindow(frame.title);
714     blackbox->saveWindowSearch(frame.close_button, this);
715   }
716 }
717
718
719 void BlackboxWindow::destroyCloseButton(void) {
720   blackbox->removeWindowSearch(frame.close_button);
721   XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
722   frame.close_button = None;
723 }
724
725
726 void BlackboxWindow::createIconifyButton(void) {
727   if (frame.title != None) {
728     frame.iconify_button = createChildWindow(frame.title);
729     blackbox->saveWindowSearch(frame.iconify_button, this);
730   }
731 }
732
733
734 void BlackboxWindow::destroyIconifyButton(void) {
735   blackbox->removeWindowSearch(frame.iconify_button);
736   XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
737   frame.iconify_button = None;
738 }
739
740
741 void BlackboxWindow::createMaximizeButton(void) {
742   if (frame.title != None) {
743     frame.maximize_button = createChildWindow(frame.title);
744     blackbox->saveWindowSearch(frame.maximize_button, this);
745   }
746 }
747
748
749 void BlackboxWindow::destroyMaximizeButton(void) {
750   blackbox->removeWindowSearch(frame.maximize_button);
751   XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
752   frame.maximize_button = None;
753 }
754
755
756 void BlackboxWindow::positionButtons(bool redecorate_label) {
757   string layout = blackbox->getTitlebarLayout();
758   string parsed;
759
760   bool hasclose, hasiconify, hasmaximize, haslabel;
761   hasclose = hasiconify = hasmaximize = haslabel = false;
762
763   string::const_iterator it, end;
764   for (it = layout.begin(), end = layout.end(); it != end; ++it) {
765     switch(*it) {
766     case 'C':
767       if (! hasclose && (decorations & Decor_Close)) {
768         hasclose = true;
769         parsed += *it;
770       }
771       break;
772     case 'I':
773       if (! hasiconify && (decorations & Decor_Iconify)) {
774         hasiconify = true;
775         parsed += *it;
776       }
777       break;
778     case 'M':
779       if (! hasmaximize && (decorations & Decor_Maximize)) {
780         hasmaximize = true;
781         parsed += *it;
782       }
783       break;
784     case 'L':
785       if (! haslabel) {
786         haslabel = true;
787         parsed += *it;
788       }
789     }
790   }
791   if (! hasclose && frame.close_button)
792     destroyCloseButton();
793   if (! hasiconify && frame.iconify_button)
794     destroyIconifyButton();
795   if (! hasmaximize && frame.maximize_button)
796     destroyMaximizeButton();
797   if (! haslabel)
798     parsed += 'L';      // require that the label be in the layout
799
800   const unsigned int bsep = frame.bevel_w + 1;  // separation between elements
801   const unsigned int by = frame.bevel_w + 1;
802   const unsigned int ty = frame.bevel_w;
803
804   frame.label_w = frame.inside_w - bsep * 2 -
805     (frame.button_w + bsep) * (parsed.size() - 1);
806
807   unsigned int x = bsep;
808   for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
809     switch(*it) {
810     case 'C':
811       if (! frame.close_button) createCloseButton();
812       XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
813                         frame.button_w, frame.button_w);
814       x += frame.button_w + bsep;
815       break;
816     case 'I':
817       if (! frame.iconify_button) createIconifyButton();
818       XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
819                         frame.button_w, frame.button_w);
820       x += frame.button_w + bsep;
821       break;
822     case 'M':
823       if (! frame.maximize_button) createMaximizeButton();
824       XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
825                         frame.button_w, frame.button_w);
826       x += frame.button_w + bsep;
827       break;
828     case 'L':
829       XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
830                         frame.label_w, frame.label_h);
831       x += frame.label_w + bsep;
832       break;
833     }
834   }
835
836   if (redecorate_label) decorateLabel();
837   redrawLabel();
838   redrawAllButtons();
839 }
840
841
842 void BlackboxWindow::reconfigure(void) {
843   restoreGravity(client.rect);
844   upsize();
845   applyGravity(frame.rect);
846   positionWindows();
847   decorate();
848   redrawWindowFrame();
849
850   ungrabButtons();
851   grabButtons();
852
853   if (windowmenu) {
854     windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
855     windowmenu->reconfigure();
856   }
857 }
858
859
860 void BlackboxWindow::grabButtons(void) {
861   if (! screen->isSloppyFocus() || screen->doClickRaise())
862     // grab button 1 for changing focus/raising
863     blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
864                          GrabModeSync, GrabModeSync, frame.plate, None,
865                          screen->allowScrollLock());
866   
867   if (functions & Func_Move)
868     blackbox->grabButton(Button1, ModMask, frame.window, True,
869                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
870                          GrabModeAsync, frame.window, None,
871                          screen->allowScrollLock());
872   if (functions & Func_Resize)
873     blackbox->grabButton(Button3, ModMask, frame.window, True,
874                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
875                          GrabModeAsync, frame.window, None,
876                          screen->allowScrollLock());
877   // alt+middle lowers the window
878   blackbox->grabButton(Button2, ModMask, frame.window, True,
879                        ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
880                        frame.window, None,
881                        screen->allowScrollLock());
882 }
883
884
885 void BlackboxWindow::ungrabButtons(void) {
886   if (! screen->isSloppyFocus() || screen->doClickRaise())
887     blackbox->ungrabButton(Button1, 0, frame.plate);
888
889   blackbox->ungrabButton(Button1, ModMask, frame.window);
890   blackbox->ungrabButton(Button2, ModMask, frame.window);
891   blackbox->ungrabButton(Button3, ModMask, frame.window);
892 }
893
894
895 void BlackboxWindow::positionWindows(void) {
896   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
897                     frame.rect.x(), frame.rect.y(), frame.inside_w,
898                     (flags.shaded) ? frame.title_h : frame.inside_h);
899   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
900                         frame.border_w);
901   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
902                         frame.mwm_border_w);
903   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
904                     frame.margin.left - frame.mwm_border_w - frame.border_w,
905                     frame.margin.top - frame.mwm_border_w - frame.border_w,
906                     client.rect.width(), client.rect.height());
907   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
908                     0, 0, client.rect.width(), client.rect.height());
909   // ensure client.rect contains the real location
910   client.rect.setPos(frame.rect.left() + frame.margin.left,
911                      frame.rect.top() + frame.margin.top);
912
913   if (decorations & Decor_Titlebar) {
914     if (frame.title == None) createTitlebar();
915
916     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
917                           frame.border_w);
918     XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
919                       -frame.border_w, frame.inside_w, frame.title_h);
920
921     positionButtons();
922     XMapSubwindows(blackbox->getXDisplay(), frame.title);
923     XMapWindow(blackbox->getXDisplay(), frame.title);
924   } else if (frame.title) {
925     destroyTitlebar();
926   }
927   if (decorations & Decor_Handle) {
928     if (frame.handle == None) createHandle();
929     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
930                           frame.border_w);
931     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
932                           frame.border_w);
933     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
934                           frame.border_w);
935
936     // use client.rect here so the value is correct even if shaded
937     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
938                       -frame.border_w,
939                       client.rect.height() + frame.margin.top +
940                       frame.mwm_border_w - frame.border_w,
941                       frame.inside_w, frame.handle_h);
942     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
943                       -frame.border_w, -frame.border_w,
944                       frame.grip_w, frame.handle_h);
945     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
946                       frame.inside_w - frame.grip_w - frame.border_w,
947                       -frame.border_w, frame.grip_w, frame.handle_h);
948
949     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
950     XMapWindow(blackbox->getXDisplay(), frame.handle);
951   } else if (frame.handle) {
952     destroyHandle();
953   }
954   XSync(blackbox->getXDisplay(), False);
955 }
956
957
958 void BlackboxWindow::updateStrut(void) {
959   unsigned long num = 4;
960   unsigned long *data;
961   if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
962                         num, &data))
963     return;
964  
965   if (num == 4) {
966     client.strut.left = data[0];
967     client.strut.right = data[1];
968     client.strut.top = data[2];
969     client.strut.bottom = data[3];
970
971     screen->updateAvailableArea();
972   }
973
974   delete [] data;
975 }
976
977
978 void BlackboxWindow::getWindowType(void) {
979   unsigned long val;
980   if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
981                       val)) {
982     if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop))
983       window_type = Type_Desktop;
984     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock))
985       window_type = Type_Dock;
986     else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
987       window_type = Type_Toolbar;
988     else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu))
989       window_type = Type_Menu;
990     else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility))
991       window_type = Type_Utility;
992     else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash))
993       window_type = Type_Splash;
994     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog))
995       window_type = Type_Dialog;
996     else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal))
997       window_type = Type_Normal;
998     return;
999   }
1000
1001   /*
1002    * the window type hint was not set, which means we either classify ourself
1003    * as a normal window or a dialog, depending on if we are a transient.
1004    */
1005   if (isTransient())
1006     window_type = Type_Dialog;
1007
1008   window_type = Type_Normal;
1009 }
1010
1011
1012 void BlackboxWindow::getWMName(void) {
1013   if (xatom->getValue(client.window, XAtom::net_wm_name,
1014                       XAtom::utf8, client.title) &&
1015       !client.title.empty()) {
1016     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1017     return;
1018   }
1019   //fall through to using WM_NAME
1020   if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
1021       && !client.title.empty()) {
1022     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1023     return;
1024   }
1025   // fall back to an internal default
1026   client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
1027   xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
1028                   client.title);
1029 }
1030
1031
1032 void BlackboxWindow::getWMIconName(void) {
1033   if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1034                       XAtom::utf8, client.icon_title) && 
1035       !client.icon_title.empty()) {
1036     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1037     return;
1038   }
1039   //fall through to using WM_ICON_NAME
1040   if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1041                       client.icon_title) && 
1042       !client.icon_title.empty()) {
1043     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1044     return;
1045   }
1046   // fall back to using the main name
1047   client.icon_title = client.title;
1048   xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1049                   client.icon_title);
1050 }
1051
1052
1053 /*
1054  * Retrieve which WM Protocols are supported by the client window.
1055  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1056  * window's decorations and allow the close behavior.
1057  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1058  * this.
1059  */
1060 void BlackboxWindow::getWMProtocols(void) {
1061   Atom *proto;
1062   int num_return = 0;
1063
1064   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1065                       &proto, &num_return)) {
1066     for (int i = 0; i < num_return; ++i) {
1067       if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1068         decorations |= Decor_Close;
1069         functions |= Func_Close;
1070       } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1071         flags.send_focus_message = True;
1072       else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1073         screen->addNetizen(new Netizen(screen, client.window));
1074     }
1075
1076     XFree(proto);
1077   }
1078 }
1079
1080
1081 /*
1082  * Gets the value of the WM_HINTS property.
1083  * If the property is not set, then use a set of default values.
1084  */
1085 void BlackboxWindow::getWMHints(void) {
1086   focus_mode = F_Passive;
1087
1088   // remove from current window group
1089   if (client.window_group) {
1090     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1091     if (group) group->removeWindow(this);
1092   }
1093   client.window_group = None;
1094
1095   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1096   if (! wmhint) {
1097     return;
1098   }
1099
1100   if (wmhint->flags & InputHint) {
1101     if (wmhint->input == True) {
1102       if (flags.send_focus_message)
1103         focus_mode = F_LocallyActive;
1104     } else {
1105       if (flags.send_focus_message)
1106         focus_mode = F_GloballyActive;
1107       else
1108         focus_mode = F_NoInput;
1109     }
1110   }
1111
1112   if (wmhint->flags & StateHint)
1113     current_state = wmhint->initial_state;
1114
1115   if (wmhint->flags & WindowGroupHint) {
1116     client.window_group = wmhint->window_group;
1117
1118     // add window to the appropriate group
1119     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1120     if (! group) { // no group found, create it!
1121       new BWindowGroup(blackbox, client.window_group);
1122       group = blackbox->searchGroup(client.window_group);
1123     }
1124     if (group)
1125       group->addWindow(this);
1126   }
1127
1128   XFree(wmhint);
1129 }
1130
1131
1132 /*
1133  * Gets the value of the WM_NORMAL_HINTS property.
1134  * If the property is not set, then use a set of default values.
1135  */
1136 void BlackboxWindow::getWMNormalHints(void) {
1137   long icccm_mask;
1138   XSizeHints sizehint;
1139
1140   client.min_width = client.min_height =
1141     client.width_inc = client.height_inc = 1;
1142   client.base_width = client.base_height = 0;
1143   client.win_gravity = NorthWestGravity;
1144 #if 0
1145   client.min_aspect_x = client.min_aspect_y =
1146     client.max_aspect_x = client.max_aspect_y = 1;
1147 #endif
1148
1149   /*
1150     use the full screen, not the strut modified size. otherwise when the
1151     availableArea changes max_width/height will be incorrect and lead to odd
1152     rendering bugs.
1153   */
1154   const Rect& screen_area = screen->getRect();
1155   client.max_width = screen_area.width();
1156   client.max_height = screen_area.height();
1157
1158   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1159                           &sizehint, &icccm_mask))
1160     return;
1161
1162   client.normal_hint_flags = sizehint.flags;
1163
1164   if (sizehint.flags & PMinSize) {
1165     if (sizehint.min_width >= 0)
1166       client.min_width = sizehint.min_width;
1167     if (sizehint.min_height >= 0)
1168       client.min_height = sizehint.min_height;
1169   }
1170
1171   if (sizehint.flags & PMaxSize) {
1172     if (sizehint.max_width > static_cast<signed>(client.min_width))
1173       client.max_width = sizehint.max_width;
1174     else
1175       client.max_width = client.min_width;
1176
1177     if (sizehint.max_height > static_cast<signed>(client.min_height))
1178       client.max_height = sizehint.max_height;
1179     else
1180       client.max_height = client.min_height;
1181   }
1182
1183   if (sizehint.flags & PResizeInc) {
1184     client.width_inc = sizehint.width_inc;
1185     client.height_inc = sizehint.height_inc;
1186   }
1187
1188 #if 0 // we do not support this at the moment
1189   if (sizehint.flags & PAspect) {
1190     client.min_aspect_x = sizehint.min_aspect.x;
1191     client.min_aspect_y = sizehint.min_aspect.y;
1192     client.max_aspect_x = sizehint.max_aspect.x;
1193     client.max_aspect_y = sizehint.max_aspect.y;
1194   }
1195 #endif
1196
1197   if (sizehint.flags & PBaseSize) {
1198     client.base_width = sizehint.base_width;
1199     client.base_height = sizehint.base_height;
1200   }
1201
1202   if (sizehint.flags & PWinGravity)
1203     client.win_gravity = sizehint.win_gravity;
1204 }
1205
1206
1207 /*
1208  * Gets the NETWM hints for the class' contained window.
1209  */
1210 void BlackboxWindow::getNetWMHints(void) {
1211   unsigned long workspace;
1212
1213   if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1214                       workspace)) {
1215     if (workspace == 0xffffffff)
1216       flags.stuck = True;
1217     else
1218       blackbox_attrib.workspace = workspace;
1219   }
1220
1221   unsigned long *state;
1222   unsigned long num = (unsigned) -1;
1223   if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1224                       num, &state)) {
1225     bool vert = False,
1226          horz = False;
1227     for (unsigned long i = 0; i < num; ++i) {
1228       if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1229         flags.modal = True;
1230       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1231         flags.shaded = True;
1232       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1233         flags.skip_taskbar = True;
1234       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1235         flags.skip_pager = True;
1236       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1237         flags.fullscreen = True;
1238       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1239         setState(IconicState);
1240       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1241         vert = True;
1242       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1243         horz = True;
1244     }
1245     if (vert && horz)
1246       flags.maximized = 1;
1247     else if (vert)
1248       flags.maximized = 2;
1249     else if (horz)
1250       flags.maximized = 3;
1251
1252     delete [] state;
1253   }
1254 }
1255
1256
1257 /*
1258  * Gets the MWM hints for the class' contained window.
1259  * This is used while initializing the window to its first state, and not
1260  * thereafter.
1261  * Returns: true if the MWM hints are successfully retreived and applied;
1262  * false if they are not.
1263  */
1264 void BlackboxWindow::getMWMHints(void) {
1265   unsigned long num;
1266   MwmHints *mwm_hint;
1267
1268   num = PropMwmHintsElements;
1269   if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1270                         XAtom::motif_wm_hints, num,
1271                         (unsigned long **)&mwm_hint))
1272     return;
1273   if (num < PropMwmHintsElements) {
1274     delete [] mwm_hint;
1275     return;
1276   }
1277
1278   if (mwm_hint->flags & MwmHintsDecorations) {
1279     if (mwm_hint->decorations & MwmDecorAll) {
1280       decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1281                     Decor_Iconify | Decor_Maximize | Decor_Close;
1282     } else {
1283       decorations = 0;
1284
1285       if (mwm_hint->decorations & MwmDecorBorder)
1286         decorations |= Decor_Border;
1287       if (mwm_hint->decorations & MwmDecorHandle)
1288         decorations |= Decor_Handle;
1289       if (mwm_hint->decorations & MwmDecorTitle)
1290         decorations |= Decor_Titlebar;
1291       if (mwm_hint->decorations & MwmDecorIconify)
1292         decorations |= Decor_Iconify;
1293       if (mwm_hint->decorations & MwmDecorMaximize)
1294         decorations |= Decor_Maximize;
1295     }
1296   }
1297
1298   if (mwm_hint->flags & MwmHintsFunctions) {
1299     if (mwm_hint->functions & MwmFuncAll) {
1300       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1301                   Func_Close;
1302     } else {
1303       functions = 0;
1304
1305       if (mwm_hint->functions & MwmFuncResize)
1306         functions |= Func_Resize;
1307       if (mwm_hint->functions & MwmFuncMove)
1308         functions |= Func_Move;
1309       if (mwm_hint->functions & MwmFuncIconify)
1310         functions |= Func_Iconify;
1311       if (mwm_hint->functions & MwmFuncMaximize)
1312         functions |= Func_Maximize;
1313       if (mwm_hint->functions & MwmFuncClose)
1314         functions |= Func_Close;
1315     }
1316   }
1317   delete [] mwm_hint;
1318 }
1319
1320
1321 /*
1322  * Gets the blackbox hints from the class' contained window.
1323  * This is used while initializing the window to its first state, and not
1324  * thereafter.
1325  * Returns: true if the hints are successfully retreived and applied; false if
1326  * they are not.
1327  */
1328 bool BlackboxWindow::getBlackboxHints(void) {
1329   unsigned long num;
1330   BlackboxHints *blackbox_hint;
1331
1332   num = PropBlackboxHintsElements;
1333   if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1334                         XAtom::blackbox_hints, num,
1335                         (unsigned long **)&blackbox_hint))
1336     return False;
1337   if (num < PropBlackboxHintsElements) {
1338     delete [] blackbox_hint;
1339     return False;
1340   }
1341
1342   if (blackbox_hint->flags & AttribShaded)
1343     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1344
1345   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1346       (blackbox_hint->flags & AttribMaxVert))
1347     flags.maximized = (blackbox_hint->attrib &
1348                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1349   else if (blackbox_hint->flags & AttribMaxVert)
1350     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1351   else if (blackbox_hint->flags & AttribMaxHoriz)
1352     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1353
1354   if (blackbox_hint->flags & AttribOmnipresent)
1355     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1356
1357   if (blackbox_hint->flags & AttribWorkspace)
1358     blackbox_attrib.workspace = blackbox_hint->workspace;
1359
1360   // if (blackbox_hint->flags & AttribStack)
1361   //   don't yet have always on top/bottom for blackbox yet... working
1362   //   on that
1363
1364   if (blackbox_hint->flags & AttribDecoration) {
1365     switch (blackbox_hint->decoration) {
1366     case DecorNone:
1367       decorations = 0;
1368       break;
1369
1370     case DecorTiny:
1371       decorations |= Decor_Titlebar | Decor_Iconify;
1372       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1373       functions &= ~(Func_Resize | Func_Maximize);
1374
1375       break;
1376
1377     case DecorTool:
1378       decorations |= Decor_Titlebar;
1379       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1380       functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1381
1382       break;
1383
1384     case DecorNormal:
1385     default:
1386       decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1387                      Decor_Iconify | Decor_Maximize;
1388       break;
1389     }
1390
1391     reconfigure();
1392   }
1393   
1394   delete [] blackbox_hint;
1395
1396   return True;
1397 }
1398
1399
1400 void BlackboxWindow::getTransientInfo(void) {
1401   if (client.transient_for &&
1402       client.transient_for != (BlackboxWindow *) ~0ul) {
1403     // reset transient_for in preparation of looking for a new owner
1404     client.transient_for->client.transientList.remove(this);
1405   }
1406
1407   // we have no transient_for until we find a new one
1408   client.transient_for = 0;
1409
1410   Window trans_for;
1411   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1412                              &trans_for)) {
1413     // transient_for hint not set
1414     return;
1415   }
1416
1417   if (trans_for == client.window) {
1418     // wierd client... treat this window as a normal window
1419     return;
1420   }
1421
1422   if (trans_for == None || trans_for == screen->getRootWindow()) {
1423     // this is an undocumented interpretation of the ICCCM. a transient
1424     // associated with None/Root/itself is assumed to be a modal root
1425     // transient.  we don't support the concept of a global transient,
1426     // so we just associate this transient with nothing, and perhaps
1427     // we will add support later for global modality.
1428     client.transient_for = (BlackboxWindow *) ~0ul;
1429     flags.modal = True;
1430     return;
1431   }
1432
1433   client.transient_for = blackbox->searchWindow(trans_for);
1434   if (! client.transient_for &&
1435       client.window_group && trans_for == client.window_group) {
1436     // no direct transient_for, perhaps this is a group transient?
1437     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1438     if (group) client.transient_for = group->find(screen);
1439   }
1440
1441   if (! client.transient_for || client.transient_for == this) {
1442     // no transient_for found, or we have a wierd client that wants to be
1443     // a transient for itself, so we treat this window as a normal window
1444     client.transient_for = (BlackboxWindow*) 0;
1445     return;
1446   }
1447
1448   // register ourselves with our new transient_for
1449   client.transient_for->client.transientList.push_back(this);
1450   flags.stuck = client.transient_for->flags.stuck;
1451 }
1452
1453
1454 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1455   if (client.transient_for &&
1456       client.transient_for != (BlackboxWindow*) ~0ul)
1457     return client.transient_for;
1458   return 0;
1459 }
1460
1461
1462 /*
1463  * This function is responsible for updating both the client and the frame
1464  * rectangles.
1465  * According to the ICCCM a client message is not sent for a resize, only a
1466  * move.
1467  */
1468 void BlackboxWindow::configure(int dx, int dy,
1469                                unsigned int dw, unsigned int dh) {
1470   bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1471                      ! flags.moving);
1472
1473   if (dw != frame.rect.width() || dh != frame.rect.height()) {
1474     frame.rect.setRect(dx, dy, dw, dh);
1475     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1476     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1477
1478     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1479       frame.rect.setPos(0, 0);
1480
1481     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1482                           frame.rect.top() + frame.margin.top,
1483                           frame.rect.right() - frame.margin.right,
1484                           frame.rect.bottom() - frame.margin.bottom);
1485
1486 #ifdef    SHAPE
1487     if (blackbox->hasShapeExtensions() && flags.shaped) {
1488       configureShape();
1489     }
1490 #endif // SHAPE
1491
1492     positionWindows();
1493     decorate();
1494     redrawWindowFrame();
1495   } else {
1496     frame.rect.setPos(dx, dy);
1497
1498     XMoveWindow(blackbox->getXDisplay(), frame.window,
1499                 frame.rect.x(), frame.rect.y());
1500     /*
1501       we may have been called just after an opaque window move, so even though
1502       the old coords match the new ones no ConfigureNotify has been sent yet.
1503       There are likely other times when this will be relevant as well.
1504     */
1505     if (! flags.moving) send_event = True;
1506   }
1507
1508   if (send_event) {
1509     // if moving, the update and event will occur when the move finishes
1510     client.rect.setPos(frame.rect.left() + frame.margin.left,
1511                        frame.rect.top() + frame.margin.top);
1512
1513     XEvent event;
1514     event.type = ConfigureNotify;
1515
1516     event.xconfigure.display = blackbox->getXDisplay();
1517     event.xconfigure.event = client.window;
1518     event.xconfigure.window = client.window;
1519     event.xconfigure.x = client.rect.x();
1520     event.xconfigure.y = client.rect.y();
1521     event.xconfigure.width = client.rect.width();
1522     event.xconfigure.height = client.rect.height();
1523     event.xconfigure.border_width = client.old_bw;
1524     event.xconfigure.above = frame.window;
1525     event.xconfigure.override_redirect = False;
1526
1527     XSendEvent(blackbox->getXDisplay(), client.window, False,
1528                StructureNotifyMask, &event);
1529     screen->updateNetizenConfigNotify(&event);
1530     XFlush(blackbox->getXDisplay());
1531   }
1532 }
1533
1534
1535 #ifdef SHAPE
1536 void BlackboxWindow::configureShape(void) {
1537   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1538                      frame.margin.left - frame.border_w,
1539                      frame.margin.top - frame.border_w,
1540                      client.window, ShapeBounding, ShapeSet);
1541
1542   int num = 0;
1543   XRectangle xrect[2];
1544
1545   if (decorations & Decor_Titlebar) {
1546     xrect[0].x = xrect[0].y = -frame.border_w;
1547     xrect[0].width = frame.rect.width();
1548     xrect[0].height = frame.title_h + (frame.border_w * 2);
1549     ++num;
1550   }
1551
1552   if (decorations & Decor_Handle) {
1553     xrect[1].x = -frame.border_w;
1554     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1555                  frame.mwm_border_w - frame.border_w;
1556     xrect[1].width = frame.rect.width();
1557     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1558     ++num;
1559   }
1560
1561   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1562                           ShapeBounding, 0, 0, xrect, num,
1563                           ShapeUnion, Unsorted);
1564 }
1565 #endif // SHAPE
1566
1567
1568 bool BlackboxWindow::setInputFocus(void) {
1569   if (flags.focused) return True;
1570
1571   assert(! flags.iconic &&
1572          (flags.stuck ||  // window must be on the current workspace or sticky
1573           blackbox_attrib.workspace == screen->getCurrentWorkspaceID()));
1574
1575   /*
1576      We only do this check for normal windows and dialogs because other windows
1577      do this on purpose, such as kde's kicker, and we don't want to go moving
1578      it.
1579   */
1580   if (window_type == Type_Normal || window_type == Type_Dialog)
1581     if (! frame.rect.intersects(screen->getRect())) {
1582       // client is outside the screen, move it to the center
1583       configure((screen->getWidth() - frame.rect.width()) / 2,
1584                 (screen->getHeight() - frame.rect.height()) / 2,
1585                 frame.rect.width(), frame.rect.height());
1586     }
1587
1588   if (client.transientList.size() > 0) {
1589     // transfer focus to any modal transients
1590     BlackboxWindowList::iterator it, end = client.transientList.end();
1591     for (it = client.transientList.begin(); it != end; ++it) {
1592       if ((*it)->flags.modal) return (*it)->setInputFocus();
1593     }
1594   }
1595
1596   bool ret = True;
1597   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1598     XSetInputFocus(blackbox->getXDisplay(), client.window,
1599                    RevertToPointerRoot, CurrentTime);
1600   } else {
1601     /* we could set the focus to none, since the window doesn't accept focus,
1602      * but we shouldn't set focus to nothing since this would surely make
1603      * someone angry
1604      */
1605     ret = False;
1606   }
1607
1608   if (flags.send_focus_message) {
1609     XEvent ce;
1610     ce.xclient.type = ClientMessage;
1611     ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1612     ce.xclient.display = blackbox->getXDisplay();
1613     ce.xclient.window = client.window;
1614     ce.xclient.format = 32;
1615     ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1616     ce.xclient.data.l[1] = blackbox->getLastTime();
1617     ce.xclient.data.l[2] = 0l;
1618     ce.xclient.data.l[3] = 0l;
1619     ce.xclient.data.l[4] = 0l;
1620     XSendEvent(blackbox->getXDisplay(), client.window, False,
1621                NoEventMask, &ce);
1622     XFlush(blackbox->getXDisplay());
1623   }
1624
1625   return ret;
1626 }
1627
1628
1629 void BlackboxWindow::iconify(void) {
1630   if (flags.iconic) return;
1631
1632   // We don't need to worry about resizing because resizing always grabs the X
1633   // server. This should only ever happen if using opaque moving.
1634   if (flags.moving)
1635     endMove();
1636     
1637   if (windowmenu) windowmenu->hide();
1638
1639   /*
1640    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1641    * we need to clear the event mask on client.window for a split second.
1642    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1643    * split second, leaving us with a ghost window... so, we need to do this
1644    * while the X server is grabbed
1645    */
1646   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1647                              StructureNotifyMask;
1648   XGrabServer(blackbox->getXDisplay());
1649   XSelectInput(blackbox->getXDisplay(), client.window,
1650                event_mask & ~StructureNotifyMask);
1651   XUnmapWindow(blackbox->getXDisplay(), client.window);
1652   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1653   XUngrabServer(blackbox->getXDisplay());
1654
1655   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1656   flags.visible = False;
1657   flags.iconic = True;
1658
1659   setState(IconicState);
1660
1661   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1662
1663   if (isTransient()) {
1664     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1665         ! client.transient_for->flags.iconic) {
1666       // iconify our transient_for
1667       client.transient_for->iconify();
1668     }
1669   }
1670
1671   screen->addIcon(this);
1672
1673   if (client.transientList.size() > 0) {
1674     // iconify all transients
1675     BlackboxWindowList::iterator it, end = client.transientList.end();
1676     for (it = client.transientList.begin(); it != end; ++it) {
1677       if (! (*it)->flags.iconic) (*it)->iconify();
1678     }
1679   }
1680   screen->updateStackingList();
1681 }
1682
1683
1684 void BlackboxWindow::show(void) {
1685   flags.visible = True;
1686   flags.iconic = False;
1687
1688   current_state = (flags.shaded) ? IconicState : NormalState;
1689   setState(current_state);
1690
1691   XMapWindow(blackbox->getXDisplay(), client.window);
1692   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1693   XMapWindow(blackbox->getXDisplay(), frame.window);
1694
1695 #if 0
1696   int real_x, real_y;
1697   Window child;
1698   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1699                         screen->getRootWindow(),
1700                         0, 0, &real_x, &real_y, &child);
1701   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1702           client.rect.left(), client.rect.top(), real_x, real_y);
1703   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1704 #endif
1705 }
1706
1707
1708 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1709   if (flags.iconic || reassoc)
1710     screen->reassociateWindow(this, BSENTINEL, False);
1711   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1712     return;
1713
1714   show();
1715
1716   // reassociate and deiconify all transients
1717   if (reassoc && client.transientList.size() > 0) {
1718     BlackboxWindowList::iterator it, end = client.transientList.end();
1719     for (it = client.transientList.begin(); it != end; ++it) {
1720       (*it)->deiconify(True, False);
1721     }
1722   }
1723
1724   if (raise)
1725     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1726 }
1727
1728
1729 void BlackboxWindow::close(void) {
1730   XEvent ce;
1731   ce.xclient.type = ClientMessage;
1732   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1733   ce.xclient.display = blackbox->getXDisplay();
1734   ce.xclient.window = client.window;
1735   ce.xclient.format = 32;
1736   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1737   ce.xclient.data.l[1] = CurrentTime;
1738   ce.xclient.data.l[2] = 0l;
1739   ce.xclient.data.l[3] = 0l;
1740   ce.xclient.data.l[4] = 0l;
1741   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1742   XFlush(blackbox->getXDisplay());
1743 }
1744
1745
1746 void BlackboxWindow::withdraw(void) {
1747   // We don't need to worry about resizing because resizing always grabs the X
1748   // server. This should only ever happen if using opaque moving.
1749   if (flags.moving)
1750     endMove();
1751     
1752   flags.visible = False;
1753   flags.iconic = False;
1754
1755   setState(current_state);
1756
1757   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1758
1759   XGrabServer(blackbox->getXDisplay());
1760
1761   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1762                              StructureNotifyMask;
1763   XSelectInput(blackbox->getXDisplay(), client.window,
1764                event_mask & ~StructureNotifyMask);
1765   XUnmapWindow(blackbox->getXDisplay(), client.window);
1766   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1767
1768   XUngrabServer(blackbox->getXDisplay());
1769
1770   if (windowmenu) windowmenu->hide();
1771 }
1772
1773
1774 void BlackboxWindow::maximize(unsigned int button) {
1775   // We don't need to worry about resizing because resizing always grabs the X
1776   // server. This should only ever happen if using opaque moving.
1777   if (flags.moving)
1778     endMove();
1779
1780   // handle case where menu is open then the max button is used instead
1781   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1782
1783   if (flags.maximized) {
1784     flags.maximized = 0;
1785
1786     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1787     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1788
1789     /*
1790       when a resize finishes, maximize(0) is called to clear any maximization
1791       flags currently set.  Otherwise it still thinks it is maximized.
1792       so we do not need to call configure() because resizing will handle it
1793     */
1794     if (! flags.resizing)
1795       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1796                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1797
1798     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1799     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1800
1801     redrawAllButtons(); // in case it is not called in configure()
1802     setState(current_state);
1803     return;
1804   }
1805
1806   blackbox_attrib.premax_x = frame.rect.x();
1807   blackbox_attrib.premax_y = frame.rect.y();
1808   blackbox_attrib.premax_w = frame.rect.width();
1809   // use client.rect so that clients can be restored even if shaded
1810   blackbox_attrib.premax_h =
1811     client.rect.height() + frame.margin.top + frame.margin.bottom;
1812
1813 #ifdef    XINERAMA
1814   if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1815     // find the area to use
1816     RectList availableAreas = screen->allAvailableAreas();
1817     RectList::iterator it, end = availableAreas.end();
1818
1819     for (it = availableAreas.begin(); it != end; ++it)
1820       if (it->intersects(frame.rect)) break;
1821     if (it == end) // the window isn't inside an area
1822       it = availableAreas.begin(); // so just default to the first one
1823
1824     frame.changing = *it;
1825   } else
1826 #endif // XINERAMA
1827   frame.changing = screen->availableArea();
1828
1829   switch(button) {
1830   case 1:
1831     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1832     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1833     break;
1834
1835   case 2:
1836     blackbox_attrib.flags |= AttribMaxVert;
1837     blackbox_attrib.attrib |= AttribMaxVert;
1838
1839     frame.changing.setX(frame.rect.x());
1840     frame.changing.setWidth(frame.rect.width());
1841     break;
1842
1843   case 3:
1844     blackbox_attrib.flags |= AttribMaxHoriz;
1845     blackbox_attrib.attrib |= AttribMaxHoriz;
1846
1847     frame.changing.setY(frame.rect.y());
1848     frame.changing.setHeight(frame.rect.height());
1849     break;
1850   }
1851
1852   constrain(TopLeft);
1853
1854   if (flags.shaded) {
1855     blackbox_attrib.flags ^= AttribShaded;
1856     blackbox_attrib.attrib ^= AttribShaded;
1857     flags.shaded = False;
1858   }
1859
1860   flags.maximized = button;
1861
1862   configure(frame.changing.x(), frame.changing.y(),
1863             frame.changing.width(), frame.changing.height());
1864   if (flags.focused)
1865     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1866   redrawAllButtons(); // in case it is not called in configure()
1867   setState(current_state);
1868 }
1869
1870
1871 // re-maximizes the window to take into account availableArea changes
1872 void BlackboxWindow::remaximize(void) {
1873   if (flags.shaded) {
1874     // we only update the window's attributes otherwise we lose the shade bit
1875     switch(flags.maximized) {
1876     case 1:
1877       blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1878       blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1879       break;
1880
1881     case 2:
1882       blackbox_attrib.flags |= AttribMaxVert;
1883       blackbox_attrib.attrib |= AttribMaxVert;
1884       break;
1885
1886     case 3:
1887       blackbox_attrib.flags |= AttribMaxHoriz;
1888       blackbox_attrib.attrib |= AttribMaxHoriz;
1889       break;
1890     }
1891     return;
1892   }
1893
1894   // save the original dimensions because maximize will wipe them out
1895   int premax_x = blackbox_attrib.premax_x,
1896     premax_y = blackbox_attrib.premax_y,
1897     premax_w = blackbox_attrib.premax_w,
1898     premax_h = blackbox_attrib.premax_h;
1899
1900   unsigned int button = flags.maximized;
1901   flags.maximized = 0; // trick maximize() into working
1902   maximize(button);
1903
1904   // restore saved values
1905   blackbox_attrib.premax_x = premax_x;
1906   blackbox_attrib.premax_y = premax_y;
1907   blackbox_attrib.premax_w = premax_w;
1908   blackbox_attrib.premax_h = premax_h;
1909 }
1910
1911
1912 void BlackboxWindow::setWorkspace(unsigned int n) {
1913   blackbox_attrib.flags |= AttribWorkspace;
1914   blackbox_attrib.workspace = n;
1915   if (n == BSENTINEL) { // iconified window
1916     /*
1917        we set the workspace to 'all workspaces' so that taskbars will show the
1918        window. otherwise, it made uniconifying a window imposible without the
1919        blackbox workspace menu
1920     */
1921     n = 0xffffffff;
1922   }
1923   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1924 }
1925
1926
1927 void BlackboxWindow::shade(void) {
1928   if (flags.shaded) {
1929     XResizeWindow(blackbox->getXDisplay(), frame.window,
1930                   frame.inside_w, frame.inside_h);
1931     flags.shaded = False;
1932     blackbox_attrib.flags ^= AttribShaded;
1933     blackbox_attrib.attrib ^= AttribShaded;
1934
1935     setState(NormalState);
1936
1937     // set the frame rect to the normal size
1938     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1939                          frame.margin.bottom);
1940   } else {
1941     if (! (decorations & Decor_Titlebar))
1942       return; // can't shade it without a titlebar!
1943
1944     XResizeWindow(blackbox->getXDisplay(), frame.window,
1945                   frame.inside_w, frame.title_h);
1946     flags.shaded = True;
1947     blackbox_attrib.flags |= AttribShaded;
1948     blackbox_attrib.attrib |= AttribShaded;
1949
1950     setState(IconicState);
1951
1952     // set the frame rect to the shaded size
1953     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1954   }
1955 }
1956
1957
1958 /*
1959  * (Un)Sticks a window and its relatives.
1960  */
1961 void BlackboxWindow::stick(void) {
1962   if (flags.stuck) {
1963     blackbox_attrib.flags ^= AttribOmnipresent;
1964     blackbox_attrib.attrib ^= AttribOmnipresent;
1965
1966     flags.stuck = False;
1967
1968     if (! flags.iconic)
1969       screen->reassociateWindow(this, BSENTINEL, True);
1970     // temporary fix since sticky windows suck. set the hint to what we
1971     // actually hold in our data.
1972     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1973                     blackbox_attrib.workspace);
1974
1975     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1976       if (i != blackbox_attrib.workspace)
1977         if (screen->getWorkspace(i)->getLastFocusedWindow() == this)
1978           screen->getWorkspace(i)->focusFallback(this);
1979
1980     setState(current_state);
1981   } else {
1982     flags.stuck = True;
1983
1984     blackbox_attrib.flags |= AttribOmnipresent;
1985     blackbox_attrib.attrib |= AttribOmnipresent;
1986
1987     // temporary fix since sticky windows suck. set the hint to a different
1988     // value than that contained in the class' data.
1989     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1990                     0xffffffff);
1991
1992     setState(current_state);
1993   }
1994   // go up the chain
1995   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1996       client.transient_for->isStuck() != flags.stuck)
1997     client.transient_for->stick();
1998   // go down the chain
1999   BlackboxWindowList::iterator it;
2000   const BlackboxWindowList::iterator end = client.transientList.end();
2001   for (it = client.transientList.begin(); it != end; ++it)
2002     if ((*it)->isStuck() != flags.stuck)
2003       (*it)->stick();
2004 }
2005
2006
2007 void BlackboxWindow::redrawWindowFrame(void) const {
2008   if (decorations & Decor_Titlebar) {
2009     if (flags.focused) {
2010       if (frame.ftitle)
2011         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2012                                    frame.title, frame.ftitle);
2013       else
2014         XSetWindowBackground(blackbox->getXDisplay(),
2015                              frame.title, frame.ftitle_pixel);
2016     } else {
2017       if (frame.utitle)
2018         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2019                                    frame.title, frame.utitle);
2020       else
2021         XSetWindowBackground(blackbox->getXDisplay(),
2022                              frame.title, frame.utitle_pixel);
2023     }
2024     XClearWindow(blackbox->getXDisplay(), frame.title);
2025
2026     redrawLabel();
2027     redrawAllButtons();
2028   }
2029
2030   if (decorations & Decor_Handle) {
2031     if (flags.focused) {
2032       if (frame.fhandle)
2033         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2034                                    frame.handle, frame.fhandle);
2035       else
2036         XSetWindowBackground(blackbox->getXDisplay(),
2037                              frame.handle, frame.fhandle_pixel);
2038
2039       if (frame.fgrip) {
2040         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2041                                    frame.left_grip, frame.fgrip);
2042         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2043                                    frame.right_grip, frame.fgrip);
2044       } else {
2045         XSetWindowBackground(blackbox->getXDisplay(),
2046                              frame.left_grip, frame.fgrip_pixel);
2047         XSetWindowBackground(blackbox->getXDisplay(),
2048                              frame.right_grip, frame.fgrip_pixel);
2049       }
2050     } else {
2051       if (frame.uhandle)
2052         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2053                                    frame.handle, frame.uhandle);
2054       else
2055         XSetWindowBackground(blackbox->getXDisplay(),
2056                              frame.handle, frame.uhandle_pixel);
2057
2058       if (frame.ugrip) {
2059         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2060                                    frame.left_grip, frame.ugrip);
2061         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2062                                    frame.right_grip, frame.ugrip);
2063       } else {
2064         XSetWindowBackground(blackbox->getXDisplay(),
2065                              frame.left_grip, frame.ugrip_pixel);
2066         XSetWindowBackground(blackbox->getXDisplay(),
2067                              frame.right_grip, frame.ugrip_pixel);
2068       }
2069     }
2070     XClearWindow(blackbox->getXDisplay(), frame.handle);
2071     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2072     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2073   }
2074
2075   if (decorations & Decor_Border) {
2076     if (flags.focused)
2077       XSetWindowBorder(blackbox->getXDisplay(),
2078                        frame.plate, frame.fborder_pixel);
2079     else
2080       XSetWindowBorder(blackbox->getXDisplay(),
2081                        frame.plate, frame.uborder_pixel);
2082   }
2083 }
2084
2085
2086 void BlackboxWindow::setFocusFlag(bool focus) {
2087   // only focus a window if it is visible
2088   if (focus && !flags.visible)
2089     return;
2090
2091   flags.focused = focus;
2092
2093   redrawWindowFrame();
2094
2095   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2096     if (isFocused()) timer->start();
2097     else timer->stop();
2098   }
2099
2100   if (flags.focused)
2101     blackbox->setFocusedWindow(this);
2102  
2103   if (! flags.iconic) {
2104     // iconic windows arent in a workspace menu!
2105     Clientmenu *menu =
2106       screen->getWorkspace(blackbox_attrib.workspace)->getMenu();
2107     menu->setItemSelected(window_number, isFocused());
2108   }
2109 }
2110
2111
2112 void BlackboxWindow::installColormap(bool install) {
2113   int i = 0, ncmap = 0;
2114   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2115                                             client.window, &ncmap);
2116   if (cmaps) {
2117     XWindowAttributes wattrib;
2118     if (XGetWindowAttributes(blackbox->getXDisplay(),
2119                              client.window, &wattrib)) {
2120       if (install) {
2121         // install the window's colormap
2122         for (i = 0; i < ncmap; i++) {
2123           if (*(cmaps + i) == wattrib.colormap)
2124             // this window is using an installed color map... do not install
2125             install = False;
2126         }
2127         // otherwise, install the window's colormap
2128         if (install)
2129           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2130       } else {
2131         // uninstall the window's colormap
2132         for (i = 0; i < ncmap; i++) {
2133           if (*(cmaps + i) == wattrib.colormap)
2134             // we found the colormap to uninstall
2135             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2136         }
2137       }
2138     }
2139
2140     XFree(cmaps);
2141   }
2142 }
2143
2144
2145 void BlackboxWindow::setAllowedActions(void) {
2146   Atom actions[7];
2147   int num = 0;
2148   
2149   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2150   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2151   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2152
2153   if (functions & Func_Move)
2154     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2155   if (functions & Func_Resize)
2156     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2157   if (functions & Func_Maximize) {
2158     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2159     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2160   }
2161
2162   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2163                   actions, num);
2164 }
2165
2166
2167 void BlackboxWindow::setState(unsigned long new_state) {
2168   current_state = new_state;
2169
2170   unsigned long state[2];
2171   state[0] = current_state;
2172   state[1] = None;
2173   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2174  
2175   xatom->setValue(client.window, XAtom::blackbox_attributes,
2176                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2177                   PropBlackboxAttributesElements);
2178
2179   Atom netstate[8];
2180   int num = 0;
2181   if (flags.modal)
2182     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2183   if (flags.shaded)
2184     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2185   if (flags.iconic)
2186     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2187   if (flags.skip_taskbar)
2188     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2189   if (flags.skip_pager)
2190     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2191   if (flags.fullscreen)
2192     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2193   if (flags.maximized == 1 || flags.maximized == 2)
2194     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2195   if (flags.maximized == 1 || flags.maximized == 3)
2196     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2197   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2198                   netstate, num);
2199 }
2200
2201
2202 bool BlackboxWindow::getState(void) {
2203   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2204                              current_state);
2205   if (! ret) current_state = 0;
2206   return ret;
2207 }
2208
2209
2210 void BlackboxWindow::restoreAttributes(void) {
2211   unsigned long num = PropBlackboxAttributesElements;
2212   BlackboxAttributes *net;
2213   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2214                         XAtom::blackbox_attributes, num,
2215                         (unsigned long **)&net))
2216     return;
2217   if (num < PropBlackboxAttributesElements) {
2218     delete [] net;
2219     return;
2220   }
2221
2222   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2223     flags.shaded = False;
2224     unsigned long orig_state = current_state;
2225     shade();
2226
2227     /*
2228       At this point in the life of a window, current_state should only be set
2229       to IconicState if the window was an *icon*, not if it was shaded.
2230     */
2231     if (orig_state != IconicState)
2232       current_state = WithdrawnState;
2233  }
2234
2235   if (net->workspace != screen->getCurrentWorkspaceID() &&
2236       net->workspace < screen->getWorkspaceCount())
2237     screen->reassociateWindow(this, net->workspace, True);
2238
2239   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2240       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2241     // set to WithdrawnState so it will be mapped on the new workspace
2242     if (current_state == NormalState) current_state = WithdrawnState;
2243   } else if (current_state == WithdrawnState) {
2244     // the window is on this workspace and is Withdrawn, so it is waiting to
2245     // be mapped
2246     current_state = NormalState;
2247   }
2248
2249   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2250     flags.stuck = False;
2251     stick();
2252
2253     // if the window was on another workspace, it was going to be hidden. this
2254     // specifies that the window should be mapped since it is sticky.
2255     if (current_state == WithdrawnState) current_state = NormalState;
2256   }
2257
2258   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2259     int x = net->premax_x, y = net->premax_y;
2260     unsigned int w = net->premax_w, h = net->premax_h;
2261     flags.maximized = 0;
2262
2263     unsigned int m = 0;
2264     if ((net->flags & AttribMaxHoriz) &&
2265         (net->flags & AttribMaxVert))
2266       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2267     else if (net->flags & AttribMaxVert)
2268       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2269     else if (net->flags & AttribMaxHoriz)
2270       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2271
2272     if (m) maximize(m);
2273
2274     blackbox_attrib.premax_x = x;
2275     blackbox_attrib.premax_y = y;
2276     blackbox_attrib.premax_w = w;
2277     blackbox_attrib.premax_h = h;
2278   }
2279
2280   if (net->flags & AttribDecoration) {
2281     switch (net->decoration) {
2282     case DecorNone:
2283       decorations = 0;
2284
2285       break;
2286
2287     default:
2288     case DecorNormal:
2289       decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2290         Decor_Iconify | Decor_Maximize;
2291
2292       break;
2293
2294     case DecorTiny:
2295       decorations |= Decor_Titlebar | Decor_Iconify;
2296       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2297
2298       break;
2299
2300     case DecorTool:
2301       decorations |= Decor_Titlebar;
2302       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2303
2304       break;
2305     }
2306
2307     // sanity check the new decor
2308     if (! (functions & Func_Resize) || isTransient())
2309       decorations &= ~(Decor_Maximize | Decor_Handle);
2310     if (! (functions & Func_Maximize))
2311       decorations &= ~Decor_Maximize;
2312
2313     if (decorations & Decor_Titlebar) {
2314       if (functions & Func_Close)   // close button is controlled by function
2315         decorations |= Decor_Close; // not decor type
2316     } else { 
2317       if (flags.shaded) // we can not be shaded if we lack a titlebar
2318         shade();
2319     }
2320
2321     if (flags.visible && frame.window) {
2322       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2323       XMapWindow(blackbox->getXDisplay(), frame.window);
2324     }
2325
2326     reconfigure();
2327     setState(current_state);
2328   }
2329
2330   // with the state set it will then be the map event's job to read the
2331   // window's state and behave accordingly
2332
2333   delete [] net;
2334 }
2335
2336
2337 /*
2338  * Positions the Rect r according the the client window position and
2339  * window gravity.
2340  */
2341 void BlackboxWindow::applyGravity(Rect &r) {
2342   // apply horizontal window gravity
2343   switch (client.win_gravity) {
2344   default:
2345   case NorthWestGravity:
2346   case SouthWestGravity:
2347   case WestGravity:
2348     r.setX(client.rect.x());
2349     break;
2350
2351   case NorthGravity:
2352   case SouthGravity:
2353   case CenterGravity:
2354     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2355     break;
2356
2357   case NorthEastGravity:
2358   case SouthEastGravity:
2359   case EastGravity:
2360     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2361     break;
2362
2363   case ForgetGravity:
2364   case StaticGravity:
2365     r.setX(client.rect.x() - frame.margin.left);
2366     break;
2367   }
2368
2369   // apply vertical window gravity
2370   switch (client.win_gravity) {
2371   default:
2372   case NorthWestGravity:
2373   case NorthEastGravity:
2374   case NorthGravity:
2375     r.setY(client.rect.y());
2376     break;
2377
2378   case CenterGravity:
2379   case EastGravity:
2380   case WestGravity:
2381     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2382     break;
2383
2384   case SouthWestGravity:
2385   case SouthEastGravity:
2386   case SouthGravity:
2387     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2388     break;
2389
2390   case ForgetGravity:
2391   case StaticGravity:
2392     r.setY(client.rect.y() - frame.margin.top);
2393     break;
2394   }
2395 }
2396
2397
2398 /*
2399  * The reverse of the applyGravity function.
2400  *
2401  * Positions the Rect r according to the frame window position and
2402  * window gravity.
2403  */
2404 void BlackboxWindow::restoreGravity(Rect &r) {
2405   // restore horizontal window gravity
2406   switch (client.win_gravity) {
2407   default:
2408   case NorthWestGravity:
2409   case SouthWestGravity:
2410   case WestGravity:
2411     r.setX(frame.rect.x());
2412     break;
2413
2414   case NorthGravity:
2415   case SouthGravity:
2416   case CenterGravity:
2417     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2418     break;
2419
2420   case NorthEastGravity:
2421   case SouthEastGravity:
2422   case EastGravity:
2423     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2424     break;
2425
2426   case ForgetGravity:
2427   case StaticGravity:
2428     r.setX(frame.rect.x() + frame.margin.left);
2429     break;
2430   }
2431
2432   // restore vertical window gravity
2433   switch (client.win_gravity) {
2434   default:
2435   case NorthWestGravity:
2436   case NorthEastGravity:
2437   case NorthGravity:
2438     r.setY(frame.rect.y());
2439     break;
2440
2441   case CenterGravity:
2442   case EastGravity:
2443   case WestGravity:
2444     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2445     break;
2446
2447   case SouthWestGravity:
2448   case SouthEastGravity:
2449   case SouthGravity:
2450     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2451     break;
2452
2453   case ForgetGravity:
2454   case StaticGravity:
2455     r.setY(frame.rect.y() + frame.margin.top);
2456     break;
2457   }
2458 }
2459
2460
2461 void BlackboxWindow::redrawLabel(void) const {
2462   if (flags.focused) {
2463     if (frame.flabel)
2464       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2465                                  frame.label, frame.flabel);
2466     else
2467       XSetWindowBackground(blackbox->getXDisplay(),
2468                            frame.label, frame.flabel_pixel);
2469   } else {
2470     if (frame.ulabel)
2471       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2472                                  frame.label, frame.ulabel);
2473     else
2474       XSetWindowBackground(blackbox->getXDisplay(),
2475                            frame.label, frame.ulabel_pixel);
2476   }
2477   XClearWindow(blackbox->getXDisplay(), frame.label);
2478
2479   WindowStyle *style = screen->getWindowStyle();
2480
2481   int pos = frame.bevel_w * 2;
2482   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2483   style->font->drawString(frame.label, pos, 1,
2484                           (flags.focused ? style->l_text_focus :
2485                            style->l_text_unfocus),
2486                           client.title);
2487 }
2488
2489
2490 void BlackboxWindow::redrawAllButtons(void) const {
2491   if (frame.iconify_button) redrawIconifyButton(False);
2492   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2493   if (frame.close_button) redrawCloseButton(False);
2494 }
2495
2496
2497 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2498   if (! pressed) {
2499     if (flags.focused) {
2500       if (frame.fbutton)
2501         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2502                                    frame.iconify_button, frame.fbutton);
2503       else
2504         XSetWindowBackground(blackbox->getXDisplay(),
2505                              frame.iconify_button, frame.fbutton_pixel);
2506     } else {
2507       if (frame.ubutton)
2508         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2509                                    frame.iconify_button, frame.ubutton);
2510       else
2511         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2512                              frame.ubutton_pixel);
2513     }
2514   } else {
2515     if (frame.pbutton)
2516       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2517                                  frame.iconify_button, frame.pbutton);
2518     else
2519       XSetWindowBackground(blackbox->getXDisplay(),
2520                            frame.iconify_button, frame.pbutton_pixel);
2521   }
2522   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2523
2524   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2525            screen->getWindowStyle()->b_pic_unfocus);
2526   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2527                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2528 }
2529
2530
2531 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2532   if (! pressed) {
2533     if (flags.focused) {
2534       if (frame.fbutton)
2535         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2536                                    frame.maximize_button, frame.fbutton);
2537       else
2538         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2539                              frame.fbutton_pixel);
2540     } else {
2541       if (frame.ubutton)
2542         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2543                                    frame.maximize_button, frame.ubutton);
2544       else
2545         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2546                              frame.ubutton_pixel);
2547     }
2548   } else {
2549     if (frame.pbutton)
2550       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2551                                  frame.maximize_button, frame.pbutton);
2552     else
2553       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2554                            frame.pbutton_pixel);
2555   }
2556   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2557
2558   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2559            screen->getWindowStyle()->b_pic_unfocus);
2560   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2561                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2562   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2563             2, 3, (frame.button_w - 3), 3);
2564 }
2565
2566
2567 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2568   if (! pressed) {
2569     if (flags.focused) {
2570       if (frame.fbutton)
2571         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2572                                    frame.fbutton);
2573       else
2574         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2575                              frame.fbutton_pixel);
2576     } else {
2577       if (frame.ubutton)
2578         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2579                                    frame.ubutton);
2580       else
2581         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2582                              frame.ubutton_pixel);
2583     }
2584   } else {
2585     if (frame.pbutton)
2586       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2587                                  frame.close_button, frame.pbutton);
2588     else
2589       XSetWindowBackground(blackbox->getXDisplay(),
2590                            frame.close_button, frame.pbutton_pixel);
2591   }
2592   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2593
2594   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2595            screen->getWindowStyle()->b_pic_unfocus);
2596   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2597             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2598   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2599             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2600 }
2601
2602
2603 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2604   if (re->window != client.window)
2605     return;
2606
2607 #ifdef    DEBUG
2608   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2609           client.window);
2610 #endif // DEBUG
2611
2612   switch (current_state) {
2613   case IconicState:
2614     iconify();
2615     break;
2616
2617   case WithdrawnState:
2618     withdraw();
2619     break;
2620
2621   case NormalState:
2622   case InactiveState:
2623   case ZoomState:
2624   default:
2625 #ifdef DEBUG
2626     fprintf(stderr, "0x%lx: just before show (%d, %d) w: %d, h: %d\n",
2627             client.window,
2628             frame.rect.x(), frame.rect.y(),
2629             frame.rect.width(), frame.rect.height());
2630 #endif // DEBUG
2631     show();
2632     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2633     if (isNormal()) {
2634       if (! blackbox->isStartup()) {
2635         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2636         if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2637                                     getTransientFor()->isFocused())) {
2638           setInputFocus();
2639         }
2640         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2641           int x, y, rx, ry;
2642           Window c, r;
2643           unsigned int m;
2644           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2645                         &r, &c, &rx, &ry, &x, &y, &m);
2646           beginMove(rx, ry);
2647         }
2648       }
2649     }
2650     break;
2651   }
2652 }
2653
2654
2655 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2656   if (ue->window != client.window)
2657     return;
2658
2659 #ifdef    DEBUG
2660   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2661           client.window);
2662 #endif // DEBUG
2663
2664   screen->unmanageWindow(this, False);
2665 }
2666
2667
2668 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2669   if (de->window != client.window)
2670     return;
2671
2672 #ifdef    DEBUG
2673   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2674           client.window);
2675 #endif // DEBUG
2676
2677   screen->unmanageWindow(this, False);
2678 }
2679
2680
2681 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2682   if (re->window != client.window || re->parent == frame.plate)
2683     return;
2684
2685 #ifdef    DEBUG
2686   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2687           "0x%lx.\n", client.window, re->parent);
2688 #endif // DEBUG
2689
2690   XEvent ev;
2691   ev.xreparent = *re;
2692   XPutBackEvent(blackbox->getXDisplay(), &ev);
2693   screen->unmanageWindow(this, True);
2694 }
2695
2696
2697 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2698   if (pe->state == PropertyDelete)
2699     return;
2700
2701 #ifdef    DEBUG
2702   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2703           client.window);
2704 #endif
2705
2706   switch(pe->atom) {
2707   case XA_WM_CLASS:
2708   case XA_WM_CLIENT_MACHINE:
2709   case XA_WM_COMMAND:
2710     break;
2711
2712   case XA_WM_TRANSIENT_FOR: {
2713     // determine if this is a transient window
2714     getTransientInfo();
2715
2716     // adjust the window decorations based on transience
2717     if (isTransient()) {
2718       decorations &= ~(Decor_Maximize | Decor_Handle);
2719       functions &= ~Func_Maximize;
2720       setAllowedActions();
2721     }
2722
2723     reconfigure();
2724 #ifdef DEBUG
2725     fprintf(stderr, "0x%lx: transient hint (%d, %d) w: %d, h: %d\n",
2726             client.window,
2727             frame.rect.x(), frame.rect.y(),
2728             frame.rect.width(), frame.rect.height());
2729 #endif
2730   }
2731     break;
2732
2733   case XA_WM_HINTS:
2734     getWMHints();
2735     break;
2736
2737   case XA_WM_ICON_NAME:
2738     getWMIconName();
2739     if (flags.iconic) screen->propagateWindowName(this);
2740     break;
2741
2742   case XAtom::net_wm_name:
2743   case XA_WM_NAME:
2744     getWMName();
2745
2746     if (decorations & Decor_Titlebar)
2747       redrawLabel();
2748
2749     screen->propagateWindowName(this);
2750     break;
2751
2752   case XA_WM_NORMAL_HINTS: {
2753     getWMNormalHints();
2754
2755     if ((client.normal_hint_flags & PMinSize) &&
2756         (client.normal_hint_flags & PMaxSize)) {
2757       // the window now can/can't resize itself, so the buttons need to be
2758       // regrabbed.
2759       ungrabButtons();
2760       if (client.max_width <= client.min_width &&
2761           client.max_height <= client.min_height) {
2762         decorations &= ~(Decor_Maximize | Decor_Handle);
2763         functions &= ~(Func_Resize | Func_Maximize);
2764       } else {
2765         if (! isTransient()) {
2766           decorations |= Decor_Maximize | Decor_Handle;
2767           functions |= Func_Maximize;
2768         }
2769         functions |= Func_Resize;
2770       }
2771       grabButtons();
2772       setAllowedActions();
2773     }
2774
2775     Rect old_rect = frame.rect;
2776
2777     upsize();
2778
2779     if (old_rect != frame.rect)
2780       reconfigure();
2781
2782 #ifdef DEBUG
2783     fprintf(stderr, "0x%lx: normal hint (%d, %d) w: %d, h: %d\n",
2784             client.window,
2785             frame.rect.x(), frame.rect.y(),
2786             frame.rect.width(), frame.rect.height());
2787 #endif // DEBUG
2788     break;
2789   }
2790
2791   default:
2792     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2793       getWMProtocols();
2794
2795       if ((decorations & Decor_Close) && (! frame.close_button)) {
2796         createCloseButton();
2797         if (decorations & Decor_Titlebar) {
2798           positionButtons(True);
2799           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2800         }
2801         if (windowmenu) windowmenu->reconfigure();
2802       }
2803     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2804       updateStrut();
2805     }
2806
2807     break;
2808   }
2809 }
2810
2811
2812 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2813 #ifdef DEBUG
2814   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2815 #endif
2816
2817   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2818     redrawLabel();
2819   else if (frame.close_button == ee->window)
2820     redrawCloseButton(False);
2821   else if (frame.maximize_button == ee->window)
2822     redrawMaximizeButton(flags.maximized);
2823   else if (frame.iconify_button == ee->window)
2824     redrawIconifyButton(False);
2825 }
2826
2827
2828 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2829   if (cr->window != client.window || flags.iconic)
2830     return;
2831
2832   if (cr->value_mask & CWBorderWidth)
2833     client.old_bw = cr->border_width;
2834
2835   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2836     Rect req = frame.rect;
2837
2838     if (cr->value_mask & (CWX | CWY)) {
2839       if (cr->value_mask & CWX)
2840         client.rect.setX(cr->x);
2841       if (cr->value_mask & CWY)
2842         client.rect.setY(cr->y);
2843
2844       applyGravity(req);
2845     }
2846
2847     if (cr->value_mask & CWWidth) {
2848       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2849 #ifdef DEBUG
2850       fprintf(stderr, "0x%lx: new width - %d\n", client.window, cr->width);
2851 #endif // DEBUG
2852     }
2853
2854     if (cr->value_mask & CWHeight) {
2855       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2856 #ifdef DEBUG
2857       fprintf(stderr, "0x%lx: new height - %d\n", client.window, cr->height);
2858 #endif // DEBUG
2859     }
2860
2861     configure(req.x(), req.y(), req.width(), req.height());
2862   }
2863
2864   if (cr->value_mask & CWStackMode && !isDesktop()) {
2865     switch (cr->detail) {
2866     case Below:
2867     case BottomIf:
2868       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2869       break;
2870
2871     case Above:
2872     case TopIf:
2873     default:
2874       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2875       break;
2876     }
2877   }
2878
2879 #ifdef DEBUG
2880   fprintf(stderr, "0x%lx: change request (%d, %d) w: %d, h: %d\n",
2881           client.window,
2882           frame.rect.x(), frame.rect.y(),
2883           frame.rect.width(), frame.rect.height());
2884 #endif // DEBUG
2885 }
2886
2887
2888 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2889 #ifdef DEBUG
2890   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2891           client.window);
2892 #endif
2893
2894   if (frame.maximize_button == be->window && be->button <= 3) {
2895     redrawMaximizeButton(True);
2896   } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2897     if (! flags.focused)
2898       setInputFocus();
2899
2900     if (frame.iconify_button == be->window) {
2901       redrawIconifyButton(True);
2902     } else if (frame.close_button == be->window) {
2903       redrawCloseButton(True);
2904     } else if (frame.plate == be->window) {
2905       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2906
2907       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2908
2909       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2910     } else {
2911       if (frame.title == be->window || frame.label == be->window) {
2912         if (((be->time - lastButtonPressTime) <=
2913              blackbox->getDoubleClickInterval()) ||
2914             (be->state == ControlMask)) {
2915           lastButtonPressTime = 0;
2916           shade();
2917         } else {
2918           lastButtonPressTime = be->time;
2919         }
2920       }
2921
2922       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2923
2924       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2925     }
2926   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2927              (be->window != frame.close_button)) {
2928     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2929   } else if (windowmenu && be->button == 3 &&
2930              (frame.title == be->window || frame.label == be->window ||
2931               frame.handle == be->window || frame.window == be->window)) {
2932     if (windowmenu->isVisible()) {
2933       windowmenu->hide();
2934     } else {
2935       int mx = be->x_root - windowmenu->getWidth() / 2,
2936           my = be->y_root - windowmenu->getHeight() / 2;
2937
2938       // snap the window menu into a corner/side if necessary
2939       int left_edge, right_edge, top_edge, bottom_edge;
2940
2941       /*
2942          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2943          and height of the menu, as the sizes returned by it do not include
2944          the borders.
2945        */
2946       left_edge = frame.rect.x();
2947       right_edge = frame.rect.right() -
2948         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2949       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2950       bottom_edge = client.rect.bottom() -
2951         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2952         (frame.border_w + frame.mwm_border_w);
2953
2954       if (mx < left_edge)
2955         mx = left_edge;
2956       if (mx > right_edge)
2957         mx = right_edge;
2958       if (my < top_edge)
2959         my = top_edge;
2960       if (my > bottom_edge)
2961         my = bottom_edge;
2962
2963       windowmenu->move(mx, my);
2964       windowmenu->show();
2965       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2966       XRaiseWindow(blackbox->getXDisplay(),
2967                    windowmenu->getSendToMenu()->getWindowID());
2968     }
2969   // mouse wheel up
2970   } else if (be->button == 4) {
2971     if ((be->window == frame.label ||
2972          be->window == frame.title ||
2973          be->window == frame.maximize_button ||
2974          be->window == frame.iconify_button ||
2975          be->window == frame.close_button) &&
2976         ! flags.shaded)
2977       shade();
2978   // mouse wheel down
2979   } else if (be->button == 5) {
2980     if ((be->window == frame.label ||
2981          be->window == frame.title ||
2982          be->window == frame.maximize_button ||
2983          be->window == frame.iconify_button ||
2984          be->window == frame.close_button) &&
2985         flags.shaded)
2986       shade();
2987   }
2988 }
2989
2990
2991 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2992 #ifdef DEBUG
2993   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2994           client.window);
2995 #endif
2996
2997   if (re->window == frame.maximize_button &&
2998       re->button >= 1 && re->button <= 3) {
2999     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3000         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3001       maximize(re->button);
3002     } else {
3003       redrawMaximizeButton(flags.maximized);
3004     }
3005   } else if (re->window == frame.iconify_button && re->button == 1) {
3006     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3007         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3008       iconify();
3009     } else {
3010       redrawIconifyButton(False);
3011     }
3012   } else if (re->window == frame.close_button & re->button == 1) {
3013     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3014         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3015       close();
3016     redrawCloseButton(False);
3017   } else if (flags.moving) {
3018     endMove();
3019   } else if (flags.resizing) {
3020     endResize();
3021   } else if (re->window == frame.window) {
3022     if (re->button == 2 && re->state == ModMask)
3023       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3024   }
3025 }
3026
3027
3028
3029 void BlackboxWindow::beginMove(int x_root, int y_root) {
3030   assert(! (flags.resizing || flags.moving));
3031
3032   /*
3033     Only one window can be moved/resized at a time. If another window is already
3034     being moved or resized, then stop it before whating to work with this one.
3035   */
3036   BlackboxWindow *changing = blackbox->getChangingWindow();
3037   if (changing && changing != this) {
3038     if (changing->flags.moving)
3039       changing->endMove();
3040     else // if (changing->flags.resizing)
3041       changing->endResize();
3042   }
3043   
3044   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3045                PointerMotionMask | ButtonReleaseMask,
3046                GrabModeAsync, GrabModeAsync,
3047                None, blackbox->getMoveCursor(), CurrentTime);
3048
3049   if (windowmenu && windowmenu->isVisible())
3050     windowmenu->hide();
3051
3052   flags.moving = True;
3053   blackbox->setChangingWindow(this);
3054
3055   if (! screen->doOpaqueMove()) {
3056     XGrabServer(blackbox->getXDisplay());
3057
3058     frame.changing = frame.rect;
3059     screen->showPosition(frame.changing.x(), frame.changing.y());
3060
3061     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3062                    screen->getOpGC(),
3063                    frame.changing.x(),
3064                    frame.changing.y(),
3065                    frame.changing.width() - 1,
3066                    frame.changing.height() - 1);
3067   }
3068
3069   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3070   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3071 }
3072
3073
3074 void BlackboxWindow::doMove(int x_root, int y_root) {
3075   assert(flags.moving);
3076   assert(blackbox->getChangingWindow() == this);
3077
3078   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3079   dx -= frame.border_w;
3080   dy -= frame.border_w;
3081
3082   if (screen->doWorkspaceWarping()) {
3083     // workspace warping
3084     bool warp = False;
3085     unsigned int dest = screen->getCurrentWorkspaceID();
3086     if (x_root <= 0) {
3087       warp = True;
3088
3089       if (dest > 0) dest--;
3090       else dest = screen->getNumberOfWorkspaces() - 1;
3091
3092     } else if (x_root >= screen->getRect().right()) {
3093       warp = True;
3094
3095       if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3096       else dest = 0;
3097     }
3098     if (warp) {
3099       endMove();
3100       bool focus = flags.focused; // had focus while moving?
3101       if (! flags.stuck)
3102         screen->reassociateWindow(this, dest, False);
3103       screen->changeWorkspaceID(dest);
3104       if (focus)
3105         setInputFocus();
3106
3107       /*
3108          If the XWarpPointer is done after the configure, we can end up
3109          grabbing another window, so made sure you do it first.
3110       */
3111       int dest_x;
3112       if (x_root <= 0) {
3113         dest_x = screen->getRect().right() - 1;
3114         XWarpPointer(blackbox->getXDisplay(), None, 
3115                      screen->getRootWindow(), 0, 0, 0, 0,
3116                      dest_x, y_root);
3117
3118         configure(dx + (screen->getRect().width() - 1), dy,
3119                   frame.rect.width(), frame.rect.height());
3120       } else {
3121         dest_x = 0;
3122         XWarpPointer(blackbox->getXDisplay(), None, 
3123                      screen->getRootWindow(), 0, 0, 0, 0,
3124                      dest_x, y_root);
3125
3126         configure(dx - (screen->getRect().width() - 1), dy,
3127                   frame.rect.width(), frame.rect.height());
3128       }
3129
3130       beginMove(dest_x, y_root);
3131       return;
3132     }
3133   }
3134
3135   const int snap_distance = screen->getEdgeSnapThreshold();
3136
3137   if (snap_distance) {
3138     // window corners
3139     const int wleft = dx,
3140               wright = dx + frame.rect.width() - 1,
3141               wtop = dy,
3142               wbottom = dy + frame.rect.height() - 1;
3143
3144     if (screen->getWindowToWindowSnap()) {
3145       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3146       assert(w);
3147
3148       // try snap to another window
3149       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
3150         BlackboxWindow *snapwin = w->getWindow(i);
3151         if (snapwin == this)
3152           continue;   // don't snap to self
3153
3154         bool snapped = False;
3155         
3156         const Rect &winrect = snapwin->frameRect();
3157         int dleft = abs(wright - winrect.left()),
3158            dright = abs(wleft - winrect.right()),
3159              dtop = abs(wbottom - winrect.top()),
3160           dbottom = abs(wtop - winrect.bottom());
3161
3162         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3163             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3164
3165           // snap left of other window?
3166           if (dleft < snap_distance && dleft <= dright) {
3167             dx = winrect.left() - frame.rect.width();
3168             snapped = True;
3169           }
3170           // snap right of other window?
3171           else if (dright < snap_distance) {
3172             dx = winrect.right() + 1;
3173             snapped = True;
3174           }
3175
3176           if (snapped) {
3177             if (screen->getWindowCornerSnap()) {
3178               // try corner-snap to its other sides
3179               dtop = abs(wtop - winrect.top());
3180               dbottom = abs(wbottom - winrect.bottom());
3181               if (dtop < snap_distance && dtop <= dbottom)
3182                 dy = winrect.top();
3183               else if (dbottom < snap_distance)
3184                 dy = winrect.bottom() - frame.rect.height() + 1;
3185             }
3186
3187             continue;
3188           }
3189         }
3190
3191         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3192             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3193
3194           // snap top of other window?
3195           if (dtop < snap_distance && dtop <= dbottom) {
3196             dy = winrect.top() - frame.rect.height();
3197             snapped = True;
3198           }
3199           // snap bottom of other window?
3200           else if (dbottom < snap_distance) {
3201             dy = winrect.bottom() + 1;
3202             snapped = True;
3203           }
3204
3205           if (snapped) {
3206             if (screen->getWindowCornerSnap()) {
3207               // try corner-snap to its other sides
3208               dleft = abs(wleft - winrect.left());
3209               dright = abs(wright - winrect.right());
3210               if (dleft < snap_distance && dleft <= dright)
3211                 dx = winrect.left();
3212               else if (dright < snap_distance)
3213                 dx = winrect.right() - frame.rect.width() + 1;
3214             }
3215
3216             continue;
3217           }
3218         }
3219       }
3220     }
3221
3222     RectList snaplist; // the list of rects we will try to snap to
3223
3224     // snap to the strut (and screen boundaries for xinerama)
3225 #ifdef    XINERAMA
3226     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3227       if (! screen->doFullMax())
3228         snaplist.insert(snaplist.begin(),
3229                         screen->allAvailableAreas().begin(),
3230                         screen->allAvailableAreas().end());
3231
3232       // always snap to the screen edges
3233       snaplist.insert(snaplist.begin(),
3234                       screen->getXineramaAreas().begin(),
3235                       screen->getXineramaAreas().end());
3236     } else
3237 #endif // XINERAMA
3238     {
3239       if (! screen->doFullMax())
3240         snaplist.push_back(screen->availableArea());
3241
3242       // always snap to the screen edges
3243       snaplist.push_back(screen->getRect());
3244     }
3245
3246     RectList::const_iterator it, end = snaplist.end();
3247     for (it = snaplist.begin(); it != end; ++it) {
3248       const Rect &srect = *it;
3249
3250       // if we're not in the rectangle then don't snap to it.
3251       if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3252                                   frame.rect.height())))
3253         continue;
3254
3255       int dleft = abs(wleft - srect.left()),
3256          dright = abs(wright - srect.right()),
3257            dtop = abs(wtop - srect.top()),
3258         dbottom = abs(wbottom - srect.bottom());
3259
3260         // snap left?
3261         if (dleft < snap_distance && dleft <= dright)
3262           dx = srect.left();
3263         // snap right?
3264         else if (dright < snap_distance)
3265           dx = srect.right() - frame.rect.width() + 1;
3266
3267         // snap top?
3268         if (dtop < snap_distance && dtop <= dbottom)
3269           dy = srect.top();
3270         // snap bottom?
3271         else if (dbottom < snap_distance)
3272           dy = srect.bottom() - frame.rect.height() + 1;
3273     }
3274   }
3275
3276   if (screen->doOpaqueMove()) {
3277     configure(dx, dy, frame.rect.width(), frame.rect.height());
3278   } else {
3279     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3280                    screen->getOpGC(),
3281                    frame.changing.x(),
3282                    frame.changing.y(),
3283                    frame.changing.width() - 1,
3284                    frame.changing.height() - 1);
3285
3286     frame.changing.setPos(dx, dy);
3287
3288     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3289                    screen->getOpGC(),
3290                    frame.changing.x(),
3291                    frame.changing.y(),
3292                    frame.changing.width() - 1,
3293                    frame.changing.height() - 1);
3294   }
3295
3296   screen->showPosition(dx, dy);
3297 }
3298
3299
3300 void BlackboxWindow::endMove(void) {
3301   assert(flags.moving);
3302   assert(blackbox->getChangingWindow() == this);
3303
3304   flags.moving = False;
3305   blackbox->setChangingWindow(0);
3306
3307   if (! screen->doOpaqueMove()) {
3308     /* when drawing the rubber band, we need to make sure we only draw inside
3309      * the frame... frame.changing_* contain the new coords for the window,
3310      * so we need to subtract 1 from changing_w/changing_h every where we
3311      * draw the rubber band (for both moving and resizing)
3312      */
3313     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3314                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3315                    frame.changing.width() - 1, frame.changing.height() - 1);
3316       XUngrabServer(blackbox->getXDisplay());
3317   
3318       configure(frame.changing.x(), frame.changing.y(),
3319                 frame.changing.width(), frame.changing.height());
3320   } else {
3321     configure(frame.rect.x(), frame.rect.y(),
3322               frame.rect.width(), frame.rect.height());
3323   }
3324   screen->hideGeometry();
3325
3326   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3327
3328   // if there are any left over motions from the move, drop them now
3329   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3330   XEvent e;
3331   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3332                                 MotionNotify, &e));
3333 }
3334
3335
3336 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3337   assert(! (flags.resizing || flags.moving));
3338
3339   /*
3340     Only one window can be moved/resized at a time. If another window is already
3341     being moved or resized, then stop it before whating to work with this one.
3342   */
3343   BlackboxWindow *changing = blackbox->getChangingWindow();
3344   if (changing && changing != this) {
3345     if (changing->flags.moving)
3346       changing->endMove();
3347     else // if (changing->flags.resizing)
3348       changing->endResize();
3349   }
3350
3351   resize_dir = dir;
3352
3353   Cursor cursor;
3354   Corner anchor;
3355   
3356   switch (resize_dir) {
3357   case BottomLeft:
3358     anchor = TopRight;
3359     cursor = blackbox->getLowerLeftAngleCursor();
3360     break;
3361
3362   case BottomRight:
3363     anchor = TopLeft;
3364     cursor = blackbox->getLowerRightAngleCursor();
3365     break;
3366
3367   case TopLeft:
3368     anchor = BottomRight;
3369     cursor = blackbox->getUpperLeftAngleCursor();
3370     break;
3371
3372   case TopRight:
3373     anchor = BottomLeft;
3374     cursor = blackbox->getUpperRightAngleCursor();
3375     break;
3376
3377   default:
3378     assert(false); // unhandled Corner
3379     return;        // unreachable, for the compiler
3380   }
3381   
3382   XGrabServer(blackbox->getXDisplay());
3383   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3384                PointerMotionMask | ButtonReleaseMask,
3385                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3386
3387   flags.resizing = True;
3388   blackbox->setChangingWindow(this);
3389
3390   int gw, gh;
3391   frame.changing = frame.rect;
3392
3393   constrain(anchor,  &gw, &gh);
3394
3395   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3396                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3397                  frame.changing.width() - 1, frame.changing.height() - 1);
3398
3399   screen->showGeometry(gw, gh);
3400   
3401   frame.grab_x = x_root;
3402   frame.grab_y = y_root;
3403 }
3404
3405
3406 void BlackboxWindow::doResize(int x_root, int y_root) {
3407   assert(flags.resizing);
3408   assert(blackbox->getChangingWindow() == this);
3409
3410   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3411                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3412                  frame.changing.width() - 1, frame.changing.height() - 1);
3413
3414   int gw, gh;
3415   Corner anchor;
3416
3417   switch (resize_dir) {
3418   case BottomLeft:
3419     anchor = TopRight;
3420     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3421                            frame.rect.height() + (y_root - frame.grab_y));
3422     break;
3423   case BottomRight:
3424     anchor = TopLeft;
3425     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3426                            frame.rect.height() + (y_root - frame.grab_y));
3427     break;
3428   case TopLeft:
3429     anchor = BottomRight;
3430     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3431                            frame.rect.height() - (y_root - frame.grab_y));
3432     break;
3433   case TopRight:
3434     anchor = BottomLeft;
3435     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3436                            frame.rect.height() - (y_root - frame.grab_y));
3437     break;
3438
3439   default:
3440     assert(false); // unhandled Corner
3441     return;        // unreachable, for the compiler
3442   }
3443   
3444   constrain(anchor, &gw, &gh);
3445
3446   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3447                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3448                  frame.changing.width() - 1, frame.changing.height() - 1);
3449
3450   screen->showGeometry(gw, gh);
3451 }
3452
3453
3454 void BlackboxWindow::endResize(void) {
3455   assert(flags.resizing);
3456   assert(blackbox->getChangingWindow() == this);
3457
3458   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3459                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3460                  frame.changing.width() - 1, frame.changing.height() - 1);
3461   XUngrabServer(blackbox->getXDisplay());
3462
3463   // unset maximized state after resized when fully maximized
3464   if (flags.maximized == 1)
3465     maximize(0);
3466   
3467   flags.resizing = False;
3468   blackbox->setChangingWindow(0);
3469
3470   configure(frame.changing.x(), frame.changing.y(),
3471             frame.changing.width(), frame.changing.height());
3472   screen->hideGeometry();
3473
3474   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3475   
3476   // if there are any left over motions from the resize, drop them now
3477   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3478   XEvent e;
3479   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3480                                 MotionNotify, &e));
3481 }
3482
3483
3484 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3485 #ifdef DEBUG
3486   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3487           client.window);
3488 #endif
3489
3490   if (flags.moving) {
3491     doMove(me->x_root, me->y_root);
3492   } else if (flags.resizing) {
3493     doResize(me->x_root, me->y_root);
3494   } else {
3495     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3496         (frame.title == me->window || frame.label == me->window ||
3497          frame.handle == me->window || frame.window == me->window)) {
3498       beginMove(me->x_root, me->y_root);
3499     } else if ((functions & Func_Resize) &&
3500                (me->state & Button1Mask && (me->window == frame.right_grip ||
3501                                             me->window == frame.left_grip)) ||
3502                (me->state & Button3Mask && me->state & ModMask &&
3503                 me->window == frame.window)) {
3504       unsigned int zones = screen->getResizeZones();
3505       Corner corner;
3506       
3507       if (me->window == frame.left_grip) {
3508         corner = BottomLeft;
3509       } else if (me->window == frame.right_grip || zones == 1) {
3510         corner = BottomRight;
3511       } else {
3512         bool top;
3513         bool left = (me->x_root - frame.rect.x() <=
3514                      static_cast<signed>(frame.rect.width() / 2));
3515         if (zones == 2)
3516           top = False;
3517         else // (zones == 4)
3518           top = (me->y_root - frame.rect.y() <=
3519                  static_cast<signed>(frame.rect.height() / 2));
3520         corner = (top ? (left ? TopLeft : TopRight) :
3521                         (left ? BottomLeft : BottomRight));
3522       }
3523
3524       beginResize(me->x_root, me->y_root, corner);
3525     }
3526   }
3527 }
3528
3529
3530 #ifdef    SHAPE
3531 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3532   if (blackbox->hasShapeExtensions() && flags.shaped) {
3533     configureShape();
3534   }
3535 }
3536 #endif // SHAPE
3537
3538
3539 bool BlackboxWindow::validateClient(void) const {
3540   XSync(blackbox->getXDisplay(), False);
3541
3542   XEvent e;
3543   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3544                              DestroyNotify, &e) ||
3545       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3546                              UnmapNotify, &e)) {
3547     XPutBackEvent(blackbox->getXDisplay(), &e);
3548
3549     return False;
3550   }
3551
3552   return True;
3553 }
3554
3555
3556 void BlackboxWindow::restore(bool remap) {
3557   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3558   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3559   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3560
3561   // do not leave a shaded window as an icon unless it was an icon
3562   if (flags.shaded && ! flags.iconic) setState(NormalState);
3563
3564   restoreGravity(client.rect);
3565
3566   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3567   XUnmapWindow(blackbox->getXDisplay(), client.window);
3568
3569   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3570
3571   XEvent ev;
3572   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3573                              ReparentNotify, &ev)) {
3574     remap = True;
3575   } else {
3576     // according to the ICCCM - if the client doesn't reparent to
3577     // root, then we have to do it for them
3578     XReparentWindow(blackbox->getXDisplay(), client.window,
3579                     screen->getRootWindow(),
3580                     client.rect.x(), client.rect.y());
3581   }
3582
3583   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3584 }
3585
3586
3587 // timer for autoraise
3588 void BlackboxWindow::timeout(void) {
3589   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3590 }
3591
3592
3593 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3594   if ((net->flags & AttribShaded) &&
3595       ((blackbox_attrib.attrib & AttribShaded) !=
3596        (net->attrib & AttribShaded)))
3597     shade();
3598
3599   if (flags.visible && // watch out for requests when we can not be seen
3600       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3601       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3602        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3603     if (flags.maximized) {
3604       maximize(0);
3605     } else {
3606       int button = 0;
3607
3608       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3609         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3610       else if (net->flags & AttribMaxVert)
3611         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3612       else if (net->flags & AttribMaxHoriz)
3613         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3614
3615       maximize(button);
3616     }
3617   }
3618
3619   if ((net->flags & AttribOmnipresent) &&
3620       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3621        (net->attrib & AttribOmnipresent)))
3622     stick();
3623
3624   if ((net->flags & AttribWorkspace) &&
3625       (blackbox_attrib.workspace != net->workspace)) {
3626     screen->reassociateWindow(this, net->workspace, True);
3627
3628     if (screen->getCurrentWorkspaceID() != net->workspace) {
3629       withdraw();
3630     } else {
3631       show();
3632       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3633     }
3634   }
3635
3636   if (net->flags & AttribDecoration) {
3637     switch (net->decoration) {
3638     case DecorNone:
3639       decorations = 0;
3640
3641       break;
3642
3643     default:
3644     case DecorNormal:
3645       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3646   
3647       decorations = ((functions & Func_Resize) && !isTransient() ?
3648                      decorations | Decor_Handle :
3649                      decorations &= ~Decor_Handle);
3650       decorations = (functions & Func_Maximize ?
3651                      decorations | Decor_Maximize :
3652                      decorations &= ~Decor_Maximize);
3653
3654       break;
3655
3656     case DecorTiny:
3657       decorations |= Decor_Titlebar | Decor_Iconify;
3658       decorations &= ~(Decor_Border | Decor_Handle);
3659       
3660       decorations = (functions & Func_Maximize ?
3661                      decorations | Decor_Maximize :
3662                      decorations &= ~Decor_Maximize);
3663
3664       break;
3665
3666     case DecorTool:
3667       decorations |= Decor_Titlebar;
3668       decorations &= ~(Decor_Iconify | Decor_Border);
3669
3670       decorations = ((functions & Func_Resize) && !isTransient() ?
3671                      decorations | Decor_Handle :
3672                      decorations &= ~Decor_Handle);
3673       decorations = (functions & Func_Maximize ?
3674                      decorations | Decor_Maximize :
3675                      decorations &= ~Decor_Maximize);
3676
3677       break;
3678     }
3679
3680     // we can not be shaded if we lack a titlebar
3681     if (flags.shaded && ! (decorations & Decor_Titlebar))
3682       shade();
3683
3684     if (flags.visible && frame.window) {
3685       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3686       XMapWindow(blackbox->getXDisplay(), frame.window);
3687     }
3688
3689     reconfigure();
3690     setState(current_state);
3691   }
3692 }
3693
3694
3695 /*
3696  * Set the sizes of all components of the window frame
3697  * (the window decorations).
3698  * These values are based upon the current style settings and the client
3699  * window's dimensions.
3700  */
3701 void BlackboxWindow::upsize(void) {
3702   frame.bevel_w = screen->getBevelWidth();
3703
3704   if (decorations & Decor_Border) {
3705     frame.border_w = screen->getBorderWidth();
3706     if (! isTransient())
3707       frame.mwm_border_w = screen->getFrameWidth();
3708     else
3709       frame.mwm_border_w = 0;
3710   } else {
3711     frame.mwm_border_w = frame.border_w = 0;
3712   }
3713
3714   if (decorations & Decor_Titlebar) {
3715     // the height of the titlebar is based upon the height of the font being
3716     // used to display the window's title
3717     WindowStyle *style = screen->getWindowStyle();
3718     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3719
3720     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3721     frame.button_w = (frame.label_h - 2);
3722
3723     // set the top frame margin
3724     frame.margin.top = frame.border_w + frame.title_h +
3725                        frame.border_w + frame.mwm_border_w;
3726   } else {
3727     frame.title_h = 0;
3728     frame.label_h = 0;
3729     frame.button_w = 0;
3730
3731     // set the top frame margin
3732     frame.margin.top = frame.border_w + frame.mwm_border_w;
3733   }
3734
3735   // set the left/right frame margin
3736   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3737
3738   if (decorations & Decor_Handle) {
3739     frame.grip_w = frame.button_w * 2;
3740     frame.handle_h = screen->getHandleWidth();
3741
3742     // set the bottom frame margin
3743     frame.margin.bottom = frame.border_w + frame.handle_h +
3744                           frame.border_w + frame.mwm_border_w;
3745   } else {
3746     frame.handle_h = 0;
3747     frame.grip_w = 0;
3748
3749     // set the bottom frame margin
3750     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3751   }
3752
3753   /*
3754     We first get the normal dimensions and use this to define the inside_w/h
3755     then we modify the height if shading is in effect.
3756     If the shade state is not considered then frame.rect gets reset to the
3757     normal window size on a reconfigure() call resulting in improper
3758     dimensions appearing in move/resize and other events.
3759   */
3760   unsigned int
3761     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3762     width = client.rect.width() + frame.margin.left + frame.margin.right;
3763
3764   frame.inside_w = width - (frame.border_w * 2);
3765   frame.inside_h = height - (frame.border_w * 2);
3766
3767   if (flags.shaded)
3768     height = frame.title_h + (frame.border_w * 2);
3769   frame.rect.setSize(width, height);
3770 }
3771
3772
3773 /*
3774  * Calculate the size of the client window and constrain it to the
3775  * size specified by the size hints of the client window.
3776  *
3777  * The logical width and height are placed into pw and ph, if they
3778  * are non-zero.  Logical size refers to the users perception of
3779  * the window size (for example an xterm resizes in cells, not in pixels).
3780  *
3781  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3782  * Physical geometry refers to the geometry of the window in pixels.
3783  */
3784 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3785   // frame.changing represents the requested frame size, we need to
3786   // strip the frame margin off and constrain the client size
3787   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3788                            frame.changing.top() + frame.margin.top,
3789                            frame.changing.right() - frame.margin.right,
3790                            frame.changing.bottom() - frame.margin.bottom);
3791
3792   int dw = frame.changing.width(), dh = frame.changing.height(),
3793     base_width = (client.base_width) ? client.base_width : client.min_width,
3794     base_height = (client.base_height) ? client.base_height :
3795                                          client.min_height;
3796
3797   // constrain
3798   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3799   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3800   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3801   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3802
3803   dw -= base_width;
3804   dw /= client.width_inc;
3805   dh -= base_height;
3806   dh /= client.height_inc;
3807
3808   if (pw) {
3809     if (client.width_inc == 1)
3810       *pw = dw + base_width;
3811     else
3812       *pw = dw;
3813   }
3814   if (ph) {
3815     if (client.height_inc == 1)
3816       *ph = dh + base_height;
3817     else
3818       *ph = dh;
3819   }
3820
3821   dw *= client.width_inc;
3822   dw += base_width;
3823   dh *= client.height_inc;
3824   dh += base_height;
3825
3826   frame.changing.setSize(dw, dh);
3827
3828   // add the frame margin back onto frame.changing
3829   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3830                            frame.changing.top() - frame.margin.top,
3831                            frame.changing.right() + frame.margin.right,
3832                            frame.changing.bottom() + frame.margin.bottom);
3833
3834   // move frame.changing to the specified anchor
3835   int dx = 0,
3836       dy = 0;
3837   switch (anchor) {
3838   case TopLeft:
3839     break;
3840
3841   case TopRight:
3842     dx = frame.rect.right() - frame.changing.right();
3843     break;
3844
3845   case BottomLeft:
3846     dy = frame.rect.bottom() - frame.changing.bottom();
3847     break;
3848
3849   case BottomRight:
3850     dx = frame.rect.right() - frame.changing.right();
3851     dy = frame.rect.bottom() - frame.changing.bottom();
3852     break;
3853
3854   default:
3855     assert(false);  // unhandled corner
3856   }
3857   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3858 }
3859
3860
3861 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3862                             unsigned int max_length,
3863                             unsigned int modifier) const {
3864   size_t text_len = text.size();
3865   unsigned int length;
3866
3867   do {
3868     length = font->measureString(string(text, 0, text_len)) + modifier;
3869   } while (length > max_length && text_len-- > 0);
3870
3871   switch (justify) {
3872   case RightJustify:
3873     start_pos += max_length - length;
3874     break;
3875
3876   case CenterJustify:
3877     start_pos += (max_length - length) / 2;
3878     break;
3879
3880   case LeftJustify:
3881   default:
3882     break;
3883   }
3884 }
3885
3886
3887 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3888   : blackbox(b), group(_group) {
3889   XWindowAttributes wattrib;
3890   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3891     // group window doesn't seem to exist anymore
3892     delete this;
3893     return;
3894   }
3895
3896   XSelectInput(blackbox->getXDisplay(), group,
3897                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3898
3899   blackbox->saveGroupSearch(group, this);
3900 }
3901
3902
3903 BWindowGroup::~BWindowGroup(void) {
3904   blackbox->removeGroupSearch(group);
3905 }
3906
3907
3908 BlackboxWindow *
3909 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3910   BlackboxWindow *ret = blackbox->getFocusedWindow();
3911
3912   // does the focus window match (or any transient_fors)?
3913   while (ret) {
3914     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3915       if (ret->isTransient() && allow_transients) break;
3916       else if (! ret->isTransient()) break;
3917     }
3918
3919     ret = ret->getTransientFor();
3920   }
3921
3922   if (ret) return ret;
3923
3924   // the focus window didn't match, look in the group's window list
3925   BlackboxWindowList::const_iterator it, end = windowList.end();
3926   for (it = windowList.begin(); it != end; ++it) {
3927     ret = *it;
3928     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3929       if (ret->isTransient() && allow_transients) break;
3930       else if (! ret->isTransient()) break;
3931     }
3932   }
3933
3934   return ret;
3935 }