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