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