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