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