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