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