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