]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
when a window is iconified, set its workspace to 'all workspaces' so it shows up...
[dana/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41 }
42
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   screen->updateStackingList();
1672 }
1673
1674
1675 void BlackboxWindow::show(void) {
1676   flags.visible = True;
1677   flags.iconic = False;
1678
1679   current_state = (flags.shaded) ? IconicState : NormalState;
1680   setState(current_state);
1681
1682   XMapWindow(blackbox->getXDisplay(), client.window);
1683   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1684   XMapWindow(blackbox->getXDisplay(), frame.window);
1685
1686 #ifdef DEBUG
1687   int real_x, real_y;
1688   Window child;
1689   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1690                         screen->getRootWindow(),
1691                         0, 0, &real_x, &real_y, &child);
1692   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1693           client.rect.left(), client.rect.top(), real_x, real_y);
1694   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1695 #endif
1696 }
1697
1698
1699 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1700   if (flags.iconic || reassoc)
1701     screen->reassociateWindow(this, BSENTINEL, False);
1702   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1703     return;
1704
1705   show();
1706
1707   // reassociate and deiconify all transients
1708   if (reassoc && client.transientList.size() > 0) {
1709     BlackboxWindowList::iterator it, end = client.transientList.end();
1710     for (it = client.transientList.begin(); it != end; ++it) {
1711       (*it)->deiconify(True, False);
1712     }
1713   }
1714
1715   if (raise)
1716     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1717 }
1718
1719
1720 void BlackboxWindow::close(void) {
1721   XEvent ce;
1722   ce.xclient.type = ClientMessage;
1723   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1724   ce.xclient.display = blackbox->getXDisplay();
1725   ce.xclient.window = client.window;
1726   ce.xclient.format = 32;
1727   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1728   ce.xclient.data.l[1] = CurrentTime;
1729   ce.xclient.data.l[2] = 0l;
1730   ce.xclient.data.l[3] = 0l;
1731   ce.xclient.data.l[4] = 0l;
1732   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1733   XFlush(blackbox->getXDisplay());
1734 }
1735
1736
1737 void BlackboxWindow::withdraw(void) {
1738   // We don't need to worry about resizing because resizing always grabs the X
1739   // server. This should only ever happen if using opaque moving.
1740   if (flags.moving)
1741     endMove();
1742     
1743   flags.visible = False;
1744   flags.iconic = False;
1745
1746   setState(current_state);
1747
1748   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1749
1750   XGrabServer(blackbox->getXDisplay());
1751
1752   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1753   XUnmapWindow(blackbox->getXDisplay(), client.window);
1754   XSelectInput(blackbox->getXDisplay(), client.window,
1755                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1756
1757   XUngrabServer(blackbox->getXDisplay());
1758
1759   if (windowmenu) windowmenu->hide();
1760 }
1761
1762
1763 void BlackboxWindow::maximize(unsigned int button) {
1764   // We don't need to worry about resizing because resizing always grabs the X
1765   // server. This should only ever happen if using opaque moving.
1766   if (flags.moving)
1767     endMove();
1768
1769   // handle case where menu is open then the max button is used instead
1770   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1771
1772   if (flags.maximized) {
1773     flags.maximized = 0;
1774
1775     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1776     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1777
1778     /*
1779       when a resize finishes, maximize(0) is called to clear any maximization
1780       flags currently set.  Otherwise it still thinks it is maximized.
1781       so we do not need to call configure() because resizing will handle it
1782     */
1783     if (! flags.resizing)
1784       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1785                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1786
1787     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1788     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1789
1790     redrawAllButtons(); // in case it is not called in configure()
1791     setState(current_state);
1792     return;
1793   }
1794
1795   blackbox_attrib.premax_x = frame.rect.x();
1796   blackbox_attrib.premax_y = frame.rect.y();
1797   blackbox_attrib.premax_w = frame.rect.width();
1798   // use client.rect so that clients can be restored even if shaded
1799   blackbox_attrib.premax_h =
1800     client.rect.height() + frame.margin.top + frame.margin.bottom;
1801
1802   const Rect &screen_area = screen->availableArea();
1803   frame.changing = screen_area;
1804
1805   switch(button) {
1806   case 1:
1807     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1808     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1809     break;
1810
1811   case 2:
1812     blackbox_attrib.flags |= AttribMaxVert;
1813     blackbox_attrib.attrib |= AttribMaxVert;
1814
1815     frame.changing.setX(frame.rect.x());
1816     frame.changing.setWidth(frame.rect.width());
1817     break;
1818
1819   case 3:
1820     blackbox_attrib.flags |= AttribMaxHoriz;
1821     blackbox_attrib.attrib |= AttribMaxHoriz;
1822
1823     frame.changing.setY(frame.rect.y());
1824     frame.changing.setHeight(frame.rect.height());
1825     break;
1826   }
1827
1828   constrain(TopLeft);
1829
1830   if (flags.shaded) {
1831     blackbox_attrib.flags ^= AttribShaded;
1832     blackbox_attrib.attrib ^= AttribShaded;
1833     flags.shaded = False;
1834   }
1835
1836   flags.maximized = button;
1837
1838   configure(frame.changing.x(), frame.changing.y(),
1839             frame.changing.width(), frame.changing.height());
1840   if (flags.focused)
1841     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1842   redrawAllButtons(); // in case it is not called in configure()
1843   setState(current_state);
1844 }
1845
1846
1847 // re-maximizes the window to take into account availableArea changes
1848 void BlackboxWindow::remaximize(void) {
1849   // save the original dimensions because maximize will wipe them out
1850   int premax_x = blackbox_attrib.premax_x,
1851     premax_y = blackbox_attrib.premax_y,
1852     premax_w = blackbox_attrib.premax_w,
1853     premax_h = blackbox_attrib.premax_h;
1854
1855   unsigned int button = flags.maximized;
1856   flags.maximized = 0; // trick maximize() into working
1857   maximize(button);
1858
1859   // restore saved values
1860   blackbox_attrib.premax_x = premax_x;
1861   blackbox_attrib.premax_y = premax_y;
1862   blackbox_attrib.premax_w = premax_w;
1863   blackbox_attrib.premax_h = premax_h;
1864 }
1865
1866
1867 void BlackboxWindow::setWorkspace(unsigned int n) {
1868   blackbox_attrib.flags |= AttribWorkspace;
1869   blackbox_attrib.workspace = n;
1870   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1871 }
1872
1873
1874 void BlackboxWindow::shade(void) {
1875   if (flags.shaded) {
1876     XResizeWindow(blackbox->getXDisplay(), frame.window,
1877                   frame.inside_w, frame.inside_h);
1878     flags.shaded = False;
1879     blackbox_attrib.flags ^= AttribShaded;
1880     blackbox_attrib.attrib ^= AttribShaded;
1881
1882     setState(NormalState);
1883
1884     // set the frame rect to the normal size
1885     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1886                          frame.margin.bottom);
1887   } else {
1888     if (! (decorations & Decor_Titlebar))
1889       return; // can't shade it without a titlebar!
1890
1891     XResizeWindow(blackbox->getXDisplay(), frame.window,
1892                   frame.inside_w, frame.title_h);
1893     flags.shaded = True;
1894     blackbox_attrib.flags |= AttribShaded;
1895     blackbox_attrib.attrib |= AttribShaded;
1896
1897     setState(IconicState);
1898
1899     // set the frame rect to the shaded size
1900     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1901   }
1902 }
1903
1904
1905 /*
1906  * (Un)Sticks a window and its relatives.
1907  */
1908 void BlackboxWindow::stick(void) {
1909   if (flags.stuck) {
1910     blackbox_attrib.flags ^= AttribOmnipresent;
1911     blackbox_attrib.attrib ^= AttribOmnipresent;
1912
1913     flags.stuck = False;
1914
1915     if (! flags.iconic)
1916       screen->reassociateWindow(this, BSENTINEL, True);
1917     else
1918       // temporary fix since sticky windows suck. set the hint to what we
1919       // actually hold in our data.
1920       xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1921                       blackbox_attrib.workspace);
1922
1923     setState(current_state);
1924   } else {
1925     flags.stuck = True;
1926
1927     blackbox_attrib.flags |= AttribOmnipresent;
1928     blackbox_attrib.attrib |= AttribOmnipresent;
1929
1930     // temporary fix since sticky windows suck. set the hint to a different
1931     // value than that contained in the class' data.
1932     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1933                     0xffffffff);
1934
1935     setState(current_state);
1936   }
1937   // go up the chain
1938   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1939       client.transient_for->isStuck() != flags.stuck)
1940     client.transient_for->stick();
1941   // go down the chain
1942   BlackboxWindowList::iterator it;
1943   const BlackboxWindowList::iterator end = client.transientList.end();
1944   for (it = client.transientList.begin(); it != end; ++it)
1945     if ((*it)->isStuck() != flags.stuck)
1946       (*it)->stick();
1947 }
1948
1949
1950 void BlackboxWindow::redrawWindowFrame(void) const {
1951   if (decorations & Decor_Titlebar) {
1952     if (flags.focused) {
1953       if (frame.ftitle)
1954         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1955                                    frame.title, frame.ftitle);
1956       else
1957         XSetWindowBackground(blackbox->getXDisplay(),
1958                              frame.title, frame.ftitle_pixel);
1959     } else {
1960       if (frame.utitle)
1961         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1962                                    frame.title, frame.utitle);
1963       else
1964         XSetWindowBackground(blackbox->getXDisplay(),
1965                              frame.title, frame.utitle_pixel);
1966     }
1967     XClearWindow(blackbox->getXDisplay(), frame.title);
1968
1969     redrawLabel();
1970     redrawAllButtons();
1971   }
1972
1973   if (decorations & Decor_Handle) {
1974     if (flags.focused) {
1975       if (frame.fhandle)
1976         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1977                                    frame.handle, frame.fhandle);
1978       else
1979         XSetWindowBackground(blackbox->getXDisplay(),
1980                              frame.handle, frame.fhandle_pixel);
1981
1982       if (frame.fgrip) {
1983         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1984                                    frame.left_grip, frame.fgrip);
1985         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1986                                    frame.right_grip, frame.fgrip);
1987       } else {
1988         XSetWindowBackground(blackbox->getXDisplay(),
1989                              frame.left_grip, frame.fgrip_pixel);
1990         XSetWindowBackground(blackbox->getXDisplay(),
1991                              frame.right_grip, frame.fgrip_pixel);
1992       }
1993     } else {
1994       if (frame.uhandle)
1995         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1996                                    frame.handle, frame.uhandle);
1997       else
1998         XSetWindowBackground(blackbox->getXDisplay(),
1999                              frame.handle, frame.uhandle_pixel);
2000
2001       if (frame.ugrip) {
2002         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2003                                    frame.left_grip, frame.ugrip);
2004         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2005                                    frame.right_grip, frame.ugrip);
2006       } else {
2007         XSetWindowBackground(blackbox->getXDisplay(),
2008                              frame.left_grip, frame.ugrip_pixel);
2009         XSetWindowBackground(blackbox->getXDisplay(),
2010                              frame.right_grip, frame.ugrip_pixel);
2011       }
2012     }
2013     XClearWindow(blackbox->getXDisplay(), frame.handle);
2014     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2015     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2016   }
2017
2018   if (decorations & Decor_Border) {
2019     if (flags.focused)
2020       XSetWindowBorder(blackbox->getXDisplay(),
2021                        frame.plate, frame.fborder_pixel);
2022     else
2023       XSetWindowBorder(blackbox->getXDisplay(),
2024                        frame.plate, frame.uborder_pixel);
2025   }
2026 }
2027
2028
2029 void BlackboxWindow::setFocusFlag(bool focus) {
2030   // only focus a window if it is visible
2031   if (focus && !flags.visible)
2032     return;
2033
2034   flags.focused = focus;
2035
2036   redrawWindowFrame();
2037
2038   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2039     if (isFocused()) timer->start();
2040     else timer->stop();
2041   }
2042
2043   if (isFocused())
2044     blackbox->setFocusedWindow(this);
2045 }
2046
2047
2048 void BlackboxWindow::installColormap(bool install) {
2049   int i = 0, ncmap = 0;
2050   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2051                                             client.window, &ncmap);
2052   if (cmaps) {
2053     XWindowAttributes wattrib;
2054     if (XGetWindowAttributes(blackbox->getXDisplay(),
2055                              client.window, &wattrib)) {
2056       if (install) {
2057         // install the window's colormap
2058         for (i = 0; i < ncmap; i++) {
2059           if (*(cmaps + i) == wattrib.colormap)
2060             // this window is using an installed color map... do not install
2061             install = False;
2062         }
2063         // otherwise, install the window's colormap
2064         if (install)
2065           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2066       } else {
2067         // uninstall the window's colormap
2068         for (i = 0; i < ncmap; i++) {
2069           if (*(cmaps + i) == wattrib.colormap)
2070             // we found the colormap to uninstall
2071             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2072         }
2073       }
2074     }
2075
2076     XFree(cmaps);
2077   }
2078 }
2079
2080
2081 void BlackboxWindow::setAllowedActions(void) {
2082   Atom actions[7];
2083   int num = 0;
2084   
2085   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2086   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2087   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2088
2089   if (functions & Func_Move)
2090     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2091   if (functions & Func_Resize)
2092     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2093   if (functions & Func_Maximize) {
2094     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2095     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2096   }
2097
2098   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2099                   actions, num);
2100 }
2101
2102
2103 void BlackboxWindow::setState(unsigned long new_state) {
2104   current_state = new_state;
2105
2106   unsigned long state[2];
2107   state[0] = current_state;
2108   state[1] = None;
2109   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2110  
2111   xatom->setValue(client.window, XAtom::blackbox_attributes,
2112                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2113                   PropBlackboxAttributesElements);
2114
2115   Atom netstate[8];
2116   int num = 0;
2117   if (flags.modal)
2118     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2119   if (flags.shaded)
2120     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2121   if (flags.iconic)
2122     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2123   if (flags.skip_taskbar)
2124     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2125   if (flags.skip_pager)
2126     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2127   if (flags.fullscreen)
2128     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2129   if (flags.maximized == 1 || flags.maximized == 2)
2130     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2131   if (flags.maximized == 1 || flags.maximized == 3)
2132     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2133   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2134                   netstate, num);
2135 }
2136
2137
2138 bool BlackboxWindow::getState(void) {
2139   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2140                              current_state);
2141   if (! ret) current_state = 0;
2142   return ret;
2143 }
2144
2145
2146 void BlackboxWindow::restoreAttributes(void) {
2147   unsigned long num = PropBlackboxAttributesElements;
2148   BlackboxAttributes *net;
2149   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2150                         XAtom::blackbox_attributes, num,
2151                         (unsigned long **)&net))
2152     return;
2153   if (num < PropBlackboxAttributesElements) {
2154     delete [] net;
2155     return;
2156   }
2157
2158   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2159     flags.shaded = False;
2160     unsigned long orig_state = current_state;
2161     shade();
2162
2163     /*
2164       At this point in the life of a window, current_state should only be set
2165       to IconicState if the window was an *icon*, not if it was shaded.
2166     */
2167     if (orig_state != IconicState)
2168       current_state = WithdrawnState;
2169  }
2170
2171   if (net->workspace != screen->getCurrentWorkspaceID() &&
2172       net->workspace < screen->getWorkspaceCount())
2173     screen->reassociateWindow(this, net->workspace, True);
2174
2175   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2176       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2177     // set to WithdrawnState so it will be mapped on the new workspace
2178     if (current_state == NormalState) current_state = WithdrawnState;
2179   } else if (current_state == WithdrawnState) {
2180     // the window is on this workspace and is Withdrawn, so it is waiting to
2181     // be mapped
2182     current_state = NormalState;
2183   }
2184
2185   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2186     flags.stuck = False;
2187     stick();
2188
2189     // if the window was on another workspace, it was going to be hidden. this
2190     // specifies that the window should be mapped since it is sticky.
2191     if (current_state == WithdrawnState) current_state = NormalState;
2192   }
2193
2194   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2195     int x = net->premax_x, y = net->premax_y;
2196     unsigned int w = net->premax_w, h = net->premax_h;
2197     flags.maximized = 0;
2198
2199     unsigned int m = 0;
2200     if ((net->flags & AttribMaxHoriz) &&
2201         (net->flags & AttribMaxVert))
2202       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2203     else if (net->flags & AttribMaxVert)
2204       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2205     else if (net->flags & AttribMaxHoriz)
2206       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2207
2208     if (m) maximize(m);
2209
2210     blackbox_attrib.premax_x = x;
2211     blackbox_attrib.premax_y = y;
2212     blackbox_attrib.premax_w = w;
2213     blackbox_attrib.premax_h = h;
2214   }
2215
2216   // with the state set it will then be the map event's job to read the
2217   // window's state and behave accordingly
2218
2219   delete [] net;
2220 }
2221
2222
2223 /*
2224  * Positions the Rect r according the the client window position and
2225  * window gravity.
2226  */
2227 void BlackboxWindow::applyGravity(Rect &r) {
2228   // apply horizontal window gravity
2229   switch (client.win_gravity) {
2230   default:
2231   case NorthWestGravity:
2232   case SouthWestGravity:
2233   case WestGravity:
2234     r.setX(client.rect.x());
2235     break;
2236
2237   case NorthGravity:
2238   case SouthGravity:
2239   case CenterGravity:
2240     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2241     break;
2242
2243   case NorthEastGravity:
2244   case SouthEastGravity:
2245   case EastGravity:
2246     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2247     break;
2248
2249   case ForgetGravity:
2250   case StaticGravity:
2251     r.setX(client.rect.x() - frame.margin.left);
2252     break;
2253   }
2254
2255   // apply vertical window gravity
2256   switch (client.win_gravity) {
2257   default:
2258   case NorthWestGravity:
2259   case NorthEastGravity:
2260   case NorthGravity:
2261     r.setY(client.rect.y());
2262     break;
2263
2264   case CenterGravity:
2265   case EastGravity:
2266   case WestGravity:
2267     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2268     break;
2269
2270   case SouthWestGravity:
2271   case SouthEastGravity:
2272   case SouthGravity:
2273     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2274     break;
2275
2276   case ForgetGravity:
2277   case StaticGravity:
2278     r.setY(client.rect.y() - frame.margin.top);
2279     break;
2280   }
2281 }
2282
2283
2284 /*
2285  * The reverse of the applyGravity function.
2286  *
2287  * Positions the Rect r according to the frame window position and
2288  * window gravity.
2289  */
2290 void BlackboxWindow::restoreGravity(Rect &r) {
2291   // restore horizontal window gravity
2292   switch (client.win_gravity) {
2293   default:
2294   case NorthWestGravity:
2295   case SouthWestGravity:
2296   case WestGravity:
2297     r.setX(frame.rect.x());
2298     break;
2299
2300   case NorthGravity:
2301   case SouthGravity:
2302   case CenterGravity:
2303     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2304     break;
2305
2306   case NorthEastGravity:
2307   case SouthEastGravity:
2308   case EastGravity:
2309     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2310     break;
2311
2312   case ForgetGravity:
2313   case StaticGravity:
2314     r.setX(frame.rect.x() + frame.margin.left);
2315     break;
2316   }
2317
2318   // restore vertical window gravity
2319   switch (client.win_gravity) {
2320   default:
2321   case NorthWestGravity:
2322   case NorthEastGravity:
2323   case NorthGravity:
2324     r.setY(frame.rect.y());
2325     break;
2326
2327   case CenterGravity:
2328   case EastGravity:
2329   case WestGravity:
2330     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2331     break;
2332
2333   case SouthWestGravity:
2334   case SouthEastGravity:
2335   case SouthGravity:
2336     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2337     break;
2338
2339   case ForgetGravity:
2340   case StaticGravity:
2341     r.setY(frame.rect.y() + frame.margin.top);
2342     break;
2343   }
2344 }
2345
2346
2347 void BlackboxWindow::redrawLabel(void) const {
2348   if (flags.focused) {
2349     if (frame.flabel)
2350       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2351                                  frame.label, frame.flabel);
2352     else
2353       XSetWindowBackground(blackbox->getXDisplay(),
2354                            frame.label, frame.flabel_pixel);
2355   } else {
2356     if (frame.ulabel)
2357       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2358                                  frame.label, frame.ulabel);
2359     else
2360       XSetWindowBackground(blackbox->getXDisplay(),
2361                            frame.label, frame.ulabel_pixel);
2362   }
2363   XClearWindow(blackbox->getXDisplay(), frame.label);
2364
2365   WindowStyle *style = screen->getWindowStyle();
2366
2367   int pos = frame.bevel_w * 2;
2368   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2369   style->font->drawString(frame.label, pos, 1,
2370                           (flags.focused ? style->l_text_focus :
2371                            style->l_text_unfocus),
2372                           client.title);
2373 }
2374
2375
2376 void BlackboxWindow::redrawAllButtons(void) const {
2377   if (frame.iconify_button) redrawIconifyButton(False);
2378   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2379   if (frame.close_button) redrawCloseButton(False);
2380 }
2381
2382
2383 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2384   if (! pressed) {
2385     if (flags.focused) {
2386       if (frame.fbutton)
2387         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2388                                    frame.iconify_button, frame.fbutton);
2389       else
2390         XSetWindowBackground(blackbox->getXDisplay(),
2391                              frame.iconify_button, frame.fbutton_pixel);
2392     } else {
2393       if (frame.ubutton)
2394         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2395                                    frame.iconify_button, frame.ubutton);
2396       else
2397         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2398                              frame.ubutton_pixel);
2399     }
2400   } else {
2401     if (frame.pbutton)
2402       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2403                                  frame.iconify_button, frame.pbutton);
2404     else
2405       XSetWindowBackground(blackbox->getXDisplay(),
2406                            frame.iconify_button, frame.pbutton_pixel);
2407   }
2408   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2409
2410   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2411            screen->getWindowStyle()->b_pic_unfocus);
2412   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2413                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2414 }
2415
2416
2417 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2418   if (! pressed) {
2419     if (flags.focused) {
2420       if (frame.fbutton)
2421         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2422                                    frame.maximize_button, frame.fbutton);
2423       else
2424         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2425                              frame.fbutton_pixel);
2426     } else {
2427       if (frame.ubutton)
2428         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2429                                    frame.maximize_button, frame.ubutton);
2430       else
2431         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2432                              frame.ubutton_pixel);
2433     }
2434   } else {
2435     if (frame.pbutton)
2436       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2437                                  frame.maximize_button, frame.pbutton);
2438     else
2439       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2440                            frame.pbutton_pixel);
2441   }
2442   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2443
2444   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2445            screen->getWindowStyle()->b_pic_unfocus);
2446   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2447                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2448   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2449             2, 3, (frame.button_w - 3), 3);
2450 }
2451
2452
2453 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2454   if (! pressed) {
2455     if (flags.focused) {
2456       if (frame.fbutton)
2457         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2458                                    frame.fbutton);
2459       else
2460         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2461                              frame.fbutton_pixel);
2462     } else {
2463       if (frame.ubutton)
2464         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2465                                    frame.ubutton);
2466       else
2467         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2468                              frame.ubutton_pixel);
2469     }
2470   } else {
2471     if (frame.pbutton)
2472       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2473                                  frame.close_button, frame.pbutton);
2474     else
2475       XSetWindowBackground(blackbox->getXDisplay(),
2476                            frame.close_button, frame.pbutton_pixel);
2477   }
2478   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2479
2480   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2481            screen->getWindowStyle()->b_pic_unfocus);
2482   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2483             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2484   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2485             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2486 }
2487
2488
2489 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2490   if (re->window != client.window)
2491     return;
2492
2493 #ifdef    DEBUG
2494   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2495           client.window);
2496 #endif // DEBUG
2497
2498   switch (current_state) {
2499   case IconicState:
2500     iconify();
2501     break;
2502
2503   case WithdrawnState:
2504     withdraw();
2505     break;
2506
2507   case NormalState:
2508   case InactiveState:
2509   case ZoomState:
2510   default:
2511     show();
2512     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2513     if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2514       XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2515       setInputFocus();
2516     }
2517     break;
2518   }
2519 }
2520
2521
2522 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2523   if (ue->window != client.window)
2524     return;
2525
2526 #ifdef    DEBUG
2527   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2528           client.window);
2529 #endif // DEBUG
2530
2531   screen->unmanageWindow(this, False);
2532 }
2533
2534
2535 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2536   if (de->window != client.window)
2537     return;
2538
2539 #ifdef    DEBUG
2540   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2541           client.window);
2542 #endif // DEBUG
2543
2544   screen->unmanageWindow(this, False);
2545 }
2546
2547
2548 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2549   if (re->window != client.window || re->parent == frame.plate)
2550     return;
2551
2552 #ifdef    DEBUG
2553   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2554           "0x%lx.\n", client.window, re->parent);
2555 #endif // DEBUG
2556
2557   XEvent ev;
2558   ev.xreparent = *re;
2559   XPutBackEvent(blackbox->getXDisplay(), &ev);
2560   screen->unmanageWindow(this, True);
2561 }
2562
2563
2564 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2565   if (pe->state == PropertyDelete)
2566     return;
2567
2568 #ifdef    DEBUG
2569   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2570           client.window);
2571 #endif
2572
2573   switch(pe->atom) {
2574   case XA_WM_CLASS:
2575   case XA_WM_CLIENT_MACHINE:
2576   case XA_WM_COMMAND:
2577     break;
2578
2579   case XA_WM_TRANSIENT_FOR: {
2580     // determine if this is a transient window
2581     getTransientInfo();
2582
2583     // adjust the window decorations based on transience
2584     if (isTransient()) {
2585       decorations &= ~(Decor_Maximize | Decor_Handle);
2586       functions &= ~Func_Maximize;
2587       setAllowedActions();
2588     }
2589
2590     reconfigure();
2591   }
2592     break;
2593
2594   case XA_WM_HINTS:
2595     getWMHints();
2596     break;
2597
2598   case XA_WM_ICON_NAME:
2599     getWMIconName();
2600     if (flags.iconic) screen->propagateWindowName(this);
2601     break;
2602
2603   case XAtom::net_wm_name:
2604   case XA_WM_NAME:
2605     getWMName();
2606
2607     if (decorations & Decor_Titlebar)
2608       redrawLabel();
2609
2610     screen->propagateWindowName(this);
2611     break;
2612
2613   case XA_WM_NORMAL_HINTS: {
2614     getWMNormalHints();
2615
2616     if ((client.normal_hint_flags & PMinSize) &&
2617         (client.normal_hint_flags & PMaxSize)) {
2618       // the window now can/can't resize itself, so the buttons need to be
2619       // regrabbed.
2620       ungrabButtons();
2621       if (client.max_width <= client.min_width &&
2622           client.max_height <= client.min_height) {
2623         decorations &= ~(Decor_Maximize | Decor_Handle);
2624         functions &= ~(Func_Resize | Func_Maximize);
2625       } else {
2626         if (! isTransient()) {
2627           decorations |= Decor_Maximize | Decor_Handle;
2628           functions |= Func_Maximize;
2629         }
2630         functions |= Func_Resize;
2631       }
2632       grabButtons();
2633       setAllowedActions();
2634     }
2635
2636     Rect old_rect = frame.rect;
2637
2638     upsize();
2639
2640     if (old_rect != frame.rect)
2641       reconfigure();
2642
2643     break;
2644   }
2645
2646   default:
2647     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2648       getWMProtocols();
2649
2650       if ((decorations & Decor_Close) && (! frame.close_button)) {
2651         createCloseButton();
2652         if (decorations & Decor_Titlebar) {
2653           positionButtons(True);
2654           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2655         }
2656         if (windowmenu) windowmenu->reconfigure();
2657       }
2658     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2659       updateStrut();
2660     }
2661
2662     break;
2663   }
2664 }
2665
2666
2667 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2668 #ifdef DEBUG
2669   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2670 #endif
2671
2672   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2673     redrawLabel();
2674   else if (frame.close_button == ee->window)
2675     redrawCloseButton(False);
2676   else if (frame.maximize_button == ee->window)
2677     redrawMaximizeButton(flags.maximized);
2678   else if (frame.iconify_button == ee->window)
2679     redrawIconifyButton(False);
2680 }
2681
2682
2683 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2684   if (cr->window != client.window || flags.iconic)
2685     return;
2686
2687   if (cr->value_mask & CWBorderWidth)
2688     client.old_bw = cr->border_width;
2689
2690   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2691     Rect req = frame.rect;
2692
2693     if (cr->value_mask & (CWX | CWY)) {
2694       if (cr->value_mask & CWX)
2695         client.rect.setX(cr->x);
2696       if (cr->value_mask & CWY)
2697         client.rect.setY(cr->y);
2698
2699       applyGravity(req);
2700     }
2701
2702     if (cr->value_mask & CWWidth)
2703       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2704
2705     if (cr->value_mask & CWHeight)
2706       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2707
2708     configure(req.x(), req.y(), req.width(), req.height());
2709   }
2710
2711   if (cr->value_mask & CWStackMode) {
2712     switch (cr->detail) {
2713     case Below:
2714     case BottomIf:
2715       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2716       break;
2717
2718     case Above:
2719     case TopIf:
2720     default:
2721       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2722       break;
2723     }
2724   }
2725 }
2726
2727
2728 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2729 #ifdef DEBUG
2730   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2731           client.window);
2732 #endif
2733
2734   if (frame.maximize_button == be->window) {
2735     redrawMaximizeButton(True);
2736   } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2737     if (! flags.focused)
2738       setInputFocus();
2739
2740     if (frame.iconify_button == be->window) {
2741       redrawIconifyButton(True);
2742     } else if (frame.close_button == be->window) {
2743       redrawCloseButton(True);
2744     } else if (frame.plate == be->window) {
2745       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2746
2747       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2748
2749       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2750     } else {
2751       if (frame.title == be->window || frame.label == be->window) {
2752         if (((be->time - lastButtonPressTime) <=
2753              blackbox->getDoubleClickInterval()) ||
2754             (be->state & ControlMask)) {
2755           lastButtonPressTime = 0;
2756           shade();
2757         } else {
2758           lastButtonPressTime = be->time;
2759         }
2760       }
2761
2762       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2763
2764       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2765     }
2766   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2767              (be->window != frame.close_button)) {
2768     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2769   } else if (windowmenu && be->button == 3 &&
2770              (frame.title == be->window || frame.label == be->window ||
2771               frame.handle == be->window || frame.window == be->window)) {
2772     if (windowmenu->isVisible()) {
2773       windowmenu->hide();
2774     } else {
2775       int mx = be->x_root - windowmenu->getWidth() / 2,
2776           my = be->y_root - windowmenu->getHeight() / 2;
2777
2778       // snap the window menu into a corner/side if necessary
2779       int left_edge, right_edge, top_edge, bottom_edge;
2780
2781       /*
2782          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2783          and height of the menu, as the sizes returned by it do not include
2784          the borders.
2785        */
2786       left_edge = frame.rect.x();
2787       right_edge = frame.rect.right() -
2788         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2789       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2790       bottom_edge = client.rect.bottom() -
2791         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2792         (frame.border_w + frame.mwm_border_w);
2793
2794       if (mx < left_edge)
2795         mx = left_edge;
2796       if (mx > right_edge)
2797         mx = right_edge;
2798       if (my < top_edge)
2799         my = top_edge;
2800       if (my > bottom_edge)
2801         my = bottom_edge;
2802
2803       windowmenu->move(mx, my);
2804       windowmenu->show();
2805       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2806       XRaiseWindow(blackbox->getXDisplay(),
2807                    windowmenu->getSendToMenu()->getWindowID());
2808     }
2809   // mouse wheel up
2810   } else if (be->button == 4) {
2811     if ((be->window == frame.label ||
2812          be->window == frame.title) &&
2813         ! flags.shaded)
2814       shade();
2815   // mouse wheel down
2816   } else if (be->button == 5) {
2817     if ((be->window == frame.label ||
2818          be->window == frame.title) &&
2819         flags.shaded)
2820       shade();
2821   }
2822 }
2823
2824
2825 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2826 #ifdef DEBUG
2827   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2828           client.window);
2829 #endif
2830
2831   if (re->window == frame.maximize_button) {
2832     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2833         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2834       maximize(re->button);
2835     } else {
2836       redrawMaximizeButton(flags.maximized);
2837     }
2838   } else if (re->window == frame.iconify_button) {
2839     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2840         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2841       iconify();
2842     } else {
2843       redrawIconifyButton(False);
2844     }
2845   } else if (re->window == frame.close_button) {
2846     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2847         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2848       close();
2849     redrawCloseButton(False);
2850   } else if (flags.moving) {
2851     endMove();
2852   } else if (flags.resizing) {
2853     endResize();
2854   } else if (re->window == frame.window) {
2855     if (re->button == 2 && re->state == Mod1Mask)
2856       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2857   }
2858 }
2859
2860
2861
2862 void BlackboxWindow::beginMove(int x_root, int y_root) {
2863   assert(! (flags.resizing || flags.moving));
2864
2865   /*
2866     Only one window can be moved/resized at a time. If another window is already
2867     being moved or resized, then stop it before whating to work with this one.
2868   */
2869   BlackboxWindow *changing = blackbox->getChangingWindow();
2870   if (changing && changing != this) {
2871     if (changing->flags.moving)
2872       changing->endMove();
2873     else // if (changing->flags.resizing)
2874       changing->endResize();
2875   }
2876   
2877   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2878                PointerMotionMask | ButtonReleaseMask,
2879                GrabModeAsync, GrabModeAsync,
2880                None, blackbox->getMoveCursor(), CurrentTime);
2881
2882   if (windowmenu && windowmenu->isVisible())
2883     windowmenu->hide();
2884
2885   flags.moving = True;
2886   blackbox->setChangingWindow(this);
2887
2888   if (! screen->doOpaqueMove()) {
2889     XGrabServer(blackbox->getXDisplay());
2890
2891     frame.changing = frame.rect;
2892     screen->showPosition(frame.changing.x(), frame.changing.y());
2893
2894     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2895                    screen->getOpGC(),
2896                    frame.changing.x(),
2897                    frame.changing.y(),
2898                    frame.changing.width() - 1,
2899                    frame.changing.height() - 1);
2900   }
2901
2902   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
2903   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
2904 }
2905
2906
2907 void BlackboxWindow::doMove(int x_root, int y_root) {
2908   assert(flags.moving);
2909   assert(blackbox->getChangingWindow() == this);
2910
2911   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
2912   dx -= frame.border_w;
2913   dy -= frame.border_w;
2914
2915   const int snap_distance = screen->getEdgeSnapThreshold();
2916
2917   if (snap_distance) {
2918     // window corners
2919     const int wleft = dx,
2920               wright = dx + frame.rect.width() - 1,
2921               wtop = dy,
2922               wbottom = dy + frame.rect.height() - 1;
2923
2924     if (screen->getWindowToWindowSnap()) {
2925       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2926       assert(w);
2927
2928       // try snap to another window
2929       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2930         BlackboxWindow *snapwin = w->getWindow(i);
2931         if (snapwin == this)
2932           continue;   // don't snap to self
2933
2934         bool snapped = False;
2935         
2936         const Rect &winrect = snapwin->frameRect();
2937         int dleft = std::abs(wright - winrect.left()),
2938            dright = std::abs(wleft - winrect.right()),
2939              dtop = std::abs(wbottom - winrect.top()),
2940           dbottom = std::abs(wtop - winrect.bottom());
2941
2942         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2943             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2944
2945           // snap left of other window?
2946           if (dleft < snap_distance && dleft <= dright) {
2947             dx = winrect.left() - frame.rect.width();
2948             snapped = True;
2949           }
2950           // snap right of other window?
2951           else if (dright < snap_distance) {
2952             dx = winrect.right() + 1;
2953             snapped = True;
2954           }
2955
2956           if (snapped) {
2957             if (screen->getWindowCornerSnap()) {
2958               // try corner-snap to its other sides
2959               dtop = std::abs(wtop - winrect.top());
2960               dbottom = std::abs(wbottom - winrect.bottom());
2961               if (dtop < snap_distance && dtop <= dbottom)
2962                 dy = winrect.top();
2963               else if (dbottom < snap_distance)
2964                 dy = winrect.bottom() - frame.rect.height() + 1;
2965             }
2966
2967             continue;
2968           }
2969         }
2970
2971         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
2972             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
2973
2974           // snap top of other window?
2975           if (dtop < snap_distance && dtop <= dbottom) {
2976             dy = winrect.top() - frame.rect.height();
2977             snapped = True;
2978           }
2979           // snap bottom of other window?
2980           else if (dbottom < snap_distance) {
2981             dy = winrect.bottom() + 1;
2982             snapped = True;
2983           }
2984
2985           if (snapped) {
2986             if (screen->getWindowCornerSnap()) {
2987               // try corner-snap to its other sides
2988               dleft = std::abs(wleft - winrect.left());
2989               dright = std::abs(wright - winrect.right());
2990               if (dleft < snap_distance && dleft <= dright)
2991                 dx = winrect.left();
2992               else if (dright < snap_distance)
2993                 dx = winrect.right() - frame.rect.width() + 1;
2994             }
2995
2996             continue;
2997           }
2998         }
2999       }
3000     }
3001
3002     // try snap to the screen's available area
3003     Rect srect = screen->availableArea();
3004
3005     int dleft = std::abs(wleft - srect.left()),
3006        dright = std::abs(wright - srect.right()),
3007          dtop = std::abs(wtop - srect.top()),
3008       dbottom = std::abs(wbottom - srect.bottom());
3009
3010     // snap left?
3011     if (dleft < snap_distance && dleft <= dright)
3012       dx = srect.left();
3013     // snap right?
3014     else if (dright < snap_distance)
3015       dx = srect.right() - frame.rect.width() + 1;
3016
3017     // snap top?
3018     if (dtop < snap_distance && dtop <= dbottom)
3019       dy = srect.top();
3020     // snap bottom?
3021     else if (dbottom < snap_distance)
3022       dy = srect.bottom() - frame.rect.height() + 1;
3023
3024     srect = screen->getRect(); // now get the full screen
3025
3026     dleft = std::abs(wleft - srect.left()),
3027       dright = std::abs(wright - srect.right()),
3028       dtop = std::abs(wtop - srect.top()),
3029       dbottom = std::abs(wbottom - srect.bottom());
3030
3031     // snap left?
3032     if (dleft < snap_distance && dleft <= dright)
3033       dx = srect.left();
3034     // snap right?
3035     else if (dright < snap_distance)
3036       dx = srect.right() - frame.rect.width() + 1;
3037
3038     // snap top?
3039     if (dtop < snap_distance && dtop <= dbottom)
3040       dy = srect.top();
3041     // snap bottom?
3042     else if (dbottom < snap_distance)
3043       dy = srect.bottom() - frame.rect.height() + 1;
3044   }
3045
3046   if (screen->doOpaqueMove()) {
3047     configure(dx, dy, frame.rect.width(), frame.rect.height());
3048   } else {
3049     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3050                    screen->getOpGC(),
3051                    frame.changing.x(),
3052                    frame.changing.y(),
3053                    frame.changing.width() - 1,
3054                    frame.changing.height() - 1);
3055
3056     frame.changing.setPos(dx, dy);
3057
3058     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3059                    screen->getOpGC(),
3060                    frame.changing.x(),
3061                    frame.changing.y(),
3062                    frame.changing.width() - 1,
3063                    frame.changing.height() - 1);
3064   }
3065
3066   screen->showPosition(dx, dy);
3067 }
3068
3069
3070 void BlackboxWindow::endMove(void) {
3071   assert(flags.moving);
3072   assert(blackbox->getChangingWindow() == this);
3073
3074   flags.moving = False;
3075   blackbox->setChangingWindow(0);
3076
3077   if (! screen->doOpaqueMove()) {
3078     /* when drawing the rubber band, we need to make sure we only draw inside
3079      * the frame... frame.changing_* contain the new coords for the window,
3080      * so we need to subtract 1 from changing_w/changing_h every where we
3081      * draw the rubber band (for both moving and resizing)
3082      */
3083     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3084                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3085                    frame.changing.width() - 1, frame.changing.height() - 1);
3086       XUngrabServer(blackbox->getXDisplay());
3087   
3088       configure(frame.changing.x(), frame.changing.y(),
3089                 frame.changing.width(), frame.changing.height());
3090   } else {
3091     configure(frame.rect.x(), frame.rect.y(),
3092               frame.rect.width(), frame.rect.height());
3093   }
3094   screen->hideGeometry();
3095
3096   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3097
3098   // if there are any left over motions from the move, drop them now
3099   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3100   XEvent e;
3101   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3102                                 MotionNotify, &e));
3103 }
3104
3105
3106 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3107   assert(! (flags.resizing || flags.moving));
3108
3109   /*
3110     Only one window can be moved/resized at a time. If another window is already
3111     being moved or resized, then stop it before whating to work with this one.
3112   */
3113   BlackboxWindow *changing = blackbox->getChangingWindow();
3114   if (changing && changing != this) {
3115     if (changing->flags.moving)
3116       changing->endMove();
3117     else // if (changing->flags.resizing)
3118       changing->endResize();
3119   }
3120
3121   resize_dir = dir;
3122
3123   Cursor cursor;
3124   Corner anchor;
3125   
3126   switch (resize_dir) {
3127   case BottomLeft:
3128     anchor = TopRight;
3129     cursor = blackbox->getLowerLeftAngleCursor();
3130     break;
3131
3132   case BottomRight:
3133     anchor = TopLeft;
3134     cursor = blackbox->getLowerRightAngleCursor();
3135     break;
3136
3137   case TopLeft:
3138     anchor = BottomRight;
3139     cursor = blackbox->getUpperLeftAngleCursor();
3140     break;
3141
3142   case TopRight:
3143     anchor = BottomLeft;
3144     cursor = blackbox->getUpperRightAngleCursor();
3145     break;
3146
3147   default:
3148     assert(false); // unhandled Corner
3149     return;        // unreachable, for the compiler
3150   }
3151   
3152   XGrabServer(blackbox->getXDisplay());
3153   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3154                PointerMotionMask | ButtonReleaseMask,
3155                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3156
3157   flags.resizing = True;
3158   blackbox->setChangingWindow(this);
3159
3160   int gw, gh;
3161   frame.changing = frame.rect;
3162
3163   constrain(anchor,  &gw, &gh);
3164
3165   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3166                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3167                  frame.changing.width() - 1, frame.changing.height() - 1);
3168
3169   screen->showGeometry(gw, gh);
3170   
3171   frame.grab_x = x_root;
3172   frame.grab_y = y_root;
3173 }
3174
3175
3176 void BlackboxWindow::doResize(int x_root, int y_root) {
3177   assert(flags.resizing);
3178   assert(blackbox->getChangingWindow() == this);
3179
3180   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3181                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3182                  frame.changing.width() - 1, frame.changing.height() - 1);
3183
3184   int gw, gh;
3185   Corner anchor;
3186
3187   switch (resize_dir) {
3188   case BottomLeft:
3189     anchor = TopRight;
3190     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3191                            frame.rect.height() + (y_root - frame.grab_y));
3192     break;
3193   case BottomRight:
3194     anchor = TopLeft;
3195     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3196                            frame.rect.height() + (y_root - frame.grab_y));
3197     break;
3198   case TopLeft:
3199     anchor = BottomRight;
3200     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3201                            frame.rect.height() - (y_root - frame.grab_y));
3202     break;
3203   case TopRight:
3204     anchor = BottomLeft;
3205     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3206                            frame.rect.height() - (y_root - frame.grab_y));
3207     break;
3208
3209   default:
3210     assert(false); // unhandled Corner
3211     return;        // unreachable, for the compiler
3212   }
3213   
3214   constrain(anchor, &gw, &gh);
3215
3216   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3217                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3218                  frame.changing.width() - 1, frame.changing.height() - 1);
3219
3220   screen->showGeometry(gw, gh);
3221 }
3222
3223
3224 void BlackboxWindow::endResize(void) {
3225   assert(flags.resizing);
3226   assert(blackbox->getChangingWindow() == this);
3227
3228   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3229                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3230                  frame.changing.width() - 1, frame.changing.height() - 1);
3231   XUngrabServer(blackbox->getXDisplay());
3232
3233   // unset maximized state after resized when fully maximized
3234   if (flags.maximized == 1)
3235     maximize(0);
3236   
3237   flags.resizing = False;
3238   blackbox->setChangingWindow(0);
3239
3240   configure(frame.changing.x(), frame.changing.y(),
3241             frame.changing.width(), frame.changing.height());
3242   screen->hideGeometry();
3243
3244   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3245   
3246   // if there are any left over motions from the resize, drop them now
3247   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3248   XEvent e;
3249   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3250                                 MotionNotify, &e));
3251 }
3252
3253
3254 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3255 #ifdef DEBUG
3256   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3257           client.window);
3258 #endif
3259
3260   if (flags.moving) {
3261     doMove(me->x_root, me->y_root);
3262   } else if (flags.resizing) {
3263     doResize(me->x_root, me->y_root);
3264   } else {
3265     if (! flags.resizing && (me->state & Button1Mask) &&
3266         (functions & Func_Move) &&
3267         (frame.title == me->window || frame.label == me->window ||
3268          frame.handle == me->window || frame.window == me->window)) {
3269       beginMove(me->x_root, me->y_root);
3270     } else if ((functions & Func_Resize) &&
3271                (((me->state & Button1Mask) &&
3272                  (me->window == frame.right_grip ||
3273                   me->window == frame.left_grip)) ||
3274                 (me->state & (Mod1Mask | Button3Mask) &&
3275                  me->window == frame.window))) {
3276       beginResize(me->x_root, me->y_root,
3277                   (me->window == frame.left_grip) ? BottomLeft : BottomRight);
3278     }
3279   }
3280 }
3281
3282
3283 #ifdef    SHAPE
3284 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3285   if (blackbox->hasShapeExtensions() && flags.shaped) {
3286     configureShape();
3287   }
3288 }
3289 #endif // SHAPE
3290
3291
3292 bool BlackboxWindow::validateClient(void) const {
3293   XSync(blackbox->getXDisplay(), False);
3294
3295   XEvent e;
3296   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3297                              DestroyNotify, &e) ||
3298       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3299                              UnmapNotify, &e)) {
3300     XPutBackEvent(blackbox->getXDisplay(), &e);
3301
3302     return False;
3303   }
3304
3305   return True;
3306 }
3307
3308
3309 void BlackboxWindow::restore(bool remap) {
3310   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3311   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3312   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3313
3314   // do not leave a shaded window as an icon unless it was an icon
3315   if (flags.shaded && ! flags.iconic) setState(NormalState);
3316
3317   restoreGravity(client.rect);
3318
3319   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3320   XUnmapWindow(blackbox->getXDisplay(), client.window);
3321
3322   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3323
3324   XEvent ev;
3325   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3326                              ReparentNotify, &ev)) {
3327     remap = True;
3328   } else {
3329     // according to the ICCCM - if the client doesn't reparent to
3330     // root, then we have to do it for them
3331     XReparentWindow(blackbox->getXDisplay(), client.window,
3332                     screen->getRootWindow(),
3333                     client.rect.x(), client.rect.y());
3334   }
3335
3336   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3337 }
3338
3339
3340 // timer for autoraise
3341 void BlackboxWindow::timeout(void) {
3342   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3343 }
3344
3345
3346 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3347   if ((net->flags & AttribShaded) &&
3348       ((blackbox_attrib.attrib & AttribShaded) !=
3349        (net->attrib & AttribShaded)))
3350     shade();
3351
3352   if (flags.visible && // watch out for requests when we can not be seen
3353       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3354       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3355        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3356     if (flags.maximized) {
3357       maximize(0);
3358     } else {
3359       int button = 0;
3360
3361       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3362         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3363       else if (net->flags & AttribMaxVert)
3364         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3365       else if (net->flags & AttribMaxHoriz)
3366         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3367
3368       maximize(button);
3369     }
3370   }
3371
3372   if ((net->flags & AttribOmnipresent) &&
3373       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3374        (net->attrib & AttribOmnipresent)))
3375     stick();
3376
3377   if ((net->flags & AttribWorkspace) &&
3378       (blackbox_attrib.workspace != net->workspace)) {
3379     screen->reassociateWindow(this, net->workspace, True);
3380
3381     if (screen->getCurrentWorkspaceID() != net->workspace) {
3382       withdraw();
3383     } else {
3384       show();
3385       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3386     }
3387   }
3388
3389   if (net->flags & AttribDecoration) {
3390     switch (net->decoration) {
3391     case DecorNone:
3392       // clear all decorations except close
3393       decorations &= Decor_Close;
3394
3395       break;
3396
3397     default:
3398     case DecorNormal:
3399       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3400   
3401       decorations = ((functions & Func_Resize) && !isTransient() ?
3402                      decorations | Decor_Handle :
3403                      decorations &= ~Decor_Handle);
3404       decorations = (functions & Func_Maximize ?
3405                      decorations | Decor_Maximize :
3406                      decorations &= ~Decor_Maximize);
3407
3408       break;
3409
3410     case DecorTiny:
3411       decorations |= Decor_Titlebar | Decor_Iconify;
3412       decorations &= ~(Decor_Border | Decor_Handle);
3413       
3414       decorations = (functions & Func_Maximize ?
3415                      decorations | Decor_Maximize :
3416                      decorations &= ~Decor_Maximize);
3417
3418       break;
3419
3420     case DecorTool:
3421       decorations |= Decor_Titlebar;
3422       decorations &= ~(Decor_Iconify | Decor_Border);
3423
3424       decorations = ((functions & Func_Resize) && !isTransient() ?
3425                      decorations | Decor_Handle :
3426                      decorations &= ~Decor_Handle);
3427       decorations = (functions & Func_Maximize ?
3428                      decorations | Decor_Maximize :
3429                      decorations &= ~Decor_Maximize);
3430
3431       break;
3432     }
3433
3434     // we can not be shaded if we lack a titlebar
3435     if (flags.shaded && ! (decorations & Decor_Titlebar))
3436       shade();
3437
3438     if (flags.visible && frame.window) {
3439       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3440       XMapWindow(blackbox->getXDisplay(), frame.window);
3441     }
3442
3443     reconfigure();
3444     setState(current_state);
3445   }
3446 }
3447
3448
3449 /*
3450  * Set the sizes of all components of the window frame
3451  * (the window decorations).
3452  * These values are based upon the current style settings and the client
3453  * window's dimensions.
3454  */
3455 void BlackboxWindow::upsize(void) {
3456   frame.bevel_w = screen->getBevelWidth();
3457
3458   if (decorations & Decor_Border) {
3459     frame.border_w = screen->getBorderWidth();
3460     if (! isTransient())
3461       frame.mwm_border_w = screen->getFrameWidth();
3462     else
3463       frame.mwm_border_w = 0;
3464   } else {
3465     frame.mwm_border_w = frame.border_w = 0;
3466   }
3467
3468   if (decorations & Decor_Titlebar) {
3469     // the height of the titlebar is based upon the height of the font being
3470     // used to display the window's title
3471     WindowStyle *style = screen->getWindowStyle();
3472     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3473
3474     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3475     frame.button_w = (frame.label_h - 2);
3476
3477     // set the top frame margin
3478     frame.margin.top = frame.border_w + frame.title_h +
3479                        frame.border_w + frame.mwm_border_w;
3480   } else {
3481     frame.title_h = 0;
3482     frame.label_h = 0;
3483     frame.button_w = 0;
3484
3485     // set the top frame margin
3486     frame.margin.top = frame.border_w + frame.mwm_border_w;
3487   }
3488
3489   // set the left/right frame margin
3490   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3491
3492   if (decorations & Decor_Handle) {
3493     frame.grip_w = frame.button_w * 2;
3494     frame.handle_h = screen->getHandleWidth();
3495
3496     // set the bottom frame margin
3497     frame.margin.bottom = frame.border_w + frame.handle_h +
3498                           frame.border_w + frame.mwm_border_w;
3499   } else {
3500     frame.handle_h = 0;
3501     frame.grip_w = 0;
3502
3503     // set the bottom frame margin
3504     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3505   }
3506
3507   /*
3508     We first get the normal dimensions and use this to define the inside_w/h
3509     then we modify the height if shading is in effect.
3510     If the shade state is not considered then frame.rect gets reset to the
3511     normal window size on a reconfigure() call resulting in improper
3512     dimensions appearing in move/resize and other events.
3513   */
3514   unsigned int
3515     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3516     width = client.rect.width() + frame.margin.left + frame.margin.right;
3517
3518   frame.inside_w = width - (frame.border_w * 2);
3519   frame.inside_h = height - (frame.border_w * 2);
3520
3521   if (flags.shaded)
3522     height = frame.title_h + (frame.border_w * 2);
3523   frame.rect.setSize(width, height);
3524 }
3525
3526
3527 /*
3528  * Calculate the size of the client window and constrain it to the
3529  * size specified by the size hints of the client window.
3530  *
3531  * The logical width and height are placed into pw and ph, if they
3532  * are non-zero.  Logical size refers to the users perception of
3533  * the window size (for example an xterm resizes in cells, not in pixels).
3534  *
3535  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3536  * Physical geometry refers to the geometry of the window in pixels.
3537  */
3538 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3539   // frame.changing represents the requested frame size, we need to
3540   // strip the frame margin off and constrain the client size
3541   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3542                            frame.changing.top() + frame.margin.top,
3543                            frame.changing.right() - frame.margin.right,
3544                            frame.changing.bottom() - frame.margin.bottom);
3545
3546   int dw = frame.changing.width(), dh = frame.changing.height(),
3547     base_width = (client.base_width) ? client.base_width : client.min_width,
3548     base_height = (client.base_height) ? client.base_height :
3549                                          client.min_height;
3550
3551   // constrain
3552   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3553   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3554   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3555   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3556
3557   dw -= base_width;
3558   dw /= client.width_inc;
3559   dh -= base_height;
3560   dh /= client.height_inc;
3561
3562   if (pw) {
3563     if (client.width_inc == 1)
3564       *pw = dw + base_width;
3565     else
3566       *pw = dw;
3567   }
3568   if (ph) {
3569     if (client.height_inc == 1)
3570       *ph = dh + base_height;
3571     else
3572       *ph = dh;
3573   }
3574
3575   dw *= client.width_inc;
3576   dw += base_width;
3577   dh *= client.height_inc;
3578   dh += base_height;
3579
3580   frame.changing.setSize(dw, dh);
3581
3582   // add the frame margin back onto frame.changing
3583   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3584                            frame.changing.top() - frame.margin.top,
3585                            frame.changing.right() + frame.margin.right,
3586                            frame.changing.bottom() + frame.margin.bottom);
3587
3588   // move frame.changing to the specified anchor
3589   int dx = 0,
3590       dy = 0;
3591   switch (anchor) {
3592   case TopLeft:
3593     break;
3594
3595   case TopRight:
3596     dx = frame.rect.right() - frame.changing.right();
3597     break;
3598
3599   case BottomLeft:
3600     dy = frame.rect.bottom() - frame.changing.bottom();
3601     break;
3602
3603   case BottomRight:
3604     dx = frame.rect.right() - frame.changing.right();
3605     dy = frame.rect.bottom() - frame.changing.bottom();
3606     break;
3607
3608   default:
3609     assert(false);  // unhandled corner
3610   }
3611   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3612 }
3613
3614
3615 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3616                             unsigned int max_length,
3617                             unsigned int modifier) const {
3618   size_t text_len = text.size();
3619   unsigned int length;
3620
3621   do {
3622     length = font->measureString(string(text, 0, text_len)) + modifier;
3623   } while (length > max_length && text_len-- > 0);
3624
3625   switch (justify) {
3626   case RightJustify:
3627     start_pos += max_length - length;
3628     break;
3629
3630   case CenterJustify:
3631     start_pos += (max_length - length) / 2;
3632     break;
3633
3634   case LeftJustify:
3635   default:
3636     break;
3637   }
3638 }
3639
3640
3641 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3642   : blackbox(b), group(_group) {
3643   XWindowAttributes wattrib;
3644   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3645     // group window doesn't seem to exist anymore
3646     delete this;
3647     return;
3648   }
3649
3650   /*
3651     watch for destroy notify on the group window (in addition to
3652     any other events we are looking for)
3653
3654     since some managed windows can also be window group controllers,
3655     we need to make sure that we don't clobber the event mask for the
3656     managed window
3657   */
3658   XSelectInput(blackbox->getXDisplay(), group,
3659                wattrib.your_event_mask | StructureNotifyMask);
3660
3661   blackbox->saveGroupSearch(group, this);
3662 }
3663
3664
3665 BWindowGroup::~BWindowGroup(void) {
3666   blackbox->removeGroupSearch(group);
3667 }
3668
3669
3670 BlackboxWindow *
3671 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3672   BlackboxWindow *ret = blackbox->getFocusedWindow();
3673
3674   // does the focus window match (or any transient_fors)?
3675   while (ret) {
3676     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3677       if (ret->isTransient() && allow_transients) break;
3678       else if (! ret->isTransient()) break;
3679     }
3680
3681     ret = ret->getTransientFor();
3682   }
3683
3684   if (ret) return ret;
3685
3686   // the focus window didn't match, look in the group's window list
3687   BlackboxWindowList::const_iterator it, end = windowList.end();
3688   for (it = windowList.begin(); it != end; ++it) {
3689     ret = *it;
3690     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3691       if (ret->isTransient() && allow_transients) break;
3692       else if (! ret->isTransient()) break;
3693     }
3694   }
3695
3696   return ret;
3697 }