]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
reindent the help/usage message
[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     // when a resize is begun, maximize(0) is called to clear any maximization
1446     // flags currently set.  Otherwise it still thinks it is maximized.
1447     // so we do not need to call configure() because resizing will handle it
1448     if (!flags.resizing)
1449       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1450                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1451
1452     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1453     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1454
1455     redrawAllButtons();
1456     setState(current_state);
1457     return;
1458   }
1459
1460   blackbox_attrib.premax_x = frame.rect.x();
1461   blackbox_attrib.premax_y = frame.rect.y();
1462   blackbox_attrib.premax_w = frame.rect.width();
1463   blackbox_attrib.premax_h = frame.rect.height();
1464
1465   const Rect &screen_area = screen->availableArea();
1466   frame.changing = screen_area;
1467   constrain(TopLeft);
1468
1469   switch(button) {
1470   case 1:
1471     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1472     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1473     break;
1474
1475   case 2:
1476     blackbox_attrib.flags |= AttribMaxVert;
1477     blackbox_attrib.attrib |= AttribMaxVert;
1478
1479     frame.changing.setX(frame.rect.x());
1480     frame.changing.setWidth(frame.rect.width());
1481     break;
1482
1483   case 3:
1484     blackbox_attrib.flags |= AttribMaxHoriz;
1485     blackbox_attrib.attrib |= AttribMaxHoriz;
1486
1487     frame.changing.setY(frame.rect.y());
1488     frame.changing.setHeight(frame.rect.height());
1489     break;
1490   }
1491
1492   if (flags.shaded) {
1493     blackbox_attrib.flags ^= AttribShaded;
1494     blackbox_attrib.attrib ^= AttribShaded;
1495     flags.shaded = False;
1496   }
1497
1498   flags.maximized = button;
1499
1500   configure(frame.changing.x(), frame.changing.y(),
1501             frame.changing.width(), frame.changing.height());
1502   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1503   redrawAllButtons();
1504   setState(current_state);
1505 }
1506
1507
1508 // re-maximizes the window to take into account availableArea changes
1509 void BlackboxWindow::remaximize(void) {
1510   // save the original dimensions because maximize will wipe them out
1511   int premax_x = blackbox_attrib.premax_x,
1512     premax_y = blackbox_attrib.premax_y,
1513     premax_w = blackbox_attrib.premax_w,
1514     premax_h = blackbox_attrib.premax_h;
1515
1516   unsigned int button = flags.maximized;
1517   flags.maximized = 0; // trick maximize() into working
1518   maximize(button);
1519
1520   // restore saved values
1521   blackbox_attrib.premax_x = premax_x;
1522   blackbox_attrib.premax_y = premax_y;
1523   blackbox_attrib.premax_w = premax_w;
1524   blackbox_attrib.premax_h = premax_h;
1525 }
1526
1527
1528 void BlackboxWindow::setWorkspace(unsigned int n) {
1529   blackbox_attrib.flags |= AttribWorkspace;
1530   blackbox_attrib.workspace = n;
1531 }
1532
1533
1534 void BlackboxWindow::shade(void) {
1535   if (! (decorations & Decor_Titlebar))
1536     return;
1537
1538   if (flags.shaded) {
1539     XResizeWindow(blackbox->getXDisplay(), frame.window,
1540                   frame.inside_w, frame.inside_h);
1541     flags.shaded = False;
1542     blackbox_attrib.flags ^= AttribShaded;
1543     blackbox_attrib.attrib ^= AttribShaded;
1544
1545     setState(NormalState);
1546
1547     // set the frame rect to the normal size
1548     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1549                          frame.margin.bottom);
1550   } else {
1551     XResizeWindow(blackbox->getXDisplay(), frame.window,
1552                   frame.inside_w, frame.title_h);
1553     flags.shaded = True;
1554     blackbox_attrib.flags |= AttribShaded;
1555     blackbox_attrib.attrib |= AttribShaded;
1556
1557     setState(IconicState);
1558
1559     // set the frame rect to the shaded size
1560     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1561   }
1562 }
1563
1564
1565 void BlackboxWindow::stick(void) {
1566   if (flags.stuck) {
1567     blackbox_attrib.flags ^= AttribOmnipresent;
1568     blackbox_attrib.attrib ^= AttribOmnipresent;
1569
1570     flags.stuck = False;
1571
1572     if (! flags.iconic)
1573       screen->reassociateWindow(this, BSENTINEL, True);
1574
1575     setState(current_state);
1576   } else {
1577     flags.stuck = True;
1578
1579     blackbox_attrib.flags |= AttribOmnipresent;
1580     blackbox_attrib.attrib |= AttribOmnipresent;
1581
1582     setState(current_state);
1583   }
1584 }
1585
1586
1587 void BlackboxWindow::setFocusFlag(bool focus) {
1588   flags.focused = focus;
1589
1590   if (decorations & Decor_Titlebar) {
1591     if (flags.focused) {
1592       if (frame.ftitle)
1593         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1594                                    frame.title, frame.ftitle);
1595       else
1596         XSetWindowBackground(blackbox->getXDisplay(),
1597                              frame.title, frame.ftitle_pixel);
1598     } else {
1599       if (frame.utitle)
1600         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1601                                    frame.title, frame.utitle);
1602       else
1603         XSetWindowBackground(blackbox->getXDisplay(),
1604                              frame.title, frame.utitle_pixel);
1605     }
1606     XClearWindow(blackbox->getXDisplay(), frame.title);
1607
1608     redrawLabel();
1609     redrawAllButtons();
1610   }
1611
1612   if (decorations & Decor_Handle) {
1613     if (flags.focused) {
1614       if (frame.fhandle)
1615         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1616                                    frame.handle, frame.fhandle);
1617       else
1618         XSetWindowBackground(blackbox->getXDisplay(),
1619                              frame.handle, frame.fhandle_pixel);
1620
1621       if (frame.fgrip) {
1622         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1623                                    frame.left_grip, frame.fgrip);
1624         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1625                                    frame.right_grip, frame.fgrip);
1626       } else {
1627         XSetWindowBackground(blackbox->getXDisplay(),
1628                              frame.left_grip, frame.fgrip_pixel);
1629         XSetWindowBackground(blackbox->getXDisplay(),
1630                              frame.right_grip, frame.fgrip_pixel);
1631       }
1632     } else {
1633       if (frame.uhandle)
1634         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1635                                    frame.handle, frame.uhandle);
1636       else
1637         XSetWindowBackground(blackbox->getXDisplay(),
1638                              frame.handle, frame.uhandle_pixel);
1639
1640       if (frame.ugrip) {
1641         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1642                                    frame.left_grip, frame.ugrip);
1643         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1644                                    frame.right_grip, frame.ugrip);
1645       } else {
1646         XSetWindowBackground(blackbox->getXDisplay(),
1647                              frame.left_grip, frame.ugrip_pixel);
1648         XSetWindowBackground(blackbox->getXDisplay(),
1649                              frame.right_grip, frame.ugrip_pixel);
1650       }
1651     }
1652     XClearWindow(blackbox->getXDisplay(), frame.handle);
1653     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
1654     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
1655   }
1656
1657   if (decorations & Decor_Border) {
1658     if (flags.focused)
1659       XSetWindowBorder(blackbox->getXDisplay(),
1660                        frame.plate, frame.fborder_pixel);
1661     else
1662       XSetWindowBorder(blackbox->getXDisplay(),
1663                        frame.plate, frame.uborder_pixel);
1664   }
1665
1666   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
1667     if (isFocused()) timer->start();
1668     else timer->stop();
1669   }
1670
1671   if (isFocused())
1672     blackbox->setFocusedWindow(this);
1673 }
1674
1675
1676 void BlackboxWindow::installColormap(bool install) {
1677   int i = 0, ncmap = 0;
1678   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
1679                                             client.window, &ncmap);
1680   XWindowAttributes wattrib;
1681   if (cmaps) {
1682     if (XGetWindowAttributes(blackbox->getXDisplay(),
1683                              client.window, &wattrib)) {
1684       if (install) {
1685         // install the window's colormap
1686         for (i = 0; i < ncmap; i++) {
1687           if (*(cmaps + i) == wattrib.colormap)
1688             // this window is using an installed color map... do not install
1689             install = False;
1690         }
1691         // otherwise, install the window's colormap
1692         if (install)
1693           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1694       } else {
1695         // uninstall the window's colormap
1696         for (i = 0; i < ncmap; i++) {
1697           if (*(cmaps + i) == wattrib.colormap)
1698             // we found the colormap to uninstall
1699             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1700         }
1701       }
1702     }
1703
1704     XFree(cmaps);
1705   }
1706 }
1707
1708
1709 void BlackboxWindow::setState(unsigned long new_state) {
1710   current_state = new_state;
1711
1712   unsigned long state[2];
1713   state[0] = current_state;
1714   state[1] = None;
1715   XChangeProperty(blackbox->getXDisplay(), client.window,
1716                   blackbox->getWMStateAtom(), blackbox->getWMStateAtom(), 32,
1717                   PropModeReplace, (unsigned char *) state, 2);
1718
1719   XChangeProperty(blackbox->getXDisplay(), client.window,
1720                   blackbox->getBlackboxAttributesAtom(),
1721                   blackbox->getBlackboxAttributesAtom(), 32, PropModeReplace,
1722                   (unsigned char *) &blackbox_attrib,
1723                   PropBlackboxAttributesElements);
1724 }
1725
1726
1727 bool BlackboxWindow::getState(void) {
1728   current_state = 0;
1729
1730   Atom atom_return;
1731   bool ret = False;
1732   int foo;
1733   unsigned long *state, ulfoo, nitems;
1734
1735   if ((XGetWindowProperty(blackbox->getXDisplay(), client.window,
1736                           blackbox->getWMStateAtom(),
1737                           0l, 2l, False, blackbox->getWMStateAtom(),
1738                           &atom_return, &foo, &nitems, &ulfoo,
1739                           (unsigned char **) &state) != Success) ||
1740       (! state)) {
1741     return False;
1742   }
1743
1744   if (nitems >= 1) {
1745     current_state = static_cast<unsigned long>(state[0]);
1746
1747     ret = True;
1748   }
1749
1750   XFree((void *) state);
1751
1752   return ret;
1753 }
1754
1755
1756 void BlackboxWindow::restoreAttributes(void) {
1757   if (! getState()) current_state = NormalState;
1758
1759   Atom atom_return;
1760   int foo;
1761   unsigned long ulfoo, nitems;
1762
1763   BlackboxAttributes *net;
1764   int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1765                                blackbox->getBlackboxAttributesAtom(), 0l,
1766                                PropBlackboxAttributesElements, False,
1767                                blackbox->getBlackboxAttributesAtom(),
1768                                &atom_return, &foo, &nitems, &ulfoo,
1769                                (unsigned char **) &net);
1770   if (ret != Success || !net || nitems != PropBlackboxAttributesElements)
1771     return;
1772
1773   if (net->flags & AttribShaded &&
1774       net->attrib & AttribShaded) {
1775     int save_state =
1776       ((current_state == IconicState) ? NormalState : current_state);
1777
1778     flags.shaded = False;
1779     shade();
1780
1781     current_state = save_state;
1782   }
1783
1784   if ((net->workspace != screen->getCurrentWorkspaceID()) &&
1785       (net->workspace < screen->getWorkspaceCount())) {
1786     screen->reassociateWindow(this, net->workspace, True);
1787
1788     if (current_state == NormalState) current_state = WithdrawnState;
1789   } else if (current_state == WithdrawnState) {
1790     current_state = NormalState;
1791   }
1792
1793   if (net->flags & AttribOmnipresent &&
1794       net->attrib & AttribOmnipresent) {
1795     flags.stuck = False;
1796     stick();
1797
1798     current_state = NormalState;
1799   }
1800
1801   if ((net->flags & AttribMaxHoriz) ||
1802       (net->flags & AttribMaxVert)) {
1803     int x = net->premax_x, y = net->premax_y;
1804     unsigned int w = net->premax_w, h = net->premax_h;
1805     flags.maximized = 0;
1806
1807     unsigned int m = 0;
1808     if ((net->flags & AttribMaxHoriz) &&
1809         (net->flags & AttribMaxVert))
1810       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1811     else if (net->flags & AttribMaxVert)
1812       m = (net->attrib & AttribMaxVert) ? 2 : 0;
1813     else if (net->flags & AttribMaxHoriz)
1814       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
1815
1816     if (m) maximize(m);
1817
1818     blackbox_attrib.premax_x = x;
1819     blackbox_attrib.premax_y = y;
1820     blackbox_attrib.premax_w = w;
1821     blackbox_attrib.premax_h = h;
1822   }
1823
1824   setState(current_state);
1825
1826   XFree((void *) net);
1827 }
1828
1829
1830 /*
1831  * Positions the frame according the the client window position and window
1832  * gravity.
1833  */
1834 void BlackboxWindow::setGravityOffsets(void) {
1835   // x coordinates for each gravity type
1836   const int x_west = client.rect.x();
1837   const int x_east = client.rect.right() - frame.inside_w + 1;
1838   const int x_center = client.rect.right() - (frame.rect.width()/2) + 1;
1839   // y coordinates for each gravity type
1840   const int y_north = client.rect.y();
1841   const int y_south = client.rect.bottom() - frame.inside_h + 1;
1842   const int y_center = client.rect.bottom() - (frame.rect.height()/2) + 1;
1843
1844   switch (client.win_gravity) {
1845   default:
1846   case NorthWestGravity: frame.rect.setPos(x_west,   y_north);  break;
1847   case NorthGravity:     frame.rect.setPos(x_center, y_north);  break;
1848   case NorthEastGravity: frame.rect.setPos(x_east,   y_north);  break;
1849   case SouthWestGravity: frame.rect.setPos(x_west,   y_south);  break;
1850   case SouthGravity:     frame.rect.setPos(x_center, y_south);  break;
1851   case SouthEastGravity: frame.rect.setPos(x_east,   y_south);  break;
1852   case WestGravity:      frame.rect.setPos(x_west,   y_center); break;
1853   case CenterGravity:    frame.rect.setPos(x_center, y_center); break;
1854   case EastGravity:      frame.rect.setPos(x_east,   y_center); break;
1855
1856   case ForgetGravity:
1857   case StaticGravity:
1858     frame.rect.setPos(client.rect.x() - frame.margin.left,
1859                       client.rect.y() - frame.margin.top);
1860     break;
1861   }
1862 }
1863
1864
1865 /*
1866  * The reverse of the setGravityOffsets function. Uses the frame window's
1867  * position to find the window's reference point.
1868  */
1869 void BlackboxWindow::restoreGravity(void) {
1870   // x coordinates for each gravity type
1871   const int x_west = frame.rect.x();
1872   const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
1873   const int x_center = frame.rect.x() + (frame.rect.width()/2) -
1874                        client.rect.width();
1875   // y coordinates for each gravity type
1876   const int y_north = frame.rect.y();
1877   const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
1878   const int y_center = frame.rect.y() + (frame.rect.height()/2) -
1879                        client.rect.height();
1880
1881   switch(client.win_gravity) {
1882   default:
1883   case NorthWestGravity: client.rect.setPos(x_west,   y_north);  break;
1884   case NorthGravity:     client.rect.setPos(x_center, y_north);  break;
1885   case NorthEastGravity: client.rect.setPos(x_east,   y_north);  break;
1886   case SouthWestGravity: client.rect.setPos(x_west,   y_south);  break;
1887   case SouthGravity:     client.rect.setPos(x_center, y_south);  break;
1888   case SouthEastGravity: client.rect.setPos(x_east,   y_south);  break;
1889   case WestGravity:      client.rect.setPos(x_west,   y_center); break;
1890   case CenterGravity:    client.rect.setPos(x_center, y_center); break;
1891   case EastGravity:      client.rect.setPos(x_east,   y_center); break;
1892
1893   case ForgetGravity:
1894   case StaticGravity:
1895     client.rect.setPos(frame.rect.left() + frame.margin.left,
1896                        frame.rect.top() + frame.margin.top);
1897     break;
1898   }
1899 }
1900
1901
1902 void BlackboxWindow::redrawLabel(void) {
1903   if (flags.focused) {
1904     if (frame.flabel)
1905       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1906                                  frame.label, frame.flabel);
1907     else
1908       XSetWindowBackground(blackbox->getXDisplay(),
1909                            frame.label, frame.flabel_pixel);
1910   } else {
1911     if (frame.ulabel)
1912       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1913                                  frame.label, frame.ulabel);
1914     else
1915       XSetWindowBackground(blackbox->getXDisplay(),
1916                            frame.label, frame.ulabel_pixel);
1917   }
1918   XClearWindow(blackbox->getXDisplay(), frame.label);
1919
1920   WindowStyle *style = screen->getWindowStyle();
1921
1922   int pos = frame.bevel_w * 2,
1923     dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
1924                             frame.bevel_w * 4, i18n.multibyte());
1925
1926   BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
1927            style->font);
1928   if (i18n.multibyte())
1929     XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
1930                   pen.gc(), pos,
1931                   (1 - style->fontset_extents->max_ink_extent.y),
1932                   client.title.c_str(), dlen);
1933   else
1934     XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
1935                 (style->font->ascent + 1), client.title.c_str(), dlen);
1936 }
1937
1938
1939 void BlackboxWindow::redrawAllButtons(void) {
1940   if (frame.iconify_button) redrawIconifyButton(False);
1941   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
1942   if (frame.close_button) redrawCloseButton(False);
1943 }
1944
1945
1946 void BlackboxWindow::redrawIconifyButton(bool pressed) {
1947   if (! pressed) {
1948     if (flags.focused) {
1949       if (frame.fbutton)
1950         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1951                                    frame.iconify_button, frame.fbutton);
1952       else
1953         XSetWindowBackground(blackbox->getXDisplay(),
1954                              frame.iconify_button, frame.fbutton_pixel);
1955     } else {
1956       if (frame.ubutton)
1957         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1958                                    frame.iconify_button, frame.ubutton);
1959       else
1960         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
1961                              frame.ubutton_pixel);
1962     }
1963   } else {
1964     if (frame.pbutton)
1965       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1966                                  frame.iconify_button, frame.pbutton);
1967     else
1968       XSetWindowBackground(blackbox->getXDisplay(),
1969                            frame.iconify_button, frame.pbutton_pixel);
1970   }
1971   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
1972
1973   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
1974            screen->getWindowStyle()->b_pic_unfocus);
1975   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
1976                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
1977 }
1978
1979
1980 void BlackboxWindow::redrawMaximizeButton(bool pressed) {
1981   if (! pressed) {
1982     if (flags.focused) {
1983       if (frame.fbutton)
1984         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1985                                    frame.maximize_button, frame.fbutton);
1986       else
1987         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
1988                              frame.fbutton_pixel);
1989     } else {
1990       if (frame.ubutton)
1991         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1992                                    frame.maximize_button, frame.ubutton);
1993       else
1994         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
1995                              frame.ubutton_pixel);
1996     }
1997   } else {
1998     if (frame.pbutton)
1999       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2000                                  frame.maximize_button, frame.pbutton);
2001     else
2002       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2003                            frame.pbutton_pixel);
2004   }
2005   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2006
2007   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2008            screen->getWindowStyle()->b_pic_unfocus);
2009   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2010                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2011   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2012             2, 3, (frame.button_w - 3), 3);
2013 }
2014
2015
2016 void BlackboxWindow::redrawCloseButton(bool pressed) {
2017   if (! pressed) {
2018     if (flags.focused) {
2019       if (frame.fbutton)
2020         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2021                                    frame.fbutton);
2022       else
2023         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2024                              frame.fbutton_pixel);
2025     } else {
2026       if (frame.ubutton)
2027         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2028                                    frame.ubutton);
2029       else
2030         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2031                              frame.ubutton_pixel);
2032     }
2033   } else {
2034     if (frame.pbutton)
2035       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2036                                  frame.close_button, frame.pbutton);
2037     else
2038       XSetWindowBackground(blackbox->getXDisplay(),
2039                            frame.close_button, frame.pbutton_pixel);
2040   }
2041   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2042
2043   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2044            screen->getWindowStyle()->b_pic_unfocus);
2045   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2046             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2047   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2048             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2049 }
2050
2051
2052 void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
2053   if (re->window != client.window)
2054     return;
2055
2056 #ifdef    DEBUG
2057   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2058           client.window);
2059 #endif // DEBUG
2060
2061   bool get_state_ret = getState();
2062   if (! (get_state_ret && blackbox->isStartup())) {
2063     if ((client.wm_hint_flags & StateHint) &&
2064         (! (current_state == NormalState || current_state == IconicState)))
2065       current_state = client.initial_state;
2066     else
2067       current_state = NormalState;
2068   } else if (flags.iconic) {
2069     current_state = NormalState;
2070   }
2071
2072   switch (current_state) {
2073   case IconicState:
2074     iconify();
2075     break;
2076
2077   case WithdrawnState:
2078     withdraw();
2079     break;
2080
2081   case NormalState:
2082   case InactiveState:
2083   case ZoomState:
2084   default:
2085     show();
2086     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2087     if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2088       XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2089       setInputFocus();
2090     }
2091     break;
2092   }
2093 }
2094
2095
2096 void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
2097   if (ue->window != client.window)
2098     return;
2099
2100 #ifdef    DEBUG
2101   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2102           client.window);
2103 #endif // DEBUG
2104
2105   screen->unmanageWindow(this, False);
2106 }
2107
2108
2109 void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
2110   if (de->window != client.window)
2111     return;
2112
2113 #ifdef    DEBUG
2114   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2115           client.window);
2116 #endif // DEBUG
2117
2118   screen->unmanageWindow(this, False);
2119 }
2120
2121
2122 void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
2123   if (re->window != client.window || re->parent == frame.plate)
2124     return;
2125
2126 #ifdef    DEBUG
2127   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2128           "0x%lx.\n", client.window, re->parent);
2129 #endif // DEBUG
2130
2131   XEvent ev;
2132   ev.xreparent = *re;
2133   XPutBackEvent(blackbox->getXDisplay(), &ev);
2134   screen->unmanageWindow(this, True);
2135 }
2136
2137
2138 void BlackboxWindow::propertyNotifyEvent(Atom atom) {
2139   switch(atom) {
2140   case XA_WM_CLASS:
2141   case XA_WM_CLIENT_MACHINE:
2142   case XA_WM_COMMAND:
2143     break;
2144
2145   case XA_WM_TRANSIENT_FOR: {
2146     // determine if this is a transient window
2147     getTransientInfo();
2148
2149     // adjust the window decorations based on transience
2150     if (isTransient()) {
2151       decorations &= ~(Decor_Maximize | Decor_Handle);
2152       functions &= ~Func_Maximize;
2153     }
2154
2155     reconfigure();
2156   }
2157     break;
2158
2159   case XA_WM_HINTS:
2160     getWMHints();
2161     break;
2162
2163   case XA_WM_ICON_NAME:
2164     getWMIconName();
2165     if (flags.iconic) screen->propagateWindowName(this);
2166     break;
2167
2168   case XA_WM_NAME:
2169     getWMName();
2170
2171     if (decorations & Decor_Titlebar)
2172       redrawLabel();
2173
2174     screen->propagateWindowName(this);
2175     break;
2176
2177   case XA_WM_NORMAL_HINTS: {
2178     getWMNormalHints();
2179
2180     if ((client.normal_hint_flags & PMinSize) &&
2181         (client.normal_hint_flags & PMaxSize)) {
2182       if (client.max_width <= client.min_width &&
2183           client.max_height <= client.min_height) {
2184         decorations &= ~(Decor_Maximize | Decor_Handle);
2185         functions &= ~(Func_Resize | Func_Maximize);
2186       } else {
2187         decorations |= Decor_Maximize | Decor_Handle;
2188         functions |= Func_Resize | Func_Maximize;
2189       }
2190     }
2191
2192     Rect old_rect = frame.rect;
2193
2194     upsize();
2195
2196     if (old_rect != frame.rect)
2197       reconfigure();
2198
2199     break;
2200   }
2201
2202   default:
2203     if (atom == blackbox->getWMProtocolsAtom()) {
2204       getWMProtocols();
2205
2206       if ((decorations & Decor_Close) && (! frame.close_button)) {
2207         createCloseButton();
2208         if (decorations & Decor_Titlebar) {
2209           positionButtons(True);
2210           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2211         }
2212         if (windowmenu) windowmenu->reconfigure();
2213       }
2214     }
2215
2216     break;
2217   }
2218 }
2219
2220
2221 void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
2222   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2223     redrawLabel();
2224   else if (frame.close_button == ee->window)
2225     redrawCloseButton(False);
2226   else if (frame.maximize_button == ee->window)
2227     redrawMaximizeButton(flags.maximized);
2228   else if (frame.iconify_button == ee->window)
2229     redrawIconifyButton(False);
2230 }
2231
2232
2233 void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
2234   if (cr->window != client.window || flags.iconic)
2235     return;
2236
2237   int cx = frame.rect.x(), cy = frame.rect.y();
2238   unsigned int cw = frame.rect.width(), ch = frame.rect.height();
2239
2240   if (cr->value_mask & CWBorderWidth)
2241     client.old_bw = cr->border_width;
2242
2243   if (cr->value_mask & CWX)
2244     cx = cr->x - frame.margin.left;
2245
2246   if (cr->value_mask & CWY)
2247     cy = cr->y - frame.margin.top;
2248
2249   if (cr->value_mask & CWWidth)
2250     cw = cr->width + frame.margin.left + frame.margin.right;
2251
2252   if (cr->value_mask & CWHeight)
2253     ch = cr->height + frame.margin.top + frame.margin.bottom;
2254
2255   if (frame.rect != Rect(cx, cy, cw, ch))
2256     configure(cx, cy, cw, ch);
2257
2258   if (cr->value_mask & CWStackMode) {
2259     switch (cr->detail) {
2260     case Below:
2261     case BottomIf:
2262       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2263       break;
2264
2265     case Above:
2266     case TopIf:
2267     default:
2268       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2269       break;
2270     }
2271   }
2272 }
2273
2274
2275 void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
2276   if (frame.maximize_button == be->window) {
2277     redrawMaximizeButton(True);
2278   } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2279     if (! flags.focused)
2280       setInputFocus();
2281
2282     if (frame.iconify_button == be->window) {
2283       redrawIconifyButton(True);
2284     } else if (frame.close_button == be->window) {
2285       redrawCloseButton(True);
2286     } else if (frame.plate == be->window) {
2287       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2288
2289       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2290
2291       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2292     } else {
2293       if (frame.title == be->window || frame.label == be->window) {
2294         if (((be->time - lastButtonPressTime) <=
2295              blackbox->getDoubleClickInterval()) ||
2296             (be->state & ControlMask)) {
2297           lastButtonPressTime = 0;
2298           shade();
2299         } else {
2300           lastButtonPressTime = be->time;
2301         }
2302       }
2303
2304       frame.grab_x = be->x_root - frame.rect.x() - frame.border_w;
2305       frame.grab_y = be->y_root - frame.rect.y() - frame.border_w;
2306
2307       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2308
2309       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2310     }
2311   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2312              (be->window != frame.close_button)) {
2313     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2314   } else if (windowmenu && be->button == 3 &&
2315              (frame.title == be->window || frame.label == be->window ||
2316               frame.handle == be->window || frame.window == be->window)) {
2317     int mx = 0, my = 0;
2318
2319     if (frame.title == be->window || frame.label == be->window) {
2320       mx = be->x_root - (windowmenu->getWidth() / 2);
2321       my = frame.rect.y() + frame.title_h + frame.border_w;
2322     } else if (frame.handle == be->window) {
2323       mx = be->x_root - (windowmenu->getWidth() / 2);
2324       my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
2325            windowmenu->getHeight();
2326     } else {
2327       mx = be->x_root - (windowmenu->getWidth() / 2);
2328
2329       if (be->y <= static_cast<signed>(frame.bevel_w))
2330         my = frame.rect.y() + frame.title_h;
2331       else
2332         my = be->y_root - (windowmenu->getHeight() / 2);
2333     }
2334
2335     // snap the window menu into a corner if necessary - we check the
2336     // position of the menu with the coordinates of the client to
2337     // make the comparisions easier.
2338     // ### this needs some work!
2339     if (mx > client.rect.right() -
2340         static_cast<signed>(windowmenu->getWidth()))
2341       mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
2342     if (mx < client.rect.left())
2343       mx = frame.rect.x();
2344
2345     if (my > client.rect.bottom() -
2346         static_cast<signed>(windowmenu->getHeight()))
2347       my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
2348     if (my < client.rect.top())
2349       my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
2350                              frame.title_h : 0);
2351
2352     if (windowmenu) {
2353       if (! windowmenu->isVisible()) {
2354         windowmenu->move(mx, my);
2355         windowmenu->show();
2356         XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2357         XRaiseWindow(blackbox->getXDisplay(),
2358                      windowmenu->getSendToMenu()->getWindowID());
2359       } else {
2360         windowmenu->hide();
2361       }
2362     }
2363   }
2364 }
2365
2366
2367 void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
2368   if (re->window == frame.maximize_button) {
2369     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2370         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2371       maximize(re->button);
2372     } else {
2373       redrawMaximizeButton(flags.maximized);
2374     }
2375   } else if (re->window == frame.iconify_button) {
2376     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2377         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2378       iconify();
2379     } else {
2380       redrawIconifyButton(False);
2381     }
2382   } else if (re->window == frame.close_button) {
2383     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2384         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2385       close();
2386     redrawCloseButton(False);
2387   } else if (flags.moving) {
2388     flags.moving = False;
2389
2390     if (! screen->doOpaqueMove()) {
2391       /* when drawing the rubber band, we need to make sure we only draw inside
2392        * the frame... frame.changing_* contain the new coords for the window,
2393        * so we need to subtract 1 from changing_w/changing_h every where we
2394        * draw the rubber band (for both moving and resizing)
2395        */
2396       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2397                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2398                      frame.changing.width() - 1, frame.changing.height() - 1);
2399       XUngrabServer(blackbox->getXDisplay());
2400
2401       configure(frame.changing.x(), frame.changing.y(),
2402                 frame.changing.width(), frame.changing.height());
2403     } else {
2404       configure(frame.rect.x(), frame.rect.y(),
2405                 frame.rect.width(), frame.rect.height());
2406     }
2407     screen->hideGeometry();
2408     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2409   } else if (flags.resizing) {
2410     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2411                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2412                    frame.changing.width() - 1, frame.changing.height() - 1);
2413     XUngrabServer(blackbox->getXDisplay());
2414
2415     screen->hideGeometry();
2416
2417     constrain((re->window == frame.left_grip) ? TopRight : TopLeft);
2418
2419     // unset maximized state when resized after fully maximized
2420     if (flags.maximized == 1)
2421       maximize(0);
2422     flags.resizing = False;
2423     configure(frame.changing.x(), frame.changing.y(),
2424               frame.changing.width(), frame.changing.height());
2425
2426     XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2427   } else if (re->window == frame.window) {
2428     if (re->button == 2 && re->state == Mod1Mask)
2429       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2430   }
2431 }
2432
2433
2434 void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
2435   if (!flags.resizing && (me->state & Button1Mask) &&
2436       (functions & Func_Move) &&
2437       (frame.title == me->window || frame.label == me->window ||
2438        frame.handle == me->window || frame.window == me->window)) {
2439     if (! flags.moving) {
2440       XGrabPointer(blackbox->getXDisplay(), me->window, False,
2441                    Button1MotionMask | ButtonReleaseMask,
2442                    GrabModeAsync, GrabModeAsync,
2443                    None, blackbox->getMoveCursor(), CurrentTime);
2444
2445       if (windowmenu && windowmenu->isVisible())
2446         windowmenu->hide();
2447
2448       flags.moving = True;
2449
2450       if (! screen->doOpaqueMove()) {
2451         XGrabServer(blackbox->getXDisplay());
2452
2453         frame.changing = frame.rect;
2454         screen->showPosition(frame.changing.x(), frame.changing.y());
2455
2456         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2457                        screen->getOpGC(),
2458                        frame.changing.x(),
2459                        frame.changing.y(),
2460                        frame.changing.width() - 1,
2461                        frame.changing.height() - 1);
2462       }
2463     } else {
2464       int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y;
2465       dx -= frame.border_w;
2466       dy -= frame.border_w;
2467
2468       const int snap_distance = screen->getEdgeSnapThreshold();
2469
2470       if (snap_distance) {
2471         Rect srect = screen->availableArea();
2472         // window corners
2473         const int wleft = dx,
2474                  wright = dx + frame.rect.width() - 1,
2475                    wtop = dy,
2476                 wbottom = dy + frame.rect.height() - 1;
2477
2478         int dleft = std::abs(wleft - srect.left()),
2479            dright = std::abs(wright - srect.right()),
2480              dtop = std::abs(wtop - srect.top()),
2481           dbottom = std::abs(wbottom - srect.bottom());
2482
2483         // snap left?
2484         if (dleft < snap_distance && dleft < dright)
2485           dx = srect.left();
2486         // snap right?
2487         else if (dright < snap_distance && dright < dleft)
2488           dx = srect.right() - frame.rect.width() + 1;
2489
2490         // snap top?
2491         if (dtop < snap_distance && dtop < dbottom)
2492           dy = srect.top();
2493         // snap bottom?
2494         else if (dbottom < snap_distance && dbottom < dtop)
2495           dy = srect.bottom() - frame.rect.height() + 1;
2496
2497         srect = screen->getRect(); // now get the full screen
2498
2499         dleft = std::abs(wleft - srect.left()),
2500         dright = std::abs(wright - srect.right()),
2501         dtop = std::abs(wtop - srect.top()),
2502         dbottom = std::abs(wbottom - srect.bottom());
2503
2504         // snap left?
2505         if (dleft < snap_distance && dleft < dright)
2506           dx = srect.left();
2507         // snap right?
2508         else if (dright < snap_distance && dright < dleft)
2509           dx = srect.right() - frame.rect.width() + 1;
2510
2511         // snap top?
2512         if (dtop < snap_distance && dtop < dbottom)
2513           dy = srect.top();
2514         // snap bottom?
2515         else if (dbottom < snap_distance && dbottom < dtop)
2516           dy = srect.bottom() - frame.rect.height() + 1;
2517       }
2518
2519       if (screen->doOpaqueMove()) {
2520         configure(dx, dy, frame.rect.width(), frame.rect.height());
2521       } else {
2522         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2523                        screen->getOpGC(),
2524                        frame.changing.x(),
2525                        frame.changing.y(),
2526                        frame.changing.width() - 1,
2527                        frame.changing.height() - 1);
2528
2529         frame.changing.setPos(dx, dy);
2530
2531         XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2532                        screen->getOpGC(),
2533                        frame.changing.x(),
2534                        frame.changing.y(),
2535                        frame.changing.width() - 1,
2536                        frame.changing.height() - 1);
2537       }
2538
2539       screen->showPosition(dx, dy);
2540     }
2541   } else if ((functions & Func_Resize) &&
2542              (((me->state & Button1Mask) &&
2543                (me->window == frame.right_grip ||
2544                 me->window == frame.left_grip)) ||
2545               (me->state & (Mod1Mask | Button3Mask) &&
2546                me->window == frame.window))) {
2547     bool left = (me->window == frame.left_grip);
2548
2549     if (! flags.resizing) {
2550       XGrabServer(blackbox->getXDisplay());
2551       XGrabPointer(blackbox->getXDisplay(), me->window, False,
2552                    ButtonMotionMask | ButtonReleaseMask,
2553                    GrabModeAsync, GrabModeAsync, None,
2554                    ((left) ? blackbox->getLowerLeftAngleCursor() :
2555                     blackbox->getLowerRightAngleCursor()),
2556                    CurrentTime);
2557
2558       flags.resizing = True;
2559
2560       int gw, gh;
2561       frame.grab_x = me->x;
2562       frame.grab_y = me->y;
2563       frame.changing = frame.rect;
2564
2565       constrain((left) ? TopRight : TopLeft, &gw, &gh);
2566
2567       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2568                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2569                      frame.changing.width() - 1, frame.changing.height() - 1);
2570
2571       screen->showGeometry(gw, gh);
2572     } else {
2573       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2574                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2575                      frame.changing.width() - 1, frame.changing.height() - 1);
2576
2577       int gw, gh;
2578
2579       Corner anchor;
2580
2581       if (left) {
2582         anchor = TopRight;
2583         frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(),
2584                                  frame.rect.right(), frame.rect.bottom());
2585         frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y));
2586       } else {
2587         anchor = TopLeft;
2588         frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x),
2589                                frame.rect.height() + (me->y - frame.grab_y));
2590       }
2591
2592       constrain(anchor, &gw, &gh);
2593
2594       XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2595                      screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2596                      frame.changing.width() - 1, frame.changing.height() - 1);
2597
2598       screen->showGeometry(gw, gh);
2599     }
2600   }
2601 }
2602
2603
2604 #ifdef    SHAPE
2605 void BlackboxWindow::shapeEvent(XShapeEvent *) {
2606   if (blackbox->hasShapeExtensions() && flags.shaped) {
2607     configureShape();
2608   }
2609 }
2610 #endif // SHAPE
2611
2612
2613 bool BlackboxWindow::validateClient(void) {
2614   XSync(blackbox->getXDisplay(), False);
2615
2616   XEvent e;
2617   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2618                              DestroyNotify, &e) ||
2619       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2620                              UnmapNotify, &e)) {
2621     XPutBackEvent(blackbox->getXDisplay(), &e);
2622
2623     return False;
2624   }
2625
2626   return True;
2627 }
2628
2629
2630 void BlackboxWindow::restore(bool remap) {
2631   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
2632   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
2633   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
2634
2635   restoreGravity();
2636
2637   XUnmapWindow(blackbox->getXDisplay(), frame.window);
2638   XUnmapWindow(blackbox->getXDisplay(), client.window);
2639
2640   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
2641
2642   XEvent ev;
2643   if (! XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2644                                ReparentNotify, &ev)) {
2645     // according to the ICCCM - if the client doesn't reparent to
2646     // root, then we have to do it for them
2647     XReparentWindow(blackbox->getXDisplay(), client.window,
2648                     screen->getRootWindow(),
2649                     client.rect.x(), client.rect.y());
2650   }
2651
2652   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
2653 }
2654
2655
2656 // timer for autoraise
2657 void BlackboxWindow::timeout(void) {
2658   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2659 }
2660
2661
2662 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
2663   if ((net->flags & AttribShaded) &&
2664       ((blackbox_attrib.attrib & AttribShaded) !=
2665        (net->attrib & AttribShaded)))
2666     shade();
2667
2668   if (flags.visible && // watch out for requests when we can not be seen
2669       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
2670       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
2671        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
2672     if (flags.maximized) {
2673       maximize(0);
2674     } else {
2675       int button = 0;
2676
2677       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
2678         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
2679       else if (net->flags & AttribMaxVert)
2680         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
2681       else if (net->flags & AttribMaxHoriz)
2682         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
2683
2684       maximize(button);
2685     }
2686   }
2687
2688   if ((net->flags & AttribOmnipresent) &&
2689       ((blackbox_attrib.attrib & AttribOmnipresent) !=
2690        (net->attrib & AttribOmnipresent)))
2691     stick();
2692
2693   if ((net->flags & AttribWorkspace) &&
2694       (blackbox_attrib.workspace != net->workspace)) {
2695     screen->reassociateWindow(this, net->workspace, True);
2696
2697     if (screen->getCurrentWorkspaceID() != net->workspace) {
2698       withdraw();
2699     } else {
2700       show();
2701       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2702     }
2703   }
2704
2705   if (net->flags & AttribDecoration) {
2706     switch (net->decoration) {
2707     case DecorNone:
2708       // clear all decorations except close
2709       decorations &= Decor_Close;
2710
2711       break;
2712
2713     default:
2714     case DecorNormal:
2715       decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2716                      Decor_Iconify | Decor_Maximize;
2717
2718       break;
2719
2720     case DecorTiny:
2721       decorations |= Decor_Titlebar | Decor_Iconify;
2722       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2723
2724       break;
2725
2726     case DecorTool:
2727       decorations |= Decor_Titlebar;
2728       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2729       functions |= Func_Move;
2730
2731       break;
2732     }
2733     if (frame.window) {
2734       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2735       XMapWindow(blackbox->getXDisplay(), frame.window);
2736     }
2737
2738     reconfigure();
2739     setState(current_state);
2740   }
2741 }
2742
2743
2744 /*
2745  * Set the sizes of all components of the window frame
2746  * (the window decorations).
2747  * These values are based upon the current style settings and the client
2748  * window's dimensions.
2749  */
2750 void BlackboxWindow::upsize(void) {
2751   frame.bevel_w = screen->getBevelWidth();
2752
2753   if (decorations & Decor_Border) {
2754     frame.border_w = screen->getBorderWidth();
2755     if (!isTransient())
2756       frame.mwm_border_w = screen->getFrameWidth();
2757     else
2758       frame.mwm_border_w = 0;
2759   } else {
2760     frame.mwm_border_w = frame.border_w = 0;
2761   }
2762
2763   if (decorations & Decor_Titlebar) {
2764     // the height of the titlebar is based upon the height of the font being
2765     // used to display the window's title
2766     WindowStyle *style = screen->getWindowStyle();
2767     if (i18n.multibyte())
2768       frame.title_h = (style->fontset_extents->max_ink_extent.height +
2769                        (frame.bevel_w * 2) + 2);
2770     else
2771       frame.title_h = (style->font->ascent + style->font->descent +
2772                        (frame.bevel_w * 2) + 2);
2773
2774     frame.label_h = frame.title_h - (frame.bevel_w * 2);
2775     frame.button_w = (frame.label_h - 2);
2776
2777     // set the top frame margin
2778     frame.margin.top = frame.border_w + frame.title_h +
2779                        frame.border_w + frame.mwm_border_w;
2780   } else {
2781     frame.title_h = 0;
2782     frame.label_h = 0;
2783     frame.button_w = 0;
2784
2785     // set the top frame margin
2786     frame.margin.top = frame.border_w + frame.mwm_border_w;
2787   }
2788
2789   // set the left/right frame margin
2790   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
2791
2792   if (decorations & Decor_Handle) {
2793     frame.grip_w = frame.button_w * 2;
2794     frame.handle_h = screen->getHandleWidth();
2795
2796     // set the bottom frame margin
2797     frame.margin.bottom = frame.border_w + frame.handle_h +
2798                           frame.border_w + frame.mwm_border_w;
2799   } else {
2800     frame.handle_h = 0;
2801     frame.grip_w = 0;
2802
2803     // set the bottom frame margin
2804     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
2805   }
2806
2807   // set the frame rect
2808   frame.rect.setSize(client.rect.width() + frame.margin.left +
2809                      frame.margin.right,
2810                      client.rect.height() + frame.margin.top +
2811                      frame.margin.bottom);
2812   frame.inside_w = frame.rect.width() - (frame.border_w * 2);
2813   frame.inside_h = frame.rect.height() - (frame.border_w * 2);
2814 }
2815
2816
2817 /*
2818  * Calculate the size of the client window and constrain it to the
2819  * size specified by the size hints of the client window.
2820  *
2821  * The logical width and height are placed into pw and ph, if they
2822  * are non-zero.  Logical size refers to the users perception of
2823  * the window size (for example an xterm has resizes in cells, not in
2824  * pixels).
2825  *
2826  * The physical geometry is placed into frame.changing_{x,y,width,height}.
2827  * Physical geometry refers to the geometry of the window in pixels.
2828  */
2829 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
2830   // frame.changing represents the requested frame size, we need to
2831   // strip the frame margin off and constrain the client size
2832   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
2833                            frame.changing.top() + frame.margin.top,
2834                            frame.changing.right() - frame.margin.right,
2835                            frame.changing.bottom() - frame.margin.bottom);
2836
2837   int dw = frame.changing.width(), dh = frame.changing.height(),
2838     base_width = (client.base_width) ? client.base_width : client.min_width,
2839     base_height = (client.base_height) ? client.base_height :
2840                                          client.min_height;
2841
2842   // constrain
2843   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
2844   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
2845   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
2846   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
2847
2848   dw -= base_width;
2849   dw /= client.width_inc;
2850   dh -= base_height;
2851   dh /= client.height_inc;
2852
2853   if (pw) *pw = dw;
2854   if (ph) *ph = dh;
2855
2856   dw *= client.width_inc;
2857   dw += base_width;
2858   dh *= client.height_inc;
2859   dh += base_height;
2860
2861   frame.changing.setSize(dw, dh);
2862
2863   // add the frame margin back onto frame.changing
2864   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
2865                            frame.changing.top() - frame.margin.top,
2866                            frame.changing.right() + frame.margin.right,
2867                            frame.changing.bottom() + frame.margin.bottom);
2868
2869   // move frame.changing to the specified anchor
2870   switch (anchor) {
2871   case TopLeft:
2872     // nothing to do
2873     break;
2874
2875   case TopRight:
2876     int dx = frame.rect.right() - frame.changing.right();
2877     frame.changing.setPos(frame.changing.x() + dx, frame.changing.y());
2878     break;
2879   }
2880 }
2881
2882
2883 int WindowStyle::doJustify(const char *text, int &start_pos,
2884                            unsigned int max_length, unsigned int modifier,
2885                            bool multibyte) const {
2886   size_t text_len = strlen(text);
2887   unsigned int length;
2888
2889   do {
2890     if (multibyte) {
2891       XRectangle ink, logical;
2892       XmbTextExtents(fontset, text, text_len, &ink, &logical);
2893       length = logical.width;
2894     } else {
2895       length = XTextWidth(font, text, text_len);
2896     }
2897     length += modifier;
2898   } while (length > max_length && text_len-- > 0);
2899
2900   switch (justify) {
2901   case RightJustify:
2902     start_pos += max_length - length;
2903     break;
2904
2905   case CenterJustify:
2906     start_pos += (max_length - length) / 2;
2907     break;
2908
2909   case LeftJustify:
2910   default:
2911     break;
2912   }
2913
2914   return text_len;
2915 }
2916
2917
2918 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
2919   : blackbox(b), group(_group) {
2920   // watch for destroy notify on the group window
2921   XSelectInput(blackbox->getXDisplay(), group, StructureNotifyMask);
2922   blackbox->saveGroupSearch(group, this);
2923 }
2924
2925
2926 BWindowGroup::~BWindowGroup(void) {
2927   blackbox->removeGroupSearch(group);
2928 }
2929
2930
2931 BlackboxWindow *
2932 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
2933   BlackboxWindow *ret = blackbox->getFocusedWindow();
2934
2935   // does the focus window match (or any transient_fors)?
2936   while (ret) {
2937     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
2938       if (ret->isTransient() && allow_transients) break;
2939       else if (! ret->isTransient()) break;
2940     }
2941
2942     ret = ret->getTransientFor();
2943   }
2944
2945   if (ret) return ret;
2946
2947   // the focus window didn't match, look in the group's window list
2948   BlackboxWindowList::const_iterator it, end = windowList.end();
2949   for (it = windowList.begin(); it != end; ++it) {
2950     ret = *it;
2951     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
2952       if (ret->isTransient() && allow_transients) break;
2953       else if (! ret->isTransient()) break;
2954     }
2955   }
2956
2957   return ret;
2958 }