]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
let normal windows be netizens again
[dana/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     setState(current_state);
1976   } else {
1977     flags.stuck = True;
1978
1979     blackbox_attrib.flags |= AttribOmnipresent;
1980     blackbox_attrib.attrib |= AttribOmnipresent;
1981
1982     // temporary fix since sticky windows suck. set the hint to a different
1983     // value than that contained in the class' data.
1984     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1985                     0xffffffff);
1986
1987     setState(current_state);
1988   }
1989   // go up the chain
1990   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1991       client.transient_for->isStuck() != flags.stuck)
1992     client.transient_for->stick();
1993   // go down the chain
1994   BlackboxWindowList::iterator it;
1995   const BlackboxWindowList::iterator end = client.transientList.end();
1996   for (it = client.transientList.begin(); it != end; ++it)
1997     if ((*it)->isStuck() != flags.stuck)
1998       (*it)->stick();
1999 }
2000
2001
2002 void BlackboxWindow::redrawWindowFrame(void) const {
2003   if (decorations & Decor_Titlebar) {
2004     if (flags.focused) {
2005       if (frame.ftitle)
2006         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2007                                    frame.title, frame.ftitle);
2008       else
2009         XSetWindowBackground(blackbox->getXDisplay(),
2010                              frame.title, frame.ftitle_pixel);
2011     } else {
2012       if (frame.utitle)
2013         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2014                                    frame.title, frame.utitle);
2015       else
2016         XSetWindowBackground(blackbox->getXDisplay(),
2017                              frame.title, frame.utitle_pixel);
2018     }
2019     XClearWindow(blackbox->getXDisplay(), frame.title);
2020
2021     redrawLabel();
2022     redrawAllButtons();
2023   }
2024
2025   if (decorations & Decor_Handle) {
2026     if (flags.focused) {
2027       if (frame.fhandle)
2028         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2029                                    frame.handle, frame.fhandle);
2030       else
2031         XSetWindowBackground(blackbox->getXDisplay(),
2032                              frame.handle, frame.fhandle_pixel);
2033
2034       if (frame.fgrip) {
2035         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2036                                    frame.left_grip, frame.fgrip);
2037         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2038                                    frame.right_grip, frame.fgrip);
2039       } else {
2040         XSetWindowBackground(blackbox->getXDisplay(),
2041                              frame.left_grip, frame.fgrip_pixel);
2042         XSetWindowBackground(blackbox->getXDisplay(),
2043                              frame.right_grip, frame.fgrip_pixel);
2044       }
2045     } else {
2046       if (frame.uhandle)
2047         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2048                                    frame.handle, frame.uhandle);
2049       else
2050         XSetWindowBackground(blackbox->getXDisplay(),
2051                              frame.handle, frame.uhandle_pixel);
2052
2053       if (frame.ugrip) {
2054         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2055                                    frame.left_grip, frame.ugrip);
2056         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2057                                    frame.right_grip, frame.ugrip);
2058       } else {
2059         XSetWindowBackground(blackbox->getXDisplay(),
2060                              frame.left_grip, frame.ugrip_pixel);
2061         XSetWindowBackground(blackbox->getXDisplay(),
2062                              frame.right_grip, frame.ugrip_pixel);
2063       }
2064     }
2065     XClearWindow(blackbox->getXDisplay(), frame.handle);
2066     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2067     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2068   }
2069
2070   if (decorations & Decor_Border) {
2071     if (flags.focused)
2072       XSetWindowBorder(blackbox->getXDisplay(),
2073                        frame.plate, frame.fborder_pixel);
2074     else
2075       XSetWindowBorder(blackbox->getXDisplay(),
2076                        frame.plate, frame.uborder_pixel);
2077   }
2078 }
2079
2080
2081 void BlackboxWindow::setFocusFlag(bool focus) {
2082   // only focus a window if it is visible
2083   if (focus && !flags.visible)
2084     return;
2085
2086   flags.focused = focus;
2087
2088   redrawWindowFrame();
2089
2090   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2091     if (isFocused()) timer->start();
2092     else timer->stop();
2093   }
2094
2095   if (flags.focused)
2096     blackbox->setFocusedWindow(this);
2097  
2098   if (! flags.iconic) {
2099     // iconic windows arent in a workspace menu!
2100     Clientmenu *menu =
2101       screen->getWorkspace(blackbox_attrib.workspace)->getMenu();
2102     menu->setItemSelected(window_number, isFocused());
2103   }
2104 }
2105
2106
2107 void BlackboxWindow::installColormap(bool install) {
2108   int i = 0, ncmap = 0;
2109   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2110                                             client.window, &ncmap);
2111   if (cmaps) {
2112     XWindowAttributes wattrib;
2113     if (XGetWindowAttributes(blackbox->getXDisplay(),
2114                              client.window, &wattrib)) {
2115       if (install) {
2116         // install the window's colormap
2117         for (i = 0; i < ncmap; i++) {
2118           if (*(cmaps + i) == wattrib.colormap)
2119             // this window is using an installed color map... do not install
2120             install = False;
2121         }
2122         // otherwise, install the window's colormap
2123         if (install)
2124           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2125       } else {
2126         // uninstall the window's colormap
2127         for (i = 0; i < ncmap; i++) {
2128           if (*(cmaps + i) == wattrib.colormap)
2129             // we found the colormap to uninstall
2130             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2131         }
2132       }
2133     }
2134
2135     XFree(cmaps);
2136   }
2137 }
2138
2139
2140 void BlackboxWindow::setAllowedActions(void) {
2141   Atom actions[7];
2142   int num = 0;
2143   
2144   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2145   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2146   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2147
2148   if (functions & Func_Move)
2149     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2150   if (functions & Func_Resize)
2151     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2152   if (functions & Func_Maximize) {
2153     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2154     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2155   }
2156
2157   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2158                   actions, num);
2159 }
2160
2161
2162 void BlackboxWindow::setState(unsigned long new_state) {
2163   current_state = new_state;
2164
2165   unsigned long state[2];
2166   state[0] = current_state;
2167   state[1] = None;
2168   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2169  
2170   xatom->setValue(client.window, XAtom::blackbox_attributes,
2171                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2172                   PropBlackboxAttributesElements);
2173
2174   Atom netstate[8];
2175   int num = 0;
2176   if (flags.modal)
2177     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2178   if (flags.shaded)
2179     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2180   if (flags.iconic)
2181     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2182   if (flags.skip_taskbar)
2183     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2184   if (flags.skip_pager)
2185     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2186   if (flags.fullscreen)
2187     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2188   if (flags.maximized == 1 || flags.maximized == 2)
2189     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2190   if (flags.maximized == 1 || flags.maximized == 3)
2191     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2192   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2193                   netstate, num);
2194 }
2195
2196
2197 bool BlackboxWindow::getState(void) {
2198   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2199                              current_state);
2200   if (! ret) current_state = 0;
2201   return ret;
2202 }
2203
2204
2205 void BlackboxWindow::restoreAttributes(void) {
2206   unsigned long num = PropBlackboxAttributesElements;
2207   BlackboxAttributes *net;
2208   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2209                         XAtom::blackbox_attributes, num,
2210                         (unsigned long **)&net))
2211     return;
2212   if (num < PropBlackboxAttributesElements) {
2213     delete [] net;
2214     return;
2215   }
2216
2217   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2218     flags.shaded = False;
2219     unsigned long orig_state = current_state;
2220     shade();
2221
2222     /*
2223       At this point in the life of a window, current_state should only be set
2224       to IconicState if the window was an *icon*, not if it was shaded.
2225     */
2226     if (orig_state != IconicState)
2227       current_state = WithdrawnState;
2228  }
2229
2230   if (net->workspace != screen->getCurrentWorkspaceID() &&
2231       net->workspace < screen->getWorkspaceCount())
2232     screen->reassociateWindow(this, net->workspace, True);
2233
2234   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2235       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2236     // set to WithdrawnState so it will be mapped on the new workspace
2237     if (current_state == NormalState) current_state = WithdrawnState;
2238   } else if (current_state == WithdrawnState) {
2239     // the window is on this workspace and is Withdrawn, so it is waiting to
2240     // be mapped
2241     current_state = NormalState;
2242   }
2243
2244   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2245     flags.stuck = False;
2246     stick();
2247
2248     // if the window was on another workspace, it was going to be hidden. this
2249     // specifies that the window should be mapped since it is sticky.
2250     if (current_state == WithdrawnState) current_state = NormalState;
2251   }
2252
2253   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2254     int x = net->premax_x, y = net->premax_y;
2255     unsigned int w = net->premax_w, h = net->premax_h;
2256     flags.maximized = 0;
2257
2258     unsigned int m = 0;
2259     if ((net->flags & AttribMaxHoriz) &&
2260         (net->flags & AttribMaxVert))
2261       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2262     else if (net->flags & AttribMaxVert)
2263       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2264     else if (net->flags & AttribMaxHoriz)
2265       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2266
2267     if (m) maximize(m);
2268
2269     blackbox_attrib.premax_x = x;
2270     blackbox_attrib.premax_y = y;
2271     blackbox_attrib.premax_w = w;
2272     blackbox_attrib.premax_h = h;
2273   }
2274
2275   if (net->flags & AttribDecoration) {
2276     switch (net->decoration) {
2277     case DecorNone:
2278       decorations = 0;
2279
2280       break;
2281
2282     default:
2283     case DecorNormal:
2284       decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2285         Decor_Iconify | Decor_Maximize;
2286
2287       break;
2288
2289     case DecorTiny:
2290       decorations |= Decor_Titlebar | Decor_Iconify;
2291       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2292
2293       break;
2294
2295     case DecorTool:
2296       decorations |= Decor_Titlebar;
2297       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2298
2299       break;
2300     }
2301
2302     // sanity check the new decor
2303     if (! (functions & Func_Resize) || isTransient())
2304       decorations &= ~(Decor_Maximize | Decor_Handle);
2305     if (! (functions & Func_Maximize))
2306       decorations &= ~Decor_Maximize;
2307
2308     if (decorations & Decor_Titlebar) {
2309       if (functions & Func_Close)   // close button is controlled by function
2310         decorations |= Decor_Close; // not decor type
2311     } else { 
2312       if (flags.shaded) // we can not be shaded if we lack a titlebar
2313         shade();
2314     }
2315
2316     if (flags.visible && frame.window) {
2317       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2318       XMapWindow(blackbox->getXDisplay(), frame.window);
2319     }
2320
2321     reconfigure();
2322     setState(current_state);
2323   }
2324
2325   // with the state set it will then be the map event's job to read the
2326   // window's state and behave accordingly
2327
2328   delete [] net;
2329 }
2330
2331
2332 /*
2333  * Positions the Rect r according the the client window position and
2334  * window gravity.
2335  */
2336 void BlackboxWindow::applyGravity(Rect &r) {
2337   // apply horizontal window gravity
2338   switch (client.win_gravity) {
2339   default:
2340   case NorthWestGravity:
2341   case SouthWestGravity:
2342   case WestGravity:
2343     r.setX(client.rect.x());
2344     break;
2345
2346   case NorthGravity:
2347   case SouthGravity:
2348   case CenterGravity:
2349     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2350     break;
2351
2352   case NorthEastGravity:
2353   case SouthEastGravity:
2354   case EastGravity:
2355     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2356     break;
2357
2358   case ForgetGravity:
2359   case StaticGravity:
2360     r.setX(client.rect.x() - frame.margin.left);
2361     break;
2362   }
2363
2364   // apply vertical window gravity
2365   switch (client.win_gravity) {
2366   default:
2367   case NorthWestGravity:
2368   case NorthEastGravity:
2369   case NorthGravity:
2370     r.setY(client.rect.y());
2371     break;
2372
2373   case CenterGravity:
2374   case EastGravity:
2375   case WestGravity:
2376     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2377     break;
2378
2379   case SouthWestGravity:
2380   case SouthEastGravity:
2381   case SouthGravity:
2382     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2383     break;
2384
2385   case ForgetGravity:
2386   case StaticGravity:
2387     r.setY(client.rect.y() - frame.margin.top);
2388     break;
2389   }
2390 }
2391
2392
2393 /*
2394  * The reverse of the applyGravity function.
2395  *
2396  * Positions the Rect r according to the frame window position and
2397  * window gravity.
2398  */
2399 void BlackboxWindow::restoreGravity(Rect &r) {
2400   // restore horizontal window gravity
2401   switch (client.win_gravity) {
2402   default:
2403   case NorthWestGravity:
2404   case SouthWestGravity:
2405   case WestGravity:
2406     r.setX(frame.rect.x());
2407     break;
2408
2409   case NorthGravity:
2410   case SouthGravity:
2411   case CenterGravity:
2412     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2413     break;
2414
2415   case NorthEastGravity:
2416   case SouthEastGravity:
2417   case EastGravity:
2418     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2419     break;
2420
2421   case ForgetGravity:
2422   case StaticGravity:
2423     r.setX(frame.rect.x() + frame.margin.left);
2424     break;
2425   }
2426
2427   // restore vertical window gravity
2428   switch (client.win_gravity) {
2429   default:
2430   case NorthWestGravity:
2431   case NorthEastGravity:
2432   case NorthGravity:
2433     r.setY(frame.rect.y());
2434     break;
2435
2436   case CenterGravity:
2437   case EastGravity:
2438   case WestGravity:
2439     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2440     break;
2441
2442   case SouthWestGravity:
2443   case SouthEastGravity:
2444   case SouthGravity:
2445     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2446     break;
2447
2448   case ForgetGravity:
2449   case StaticGravity:
2450     r.setY(frame.rect.y() + frame.margin.top);
2451     break;
2452   }
2453 }
2454
2455
2456 void BlackboxWindow::redrawLabel(void) const {
2457   if (flags.focused) {
2458     if (frame.flabel)
2459       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2460                                  frame.label, frame.flabel);
2461     else
2462       XSetWindowBackground(blackbox->getXDisplay(),
2463                            frame.label, frame.flabel_pixel);
2464   } else {
2465     if (frame.ulabel)
2466       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2467                                  frame.label, frame.ulabel);
2468     else
2469       XSetWindowBackground(blackbox->getXDisplay(),
2470                            frame.label, frame.ulabel_pixel);
2471   }
2472   XClearWindow(blackbox->getXDisplay(), frame.label);
2473
2474   WindowStyle *style = screen->getWindowStyle();
2475
2476   int pos = frame.bevel_w * 2;
2477   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2478   style->font->drawString(frame.label, pos, 1,
2479                           (flags.focused ? style->l_text_focus :
2480                            style->l_text_unfocus),
2481                           client.title);
2482 }
2483
2484
2485 void BlackboxWindow::redrawAllButtons(void) const {
2486   if (frame.iconify_button) redrawIconifyButton(False);
2487   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2488   if (frame.close_button) redrawCloseButton(False);
2489 }
2490
2491
2492 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2493   if (! pressed) {
2494     if (flags.focused) {
2495       if (frame.fbutton)
2496         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2497                                    frame.iconify_button, frame.fbutton);
2498       else
2499         XSetWindowBackground(blackbox->getXDisplay(),
2500                              frame.iconify_button, frame.fbutton_pixel);
2501     } else {
2502       if (frame.ubutton)
2503         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2504                                    frame.iconify_button, frame.ubutton);
2505       else
2506         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2507                              frame.ubutton_pixel);
2508     }
2509   } else {
2510     if (frame.pbutton)
2511       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2512                                  frame.iconify_button, frame.pbutton);
2513     else
2514       XSetWindowBackground(blackbox->getXDisplay(),
2515                            frame.iconify_button, frame.pbutton_pixel);
2516   }
2517   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2518
2519   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2520            screen->getWindowStyle()->b_pic_unfocus);
2521   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2522                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2523 }
2524
2525
2526 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2527   if (! pressed) {
2528     if (flags.focused) {
2529       if (frame.fbutton)
2530         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2531                                    frame.maximize_button, frame.fbutton);
2532       else
2533         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2534                              frame.fbutton_pixel);
2535     } else {
2536       if (frame.ubutton)
2537         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2538                                    frame.maximize_button, frame.ubutton);
2539       else
2540         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2541                              frame.ubutton_pixel);
2542     }
2543   } else {
2544     if (frame.pbutton)
2545       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2546                                  frame.maximize_button, frame.pbutton);
2547     else
2548       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2549                            frame.pbutton_pixel);
2550   }
2551   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2552
2553   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2554            screen->getWindowStyle()->b_pic_unfocus);
2555   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2556                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2557   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2558             2, 3, (frame.button_w - 3), 3);
2559 }
2560
2561
2562 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2563   if (! pressed) {
2564     if (flags.focused) {
2565       if (frame.fbutton)
2566         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2567                                    frame.fbutton);
2568       else
2569         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2570                              frame.fbutton_pixel);
2571     } else {
2572       if (frame.ubutton)
2573         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2574                                    frame.ubutton);
2575       else
2576         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2577                              frame.ubutton_pixel);
2578     }
2579   } else {
2580     if (frame.pbutton)
2581       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2582                                  frame.close_button, frame.pbutton);
2583     else
2584       XSetWindowBackground(blackbox->getXDisplay(),
2585                            frame.close_button, frame.pbutton_pixel);
2586   }
2587   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2588
2589   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2590            screen->getWindowStyle()->b_pic_unfocus);
2591   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2592             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2593   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2594             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2595 }
2596
2597
2598 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2599   if (re->window != client.window)
2600     return;
2601
2602 #ifdef    DEBUG
2603   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2604           client.window);
2605 #endif // DEBUG
2606
2607   switch (current_state) {
2608   case IconicState:
2609     iconify();
2610     break;
2611
2612   case WithdrawnState:
2613     withdraw();
2614     break;
2615
2616   case NormalState:
2617   case InactiveState:
2618   case ZoomState:
2619   default:
2620 #ifdef DEBUG
2621     fprintf(stderr, "0x%lx: just before show (%d, %d) w: %d, h: %d\n",
2622             client.window,
2623             frame.rect.x(), frame.rect.y(),
2624             frame.rect.width(), frame.rect.height());
2625 #endif // DEBUG
2626     show();
2627     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2628     if (isNormal()) {
2629       if (! blackbox->isStartup()) {
2630         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2631         if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2632                                     getTransientFor()->isFocused())) {
2633           setInputFocus();
2634         }
2635         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2636           int x, y, rx, ry;
2637           Window c, r;
2638           unsigned int m;
2639           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2640                         &r, &c, &rx, &ry, &x, &y, &m);
2641           beginMove(rx, ry);
2642         }
2643       }
2644     }
2645     break;
2646   }
2647 }
2648
2649
2650 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2651   if (ue->window != client.window)
2652     return;
2653
2654 #ifdef    DEBUG
2655   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2656           client.window);
2657 #endif // DEBUG
2658
2659   screen->unmanageWindow(this, False);
2660 }
2661
2662
2663 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2664   if (de->window != client.window)
2665     return;
2666
2667 #ifdef    DEBUG
2668   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2669           client.window);
2670 #endif // DEBUG
2671
2672   screen->unmanageWindow(this, False);
2673 }
2674
2675
2676 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2677   if (re->window != client.window || re->parent == frame.plate)
2678     return;
2679
2680 #ifdef    DEBUG
2681   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2682           "0x%lx.\n", client.window, re->parent);
2683 #endif // DEBUG
2684
2685   XEvent ev;
2686   ev.xreparent = *re;
2687   XPutBackEvent(blackbox->getXDisplay(), &ev);
2688   screen->unmanageWindow(this, True);
2689 }
2690
2691
2692 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2693   if (pe->state == PropertyDelete)
2694     return;
2695
2696 #ifdef    DEBUG
2697   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2698           client.window);
2699 #endif
2700
2701   switch(pe->atom) {
2702   case XA_WM_CLASS:
2703   case XA_WM_CLIENT_MACHINE:
2704   case XA_WM_COMMAND:
2705     break;
2706
2707   case XA_WM_TRANSIENT_FOR: {
2708     // determine if this is a transient window
2709     getTransientInfo();
2710
2711     // adjust the window decorations based on transience
2712     if (isTransient()) {
2713       decorations &= ~(Decor_Maximize | Decor_Handle);
2714       functions &= ~Func_Maximize;
2715       setAllowedActions();
2716     }
2717
2718     reconfigure();
2719 #ifdef DEBUG
2720     fprintf(stderr, "0x%lx: transient hint (%d, %d) w: %d, h: %d\n",
2721             client.window,
2722             frame.rect.x(), frame.rect.y(),
2723             frame.rect.width(), frame.rect.height());
2724 #endif
2725   }
2726     break;
2727
2728   case XA_WM_HINTS:
2729     getWMHints();
2730     break;
2731
2732   case XA_WM_ICON_NAME:
2733     getWMIconName();
2734     if (flags.iconic) screen->propagateWindowName(this);
2735     break;
2736
2737   case XAtom::net_wm_name:
2738   case XA_WM_NAME:
2739     getWMName();
2740
2741     if (decorations & Decor_Titlebar)
2742       redrawLabel();
2743
2744     screen->propagateWindowName(this);
2745     break;
2746
2747   case XA_WM_NORMAL_HINTS: {
2748     getWMNormalHints();
2749
2750     if ((client.normal_hint_flags & PMinSize) &&
2751         (client.normal_hint_flags & PMaxSize)) {
2752       // the window now can/can't resize itself, so the buttons need to be
2753       // regrabbed.
2754       ungrabButtons();
2755       if (client.max_width <= client.min_width &&
2756           client.max_height <= client.min_height) {
2757         decorations &= ~(Decor_Maximize | Decor_Handle);
2758         functions &= ~(Func_Resize | Func_Maximize);
2759       } else {
2760         if (! isTransient()) {
2761           decorations |= Decor_Maximize | Decor_Handle;
2762           functions |= Func_Maximize;
2763         }
2764         functions |= Func_Resize;
2765       }
2766       grabButtons();
2767       setAllowedActions();
2768     }
2769
2770     Rect old_rect = frame.rect;
2771
2772     upsize();
2773
2774     if (old_rect != frame.rect)
2775       reconfigure();
2776
2777 #ifdef DEBUG
2778     fprintf(stderr, "0x%lx: normal hint (%d, %d) w: %d, h: %d\n",
2779             client.window,
2780             frame.rect.x(), frame.rect.y(),
2781             frame.rect.width(), frame.rect.height());
2782 #endif // DEBUG
2783     break;
2784   }
2785
2786   default:
2787     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2788       getWMProtocols();
2789
2790       if ((decorations & Decor_Close) && (! frame.close_button)) {
2791         createCloseButton();
2792         if (decorations & Decor_Titlebar) {
2793           positionButtons(True);
2794           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2795         }
2796         if (windowmenu) windowmenu->reconfigure();
2797       }
2798     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2799       updateStrut();
2800     }
2801
2802     break;
2803   }
2804 }
2805
2806
2807 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2808 #ifdef DEBUG
2809   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2810 #endif
2811
2812   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2813     redrawLabel();
2814   else if (frame.close_button == ee->window)
2815     redrawCloseButton(False);
2816   else if (frame.maximize_button == ee->window)
2817     redrawMaximizeButton(flags.maximized);
2818   else if (frame.iconify_button == ee->window)
2819     redrawIconifyButton(False);
2820 }
2821
2822
2823 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2824   if (cr->window != client.window || flags.iconic)
2825     return;
2826
2827   if (cr->value_mask & CWBorderWidth)
2828     client.old_bw = cr->border_width;
2829
2830   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2831     Rect req = frame.rect;
2832
2833     if (cr->value_mask & (CWX | CWY)) {
2834       if (cr->value_mask & CWX)
2835         client.rect.setX(cr->x);
2836       if (cr->value_mask & CWY)
2837         client.rect.setY(cr->y);
2838
2839       applyGravity(req);
2840     }
2841
2842     if (cr->value_mask & CWWidth) {
2843       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2844 #ifdef DEBUG
2845       fprintf(stderr, "0x%lx: new width - %d\n", client.window, cr->width);
2846 #endif // DEBUG
2847     }
2848
2849     if (cr->value_mask & CWHeight) {
2850       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2851 #ifdef DEBUG
2852       fprintf(stderr, "0x%lx: new height - %d\n", client.window, cr->height);
2853 #endif // DEBUG
2854     }
2855
2856     configure(req.x(), req.y(), req.width(), req.height());
2857   }
2858
2859   if (cr->value_mask & CWStackMode && !isDesktop()) {
2860     switch (cr->detail) {
2861     case Below:
2862     case BottomIf:
2863       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2864       break;
2865
2866     case Above:
2867     case TopIf:
2868     default:
2869       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2870       break;
2871     }
2872   }
2873
2874 #ifdef DEBUG
2875   fprintf(stderr, "0x%lx: change request (%d, %d) w: %d, h: %d\n",
2876           client.window,
2877           frame.rect.x(), frame.rect.y(),
2878           frame.rect.width(), frame.rect.height());
2879 #endif // DEBUG
2880 }
2881
2882
2883 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2884 #ifdef DEBUG
2885   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2886           client.window);
2887 #endif
2888
2889   if (frame.maximize_button == be->window && be->button <= 3) {
2890     redrawMaximizeButton(True);
2891   } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2892     if (! flags.focused)
2893       setInputFocus();
2894
2895     if (frame.iconify_button == be->window) {
2896       redrawIconifyButton(True);
2897     } else if (frame.close_button == be->window) {
2898       redrawCloseButton(True);
2899     } else if (frame.plate == be->window) {
2900       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2901
2902       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2903
2904       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2905     } else {
2906       if (frame.title == be->window || frame.label == be->window) {
2907         if (((be->time - lastButtonPressTime) <=
2908              blackbox->getDoubleClickInterval()) ||
2909             (be->state == ControlMask)) {
2910           lastButtonPressTime = 0;
2911           shade();
2912         } else {
2913           lastButtonPressTime = be->time;
2914         }
2915       }
2916
2917       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2918
2919       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2920     }
2921   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2922              (be->window != frame.close_button)) {
2923     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2924   } else if (windowmenu && be->button == 3 &&
2925              (frame.title == be->window || frame.label == be->window ||
2926               frame.handle == be->window || frame.window == be->window)) {
2927     if (windowmenu->isVisible()) {
2928       windowmenu->hide();
2929     } else {
2930       int mx = be->x_root - windowmenu->getWidth() / 2,
2931           my = be->y_root - windowmenu->getHeight() / 2;
2932
2933       // snap the window menu into a corner/side if necessary
2934       int left_edge, right_edge, top_edge, bottom_edge;
2935
2936       /*
2937          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2938          and height of the menu, as the sizes returned by it do not include
2939          the borders.
2940        */
2941       left_edge = frame.rect.x();
2942       right_edge = frame.rect.right() -
2943         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2944       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2945       bottom_edge = client.rect.bottom() -
2946         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2947         (frame.border_w + frame.mwm_border_w);
2948
2949       if (mx < left_edge)
2950         mx = left_edge;
2951       if (mx > right_edge)
2952         mx = right_edge;
2953       if (my < top_edge)
2954         my = top_edge;
2955       if (my > bottom_edge)
2956         my = bottom_edge;
2957
2958       windowmenu->move(mx, my);
2959       windowmenu->show();
2960       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2961       XRaiseWindow(blackbox->getXDisplay(),
2962                    windowmenu->getSendToMenu()->getWindowID());
2963     }
2964   // mouse wheel up
2965   } else if (be->button == 4) {
2966     if ((be->window == frame.label ||
2967          be->window == frame.title ||
2968          be->window == frame.maximize_button ||
2969          be->window == frame.iconify_button ||
2970          be->window == frame.close_button) &&
2971         ! flags.shaded)
2972       shade();
2973   // mouse wheel down
2974   } else if (be->button == 5) {
2975     if ((be->window == frame.label ||
2976          be->window == frame.title ||
2977          be->window == frame.maximize_button ||
2978          be->window == frame.iconify_button ||
2979          be->window == frame.close_button) &&
2980         flags.shaded)
2981       shade();
2982   }
2983 }
2984
2985
2986 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2987 #ifdef DEBUG
2988   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2989           client.window);
2990 #endif
2991
2992   if (re->window == frame.maximize_button &&
2993       re->button >= 1 && re->button <= 3) {
2994     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2995         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2996       maximize(re->button);
2997     } else {
2998       redrawMaximizeButton(flags.maximized);
2999     }
3000   } else if (re->window == frame.iconify_button && re->button == 1) {
3001     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3002         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3003       iconify();
3004     } else {
3005       redrawIconifyButton(False);
3006     }
3007   } else if (re->window == frame.close_button & re->button == 1) {
3008     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3009         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3010       close();
3011     redrawCloseButton(False);
3012   } else if (flags.moving) {
3013     endMove();
3014   } else if (flags.resizing) {
3015     endResize();
3016   } else if (re->window == frame.window) {
3017     if (re->button == 2 && re->state == ModMask)
3018       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3019   }
3020 }
3021
3022
3023
3024 void BlackboxWindow::beginMove(int x_root, int y_root) {
3025   assert(! (flags.resizing || flags.moving));
3026
3027   /*
3028     Only one window can be moved/resized at a time. If another window is already
3029     being moved or resized, then stop it before whating to work with this one.
3030   */
3031   BlackboxWindow *changing = blackbox->getChangingWindow();
3032   if (changing && changing != this) {
3033     if (changing->flags.moving)
3034       changing->endMove();
3035     else // if (changing->flags.resizing)
3036       changing->endResize();
3037   }
3038   
3039   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3040                PointerMotionMask | ButtonReleaseMask,
3041                GrabModeAsync, GrabModeAsync,
3042                None, blackbox->getMoveCursor(), CurrentTime);
3043
3044   if (windowmenu && windowmenu->isVisible())
3045     windowmenu->hide();
3046
3047   flags.moving = True;
3048   blackbox->setChangingWindow(this);
3049
3050   if (! screen->doOpaqueMove()) {
3051     XGrabServer(blackbox->getXDisplay());
3052
3053     frame.changing = frame.rect;
3054     screen->showPosition(frame.changing.x(), frame.changing.y());
3055
3056     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3057                    screen->getOpGC(),
3058                    frame.changing.x(),
3059                    frame.changing.y(),
3060                    frame.changing.width() - 1,
3061                    frame.changing.height() - 1);
3062   }
3063
3064   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3065   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3066 }
3067
3068
3069 void BlackboxWindow::doMove(int x_root, int y_root) {
3070   assert(flags.moving);
3071   assert(blackbox->getChangingWindow() == this);
3072
3073   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3074   dx -= frame.border_w;
3075   dy -= frame.border_w;
3076
3077   if (screen->doWorkspaceWarping()) {
3078     // workspace warping
3079     bool warp = False;
3080     unsigned int dest = screen->getCurrentWorkspaceID();
3081     if (x_root <= 0) {
3082       warp = True;
3083
3084       if (dest > 0) dest--;
3085       else dest = screen->getNumberOfWorkspaces() - 1;
3086
3087     } else if (x_root >= screen->getRect().right()) {
3088       warp = True;
3089
3090       if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3091       else dest = 0;
3092     }
3093     if (warp) {
3094       endMove();
3095       bool focus = flags.focused; // had focus while moving?
3096       if (! flags.stuck)
3097         screen->reassociateWindow(this, dest, False);
3098       screen->changeWorkspaceID(dest);
3099       if (focus)
3100         setInputFocus();
3101
3102       /*
3103          If the XWarpPointer is done after the configure, we can end up
3104          grabbing another window, so made sure you do it first.
3105       */
3106       int dest_x;
3107       if (x_root <= 0) {
3108         dest_x = screen->getRect().right() - 1;
3109         XWarpPointer(blackbox->getXDisplay(), None, 
3110                      screen->getRootWindow(), 0, 0, 0, 0,
3111                      dest_x, y_root);
3112
3113         configure(dx + (screen->getRect().width() - 1), dy,
3114                   frame.rect.width(), frame.rect.height());
3115       } else {
3116         dest_x = 0;
3117         XWarpPointer(blackbox->getXDisplay(), None, 
3118                      screen->getRootWindow(), 0, 0, 0, 0,
3119                      dest_x, y_root);
3120
3121         configure(dx - (screen->getRect().width() - 1), dy,
3122                   frame.rect.width(), frame.rect.height());
3123       }
3124
3125       beginMove(dest_x, y_root);
3126       return;
3127     }
3128   }
3129
3130   const int snap_distance = screen->getEdgeSnapThreshold();
3131
3132   if (snap_distance) {
3133     // window corners
3134     const int wleft = dx,
3135               wright = dx + frame.rect.width() - 1,
3136               wtop = dy,
3137               wbottom = dy + frame.rect.height() - 1;
3138
3139     if (screen->getWindowToWindowSnap()) {
3140       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3141       assert(w);
3142
3143       // try snap to another window
3144       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
3145         BlackboxWindow *snapwin = w->getWindow(i);
3146         if (snapwin == this)
3147           continue;   // don't snap to self
3148
3149         bool snapped = False;
3150         
3151         const Rect &winrect = snapwin->frameRect();
3152         int dleft = abs(wright - winrect.left()),
3153            dright = abs(wleft - winrect.right()),
3154              dtop = abs(wbottom - winrect.top()),
3155           dbottom = abs(wtop - winrect.bottom());
3156
3157         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3158             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3159
3160           // snap left of other window?
3161           if (dleft < snap_distance && dleft <= dright) {
3162             dx = winrect.left() - frame.rect.width();
3163             snapped = True;
3164           }
3165           // snap right of other window?
3166           else if (dright < snap_distance) {
3167             dx = winrect.right() + 1;
3168             snapped = True;
3169           }
3170
3171           if (snapped) {
3172             if (screen->getWindowCornerSnap()) {
3173               // try corner-snap to its other sides
3174               dtop = abs(wtop - winrect.top());
3175               dbottom = abs(wbottom - winrect.bottom());
3176               if (dtop < snap_distance && dtop <= dbottom)
3177                 dy = winrect.top();
3178               else if (dbottom < snap_distance)
3179                 dy = winrect.bottom() - frame.rect.height() + 1;
3180             }
3181
3182             continue;
3183           }
3184         }
3185
3186         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3187             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3188
3189           // snap top of other window?
3190           if (dtop < snap_distance && dtop <= dbottom) {
3191             dy = winrect.top() - frame.rect.height();
3192             snapped = True;
3193           }
3194           // snap bottom of other window?
3195           else if (dbottom < snap_distance) {
3196             dy = winrect.bottom() + 1;
3197             snapped = True;
3198           }
3199
3200           if (snapped) {
3201             if (screen->getWindowCornerSnap()) {
3202               // try corner-snap to its other sides
3203               dleft = abs(wleft - winrect.left());
3204               dright = abs(wright - winrect.right());
3205               if (dleft < snap_distance && dleft <= dright)
3206                 dx = winrect.left();
3207               else if (dright < snap_distance)
3208                 dx = winrect.right() - frame.rect.width() + 1;
3209             }
3210
3211             continue;
3212           }
3213         }
3214       }
3215     }
3216
3217     RectList snaplist; // the list of rects we will try to snap to
3218
3219     // snap to the strut (and screen boundaries for xinerama)
3220 #ifdef    XINERAMA
3221     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3222       if (! screen->doFullMax())
3223         snaplist.insert(snaplist.begin(),
3224                         screen->allAvailableAreas().begin(),
3225                         screen->allAvailableAreas().end());
3226
3227       // always snap to the screen edges
3228       snaplist.insert(snaplist.begin(),
3229                       screen->getXineramaAreas().begin(),
3230                       screen->getXineramaAreas().end());
3231     } else
3232 #endif // XINERAMA
3233     {
3234       if (! screen->doFullMax())
3235         snaplist.push_back(screen->availableArea());
3236
3237       // always snap to the screen edges
3238       snaplist.push_back(screen->getRect());
3239     }
3240
3241     RectList::const_iterator it, end = snaplist.end();
3242     for (it = snaplist.begin(); it != end; ++it) {
3243       const Rect &srect = *it;
3244
3245       // if we're not in the rectangle then don't snap to it.
3246       if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3247                                   frame.rect.height())))
3248         continue;
3249
3250       int dleft = abs(wleft - srect.left()),
3251          dright = abs(wright - srect.right()),
3252            dtop = abs(wtop - srect.top()),
3253         dbottom = abs(wbottom - srect.bottom());
3254
3255         // snap left?
3256         if (dleft < snap_distance && dleft <= dright)
3257           dx = srect.left();
3258         // snap right?
3259         else if (dright < snap_distance)
3260           dx = srect.right() - frame.rect.width() + 1;
3261
3262         // snap top?
3263         if (dtop < snap_distance && dtop <= dbottom)
3264           dy = srect.top();
3265         // snap bottom?
3266         else if (dbottom < snap_distance)
3267           dy = srect.bottom() - frame.rect.height() + 1;
3268     }
3269   }
3270
3271   if (screen->doOpaqueMove()) {
3272     configure(dx, dy, frame.rect.width(), frame.rect.height());
3273   } else {
3274     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3275                    screen->getOpGC(),
3276                    frame.changing.x(),
3277                    frame.changing.y(),
3278                    frame.changing.width() - 1,
3279                    frame.changing.height() - 1);
3280
3281     frame.changing.setPos(dx, dy);
3282
3283     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3284                    screen->getOpGC(),
3285                    frame.changing.x(),
3286                    frame.changing.y(),
3287                    frame.changing.width() - 1,
3288                    frame.changing.height() - 1);
3289   }
3290
3291   screen->showPosition(dx, dy);
3292 }
3293
3294
3295 void BlackboxWindow::endMove(void) {
3296   assert(flags.moving);
3297   assert(blackbox->getChangingWindow() == this);
3298
3299   flags.moving = False;
3300   blackbox->setChangingWindow(0);
3301
3302   if (! screen->doOpaqueMove()) {
3303     /* when drawing the rubber band, we need to make sure we only draw inside
3304      * the frame... frame.changing_* contain the new coords for the window,
3305      * so we need to subtract 1 from changing_w/changing_h every where we
3306      * draw the rubber band (for both moving and resizing)
3307      */
3308     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3309                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3310                    frame.changing.width() - 1, frame.changing.height() - 1);
3311       XUngrabServer(blackbox->getXDisplay());
3312   
3313       configure(frame.changing.x(), frame.changing.y(),
3314                 frame.changing.width(), frame.changing.height());
3315   } else {
3316     configure(frame.rect.x(), frame.rect.y(),
3317               frame.rect.width(), frame.rect.height());
3318   }
3319   screen->hideGeometry();
3320
3321   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3322
3323   // if there are any left over motions from the move, drop them now
3324   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3325   XEvent e;
3326   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3327                                 MotionNotify, &e));
3328 }
3329
3330
3331 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3332   assert(! (flags.resizing || flags.moving));
3333
3334   /*
3335     Only one window can be moved/resized at a time. If another window is already
3336     being moved or resized, then stop it before whating to work with this one.
3337   */
3338   BlackboxWindow *changing = blackbox->getChangingWindow();
3339   if (changing && changing != this) {
3340     if (changing->flags.moving)
3341       changing->endMove();
3342     else // if (changing->flags.resizing)
3343       changing->endResize();
3344   }
3345
3346   resize_dir = dir;
3347
3348   Cursor cursor;
3349   Corner anchor;
3350   
3351   switch (resize_dir) {
3352   case BottomLeft:
3353     anchor = TopRight;
3354     cursor = blackbox->getLowerLeftAngleCursor();
3355     break;
3356
3357   case BottomRight:
3358     anchor = TopLeft;
3359     cursor = blackbox->getLowerRightAngleCursor();
3360     break;
3361
3362   case TopLeft:
3363     anchor = BottomRight;
3364     cursor = blackbox->getUpperLeftAngleCursor();
3365     break;
3366
3367   case TopRight:
3368     anchor = BottomLeft;
3369     cursor = blackbox->getUpperRightAngleCursor();
3370     break;
3371
3372   default:
3373     assert(false); // unhandled Corner
3374     return;        // unreachable, for the compiler
3375   }
3376   
3377   XGrabServer(blackbox->getXDisplay());
3378   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3379                PointerMotionMask | ButtonReleaseMask,
3380                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3381
3382   flags.resizing = True;
3383   blackbox->setChangingWindow(this);
3384
3385   int gw, gh;
3386   frame.changing = frame.rect;
3387
3388   constrain(anchor,  &gw, &gh);
3389
3390   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3391                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3392                  frame.changing.width() - 1, frame.changing.height() - 1);
3393
3394   screen->showGeometry(gw, gh);
3395   
3396   frame.grab_x = x_root;
3397   frame.grab_y = y_root;
3398 }
3399
3400
3401 void BlackboxWindow::doResize(int x_root, int y_root) {
3402   assert(flags.resizing);
3403   assert(blackbox->getChangingWindow() == this);
3404
3405   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3406                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3407                  frame.changing.width() - 1, frame.changing.height() - 1);
3408
3409   int gw, gh;
3410   Corner anchor;
3411
3412   switch (resize_dir) {
3413   case BottomLeft:
3414     anchor = TopRight;
3415     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3416                            frame.rect.height() + (y_root - frame.grab_y));
3417     break;
3418   case BottomRight:
3419     anchor = TopLeft;
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 TopLeft:
3424     anchor = BottomRight;
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 TopRight:
3429     anchor = BottomLeft;
3430     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3431                            frame.rect.height() - (y_root - frame.grab_y));
3432     break;
3433
3434   default:
3435     assert(false); // unhandled Corner
3436     return;        // unreachable, for the compiler
3437   }
3438   
3439   constrain(anchor, &gw, &gh);
3440
3441   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3442                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3443                  frame.changing.width() - 1, frame.changing.height() - 1);
3444
3445   screen->showGeometry(gw, gh);
3446 }
3447
3448
3449 void BlackboxWindow::endResize(void) {
3450   assert(flags.resizing);
3451   assert(blackbox->getChangingWindow() == this);
3452
3453   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3454                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3455                  frame.changing.width() - 1, frame.changing.height() - 1);
3456   XUngrabServer(blackbox->getXDisplay());
3457
3458   // unset maximized state after resized when fully maximized
3459   if (flags.maximized == 1)
3460     maximize(0);
3461   
3462   flags.resizing = False;
3463   blackbox->setChangingWindow(0);
3464
3465   configure(frame.changing.x(), frame.changing.y(),
3466             frame.changing.width(), frame.changing.height());
3467   screen->hideGeometry();
3468
3469   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3470   
3471   // if there are any left over motions from the resize, drop them now
3472   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3473   XEvent e;
3474   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3475                                 MotionNotify, &e));
3476 }
3477
3478
3479 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3480 #ifdef DEBUG
3481   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3482           client.window);
3483 #endif
3484
3485   if (flags.moving) {
3486     doMove(me->x_root, me->y_root);
3487   } else if (flags.resizing) {
3488     doResize(me->x_root, me->y_root);
3489   } else {
3490     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3491         (frame.title == me->window || frame.label == me->window ||
3492          frame.handle == me->window || frame.window == me->window)) {
3493       beginMove(me->x_root, me->y_root);
3494     } else if ((functions & Func_Resize) &&
3495                (me->state & Button1Mask && (me->window == frame.right_grip ||
3496                                             me->window == frame.left_grip)) ||
3497                (me->state & Button3Mask && me->state & ModMask &&
3498                 me->window == frame.window)) {
3499       unsigned int zones = screen->getResizeZones();
3500       Corner corner;
3501       
3502       if (me->window == frame.left_grip) {
3503         corner = BottomLeft;
3504       } else if (me->window == frame.right_grip || zones == 1) {
3505         corner = BottomRight;
3506       } else {
3507         bool top;
3508         bool left = (me->x_root - frame.rect.x() <=
3509                      static_cast<signed>(frame.rect.width() / 2));
3510         if (zones == 2)
3511           top = False;
3512         else // (zones == 4)
3513           top = (me->y_root - frame.rect.y() <=
3514                  static_cast<signed>(frame.rect.height() / 2));
3515         corner = (top ? (left ? TopLeft : TopRight) :
3516                         (left ? BottomLeft : BottomRight));
3517       }
3518
3519       beginResize(me->x_root, me->y_root, corner);
3520     }
3521   }
3522 }
3523
3524
3525 #ifdef    SHAPE
3526 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3527   if (blackbox->hasShapeExtensions() && flags.shaped) {
3528     configureShape();
3529   }
3530 }
3531 #endif // SHAPE
3532
3533
3534 bool BlackboxWindow::validateClient(void) const {
3535   XSync(blackbox->getXDisplay(), False);
3536
3537   XEvent e;
3538   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3539                              DestroyNotify, &e) ||
3540       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3541                              UnmapNotify, &e)) {
3542     XPutBackEvent(blackbox->getXDisplay(), &e);
3543
3544     return False;
3545   }
3546
3547   return True;
3548 }
3549
3550
3551 void BlackboxWindow::restore(bool remap) {
3552   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3553   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3554   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3555
3556   // do not leave a shaded window as an icon unless it was an icon
3557   if (flags.shaded && ! flags.iconic) setState(NormalState);
3558
3559   restoreGravity(client.rect);
3560
3561   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3562   XUnmapWindow(blackbox->getXDisplay(), client.window);
3563
3564   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3565
3566   XEvent ev;
3567   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3568                              ReparentNotify, &ev)) {
3569     remap = True;
3570   } else {
3571     // according to the ICCCM - if the client doesn't reparent to
3572     // root, then we have to do it for them
3573     XReparentWindow(blackbox->getXDisplay(), client.window,
3574                     screen->getRootWindow(),
3575                     client.rect.x(), client.rect.y());
3576   }
3577
3578   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3579 }
3580
3581
3582 // timer for autoraise
3583 void BlackboxWindow::timeout(void) {
3584   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3585 }
3586
3587
3588 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3589   if ((net->flags & AttribShaded) &&
3590       ((blackbox_attrib.attrib & AttribShaded) !=
3591        (net->attrib & AttribShaded)))
3592     shade();
3593
3594   if (flags.visible && // watch out for requests when we can not be seen
3595       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3596       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3597        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3598     if (flags.maximized) {
3599       maximize(0);
3600     } else {
3601       int button = 0;
3602
3603       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3604         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3605       else if (net->flags & AttribMaxVert)
3606         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3607       else if (net->flags & AttribMaxHoriz)
3608         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3609
3610       maximize(button);
3611     }
3612   }
3613
3614   if ((net->flags & AttribOmnipresent) &&
3615       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3616        (net->attrib & AttribOmnipresent)))
3617     stick();
3618
3619   if ((net->flags & AttribWorkspace) &&
3620       (blackbox_attrib.workspace != net->workspace)) {
3621     screen->reassociateWindow(this, net->workspace, True);
3622
3623     if (screen->getCurrentWorkspaceID() != net->workspace) {
3624       withdraw();
3625     } else {
3626       show();
3627       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3628     }
3629   }
3630
3631   if (net->flags & AttribDecoration) {
3632     switch (net->decoration) {
3633     case DecorNone:
3634       decorations = 0;
3635
3636       break;
3637
3638     default:
3639     case DecorNormal:
3640       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3641   
3642       decorations = ((functions & Func_Resize) && !isTransient() ?
3643                      decorations | Decor_Handle :
3644                      decorations &= ~Decor_Handle);
3645       decorations = (functions & Func_Maximize ?
3646                      decorations | Decor_Maximize :
3647                      decorations &= ~Decor_Maximize);
3648
3649       break;
3650
3651     case DecorTiny:
3652       decorations |= Decor_Titlebar | Decor_Iconify;
3653       decorations &= ~(Decor_Border | Decor_Handle);
3654       
3655       decorations = (functions & Func_Maximize ?
3656                      decorations | Decor_Maximize :
3657                      decorations &= ~Decor_Maximize);
3658
3659       break;
3660
3661     case DecorTool:
3662       decorations |= Decor_Titlebar;
3663       decorations &= ~(Decor_Iconify | Decor_Border);
3664
3665       decorations = ((functions & Func_Resize) && !isTransient() ?
3666                      decorations | Decor_Handle :
3667                      decorations &= ~Decor_Handle);
3668       decorations = (functions & Func_Maximize ?
3669                      decorations | Decor_Maximize :
3670                      decorations &= ~Decor_Maximize);
3671
3672       break;
3673     }
3674
3675     // we can not be shaded if we lack a titlebar
3676     if (flags.shaded && ! (decorations & Decor_Titlebar))
3677       shade();
3678
3679     if (flags.visible && frame.window) {
3680       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3681       XMapWindow(blackbox->getXDisplay(), frame.window);
3682     }
3683
3684     reconfigure();
3685     setState(current_state);
3686   }
3687 }
3688
3689
3690 /*
3691  * Set the sizes of all components of the window frame
3692  * (the window decorations).
3693  * These values are based upon the current style settings and the client
3694  * window's dimensions.
3695  */
3696 void BlackboxWindow::upsize(void) {
3697   frame.bevel_w = screen->getBevelWidth();
3698
3699   if (decorations & Decor_Border) {
3700     frame.border_w = screen->getBorderWidth();
3701     if (! isTransient())
3702       frame.mwm_border_w = screen->getFrameWidth();
3703     else
3704       frame.mwm_border_w = 0;
3705   } else {
3706     frame.mwm_border_w = frame.border_w = 0;
3707   }
3708
3709   if (decorations & Decor_Titlebar) {
3710     // the height of the titlebar is based upon the height of the font being
3711     // used to display the window's title
3712     WindowStyle *style = screen->getWindowStyle();
3713     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3714
3715     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3716     frame.button_w = (frame.label_h - 2);
3717
3718     // set the top frame margin
3719     frame.margin.top = frame.border_w + frame.title_h +
3720                        frame.border_w + frame.mwm_border_w;
3721   } else {
3722     frame.title_h = 0;
3723     frame.label_h = 0;
3724     frame.button_w = 0;
3725
3726     // set the top frame margin
3727     frame.margin.top = frame.border_w + frame.mwm_border_w;
3728   }
3729
3730   // set the left/right frame margin
3731   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3732
3733   if (decorations & Decor_Handle) {
3734     frame.grip_w = frame.button_w * 2;
3735     frame.handle_h = screen->getHandleWidth();
3736
3737     // set the bottom frame margin
3738     frame.margin.bottom = frame.border_w + frame.handle_h +
3739                           frame.border_w + frame.mwm_border_w;
3740   } else {
3741     frame.handle_h = 0;
3742     frame.grip_w = 0;
3743
3744     // set the bottom frame margin
3745     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3746   }
3747
3748   /*
3749     We first get the normal dimensions and use this to define the inside_w/h
3750     then we modify the height if shading is in effect.
3751     If the shade state is not considered then frame.rect gets reset to the
3752     normal window size on a reconfigure() call resulting in improper
3753     dimensions appearing in move/resize and other events.
3754   */
3755   unsigned int
3756     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3757     width = client.rect.width() + frame.margin.left + frame.margin.right;
3758
3759   frame.inside_w = width - (frame.border_w * 2);
3760   frame.inside_h = height - (frame.border_w * 2);
3761
3762   if (flags.shaded)
3763     height = frame.title_h + (frame.border_w * 2);
3764   frame.rect.setSize(width, height);
3765 }
3766
3767
3768 /*
3769  * Calculate the size of the client window and constrain it to the
3770  * size specified by the size hints of the client window.
3771  *
3772  * The logical width and height are placed into pw and ph, if they
3773  * are non-zero.  Logical size refers to the users perception of
3774  * the window size (for example an xterm resizes in cells, not in pixels).
3775  *
3776  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3777  * Physical geometry refers to the geometry of the window in pixels.
3778  */
3779 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3780   // frame.changing represents the requested frame size, we need to
3781   // strip the frame margin off and constrain the client size
3782   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3783                            frame.changing.top() + frame.margin.top,
3784                            frame.changing.right() - frame.margin.right,
3785                            frame.changing.bottom() - frame.margin.bottom);
3786
3787   int dw = frame.changing.width(), dh = frame.changing.height(),
3788     base_width = (client.base_width) ? client.base_width : client.min_width,
3789     base_height = (client.base_height) ? client.base_height :
3790                                          client.min_height;
3791
3792   // constrain
3793   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3794   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3795   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3796   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3797
3798   dw -= base_width;
3799   dw /= client.width_inc;
3800   dh -= base_height;
3801   dh /= client.height_inc;
3802
3803   if (pw) {
3804     if (client.width_inc == 1)
3805       *pw = dw + base_width;
3806     else
3807       *pw = dw;
3808   }
3809   if (ph) {
3810     if (client.height_inc == 1)
3811       *ph = dh + base_height;
3812     else
3813       *ph = dh;
3814   }
3815
3816   dw *= client.width_inc;
3817   dw += base_width;
3818   dh *= client.height_inc;
3819   dh += base_height;
3820
3821   frame.changing.setSize(dw, dh);
3822
3823   // add the frame margin back onto frame.changing
3824   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3825                            frame.changing.top() - frame.margin.top,
3826                            frame.changing.right() + frame.margin.right,
3827                            frame.changing.bottom() + frame.margin.bottom);
3828
3829   // move frame.changing to the specified anchor
3830   int dx = 0,
3831       dy = 0;
3832   switch (anchor) {
3833   case TopLeft:
3834     break;
3835
3836   case TopRight:
3837     dx = frame.rect.right() - frame.changing.right();
3838     break;
3839
3840   case BottomLeft:
3841     dy = frame.rect.bottom() - frame.changing.bottom();
3842     break;
3843
3844   case BottomRight:
3845     dx = frame.rect.right() - frame.changing.right();
3846     dy = frame.rect.bottom() - frame.changing.bottom();
3847     break;
3848
3849   default:
3850     assert(false);  // unhandled corner
3851   }
3852   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3853 }
3854
3855
3856 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3857                             unsigned int max_length,
3858                             unsigned int modifier) const {
3859   size_t text_len = text.size();
3860   unsigned int length;
3861
3862   do {
3863     length = font->measureString(string(text, 0, text_len)) + modifier;
3864   } while (length > max_length && text_len-- > 0);
3865
3866   switch (justify) {
3867   case RightJustify:
3868     start_pos += max_length - length;
3869     break;
3870
3871   case CenterJustify:
3872     start_pos += (max_length - length) / 2;
3873     break;
3874
3875   case LeftJustify:
3876   default:
3877     break;
3878   }
3879 }
3880
3881
3882 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3883   : blackbox(b), group(_group) {
3884   XWindowAttributes wattrib;
3885   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3886     // group window doesn't seem to exist anymore
3887     delete this;
3888     return;
3889   }
3890
3891   XSelectInput(blackbox->getXDisplay(), group,
3892                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3893
3894   blackbox->saveGroupSearch(group, this);
3895 }
3896
3897
3898 BWindowGroup::~BWindowGroup(void) {
3899   blackbox->removeGroupSearch(group);
3900 }
3901
3902
3903 BlackboxWindow *
3904 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3905   BlackboxWindow *ret = blackbox->getFocusedWindow();
3906
3907   // does the focus window match (or any transient_fors)?
3908   while (ret) {
3909     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3910       if (ret->isTransient() && allow_transients) break;
3911       else if (! ret->isTransient()) break;
3912     }
3913
3914     ret = ret->getTransientFor();
3915   }
3916
3917   if (ret) return ret;
3918
3919   // the focus window didn't match, look in the group's window list
3920   BlackboxWindowList::const_iterator it, end = windowList.end();
3921   for (it = windowList.begin(); it != end; ++it) {
3922     ret = *it;
3923     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3924       if (ret->isTransient() && allow_transients) break;
3925       else if (! ret->isTransient()) break;
3926     }
3927   }
3928
3929   return ret;
3930 }