]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
mouse wheel support
[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)
688     destroyCloseButton();
689   if (! hasiconify)
690     destroyCloseButton();
691   if (! hasmaximize)
692     destroyCloseButton();
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     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
809                       -frame.border_w,
810                       frame.rect.height() - frame.margin.bottom +
811                       frame.mwm_border_w - frame.border_w,
812                       frame.inside_w, frame.handle_h);
813     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
814                       -frame.border_w, -frame.border_w,
815                       frame.grip_w, frame.handle_h);
816     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
817                       frame.inside_w - frame.grip_w - frame.border_w,
818                       -frame.border_w, frame.grip_w, frame.handle_h);
819     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
820     XMapWindow(blackbox->getXDisplay(), frame.handle);
821   } else if (frame.handle) {
822     destroyHandle();
823   }
824 }
825
826
827 void BlackboxWindow::getWMName(void) {
828   XTextProperty text_prop;
829
830   if (XGetWMName(blackbox->getXDisplay(), client.window, &text_prop)) {
831     client.title = textPropertyToString(blackbox->getXDisplay(), text_prop);
832     if (client.title.empty())
833       client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
834     XFree((char *) text_prop.value);
835   } else {
836     client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
837   }
838 }
839
840
841 void BlackboxWindow::getWMIconName(void) {
842   XTextProperty text_prop;
843
844   if (XGetWMIconName(blackbox->getXDisplay(), client.window, &text_prop)) {
845     client.icon_title =
846       textPropertyToString(blackbox->getXDisplay(), text_prop);
847     if (client.icon_title.empty())
848       client.icon_title = client.title;
849     XFree((char *) text_prop.value);
850   } else {
851     client.icon_title = client.title;
852   }
853 }
854
855
856 /*
857  * Retrieve which WM Protocols are supported by the client window.
858  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
859  * window's decorations and allow the close behavior.
860  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
861  * this.
862  */
863 void BlackboxWindow::getWMProtocols(void) {
864   Atom *proto;
865   int num_return = 0;
866
867   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
868                       &proto, &num_return)) {
869     for (int i = 0; i < num_return; ++i) {
870       if (proto[i] == blackbox->getWMDeleteAtom()) {
871         decorations |= Decor_Close;
872         functions |= Func_Close;
873       } else if (proto[i] == blackbox->getWMTakeFocusAtom())
874         flags.send_focus_message = True;
875       else if (proto[i] == blackbox->getBlackboxStructureMessagesAtom())
876         screen->addNetizen(new Netizen(screen, client.window));
877     }
878
879     XFree(proto);
880   }
881 }
882
883
884 /*
885  * Gets the value of the WM_HINTS property.
886  * If the property is not set, then use a set of default values.
887  */
888 void BlackboxWindow::getWMHints(void) {
889   focus_mode = F_Passive;
890   client.initial_state = NormalState;
891
892   // remove from current window group
893   if (client.window_group) {
894     BWindowGroup *group = blackbox->searchGroup(client.window_group);
895     if (group) group->removeWindow(this);
896   }
897   client.window_group = None;
898
899   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
900   if (! wmhint) {
901     return;
902   }
903
904   if (wmhint->flags & InputHint) {
905     if (wmhint->input == True) {
906       if (flags.send_focus_message)
907         focus_mode = F_LocallyActive;
908     } else {
909       if (flags.send_focus_message)
910         focus_mode = F_GloballyActive;
911       else
912         focus_mode = F_NoInput;
913     }
914   }
915
916   if (wmhint->flags & StateHint)
917     client.initial_state = wmhint->initial_state;
918
919   if (wmhint->flags & WindowGroupHint) {
920     client.window_group = wmhint->window_group;
921
922     // add window to the appropriate group
923     BWindowGroup *group = blackbox->searchGroup(client.window_group);
924     if (! group) // no group found, create it!
925       group = new BWindowGroup(blackbox, client.window_group);
926     group->addWindow(this);
927   }
928
929   client.wm_hint_flags = wmhint->flags;
930   XFree(wmhint);
931 }
932
933
934 /*
935  * Gets the value of the WM_NORMAL_HINTS property.
936  * If the property is not set, then use a set of default values.
937  */
938 void BlackboxWindow::getWMNormalHints(void) {
939   long icccm_mask;
940   XSizeHints sizehint;
941
942   client.min_width = client.min_height =
943     client.width_inc = client.height_inc = 1;
944   client.base_width = client.base_height = 0;
945
946   /*
947     use the full screen, not the strut modified size. otherwise when the
948     availableArea changes max_width/height will be incorrect and lead to odd
949     rendering bugs.
950   */
951   const Rect& screen_area = screen->getRect();
952   client.max_width = screen_area.width();
953
954   client.max_height = screen_area.height();
955   client.min_aspect_x = client.min_aspect_y =
956     client.max_aspect_x = client.max_aspect_y = 1;
957   client.win_gravity = NorthWestGravity;
958
959   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
960                           &sizehint, &icccm_mask))
961     return;
962
963   client.normal_hint_flags = sizehint.flags;
964
965   if (sizehint.flags & PMinSize) {
966     client.min_width = sizehint.min_width;
967     client.min_height = sizehint.min_height;
968   }
969
970   if (sizehint.flags & PMaxSize) {
971     client.max_width = sizehint.max_width;
972     client.max_height = sizehint.max_height;
973   }
974
975   if (sizehint.flags & PResizeInc) {
976     client.width_inc = sizehint.width_inc;
977     client.height_inc = sizehint.height_inc;
978   }
979
980   if (sizehint.flags & PAspect) {
981     client.min_aspect_x = sizehint.min_aspect.x;
982     client.min_aspect_y = sizehint.min_aspect.y;
983     client.max_aspect_x = sizehint.max_aspect.x;
984     client.max_aspect_y = sizehint.max_aspect.y;
985   }
986
987   if (sizehint.flags & PBaseSize) {
988     client.base_width = sizehint.base_width;
989     client.base_height = sizehint.base_height;
990   }
991
992   if (sizehint.flags & PWinGravity)
993     client.win_gravity = sizehint.win_gravity;
994 }
995
996
997 /*
998  * Gets the MWM hints for the class' contained window.
999  * This is used while initializing the window to its first state, and not
1000  * thereafter.
1001  * Returns: true if the MWM hints are successfully retreived and applied;
1002  * false if they are not.
1003  */
1004 void BlackboxWindow::getMWMHints(void) {
1005   int format;
1006   Atom atom_return;
1007   unsigned long num, len;
1008   MwmHints *mwm_hint = 0;
1009
1010   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1011                                blackbox->getMotifWMHintsAtom(), 0,
1012                                PropMwmHintsElements, False,
1013                                blackbox->getMotifWMHintsAtom(), &atom_return,
1014                                &format, &num, &len,
1015                                (unsigned char **) &mwm_hint);
1016
1017   if (ret != Success || ! mwm_hint || num != PropMwmHintsElements)
1018     return;
1019
1020   if (mwm_hint->flags & MwmHintsDecorations) {
1021     if (mwm_hint->decorations & MwmDecorAll) {
1022       decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1023                     Decor_Iconify | Decor_Maximize | Decor_Close;
1024     } else {
1025       decorations = 0;
1026
1027       if (mwm_hint->decorations & MwmDecorBorder)
1028         decorations |= Decor_Border;
1029       if (mwm_hint->decorations & MwmDecorHandle)
1030         decorations |= Decor_Handle;
1031       if (mwm_hint->decorations & MwmDecorTitle)
1032         decorations |= Decor_Titlebar;
1033       if (mwm_hint->decorations & MwmDecorIconify)
1034         decorations |= Decor_Iconify;
1035       if (mwm_hint->decorations & MwmDecorMaximize)
1036         decorations |= Decor_Maximize;
1037     }
1038   }
1039
1040   if (mwm_hint->flags & MwmHintsFunctions) {
1041     if (mwm_hint->functions & MwmFuncAll) {
1042       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1043                   Func_Close;
1044     } else {
1045       functions = 0;
1046
1047       if (mwm_hint->functions & MwmFuncResize)
1048         functions |= Func_Resize;
1049       if (mwm_hint->functions & MwmFuncMove)
1050         functions |= Func_Move;
1051       if (mwm_hint->functions & MwmFuncIconify)
1052         functions |= Func_Iconify;
1053       if (mwm_hint->functions & MwmFuncMaximize)
1054         functions |= Func_Maximize;
1055       if (mwm_hint->functions & MwmFuncClose)
1056         functions |= Func_Close;
1057     }
1058   }
1059   XFree(mwm_hint);
1060 }
1061
1062
1063 /*
1064  * Gets the blackbox hints from the class' contained window.
1065  * This is used while initializing the window to its first state, and not
1066  * thereafter.
1067  * Returns: true if the hints are successfully retreived and applied; false if
1068  * they are not.
1069  */
1070 bool BlackboxWindow::getBlackboxHints(void) {
1071   int format;
1072   Atom atom_return;
1073   unsigned long num, len;
1074   BlackboxHints *blackbox_hint = 0;
1075
1076   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1077                                blackbox->getBlackboxHintsAtom(), 0,
1078                                PropBlackboxHintsElements, False,
1079                                blackbox->getBlackboxHintsAtom(), &atom_return,
1080                                &format, &num, &len,
1081                                (unsigned char **) &blackbox_hint);
1082   if (ret != Success || ! blackbox_hint || num != PropBlackboxHintsElements)
1083     return False;
1084
1085   if (blackbox_hint->flags & AttribShaded)
1086     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1087
1088   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1089       (blackbox_hint->flags & AttribMaxVert))
1090     flags.maximized = (blackbox_hint->attrib &
1091                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1092   else if (blackbox_hint->flags & AttribMaxVert)
1093     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1094   else if (blackbox_hint->flags & AttribMaxHoriz)
1095     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1096
1097   if (blackbox_hint->flags & AttribOmnipresent)
1098     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1099
1100   if (blackbox_hint->flags & AttribWorkspace)
1101     blackbox_attrib.workspace = blackbox_hint->workspace;
1102
1103   // if (blackbox_hint->flags & AttribStack)
1104   //   don't yet have always on top/bottom for blackbox yet... working
1105   //   on that
1106
1107   if (blackbox_hint->flags & AttribDecoration) {
1108     switch (blackbox_hint->decoration) {
1109     case DecorNone:
1110       // clear all decorations except close
1111       decorations &= Decor_Close;
1112       // clear all functions except close
1113       functions &= Func_Close;
1114
1115       break;
1116
1117     case DecorTiny:
1118       decorations |= Decor_Titlebar | Decor_Iconify;
1119       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1120       functions |= Func_Move | Func_Iconify;
1121       functions &= ~(Func_Resize | Func_Maximize);
1122
1123       break;
1124
1125     case DecorTool:
1126       decorations |= Decor_Titlebar;
1127       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1128       functions |= Func_Move;
1129       functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1130
1131       break;
1132
1133     case DecorNormal:
1134     default:
1135       decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1136                      Decor_Iconify | Decor_Maximize;
1137       functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
1138
1139       break;
1140     }
1141
1142     reconfigure();
1143   }
1144   XFree(blackbox_hint);
1145   return True;
1146 }
1147
1148
1149 void BlackboxWindow::getTransientInfo(void) {
1150   if (client.transient_for &&
1151       client.transient_for != (BlackboxWindow *) ~0ul) {
1152     // the transient for hint was removed, so we need to tell our
1153     // previous transient_for that we are going away
1154     client.transient_for->client.transientList.remove(this);
1155   }
1156
1157   // we have no transient_for until we find a new one
1158   client.transient_for = 0;
1159
1160   Window trans_for;
1161   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1162                              &trans_for)) {
1163     // transient_for hint not set
1164     return;
1165   }
1166
1167   if (trans_for == client.window) {
1168     // wierd client... treat this window as a normal window
1169     return;
1170   }
1171
1172   if (trans_for == None || trans_for == screen->getRootWindow()) {
1173     // this is an undocumented interpretation of the ICCCM. a transient
1174     // associated with None/Root/itself is assumed to be a modal root
1175     // transient.  we don't support the concept of a global transient,
1176     // so we just associate this transient with nothing, and perhaps
1177     // we will add support later for global modality.
1178     client.transient_for = (BlackboxWindow *) ~0ul;
1179     flags.modal = True;
1180     return;
1181   }
1182
1183   client.transient_for = blackbox->searchWindow(trans_for);
1184   if (! client.transient_for &&
1185       client.window_group && trans_for == client.window_group) {
1186     // no direct transient_for, perhaps this is a group transient?
1187     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1188     if (group) client.transient_for = group->find(screen);
1189   }
1190
1191   if (! client.transient_for || client.transient_for == this) {
1192     // no transient_for found, or we have a wierd client that wants to be
1193     // a transient for itself, so we treat this window as a normal window
1194     client.transient_for = (BlackboxWindow*) 0;
1195     return;
1196   }
1197
1198   // register ourselves with our new transient_for
1199   client.transient_for->client.transientList.push_back(this);
1200   flags.stuck = client.transient_for->flags.stuck;
1201 }
1202
1203
1204 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1205   if (client.transient_for &&
1206       client.transient_for != (BlackboxWindow*) ~0ul)
1207     return client.transient_for;
1208   return 0;
1209 }
1210
1211
1212 void BlackboxWindow::configure(int dx, int dy,
1213                                unsigned int dw, unsigned int dh) {
1214   bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
1215
1216   if ((dw != frame.rect.width()) || (dh != frame.rect.height())) {
1217     frame.rect.setRect(dx, dy, dw, dh);
1218     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1219     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1220
1221     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1222       frame.rect.setPos(0, 0);
1223
1224     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1225                           frame.rect.top() + frame.margin.top,
1226                           frame.rect.right() - frame.margin.right,
1227                           frame.rect.bottom() - frame.margin.bottom);
1228
1229 #ifdef    SHAPE
1230     if (blackbox->hasShapeExtensions() && flags.shaped) {
1231       configureShape();
1232     }
1233 #endif // SHAPE
1234
1235     positionWindows();
1236     decorate();
1237     setFocusFlag(flags.focused);
1238     redrawAllButtons();
1239   } else {
1240     frame.rect.setPos(dx, dy);
1241
1242     XMoveWindow(blackbox->getXDisplay(), frame.window,
1243                 frame.rect.x(), frame.rect.y());
1244
1245     if (! flags.moving) send_event = True;
1246   }
1247
1248   if (send_event && ! flags.moving) {
1249     client.rect.setPos(frame.rect.left() + frame.margin.left,
1250                        frame.rect.top() + frame.margin.top);
1251
1252     XEvent event;
1253     event.type = ConfigureNotify;
1254
1255     event.xconfigure.display = blackbox->getXDisplay();
1256     event.xconfigure.event = client.window;
1257     event.xconfigure.window = client.window;
1258     event.xconfigure.x = client.rect.x();
1259     event.xconfigure.y = client.rect.y();
1260     event.xconfigure.width = client.rect.width();
1261     event.xconfigure.height = client.rect.height();
1262     event.xconfigure.border_width = client.old_bw;
1263     event.xconfigure.above = frame.window;
1264     event.xconfigure.override_redirect = False;
1265
1266     XSendEvent(blackbox->getXDisplay(), client.window, True,
1267                NoEventMask, &event);
1268
1269     screen->updateNetizenConfigNotify(&event);
1270   }
1271 }
1272
1273
1274 #ifdef SHAPE
1275 void BlackboxWindow::configureShape(void) {
1276   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1277                      frame.margin.left - frame.border_w,
1278                      frame.margin.top - frame.border_w,
1279                      client.window, ShapeBounding, ShapeSet);
1280
1281   int num = 0;
1282   XRectangle xrect[2];
1283
1284   if (decorations & Decor_Titlebar) {
1285     xrect[0].x = xrect[0].y = -frame.border_w;
1286     xrect[0].width = frame.rect.width();
1287     xrect[0].height = frame.title_h + (frame.border_w * 2);
1288     ++num;
1289   }
1290
1291   if (decorations & Decor_Handle) {
1292     xrect[1].x = -frame.border_w;
1293     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1294                  frame.mwm_border_w - frame.border_w;
1295     xrect[1].width = frame.rect.width();
1296     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1297     ++num;
1298   }
1299
1300   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1301                           ShapeBounding, 0, 0, xrect, num,
1302                           ShapeUnion, Unsorted);
1303 }
1304 #endif // SHAPE
1305
1306
1307 bool BlackboxWindow::setInputFocus(void) {
1308   if (flags.focused) return True;
1309
1310   if (! client.rect.intersects(screen->getRect())) {
1311     // client is outside the screen, move it to the center
1312     configure((screen->getWidth() - frame.rect.width()) / 2,
1313               (screen->getHeight() - frame.rect.height()) / 2,
1314               frame.rect.width(), frame.rect.height());
1315   }
1316
1317   if (client.transientList.size() > 0) {
1318     // transfer focus to any modal transients
1319     BlackboxWindowList::iterator it, end = client.transientList.end();
1320     for (it = client.transientList.begin(); it != end; ++it) {
1321       if ((*it)->flags.modal) return (*it)->setInputFocus();
1322     }
1323   }
1324
1325   bool ret = True;
1326   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1327     XSetInputFocus(blackbox->getXDisplay(), client.window,
1328                    RevertToPointerRoot, CurrentTime);
1329
1330     blackbox->setFocusedWindow(this);
1331   } else {
1332     /* we could set the focus to none, since the window doesn't accept focus,
1333      * but we shouldn't set focus to nothing since this would surely make
1334      * someone angry
1335      */
1336     ret = False;
1337   }
1338
1339   if (flags.send_focus_message) {
1340     XEvent ce;
1341     ce.xclient.type = ClientMessage;
1342     ce.xclient.message_type = blackbox->getWMProtocolsAtom();
1343     ce.xclient.display = blackbox->getXDisplay();
1344     ce.xclient.window = client.window;
1345     ce.xclient.format = 32;
1346     ce.xclient.data.l[0] = blackbox->getWMTakeFocusAtom();
1347     ce.xclient.data.l[1] = blackbox->getLastTime();
1348     ce.xclient.data.l[2] = 0l;
1349     ce.xclient.data.l[3] = 0l;
1350     ce.xclient.data.l[4] = 0l;
1351     XSendEvent(blackbox->getXDisplay(), client.window, False,
1352                NoEventMask, &ce);
1353   }
1354
1355   return ret;
1356 }
1357
1358
1359 void BlackboxWindow::iconify(void) {
1360   if (flags.iconic) return;
1361
1362   if (windowmenu) windowmenu->hide();
1363
1364   setState(IconicState);
1365
1366   /*
1367    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1368    * we need to clear the event mask on client.window for a split second.
1369    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1370    * split second, leaving us with a ghost window... so, we need to do this
1371    * while the X server is grabbed
1372    */
1373   XGrabServer(blackbox->getXDisplay());
1374   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1375   XUnmapWindow(blackbox->getXDisplay(), client.window);
1376   XSelectInput(blackbox->getXDisplay(), client.window,
1377                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1378   XUngrabServer(blackbox->getXDisplay());
1379
1380   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1381   flags.visible = False;
1382   flags.iconic = True;
1383
1384   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1385
1386   if (isTransient()) {
1387     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1388         ! client.transient_for->flags.iconic) {
1389       // iconify our transient_for
1390       client.transient_for->iconify();
1391     }
1392   }
1393
1394   screen->addIcon(this);
1395
1396   if (client.transientList.size() > 0) {
1397     // iconify all transients
1398     BlackboxWindowList::iterator it, end = client.transientList.end();
1399     for (it = client.transientList.begin(); it != end; ++it) {
1400       if (! (*it)->flags.iconic) (*it)->iconify();
1401     }
1402   }
1403 }
1404
1405
1406 void BlackboxWindow::show(void) {
1407   setState(NormalState);
1408
1409   XMapWindow(blackbox->getXDisplay(), client.window);
1410   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1411   XMapWindow(blackbox->getXDisplay(), frame.window);
1412
1413   flags.visible = True;
1414   flags.iconic = False;
1415 }
1416
1417
1418 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1419   if (flags.iconic || reassoc)
1420     screen->reassociateWindow(this, BSENTINEL, False);
1421   else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID())
1422     return;
1423
1424   show();
1425
1426   // reassociate and deiconify all transients
1427   if (reassoc && client.transientList.size() > 0) {
1428     BlackboxWindowList::iterator it, end = client.transientList.end();
1429     for (it = client.transientList.begin(); it != end; ++it) {
1430       (*it)->deiconify(True, False);
1431     }
1432   }
1433
1434   if (raise)
1435     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1436 }
1437
1438
1439 void BlackboxWindow::close(void) {
1440   XEvent ce;
1441   ce.xclient.type = ClientMessage;
1442   ce.xclient.message_type = blackbox->getWMProtocolsAtom();
1443   ce.xclient.display = blackbox->getXDisplay();
1444   ce.xclient.window = client.window;
1445   ce.xclient.format = 32;
1446   ce.xclient.data.l[0] = blackbox->getWMDeleteAtom();
1447   ce.xclient.data.l[1] = CurrentTime;
1448   ce.xclient.data.l[2] = 0l;
1449   ce.xclient.data.l[3] = 0l;
1450   ce.xclient.data.l[4] = 0l;
1451   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1452 }
1453
1454
1455 void BlackboxWindow::withdraw(void) {
1456   setState(current_state);
1457
1458   flags.visible = False;
1459   flags.iconic = False;
1460
1461   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1462
1463   XGrabServer(blackbox->getXDisplay());
1464   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1465   XUnmapWindow(blackbox->getXDisplay(), client.window);
1466   XSelectInput(blackbox->getXDisplay(), client.window,
1467                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1468     XUngrabServer(blackbox->getXDisplay());
1469
1470   if (windowmenu) windowmenu->hide();
1471 }
1472
1473
1474 void BlackboxWindow::maximize(unsigned int button) {
1475   // handle case where menu is open then the max button is used instead
1476   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1477
1478   if (flags.maximized) {
1479     flags.maximized = 0;
1480
1481     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1482     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1483
1484     /*
1485       when a resize is begun, maximize(0) is called to clear any maximization
1486       flags currently set.  Otherwise it still thinks it is maximized.
1487       so we do not need to call configure() because resizing will handle it
1488     */
1489     if (! flags.resizing)
1490       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1491                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1492
1493     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1494     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1495
1496     redrawAllButtons();
1497     setState(current_state);
1498     return;
1499   }
1500
1501   blackbox_attrib.premax_x = frame.rect.x();
1502   blackbox_attrib.premax_y = frame.rect.y();
1503   blackbox_attrib.premax_w = frame.rect.width();
1504   blackbox_attrib.premax_h = frame.rect.height();
1505
1506   const Rect &screen_area = screen->availableArea();
1507   frame.changing = screen_area;
1508   constrain(TopLeft);
1509
1510   switch(button) {
1511   case 1:
1512     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1513     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1514     break;
1515
1516   case 2:
1517     blackbox_attrib.flags |= AttribMaxVert;
1518     blackbox_attrib.attrib |= AttribMaxVert;
1519
1520     frame.changing.setX(frame.rect.x());
1521     frame.changing.setWidth(frame.rect.width());
1522     break;
1523
1524   case 3:
1525     blackbox_attrib.flags |= AttribMaxHoriz;
1526     blackbox_attrib.attrib |= AttribMaxHoriz;
1527
1528     frame.changing.setY(frame.rect.y());
1529     frame.changing.setHeight(frame.rect.height());
1530     break;
1531   }
1532
1533   if (flags.shaded) {
1534     blackbox_attrib.flags ^= AttribShaded;
1535     blackbox_attrib.attrib ^= AttribShaded;
1536     flags.shaded = False;
1537   }
1538
1539   flags.maximized = button;
1540
1541   configure(frame.changing.x(), frame.changing.y(),
1542             frame.changing.width(), frame.changing.height());
1543   if (flags.focused)
1544     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1545   redrawAllButtons();
1546   setState(current_state);
1547 }
1548
1549
1550 // re-maximizes the window to take into account availableArea changes
1551 void BlackboxWindow::remaximize(void) {
1552   // save the original dimensions because maximize will wipe them out
1553   int premax_x = blackbox_attrib.premax_x,
1554     premax_y = blackbox_attrib.premax_y,
1555     premax_w = blackbox_attrib.premax_w,
1556     premax_h = blackbox_attrib.premax_h;
1557
1558   unsigned int button = flags.maximized;
1559   flags.maximized = 0; // trick maximize() into working
1560   maximize(button);
1561
1562   // restore saved values
1563   blackbox_attrib.premax_x = premax_x;
1564   blackbox_attrib.premax_y = premax_y;
1565   blackbox_attrib.premax_w = premax_w;
1566   blackbox_attrib.premax_h = premax_h;
1567 }
1568
1569
1570 void BlackboxWindow::setWorkspace(unsigned int n) {
1571   blackbox_attrib.flags |= AttribWorkspace;
1572   blackbox_attrib.workspace = n;
1573 }
1574
1575
1576 void BlackboxWindow::shade(void) {
1577   if (flags.shaded) {
1578     XResizeWindow(blackbox->getXDisplay(), frame.window,
1579                   frame.inside_w, frame.inside_h);
1580     flags.shaded = False;
1581     blackbox_attrib.flags ^= AttribShaded;
1582     blackbox_attrib.attrib ^= AttribShaded;
1583
1584     setState(NormalState);
1585
1586     // set the frame rect to the normal size
1587     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1588                          frame.margin.bottom);
1589   } else {
1590     if (! (decorations & Decor_Titlebar))
1591       return;
1592
1593     XResizeWindow(blackbox->getXDisplay(), frame.window,
1594                   frame.inside_w, frame.title_h);
1595     flags.shaded = True;
1596     blackbox_attrib.flags |= AttribShaded;
1597     blackbox_attrib.attrib |= AttribShaded;
1598
1599     setState(IconicState);
1600
1601     // set the frame rect to the shaded size
1602     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1603   }
1604 }
1605
1606
1607 void BlackboxWindow::stick(void) {
1608   if (flags.stuck) {
1609     blackbox_attrib.flags ^= AttribOmnipresent;
1610     blackbox_attrib.attrib ^= AttribOmnipresent;
1611
1612     flags.stuck = False;
1613
1614     if (! flags.iconic)
1615       screen->reassociateWindow(this, BSENTINEL, True);
1616
1617     setState(current_state);
1618   } else {
1619     flags.stuck = True;
1620
1621     blackbox_attrib.flags |= AttribOmnipresent;
1622     blackbox_attrib.attrib |= AttribOmnipresent;
1623
1624     setState(current_state);
1625   }
1626 }
1627
1628
1629 void BlackboxWindow::setFocusFlag(bool focus) {
1630   flags.focused = focus;
1631
1632   if (decorations & Decor_Titlebar) {
1633     if (flags.focused) {
1634       if (frame.ftitle)
1635         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1636                                    frame.title, frame.ftitle);
1637       else
1638         XSetWindowBackground(blackbox->getXDisplay(),
1639                              frame.title, frame.ftitle_pixel);
1640     } else {
1641       if (frame.utitle)
1642         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1643                                    frame.title, frame.utitle);
1644       else
1645         XSetWindowBackground(blackbox->getXDisplay(),
1646                              frame.title, frame.utitle_pixel);
1647     }
1648     XClearWindow(blackbox->getXDisplay(), frame.title);
1649
1650     redrawLabel();
1651     redrawAllButtons();
1652   }
1653
1654   if (decorations & Decor_Handle) {
1655     if (flags.focused) {
1656       if (frame.fhandle)
1657         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1658                                    frame.handle, frame.fhandle);
1659       else
1660         XSetWindowBackground(blackbox->getXDisplay(),
1661                              frame.handle, frame.fhandle_pixel);
1662
1663       if (frame.fgrip) {
1664         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1665                                    frame.left_grip, frame.fgrip);
1666         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1667                                    frame.right_grip, frame.fgrip);
1668       } else {
1669         XSetWindowBackground(blackbox->getXDisplay(),
1670                              frame.left_grip, frame.fgrip_pixel);
1671         XSetWindowBackground(blackbox->getXDisplay(),
1672                              frame.right_grip, frame.fgrip_pixel);
1673       }
1674     } else {
1675       if (frame.uhandle)
1676         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1677                                    frame.handle, frame.uhandle);
1678       else
1679         XSetWindowBackground(blackbox->getXDisplay(),
1680                              frame.handle, frame.uhandle_pixel);
1681
1682       if (frame.ugrip) {
1683         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1684                                    frame.left_grip, frame.ugrip);
1685         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1686                                    frame.right_grip, frame.ugrip);
1687       } else {
1688         XSetWindowBackground(blackbox->getXDisplay(),
1689                              frame.left_grip, frame.ugrip_pixel);
1690         XSetWindowBackground(blackbox->getXDisplay(),
1691                              frame.right_grip, frame.ugrip_pixel);
1692       }
1693     }
1694     XClearWindow(blackbox->getXDisplay(), frame.handle);
1695     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
1696     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
1697   }
1698
1699   if (decorations & Decor_Border) {
1700     if (flags.focused)
1701       XSetWindowBorder(blackbox->getXDisplay(),
1702                        frame.plate, frame.fborder_pixel);
1703     else
1704       XSetWindowBorder(blackbox->getXDisplay(),
1705                        frame.plate, frame.uborder_pixel);
1706   }
1707
1708   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
1709     if (isFocused()) timer->start();
1710     else timer->stop();
1711   }
1712
1713   if (isFocused())
1714     blackbox->setFocusedWindow(this);
1715 }
1716
1717
1718 void BlackboxWindow::installColormap(bool install) {
1719   int i = 0, ncmap = 0;
1720   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
1721                                             client.window, &ncmap);
1722   XWindowAttributes wattrib;
1723   if (cmaps) {
1724     if (XGetWindowAttributes(blackbox->getXDisplay(),
1725                              client.window, &wattrib)) {
1726       if (install) {
1727         // install the window's colormap
1728         for (i = 0; i < ncmap; i++) {
1729           if (*(cmaps + i) == wattrib.colormap)
1730             // this window is using an installed color map... do not install
1731             install = False;
1732         }
1733         // otherwise, install the window's colormap
1734         if (install)
1735           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1736       } else {
1737         // uninstall the window's colormap
1738         for (i = 0; i < ncmap; i++) {
1739           if (*(cmaps + i) == wattrib.colormap)
1740             // we found the colormap to uninstall
1741             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1742         }
1743       }
1744     }
1745
1746     XFree(cmaps);
1747   }
1748 }
1749
1750
1751 void BlackboxWindow::setState(unsigned long new_state) {
1752   current_state = new_state;
1753
1754   unsigned long state[2];
1755   state[0] = current_state;
1756   state[1] = None;
1757   XChangeProperty(blackbox->getXDisplay(), client.window,
1758                   blackbox->getWMStateAtom(), blackbox->getWMStateAtom(), 32,
1759                   PropModeReplace, (unsigned char *) state, 2);
1760
1761   XChangeProperty(blackbox->getXDisplay(), client.window,
1762                   blackbox->getBlackboxAttributesAtom(),
1763                   blackbox->getBlackboxAttributesAtom(), 32, PropModeReplace,
1764                   (unsigned char *) &blackbox_attrib,
1765                   PropBlackboxAttributesElements);
1766 }
1767
1768
1769 bool BlackboxWindow::getState(void) {
1770   current_state = 0;
1771
1772   Atom atom_return;
1773   bool ret = False;
1774   int foo;
1775   unsigned long *state, ulfoo, nitems;
1776
1777   if ((XGetWindowProperty(blackbox->getXDisplay(), client.window,
1778                           blackbox->getWMStateAtom(),
1779                           0l, 2l, False, blackbox->getWMStateAtom(),
1780                           &atom_return, &foo, &nitems, &ulfoo,
1781                           (unsigned char **) &state) != Success) ||
1782       (! state)) {
1783     return False;
1784   }
1785
1786   if (nitems >= 1) {
1787     current_state = static_cast<unsigned long>(state[0]);
1788
1789     ret = True;
1790   }
1791
1792   XFree((void *) state);
1793
1794   return ret;
1795 }
1796
1797
1798 void BlackboxWindow::restoreAttributes(void) {
1799   if (! getState()) current_state = NormalState;
1800
1801   Atom atom_return;
1802   int foo;
1803   unsigned long ulfoo, nitems;
1804
1805   BlackboxAttributes *net;
1806   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1807                                blackbox->getBlackboxAttributesAtom(), 0l,
1808                                PropBlackboxAttributesElements, False,
1809                                blackbox->getBlackboxAttributesAtom(),
1810                                &atom_return, &foo, &nitems, &ulfoo,
1811                                (unsigned char **) &net);
1812   if (ret != Success || ! net || nitems != PropBlackboxAttributesElements)
1813     return;
1814
1815   if (net->flags & AttribShaded &&
1816       net->attrib & AttribShaded) {
1817     int save_state =
1818       ((current_state == IconicState) ? NormalState : current_state);
1819
1820     flags.shaded = False;
1821     shade();
1822
1823     current_state = save_state;
1824   }
1825
1826   if ((net->workspace != screen->getCurrentWorkspaceID()) &&
1827       (net->workspace < screen->getWorkspaceCount())) {
1828     screen->reassociateWindow(this, net->workspace, True);
1829
1830     if (current_state == NormalState) current_state = WithdrawnState;
1831   } else if (current_state == WithdrawnState) {
1832     current_state = NormalState;
1833   }
1834
1835   if (net->flags & AttribOmnipresent &&
1836       net->attrib & AttribOmnipresent) {
1837     flags.stuck = False;
1838     stick();
1839
1840     current_state = NormalState;
1841   }
1842
1843   if ((net->flags & AttribMaxHoriz) ||
1844       (net->flags & AttribMaxVert)) {
1845     int x = net->premax_x, y = net->premax_y;
1846     unsigned int w = net->premax_w, h = net->premax_h;
1847     flags.maximized = 0;
1848
1849     unsigned int m = 0;
1850     if ((net->flags & AttribMaxHoriz) &&
1851         (net->flags & AttribMaxVert))
1852       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1853     else if (net->flags & AttribMaxVert)
1854       m = (net->attrib & AttribMaxVert) ? 2 : 0;
1855     else if (net->flags & AttribMaxHoriz)
1856       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
1857
1858     if (m) maximize(m);
1859
1860     blackbox_attrib.premax_x = x;
1861     blackbox_attrib.premax_y = y;
1862     blackbox_attrib.premax_w = w;
1863     blackbox_attrib.premax_h = h;
1864   }
1865
1866   setState(current_state);
1867
1868   XFree((void *) net);
1869 }
1870
1871
1872 /*
1873  * Positions the frame according the the client window position and window
1874  * gravity.
1875  */
1876 void BlackboxWindow::setGravityOffsets(void) {
1877   // x coordinates for each gravity type
1878   const int x_west = client.rect.x();
1879   const int x_east = client.rect.right() - frame.inside_w + 1;
1880   const int x_center = client.rect.right() - (frame.rect.width()/2) + 1;
1881   // y coordinates for each gravity type
1882   const int y_north = client.rect.y();
1883   const int y_south = client.rect.bottom() - frame.inside_h + 1;
1884   const int y_center = client.rect.bottom() - (frame.rect.height()/2) + 1;
1885
1886   switch (client.win_gravity) {
1887   default:
1888   case NorthWestGravity: frame.rect.setPos(x_west,   y_north);  break;
1889   case NorthGravity:     frame.rect.setPos(x_center, y_north);  break;
1890   case NorthEastGravity: frame.rect.setPos(x_east,   y_north);  break;
1891   case SouthWestGravity: frame.rect.setPos(x_west,   y_south);  break;
1892   case SouthGravity:     frame.rect.setPos(x_center, y_south);  break;
1893   case SouthEastGravity: frame.rect.setPos(x_east,   y_south);  break;
1894   case WestGravity:      frame.rect.setPos(x_west,   y_center); break;
1895   case CenterGravity:    frame.rect.setPos(x_center, y_center); break;
1896   case EastGravity:      frame.rect.setPos(x_east,   y_center); break;
1897
1898   case ForgetGravity:
1899   case StaticGravity:
1900     frame.rect.setPos(client.rect.x() - frame.margin.left,
1901                       client.rect.y() - frame.margin.top);
1902     break;
1903   }
1904 }
1905
1906
1907 /*
1908  * The reverse of the setGravityOffsets function. Uses the frame window's
1909  * position to find the window's reference point.
1910  */
1911 void BlackboxWindow::restoreGravity(void) {
1912   // x coordinates for each gravity type
1913   const int x_west = frame.rect.x();
1914   const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
1915   const int x_center = frame.rect.x() + (frame.rect.width()/2) -
1916                        client.rect.width();
1917   // y coordinates for each gravity type
1918   const int y_north = frame.rect.y();
1919   const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
1920   const int y_center = frame.rect.y() + (frame.rect.height()/2) -
1921                        client.rect.height();
1922
1923   switch(client.win_gravity) {
1924   default:
1925   case NorthWestGravity: client.rect.setPos(x_west,   y_north);  break;
1926   case NorthGravity:     client.rect.setPos(x_center, y_north);  break;
1927   case NorthEastGravity: client.rect.setPos(x_east,   y_north);  break;
1928   case SouthWestGravity: client.rect.setPos(x_west,   y_south);  break;
1929   case SouthGravity:     client.rect.setPos(x_center, y_south);  break;
1930   case SouthEastGravity: client.rect.setPos(x_east,   y_south);  break;
1931   case WestGravity:      client.rect.setPos(x_west,   y_center); break;
1932   case CenterGravity:    client.rect.setPos(x_center, y_center); break;
1933   case EastGravity:      client.rect.setPos(x_east,   y_center); break;
1934
1935   case ForgetGravity:
1936   case StaticGravity:
1937     client.rect.setPos(frame.rect.left() + frame.margin.left,
1938                        frame.rect.top() + frame.margin.top);
1939     break;
1940   }
1941 }
1942
1943
1944 void BlackboxWindow::redrawLabel(void) {
1945   if (flags.focused) {
1946     if (frame.flabel)
1947       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1948                                  frame.label, frame.flabel);
1949     else
1950       XSetWindowBackground(blackbox->getXDisplay(),
1951                            frame.label, frame.flabel_pixel);
1952   } else {
1953     if (frame.ulabel)
1954       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1955                                  frame.label, frame.ulabel);
1956     else
1957       XSetWindowBackground(blackbox->getXDisplay(),
1958                            frame.label, frame.ulabel_pixel);
1959   }
1960   XClearWindow(blackbox->getXDisplay(), frame.label);
1961
1962   WindowStyle *style = screen->getWindowStyle();
1963
1964   int pos = frame.bevel_w * 2,
1965     dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
1966                             frame.bevel_w * 4, i18n.multibyte());
1967
1968   BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
1969            style->font);
1970   if (i18n.multibyte())
1971     XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
1972                   pen.gc(), pos,
1973                   (1 - style->fontset_extents->max_ink_extent.y),
1974                   client.title.c_str(), dlen);
1975   else
1976     XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
1977                 (style->font->ascent + 1), client.title.c_str(), dlen);
1978 }
1979
1980
1981 void BlackboxWindow::redrawAllButtons(void) {
1982   if (frame.iconify_button) redrawIconifyButton(False);
1983   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
1984   if (frame.close_button) redrawCloseButton(False);
1985 }
1986
1987
1988 void BlackboxWindow::redrawIconifyButton(bool pressed) {
1989   if (! pressed) {
1990     if (flags.focused) {
1991       if (frame.fbutton)
1992         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1993                                    frame.iconify_button, frame.fbutton);
1994       else
1995         XSetWindowBackground(blackbox->getXDisplay(),
1996                              frame.iconify_button, frame.fbutton_pixel);
1997     } else {
1998       if (frame.ubutton)
1999         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2000                                    frame.iconify_button, frame.ubutton);
2001       else
2002         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2003                              frame.ubutton_pixel);
2004     }
2005   } else {
2006     if (frame.pbutton)
2007       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2008                                  frame.iconify_button, frame.pbutton);
2009     else
2010       XSetWindowBackground(blackbox->getXDisplay(),
2011                            frame.iconify_button, frame.pbutton_pixel);
2012   }
2013   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2014
2015   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2016            screen->getWindowStyle()->b_pic_unfocus);
2017   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2018                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2019 }
2020
2021
2022 void BlackboxWindow::redrawMaximizeButton(bool pressed) {
2023   if (! pressed) {
2024     if (flags.focused) {
2025       if (frame.fbutton)
2026         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2027                                    frame.maximize_button, frame.fbutton);
2028       else
2029         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2030                              frame.fbutton_pixel);
2031     } else {
2032       if (frame.ubutton)
2033         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2034                                    frame.maximize_button, frame.ubutton);
2035       else
2036         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2037                              frame.ubutton_pixel);
2038     }
2039   } else {
2040     if (frame.pbutton)
2041       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2042                                  frame.maximize_button, frame.pbutton);
2043     else
2044       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2045                            frame.pbutton_pixel);
2046   }
2047   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2048
2049   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2050            screen->getWindowStyle()->b_pic_unfocus);
2051   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2052                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2053   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2054             2, 3, (frame.button_w - 3), 3);
2055 }
2056
2057
2058 void BlackboxWindow::redrawCloseButton(bool pressed) {
2059   if (! pressed) {
2060     if (flags.focused) {
2061       if (frame.fbutton)
2062         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2063                                    frame.fbutton);
2064       else
2065         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2066                              frame.fbutton_pixel);
2067     } else {
2068       if (frame.ubutton)
2069         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2070                                    frame.ubutton);
2071       else
2072         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2073                              frame.ubutton_pixel);
2074     }
2075   } else {
2076     if (frame.pbutton)
2077       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2078                                  frame.close_button, frame.pbutton);
2079     else
2080       XSetWindowBackground(blackbox->getXDisplay(),
2081                            frame.close_button, frame.pbutton_pixel);
2082   }
2083   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2084
2085   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2086            screen->getWindowStyle()->b_pic_unfocus);
2087   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2088             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2089   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2090             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2091 }
2092
2093
2094 void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
2095   if (re->window != client.window)
2096     return;
2097
2098 #ifdef    DEBUG
2099   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2100           client.window);
2101 #endif // DEBUG
2102
2103   bool get_state_ret = getState();
2104   if (! (get_state_ret && blackbox->isStartup())) {
2105     if ((client.wm_hint_flags & StateHint) &&
2106         (! (current_state == NormalState || current_state == IconicState)))
2107       current_state = client.initial_state;
2108     else
2109       current_state = NormalState;
2110   } else if (flags.iconic) {
2111     current_state = NormalState;
2112   }
2113
2114   switch (current_state) {
2115   case IconicState:
2116     iconify();
2117     break;
2118
2119   case WithdrawnState:
2120     withdraw();
2121     break;
2122
2123   case NormalState:
2124   case InactiveState:
2125   case ZoomState:
2126   default:
2127     show();
2128     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2129     if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2130       XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2131       setInputFocus();
2132     }
2133     break;
2134   }
2135 }
2136
2137
2138 void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
2139   if (ue->window != client.window)
2140     return;
2141
2142 #ifdef    DEBUG
2143   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2144           client.window);
2145 #endif // DEBUG
2146
2147   screen->unmanageWindow(this, False);
2148 }
2149
2150
2151 void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
2152   if (de->window != client.window)
2153     return;
2154
2155 #ifdef    DEBUG
2156   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2157           client.window);
2158 #endif // DEBUG
2159
2160   screen->unmanageWindow(this, False);
2161 }
2162
2163
2164 void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
2165   if (re->window != client.window || re->parent == frame.plate)
2166     return;
2167
2168 #ifdef    DEBUG
2169   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2170           "0x%lx.\n", client.window, re->parent);
2171 #endif // DEBUG
2172
2173   XEvent ev;
2174   ev.xreparent = *re;
2175   XPutBackEvent(blackbox->getXDisplay(), &ev);
2176   screen->unmanageWindow(this, True);
2177 }
2178
2179
2180 void BlackboxWindow::propertyNotifyEvent(Atom atom) {
2181   switch(atom) {
2182   case XA_WM_CLASS:
2183   case XA_WM_CLIENT_MACHINE:
2184   case XA_WM_COMMAND:
2185     break;
2186
2187   case XA_WM_TRANSIENT_FOR: {
2188     // determine if this is a transient window
2189     getTransientInfo();
2190
2191     // adjust the window decorations based on transience
2192     if (isTransient()) {
2193       decorations &= ~(Decor_Maximize | Decor_Handle);
2194       functions &= ~Func_Maximize;
2195     }
2196
2197     reconfigure();
2198   }
2199     break;
2200
2201   case XA_WM_HINTS:
2202     getWMHints();
2203     break;
2204
2205   case XA_WM_ICON_NAME:
2206     getWMIconName();
2207     if (flags.iconic) screen->propagateWindowName(this);
2208     break;
2209
2210   case XA_WM_NAME:
2211     getWMName();
2212
2213     if (decorations & Decor_Titlebar)
2214       redrawLabel();
2215
2216     screen->propagateWindowName(this);
2217     break;
2218
2219   case XA_WM_NORMAL_HINTS: {
2220     getWMNormalHints();
2221
2222     if ((client.normal_hint_flags & PMinSize) &&
2223         (client.normal_hint_flags & PMaxSize)) {
2224       if (client.max_width <= client.min_width &&
2225           client.max_height <= client.min_height) {
2226         decorations &= ~(Decor_Maximize | Decor_Handle);
2227         functions &= ~(Func_Resize | Func_Maximize);
2228       } else {
2229         decorations |= Decor_Maximize | Decor_Handle;
2230         functions |= Func_Resize | Func_Maximize;
2231       }
2232     }
2233
2234     Rect old_rect = frame.rect;
2235
2236     upsize();
2237
2238     if (old_rect != frame.rect)
2239       reconfigure();
2240
2241     break;
2242   }
2243
2244   default:
2245     if (atom == blackbox->getWMProtocolsAtom()) {
2246       getWMProtocols();
2247
2248       if ((decorations & Decor_Close) && (! frame.close_button)) {
2249         createCloseButton();
2250         if (decorations & Decor_Titlebar) {
2251           positionButtons(True);
2252           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2253         }
2254         if (windowmenu) windowmenu->reconfigure();
2255       }
2256     }
2257
2258     break;
2259   }
2260 }
2261
2262
2263 void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
2264   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2265     redrawLabel();
2266   else if (frame.close_button == ee->window)
2267     redrawCloseButton(False);
2268   else if (frame.maximize_button == ee->window)
2269     redrawMaximizeButton(flags.maximized);
2270   else if (frame.iconify_button == ee->window)
2271     redrawIconifyButton(False);
2272 }
2273
2274
2275 void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
2276   if (cr->window != client.window || flags.iconic)
2277     return;
2278
2279   int cx = frame.rect.x(), cy = frame.rect.y();
2280   unsigned int cw = frame.rect.width(), ch = frame.rect.height();
2281
2282   if (cr->value_mask & CWBorderWidth)
2283     client.old_bw = cr->border_width;
2284
2285   if (cr->value_mask & CWX)
2286     cx = cr->x - frame.margin.left;
2287
2288   if (cr->value_mask & CWY)
2289     cy = cr->y - frame.margin.top;
2290
2291   if (cr->value_mask & CWWidth)
2292     cw = cr->width + frame.margin.left + frame.margin.right;
2293
2294   if (cr->value_mask & CWHeight)
2295     ch = cr->height + frame.margin.top + frame.margin.bottom;
2296
2297   if (frame.rect != Rect(cx, cy, cw, ch))
2298     configure(cx, cy, cw, ch);
2299
2300   if (cr->value_mask & CWStackMode) {
2301     switch (cr->detail) {
2302     case Below:
2303     case BottomIf:
2304       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2305       break;
2306
2307     case Above:
2308     case TopIf:
2309     default:
2310       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2311       break;
2312     }
2313   }
2314 }
2315
2316
2317 void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
2318   if (frame.maximize_button == be->window) {
2319     redrawMaximizeButton(True);
2320   } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2321     if (! flags.focused)
2322       setInputFocus();
2323
2324     if (frame.iconify_button == be->window) {
2325       redrawIconifyButton(True);
2326     } else if (frame.close_button == be->window) {
2327       redrawCloseButton(True);
2328     } else if (frame.plate == be->window) {
2329       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2330
2331       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2332
2333       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2334     } else {
2335       if (frame.title == be->window || frame.label == be->window) {
2336         if (((be->time - lastButtonPressTime) <=
2337              blackbox->getDoubleClickInterval()) ||
2338             (be->state & ControlMask)) {
2339           lastButtonPressTime = 0;
2340           shade();
2341         } else {
2342           lastButtonPressTime = be->time;
2343         }
2344       }
2345
2346       frame.grab_x = be->x_root - frame.rect.x() - frame.border_w;
2347       frame.grab_y = be->y_root - frame.rect.y() - frame.border_w;
2348
2349       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2350
2351       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2352     }
2353   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2354              (be->window != frame.close_button)) {
2355     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2356   } else if (windowmenu && be->button == 3 &&
2357              (frame.title == be->window || frame.label == be->window ||
2358               frame.handle == be->window || frame.window == be->window)) {
2359     int mx = 0, my = 0;
2360
2361     if (frame.title == be->window || frame.label == be->window) {
2362       mx = be->x_root - (windowmenu->getWidth() / 2);
2363       my = frame.rect.y() + frame.title_h + frame.border_w;
2364     } else if (frame.handle == be->window) {
2365       mx = be->x_root - (windowmenu->getWidth() / 2);
2366       my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
2367            windowmenu->getHeight();
2368     } else {
2369       mx = be->x_root - (windowmenu->getWidth() / 2);
2370
2371       if (be->y <= static_cast<signed>(frame.bevel_w))
2372         my = frame.rect.y() + frame.title_h;
2373       else
2374         my = be->y_root - (windowmenu->getHeight() / 2);
2375     }
2376
2377     // snap the window menu into a corner if necessary - we check the
2378     // position of the menu with the coordinates of the client to
2379     // make the comparisions easier.
2380     // ### this needs some work!
2381     if (mx > client.rect.right() -
2382         static_cast<signed>(windowmenu->getWidth()))
2383       mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
2384     if (mx < client.rect.left())
2385       mx = frame.rect.x();
2386
2387     if (my > client.rect.bottom() -
2388         static_cast<signed>(windowmenu->getHeight()))
2389       my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
2390     if (my < client.rect.top())
2391       my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
2392                              frame.title_h : 0);
2393
2394     if (windowmenu) {
2395       if (! windowmenu->isVisible()) {
2396         windowmenu->move(mx, my);
2397         windowmenu->show();
2398         XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2399         XRaiseWindow(blackbox->getXDisplay(),
2400                      windowmenu->getSendToMenu()->getWindowID());
2401       } else {
2402         windowmenu->hide();
2403       }
2404     }
2405   // mouse wheel up
2406   } else if (be->button == 4) {
2407     if ((be->window == frame.label ||
2408          be->window == frame.title) &&
2409         ! flags.shaded)
2410       shade();
2411   // mouse wheel down
2412   } else if (be->button == 5) {
2413     if ((be->window == frame.label ||
2414          be->window == frame.title) &&
2415         flags.shaded)
2416       shade();
2417   }
2418 }
2419
2420
2421 void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
2422   if (re->window == frame.maximize_button) {
2423     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2424         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2425       maximize(re->button);
2426     } else {
2427       redrawMaximizeButton(flags.maximized);
2428     }
2429   } else if (re->window == frame.iconify_button) {
2430     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2431         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2432       iconify();
2433     } else {
2434       redrawIconifyButton(False);
2435     }
2436   } else if (re->window == frame.close_button) {
2437     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2438         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2439       close();
2440     redrawCloseButton(False);
2441   } else if (flags.moving) {
2442     flags.moving = False;
2443
2444     if (! screen->doOpaqueMove()) {
2445       /* when drawing the rubber band, we need to make sure we only draw inside
2446        * the frame... frame.changing_* contain the new coords for the window,
2447        * so we need to subtract 1 from changing_w/changing_h every where we
2448        * draw the rubber band (for both moving and resizing)
2449        */
2450       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2451                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2452                      frame.changing.width() - 1, frame.changing.height() - 1);
2453       XUngrabServer(blackbox->getXDisplay());
2454
2455       configure(frame.changing.x(), frame.changing.y(),
2456                 frame.changing.width(), frame.changing.height());
2457     } else {
2458       configure(frame.rect.x(), frame.rect.y(),
2459                 frame.rect.width(), frame.rect.height());
2460     }
2461     screen->hideGeometry();
2462     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2463   } else if (flags.resizing) {
2464     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2465                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2466                    frame.changing.width() - 1, frame.changing.height() - 1);
2467     XUngrabServer(blackbox->getXDisplay());
2468
2469     screen->hideGeometry();
2470
2471     constrain((re->window == frame.left_grip) ? TopRight : TopLeft);
2472
2473     // unset maximized state when resized after fully maximized
2474     if (flags.maximized == 1)
2475       maximize(0);
2476     flags.resizing = False;
2477     configure(frame.changing.x(), frame.changing.y(),
2478               frame.changing.width(), frame.changing.height());
2479
2480     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2481   } else if (re->window == frame.window) {
2482     if (re->button == 2 && re->state == Mod1Mask)
2483       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2484   }
2485 }
2486
2487
2488 void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
2489   if (! flags.resizing && (me->state & Button1Mask) &&
2490       (functions & Func_Move) &&
2491       (frame.title == me->window || frame.label == me->window ||
2492        frame.handle == me->window || frame.window == me->window)) {
2493     if (! flags.moving) {
2494       XGrabPointer(blackbox->getXDisplay(), me->window, False,
2495                    Button1MotionMask | ButtonReleaseMask,
2496                    GrabModeAsync, GrabModeAsync,
2497                    None, blackbox->getMoveCursor(), CurrentTime);
2498
2499       if (windowmenu && windowmenu->isVisible())
2500         windowmenu->hide();
2501
2502       flags.moving = True;
2503
2504       if (! screen->doOpaqueMove()) {
2505         XGrabServer(blackbox->getXDisplay());
2506
2507         frame.changing = frame.rect;
2508         screen->showPosition(frame.changing.x(), frame.changing.y());
2509
2510         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2511                        screen->getOpGC(),
2512                        frame.changing.x(),
2513                        frame.changing.y(),
2514                        frame.changing.width() - 1,
2515                        frame.changing.height() - 1);
2516       }
2517     } else {
2518       int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y;
2519       dx -= frame.border_w;
2520       dy -= frame.border_w;
2521
2522       const int snap_distance = screen->getEdgeSnapThreshold();
2523
2524       if (snap_distance) {
2525         Rect srect = screen->availableArea();
2526         // window corners
2527         const int wleft = dx,
2528                  wright = dx + frame.rect.width() - 1,
2529                    wtop = dy,
2530                 wbottom = dy + frame.rect.height() - 1;
2531
2532         int dleft = std::abs(wleft - srect.left()),
2533            dright = std::abs(wright - srect.right()),
2534              dtop = std::abs(wtop - srect.top()),
2535           dbottom = std::abs(wbottom - srect.bottom());
2536
2537         // snap left?
2538         if (dleft < snap_distance && dleft < dright)
2539           dx = srect.left();
2540         // snap right?
2541         else if (dright < snap_distance && dright < dleft)
2542           dx = srect.right() - frame.rect.width() + 1;
2543
2544         // snap top?
2545         if (dtop < snap_distance && dtop < dbottom)
2546           dy = srect.top();
2547         // snap bottom?
2548         else if (dbottom < snap_distance && dbottom < dtop)
2549           dy = srect.bottom() - frame.rect.height() + 1;
2550
2551         srect = screen->getRect(); // now get the full screen
2552
2553         dleft = std::abs(wleft - srect.left()),
2554         dright = std::abs(wright - srect.right()),
2555         dtop = std::abs(wtop - srect.top()),
2556         dbottom = std::abs(wbottom - srect.bottom());
2557
2558         // snap left?
2559         if (dleft < snap_distance && dleft < dright)
2560           dx = srect.left();
2561         // snap right?
2562         else if (dright < snap_distance && dright < dleft)
2563           dx = srect.right() - frame.rect.width() + 1;
2564
2565         // snap top?
2566         if (dtop < snap_distance && dtop < dbottom)
2567           dy = srect.top();
2568         // snap bottom?
2569         else if (dbottom < snap_distance && dbottom < dtop)
2570           dy = srect.bottom() - frame.rect.height() + 1;
2571       }
2572
2573       if (screen->doOpaqueMove()) {
2574         configure(dx, dy, frame.rect.width(), frame.rect.height());
2575       } else {
2576         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2577                        screen->getOpGC(),
2578                        frame.changing.x(),
2579                        frame.changing.y(),
2580                        frame.changing.width() - 1,
2581                        frame.changing.height() - 1);
2582
2583         frame.changing.setPos(dx, dy);
2584
2585         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2586                        screen->getOpGC(),
2587                        frame.changing.x(),
2588                        frame.changing.y(),
2589                        frame.changing.width() - 1,
2590                        frame.changing.height() - 1);
2591       }
2592
2593       screen->showPosition(dx, dy);
2594     }
2595   } else if ((functions & Func_Resize) &&
2596              (((me->state & Button1Mask) &&
2597                (me->window == frame.right_grip ||
2598                 me->window == frame.left_grip)) ||
2599               (me->state & (Mod1Mask | Button3Mask) &&
2600                me->window == frame.window))) {
2601     bool left = (me->window == frame.left_grip);
2602
2603     if (! flags.resizing) {
2604       XGrabServer(blackbox->getXDisplay());
2605       XGrabPointer(blackbox->getXDisplay(), me->window, False,
2606                    ButtonMotionMask | ButtonReleaseMask,
2607                    GrabModeAsync, GrabModeAsync, None,
2608                    ((left) ? blackbox->getLowerLeftAngleCursor() :
2609                     blackbox->getLowerRightAngleCursor()),
2610                    CurrentTime);
2611
2612       flags.resizing = True;
2613
2614       int gw, gh;
2615       frame.grab_x = me->x;
2616       frame.grab_y = me->y;
2617       frame.changing = frame.rect;
2618
2619       constrain((left) ? TopRight : TopLeft, &gw, &gh);
2620
2621       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2622                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2623                      frame.changing.width() - 1, frame.changing.height() - 1);
2624
2625       screen->showGeometry(gw, gh);
2626     } else {
2627       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2628                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2629                      frame.changing.width() - 1, frame.changing.height() - 1);
2630
2631       int gw, gh;
2632
2633       Corner anchor;
2634
2635       if (left) {
2636         anchor = TopRight;
2637         frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(),
2638                                  frame.rect.right(), frame.rect.bottom());
2639         frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y));
2640       } else {
2641         anchor = TopLeft;
2642         frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x),
2643                                frame.rect.height() + (me->y - frame.grab_y));
2644       }
2645
2646       constrain(anchor, &gw, &gh);
2647
2648       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2649                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2650                      frame.changing.width() - 1, frame.changing.height() - 1);
2651
2652       screen->showGeometry(gw, gh);
2653     }
2654   }
2655 }
2656
2657
2658 #ifdef    SHAPE
2659 void BlackboxWindow::shapeEvent(XShapeEvent *) {
2660   if (blackbox->hasShapeExtensions() && flags.shaped) {
2661     configureShape();
2662   }
2663 }
2664 #endif // SHAPE
2665
2666
2667 bool BlackboxWindow::validateClient(void) {
2668   XSync(blackbox->getXDisplay(), False);
2669
2670   XEvent e;
2671   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2672                              DestroyNotify, &e) ||
2673       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2674                              UnmapNotify, &e)) {
2675     XPutBackEvent(blackbox->getXDisplay(), &e);
2676
2677     return False;
2678   }
2679
2680   return True;
2681 }
2682
2683
2684 void BlackboxWindow::restore(bool remap) {
2685   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
2686   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
2687   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
2688
2689   restoreGravity();
2690
2691   XUnmapWindow(blackbox->getXDisplay(), frame.window);
2692   XUnmapWindow(blackbox->getXDisplay(), client.window);
2693
2694   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
2695
2696   XEvent ev;
2697   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2698                              ReparentNotify, &ev)) {
2699     remap = True;
2700   } else {
2701     // according to the ICCCM - if the client doesn't reparent to
2702     // root, then we have to do it for them
2703     XReparentWindow(blackbox->getXDisplay(), client.window,
2704                     screen->getRootWindow(),
2705                     client.rect.x(), client.rect.y());
2706   }
2707
2708   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
2709 }
2710
2711
2712 // timer for autoraise
2713 void BlackboxWindow::timeout(void) {
2714   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2715 }
2716
2717
2718 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
2719   if ((net->flags & AttribShaded) &&
2720       ((blackbox_attrib.attrib & AttribShaded) !=
2721        (net->attrib & AttribShaded)))
2722     shade();
2723
2724   if (flags.visible && // watch out for requests when we can not be seen
2725       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
2726       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
2727        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
2728     if (flags.maximized) {
2729       maximize(0);
2730     } else {
2731       int button = 0;
2732
2733       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
2734         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
2735       else if (net->flags & AttribMaxVert)
2736         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
2737       else if (net->flags & AttribMaxHoriz)
2738         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
2739
2740       maximize(button);
2741     }
2742   }
2743
2744   if ((net->flags & AttribOmnipresent) &&
2745       ((blackbox_attrib.attrib & AttribOmnipresent) !=
2746        (net->attrib & AttribOmnipresent)))
2747     stick();
2748
2749   if ((net->flags & AttribWorkspace) &&
2750       (blackbox_attrib.workspace != net->workspace)) {
2751     screen->reassociateWindow(this, net->workspace, True);
2752
2753     if (screen->getCurrentWorkspaceID() != net->workspace) {
2754       withdraw();
2755     } else {
2756       show();
2757       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2758     }
2759   }
2760
2761   if (net->flags & AttribDecoration) {
2762     switch (net->decoration) {
2763     case DecorNone:
2764       // clear all decorations except close
2765       decorations &= Decor_Close;
2766
2767       break;
2768
2769     default:
2770     case DecorNormal:
2771       decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2772                      Decor_Iconify | Decor_Maximize;
2773
2774       break;
2775
2776     case DecorTiny:
2777       decorations |= Decor_Titlebar | Decor_Iconify;
2778       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2779
2780       break;
2781
2782     case DecorTool:
2783       decorations |= Decor_Titlebar;
2784       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2785       functions |= Func_Move;
2786
2787       break;
2788     }
2789
2790     // we can not be shaded if we lack a titlebar
2791     if (flags.shaded && ! (decorations & Decor_Titlebar))
2792       shade();
2793
2794     if (frame.window) {
2795       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2796       XMapWindow(blackbox->getXDisplay(), frame.window);
2797     }
2798
2799     reconfigure();
2800     setState(current_state);
2801   }
2802 }
2803
2804
2805 /*
2806  * Set the sizes of all components of the window frame
2807  * (the window decorations).
2808  * These values are based upon the current style settings and the client
2809  * window's dimensions.
2810  */
2811 void BlackboxWindow::upsize(void) {
2812   frame.bevel_w = screen->getBevelWidth();
2813
2814   if (decorations & Decor_Border) {
2815     frame.border_w = screen->getBorderWidth();
2816     if (! isTransient())
2817       frame.mwm_border_w = screen->getFrameWidth();
2818     else
2819       frame.mwm_border_w = 0;
2820   } else {
2821     frame.mwm_border_w = frame.border_w = 0;
2822   }
2823
2824   if (decorations & Decor_Titlebar) {
2825     // the height of the titlebar is based upon the height of the font being
2826     // used to display the window's title
2827     WindowStyle *style = screen->getWindowStyle();
2828     if (i18n.multibyte())
2829       frame.title_h = (style->fontset_extents->max_ink_extent.height +
2830                        (frame.bevel_w * 2) + 2);
2831     else
2832       frame.title_h = (style->font->ascent + style->font->descent +
2833                        (frame.bevel_w * 2) + 2);
2834
2835     frame.label_h = frame.title_h - (frame.bevel_w * 2);
2836     frame.button_w = (frame.label_h - 2);
2837
2838     // set the top frame margin
2839     frame.margin.top = frame.border_w + frame.title_h +
2840                        frame.border_w + frame.mwm_border_w;
2841   } else {
2842     frame.title_h = 0;
2843     frame.label_h = 0;
2844     frame.button_w = 0;
2845
2846     // set the top frame margin
2847     frame.margin.top = frame.border_w + frame.mwm_border_w;
2848   }
2849
2850   // set the left/right frame margin
2851   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
2852
2853   if (decorations & Decor_Handle) {
2854     frame.grip_w = frame.button_w * 2;
2855     frame.handle_h = screen->getHandleWidth();
2856
2857     // set the bottom frame margin
2858     frame.margin.bottom = frame.border_w + frame.handle_h +
2859                           frame.border_w + frame.mwm_border_w;
2860   } else {
2861     frame.handle_h = 0;
2862     frame.grip_w = 0;
2863
2864     // set the bottom frame margin
2865     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
2866   }
2867
2868   // set the frame rect
2869   frame.rect.setSize(client.rect.width() + frame.margin.left +
2870                      frame.margin.right,
2871                      client.rect.height() + frame.margin.top +
2872                      frame.margin.bottom);
2873   frame.inside_w = frame.rect.width() - (frame.border_w * 2);
2874   frame.inside_h = frame.rect.height() - (frame.border_w * 2);
2875 }
2876
2877
2878 /*
2879  * Calculate the size of the client window and constrain it to the
2880  * size specified by the size hints of the client window.
2881  *
2882  * The logical width and height are placed into pw and ph, if they
2883  * are non-zero.  Logical size refers to the users perception of
2884  * the window size (for example an xterm has resizes in cells, not in
2885  * pixels).
2886  *
2887  * The physical geometry is placed into frame.changing_{x,y,width,height}.
2888  * Physical geometry refers to the geometry of the window in pixels.
2889  */
2890 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
2891   // frame.changing represents the requested frame size, we need to
2892   // strip the frame margin off and constrain the client size
2893   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
2894                            frame.changing.top() + frame.margin.top,
2895                            frame.changing.right() - frame.margin.right,
2896                            frame.changing.bottom() - frame.margin.bottom);
2897
2898   int dw = frame.changing.width(), dh = frame.changing.height(),
2899     base_width = (client.base_width) ? client.base_width : client.min_width,
2900     base_height = (client.base_height) ? client.base_height :
2901                                          client.min_height;
2902
2903   // constrain
2904   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
2905   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
2906   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
2907   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
2908
2909   dw -= base_width;
2910   dw /= client.width_inc;
2911   dh -= base_height;
2912   dh /= client.height_inc;
2913
2914   if (pw) *pw = dw;
2915   if (ph) *ph = dh;
2916
2917   dw *= client.width_inc;
2918   dw += base_width;
2919   dh *= client.height_inc;
2920   dh += base_height;
2921
2922   frame.changing.setSize(dw, dh);
2923
2924   // add the frame margin back onto frame.changing
2925   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
2926                            frame.changing.top() - frame.margin.top,
2927                            frame.changing.right() + frame.margin.right,
2928                            frame.changing.bottom() + frame.margin.bottom);
2929
2930   // move frame.changing to the specified anchor
2931   switch (anchor) {
2932   case TopLeft:
2933     // nothing to do
2934     break;
2935
2936   case TopRight:
2937     int dx = frame.rect.right() - frame.changing.right();
2938     frame.changing.setPos(frame.changing.x() + dx, frame.changing.y());
2939     break;
2940   }
2941 }
2942
2943
2944 int WindowStyle::doJustify(const char *text, int &start_pos,
2945                            unsigned int max_length, unsigned int modifier,
2946                            bool multibyte) const {
2947   size_t text_len = strlen(text);
2948   unsigned int length;
2949
2950   do {
2951     if (multibyte) {
2952       XRectangle ink, logical;
2953       XmbTextExtents(fontset, text, text_len, &ink, &logical);
2954       length = logical.width;
2955     } else {
2956       length = XTextWidth(font, text, text_len);
2957     }
2958     length += modifier;
2959   } while (length > max_length && text_len-- > 0);
2960
2961   switch (justify) {
2962   case RightJustify:
2963     start_pos += max_length - length;
2964     break;
2965
2966   case CenterJustify:
2967     start_pos += (max_length - length) / 2;
2968     break;
2969
2970   case LeftJustify:
2971   default:
2972     break;
2973   }
2974
2975   return text_len;
2976 }
2977
2978
2979 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
2980   : blackbox(b), group(_group) {
2981   // watch for destroy notify on the group window
2982   XSelectInput(blackbox->getXDisplay(), group, StructureNotifyMask);
2983   blackbox->saveGroupSearch(group, this);
2984 }
2985
2986
2987 BWindowGroup::~BWindowGroup(void) {
2988   blackbox->removeGroupSearch(group);
2989 }
2990
2991
2992 BlackboxWindow *
2993 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
2994   BlackboxWindow *ret = blackbox->getFocusedWindow();
2995
2996   // does the focus window match (or any transient_fors)?
2997   while (ret) {
2998     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
2999       if (ret->isTransient() && allow_transients) break;
3000       else if (! ret->isTransient()) break;
3001     }
3002
3003     ret = ret->getTransientFor();
3004   }
3005
3006   if (ret) return ret;
3007
3008   // the focus window didn't match, look in the group's window list
3009   BlackboxWindowList::const_iterator it, end = windowList.end();
3010   for (it = windowList.begin(); it != end; ++it) {
3011     ret = *it;
3012     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3013       if (ret->isTransient() && allow_transients) break;
3014       else if (! ret->isTransient()) break;
3015     }
3016   }
3017
3018   return ret;
3019 }