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