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