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