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