]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
snapping better to the xinerama regions
[dana/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 "Clientmenu.hh"
48 #include "Font.hh"
49 #include "GCCache.hh"
50 #include "Iconmenu.hh"
51 #include "Image.hh"
52 #include "Screen.hh"
53 #include "Toolbar.hh"
54 #include "Util.hh"
55 #include "Window.hh"
56 #include "Windowmenu.hh"
57 #include "Workspace.hh"
58 #include "Slit.hh"
59
60 using std::string;
61
62 // change this to change what modifier keys openbox uses for mouse bindings
63 // for example: Mod1Mask | ControlMask
64 //          or: ControlMask| ShiftMask
65 const unsigned int ModMask = Mod1Mask;
66
67 /*
68  * Initializes the class with default values/the window's set initial values.
69  */
70 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
71   // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
72   // sizeof(BlackboxWindow));
73
74 #ifdef    DEBUG
75   fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
76 #endif // DEBUG
77
78   /*
79     set timer to zero... it is initialized properly later, so we check
80     if timer is zero in the destructor, and assume that the window is not
81     fully constructed if timer is zero...
82   */
83   timer = 0;
84   blackbox = b;
85   client.window = w;
86   screen = s;
87   xatom = blackbox->getXAtom();
88
89   if (! validateClient()) {
90     delete this;
91     return;
92   }
93
94   // set the eventmask early in the game so that we make sure we get
95   // all the events we are interested in
96   XSetWindowAttributes attrib_set;
97   attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
98                           StructureNotifyMask;
99   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
100                                      ButtonMotionMask;
101   XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
102                           CWEventMask|CWDontPropagate, &attrib_set);
103
104   // fetch client size and placement
105   XWindowAttributes wattrib;
106   if ((! XGetWindowAttributes(blackbox->getXDisplay(),
107                               client.window, &wattrib)) ||
108       (! wattrib.screen) || wattrib.override_redirect) {
109 #ifdef    DEBUG
110     fprintf(stderr,
111             "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
112 #endif // DEBUG
113
114     delete this;
115     return;
116   }
117
118   flags.moving = flags.resizing = flags.shaded = flags.visible =
119     flags.iconic = flags.focused = flags.stuck = flags.modal =
120     flags.send_focus_message = flags.shaped = flags.skip_taskbar =
121     flags.skip_pager = flags.fullscreen = False;
122   flags.maximized = 0;
123
124   blackbox_attrib.workspace = window_number = BSENTINEL;
125
126   blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack
127     = blackbox_attrib.decoration = 0l;
128   blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
129   blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
130
131   frame.border_w = 1;
132   frame.window = frame.plate = frame.title = frame.handle = None;
133   frame.close_button = frame.iconify_button = frame.maximize_button = None;
134   frame.right_grip = frame.left_grip = None;
135
136   frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
137   frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
138     frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
139     frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
140     frame.fgrip_pixel = 0;
141   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
142   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
143   frame.pbutton = frame.ugrip = frame.fgrip = None;
144
145   decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
146                 Decor_Iconify | Decor_Maximize;
147   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
148
149   client.wm_hint_flags = client.normal_hint_flags = 0;
150   client.window_group = None;
151   client.transient_for = 0;
152
153   /*
154     get the initial size and location of client window (relative to the
155     _root window_). This position is the reference point used with the
156     window's gravity to find the window's initial position.
157   */
158   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
159   client.old_bw = wattrib.border_width;
160
161   windowmenu = 0;
162   lastButtonPressTime = 0;
163
164   timer = new BTimer(blackbox, this);
165   timer->setTimeout(blackbox->getAutoRaiseDelay());
166
167   if (! getBlackboxHints()) {
168     getMWMHints();
169     getNetWMHints();
170   }
171
172   // get size, aspect, minimum/maximum size and other hints set by the
173   // client
174   getWMProtocols();
175   getWMHints();
176   getWMNormalHints();
177
178   if (client.initial_state == WithdrawnState) {
179     screen->getSlit()->addClient(client.window);
180     delete this;
181     return;
182   }
183
184   frame.window = createToplevelWindow();
185   frame.plate = createChildWindow(frame.window);
186   associateClientWindow();
187
188   blackbox->saveWindowSearch(frame.window, this);
189   blackbox->saveWindowSearch(frame.plate, this);
190   blackbox->saveWindowSearch(client.window, this);
191
192   // determine if this is a transient window
193   getTransientInfo();
194
195   // determine the window's type, so we can decide its decorations and
196   // functionality, or if we should not manage it at all
197   getWindowType();
198
199   // adjust the window decorations/behavior based on the window type
200   switch (window_type) {
201   case Type_Desktop:
202   case Type_Dock:
203   case Type_Menu:
204   case Type_Toolbar:
205   case Type_Utility:
206   case Type_Splash:
207     // none of these windows are decorated or manipulated by the window manager
208     decorations = 0;
209     functions = 0;
210     blackbox_attrib.workspace = 0;  // we do need to belong to a workspace
211     flags.stuck = True;             // we show up on all workspaces
212     break;
213
214   case Type_Dialog:
215     // dialogs cannot be maximized, and don't display a handle
216     decorations &= ~(Decor_Maximize | Decor_Handle);
217     functions &= ~Func_Maximize;
218     break;
219
220   case Type_Normal:
221     // normal windows retain all of the possible decorations and functionality
222     break;
223   }
224
225   // further adjeust the window's decorations/behavior based on window sizes
226   if ((client.normal_hint_flags & PMinSize) &&
227       (client.normal_hint_flags & PMaxSize) &&
228       client.max_width <= client.min_width &&
229       client.max_height <= client.min_height) {
230     decorations &= ~(Decor_Maximize | Decor_Handle);
231     functions &= ~(Func_Resize | Func_Maximize);
232   }
233   upsize();
234
235   setAllowedActions();
236
237   bool place_window = True;
238   if (blackbox->isStartup() || isTransient() ||
239       client.normal_hint_flags & (PPosition|USPosition)) {
240     applyGravity(frame.rect);
241
242     if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
243       place_window = False;
244   }
245
246   // add the window's strut. note this is done *after* placing the window.
247   screen->addStrut(&client.strut);
248   updateStrut();
249   
250   if (decorations & Decor_Titlebar)
251     createTitlebar();
252
253   if (decorations & Decor_Handle)
254     createHandle();
255
256 #ifdef    SHAPE
257   if (blackbox->hasShapeExtensions() && flags.shaped) {
258     configureShape();
259   }
260 #endif // SHAPE
261
262   windowmenu = new Windowmenu(this);
263
264   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
265     screen->getCurrentWorkspace()->addWindow(this, place_window);
266   else
267     screen->getWorkspace(blackbox_attrib.workspace)->
268       addWindow(this, place_window);
269
270   if (! place_window) {
271     // don't need to call configure if we are letting the workspace
272     // place the window
273     configure(frame.rect.x(), frame.rect.y(),
274               frame.rect.width(), frame.rect.height());
275   }
276
277   // preserve the window's initial state on first map, and its current state
278   // across a restart
279   if (! getState()) {
280     if (client.wm_hint_flags & StateHint)
281       current_state = client.initial_state;
282     else
283       current_state = NormalState;
284   }
285
286   // get sticky state from our parent window if we've got one
287   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
288       client.transient_for->isStuck() != flags.stuck)
289     stick();
290
291   if (flags.shaded) {
292     flags.shaded = False;
293     unsigned long orig_state = current_state;
294     shade();
295
296     /*
297       At this point in the life of a window, current_state should only be set
298       to IconicState if the window was an *icon*, not if it was shaded.
299     */
300     if (orig_state != IconicState)
301       current_state = NormalState;
302   }
303
304   if (flags.stuck) {
305     flags.stuck = False;
306     stick();
307   }
308
309   if (flags.maximized && (functions & Func_Maximize)) {
310     remaximize();
311   }
312
313   /*
314     When the window is mapped (and also when its attributes are restored), the
315     current_state that was set here will be used.
316     It is set to Normal if the window is to be mapped or it is set to Iconic
317     if the window is to be iconified.
318     *Note* that for sticky windows, the same rules apply here, they are in
319     fact never set to Iconic since there is no way for us to tell if a sticky
320     window was iconified previously.
321   */
322  
323   positionWindows();
324   decorate();
325   grabButtons();
326
327   XMapSubwindows(blackbox->getXDisplay(), frame.window);
328
329   redrawWindowFrame();
330 }
331
332
333 BlackboxWindow::~BlackboxWindow(void) {
334 #ifdef    DEBUG
335   fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
336           client.window);
337 #endif // DEBUG
338
339   if (! timer) // window not managed...
340     return;
341
342   screen->removeStrut(&client.strut);
343   screen->updateAvailableArea();
344
345   // We don't need to worry about resizing because resizing always grabs the X
346   // server. This should only ever happen if using opaque moving.
347   if (flags.moving)
348     endMove();
349
350   delete timer;
351
352   delete windowmenu;
353
354   if (client.window_group) {
355     BWindowGroup *group = blackbox->searchGroup(client.window_group);
356     if (group) group->removeWindow(this);
357   }
358
359   // remove ourselves from our transient_for
360   if (isTransient()) {
361     if (client.transient_for != (BlackboxWindow *) ~0ul) {
362       client.transient_for->client.transientList.remove(this);
363     }
364     client.transient_for = (BlackboxWindow*) 0;
365   }
366
367   if (client.transientList.size() > 0) {
368     // reset transient_for for all transients
369     BlackboxWindowList::iterator it, end = client.transientList.end();
370     for (it = client.transientList.begin(); it != end; ++it) {
371       (*it)->client.transient_for = (BlackboxWindow*) 0;
372     }
373   }
374
375   if (frame.title)
376     destroyTitlebar();
377
378   if (frame.handle)
379     destroyHandle();
380
381   if (frame.plate) {
382     blackbox->removeWindowSearch(frame.plate);
383     XDestroyWindow(blackbox->getXDisplay(), frame.plate);
384   }
385
386   if (frame.window) {
387     blackbox->removeWindowSearch(frame.window);
388     XDestroyWindow(blackbox->getXDisplay(), frame.window);
389   }
390
391   blackbox->removeWindowSearch(client.window);
392 }
393
394
395 /*
396  * Creates a new top level window, with a given location, size, and border
397  * width.
398  * Returns: the newly created window
399  */
400 Window BlackboxWindow::createToplevelWindow(void) {
401   XSetWindowAttributes attrib_create;
402   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
403                               CWOverrideRedirect | CWEventMask;
404
405   attrib_create.background_pixmap = None;
406   attrib_create.colormap = screen->getColormap();
407   attrib_create.override_redirect = True;
408   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
409                              ButtonMotionMask | EnterWindowMask;
410
411   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
412                        0, 0, 1, 1, frame.border_w, screen->getDepth(),
413                        InputOutput, screen->getVisual(), create_mask,
414                        &attrib_create);
415 }
416
417
418 /*
419  * Creates a child window, and optionally associates a given cursor with
420  * the new window.
421  */
422 Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) {
423   XSetWindowAttributes attrib_create;
424   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
425                               CWEventMask;
426
427   attrib_create.background_pixmap = None;
428   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
429                              ButtonMotionMask | ExposureMask;
430
431   if (cursor) {
432     create_mask |= CWCursor;
433     attrib_create.cursor = cursor;
434   }
435
436   return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
437                        screen->getDepth(), InputOutput, screen->getVisual(),
438                        create_mask, &attrib_create);
439 }
440
441
442 void BlackboxWindow::associateClientWindow(void) {
443   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
444   getWMName();
445   getWMIconName();
446
447   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
448
449   XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
450
451   XGrabServer(blackbox->getXDisplay());
452
453   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
454                              StructureNotifyMask;
455   XSelectInput(blackbox->getXDisplay(), client.window,
456                event_mask & ~StructureNotifyMask);
457   XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
458   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
459
460   XUngrabServer(blackbox->getXDisplay());
461
462   XRaiseWindow(blackbox->getXDisplay(), frame.plate);
463   XMapSubwindows(blackbox->getXDisplay(), frame.plate);
464
465
466 #ifdef    SHAPE
467   if (blackbox->hasShapeExtensions()) {
468     XShapeSelectInput(blackbox->getXDisplay(), client.window,
469                       ShapeNotifyMask);
470
471     Bool shaped = False;
472     int foo;
473     unsigned int ufoo;
474
475     XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
476                        &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
477                        &ufoo, &ufoo);
478     flags.shaped = shaped;
479   }
480 #endif // SHAPE
481 }
482
483
484 void BlackboxWindow::decorate(void) {
485   BTexture* texture;
486
487   texture = &(screen->getWindowStyle()->b_focus);
488   frame.fbutton = texture->render(frame.button_w, frame.button_w,
489                                   frame.fbutton);
490   if (! frame.fbutton)
491     frame.fbutton_pixel = texture->color().pixel();
492
493   texture = &(screen->getWindowStyle()->b_unfocus);
494   frame.ubutton = texture->render(frame.button_w, frame.button_w,
495                                   frame.ubutton);
496   if (! frame.ubutton)
497     frame.ubutton_pixel = texture->color().pixel();
498
499   texture = &(screen->getWindowStyle()->b_pressed);
500   frame.pbutton = texture->render(frame.button_w, frame.button_w,
501                                   frame.pbutton);
502   if (! frame.pbutton)
503     frame.pbutton_pixel = texture->color().pixel();
504
505   if (decorations & Decor_Titlebar) {
506     texture = &(screen->getWindowStyle()->t_focus);
507     frame.ftitle = texture->render(frame.inside_w, frame.title_h,
508                                    frame.ftitle);
509     if (! frame.ftitle)
510       frame.ftitle_pixel = texture->color().pixel();
511
512     texture = &(screen->getWindowStyle()->t_unfocus);
513     frame.utitle = texture->render(frame.inside_w, frame.title_h,
514                                    frame.utitle);
515     if (! frame.utitle)
516       frame.utitle_pixel = texture->color().pixel();
517
518     XSetWindowBorder(blackbox->getXDisplay(), frame.title,
519                      screen->getBorderColor()->pixel());
520
521     decorateLabel();
522   }
523
524   if (decorations & Decor_Border) {
525     frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel();
526     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel();
527     blackbox_attrib.flags |= AttribDecoration;
528     blackbox_attrib.decoration = DecorNormal;
529   } else {
530     blackbox_attrib.flags |= AttribDecoration;
531     blackbox_attrib.decoration = DecorNone;
532   }
533
534   if (decorations & Decor_Handle) {
535     texture = &(screen->getWindowStyle()->h_focus);
536     frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
537                                     frame.fhandle);
538     if (! frame.fhandle)
539       frame.fhandle_pixel = texture->color().pixel();
540
541     texture = &(screen->getWindowStyle()->h_unfocus);
542     frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
543                                     frame.uhandle);
544     if (! frame.uhandle)
545       frame.uhandle_pixel = texture->color().pixel();
546
547     texture = &(screen->getWindowStyle()->g_focus);
548     frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
549     if (! frame.fgrip)
550       frame.fgrip_pixel = texture->color().pixel();
551
552     texture = &(screen->getWindowStyle()->g_unfocus);
553     frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
554     if (! frame.ugrip)
555       frame.ugrip_pixel = texture->color().pixel();
556
557     XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
558                      screen->getBorderColor()->pixel());
559     XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
560                      screen->getBorderColor()->pixel());
561     XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
562                      screen->getBorderColor()->pixel());
563   }
564
565   XSetWindowBorder(blackbox->getXDisplay(), frame.window,
566                    screen->getBorderColor()->pixel());
567 }
568
569
570 void BlackboxWindow::decorateLabel(void) {
571   BTexture *texture;
572
573   texture = &(screen->getWindowStyle()->l_focus);
574   frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
575   if (! frame.flabel)
576     frame.flabel_pixel = texture->color().pixel();
577
578   texture = &(screen->getWindowStyle()->l_unfocus);
579   frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
580   if (! frame.ulabel)
581     frame.ulabel_pixel = texture->color().pixel();
582 }
583
584
585 void BlackboxWindow::createHandle(void) {
586   frame.handle = createChildWindow(frame.window);
587   blackbox->saveWindowSearch(frame.handle, this);
588
589   frame.left_grip =
590     createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor());
591   blackbox->saveWindowSearch(frame.left_grip, this);
592
593   frame.right_grip =
594     createChildWindow(frame.handle, blackbox->getLowerRightAngleCursor());
595   blackbox->saveWindowSearch(frame.right_grip, this);
596 }
597
598
599 void BlackboxWindow::destroyHandle(void) {
600   if (frame.fhandle)
601     screen->getImageControl()->removeImage(frame.fhandle);
602
603   if (frame.uhandle)
604     screen->getImageControl()->removeImage(frame.uhandle);
605
606   if (frame.fgrip)
607     screen->getImageControl()->removeImage(frame.fgrip);
608
609   if (frame.ugrip)
610     screen->getImageControl()->removeImage(frame.ugrip);
611
612   blackbox->removeWindowSearch(frame.left_grip);
613   blackbox->removeWindowSearch(frame.right_grip);
614
615   XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
616   XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
617   frame.left_grip = frame.right_grip = None;
618
619   blackbox->removeWindowSearch(frame.handle);
620   XDestroyWindow(blackbox->getXDisplay(), frame.handle);
621   frame.handle = None;
622 }
623
624
625 void BlackboxWindow::createTitlebar(void) {
626   frame.title = createChildWindow(frame.window);
627   frame.label = createChildWindow(frame.title);
628   blackbox->saveWindowSearch(frame.title, this);
629   blackbox->saveWindowSearch(frame.label, this);
630
631   if (decorations & Decor_Iconify) createIconifyButton();
632   if (decorations & Decor_Maximize) createMaximizeButton();
633   if (decorations & Decor_Close) createCloseButton();
634 }
635
636
637 void BlackboxWindow::destroyTitlebar(void) {
638   if (frame.close_button)
639     destroyCloseButton();
640
641   if (frame.iconify_button)
642     destroyIconifyButton();
643
644   if (frame.maximize_button)
645     destroyMaximizeButton();
646
647   if (frame.ftitle)
648     screen->getImageControl()->removeImage(frame.ftitle);
649
650   if (frame.utitle)
651     screen->getImageControl()->removeImage(frame.utitle);
652
653   if (frame.flabel)
654     screen->getImageControl()->removeImage(frame.flabel);
655
656   if( frame.ulabel)
657     screen->getImageControl()->removeImage(frame.ulabel);
658
659   if (frame.fbutton)
660     screen->getImageControl()->removeImage(frame.fbutton);
661
662   if (frame.ubutton)
663     screen->getImageControl()->removeImage(frame.ubutton);
664
665   if (frame.pbutton)
666     screen->getImageControl()->removeImage(frame.pbutton);
667
668   blackbox->removeWindowSearch(frame.title);
669   blackbox->removeWindowSearch(frame.label);
670
671   XDestroyWindow(blackbox->getXDisplay(), frame.label);
672   XDestroyWindow(blackbox->getXDisplay(), frame.title);
673   frame.title = frame.label = None;
674 }
675
676
677 void BlackboxWindow::createCloseButton(void) {
678   if (frame.title != None) {
679     frame.close_button = createChildWindow(frame.title);
680     blackbox->saveWindowSearch(frame.close_button, this);
681   }
682 }
683
684
685 void BlackboxWindow::destroyCloseButton(void) {
686   blackbox->removeWindowSearch(frame.close_button);
687   XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
688   frame.close_button = None;
689 }
690
691
692 void BlackboxWindow::createIconifyButton(void) {
693   if (frame.title != None) {
694     frame.iconify_button = createChildWindow(frame.title);
695     blackbox->saveWindowSearch(frame.iconify_button, this);
696   }
697 }
698
699
700 void BlackboxWindow::destroyIconifyButton(void) {
701   blackbox->removeWindowSearch(frame.iconify_button);
702   XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
703   frame.iconify_button = None;
704 }
705
706
707 void BlackboxWindow::createMaximizeButton(void) {
708   if (frame.title != None) {
709     frame.maximize_button = createChildWindow(frame.title);
710     blackbox->saveWindowSearch(frame.maximize_button, this);
711   }
712 }
713
714
715 void BlackboxWindow::destroyMaximizeButton(void) {
716   blackbox->removeWindowSearch(frame.maximize_button);
717   XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
718   frame.maximize_button = None;
719 }
720
721
722 void BlackboxWindow::positionButtons(bool redecorate_label) {
723   string layout = blackbox->getTitlebarLayout();
724   string parsed;
725
726   bool hasclose, hasiconify, hasmaximize, haslabel;
727   hasclose = hasiconify = hasmaximize = haslabel = false;
728
729   string::const_iterator it, end;
730   for (it = layout.begin(), end = layout.end(); it != end; ++it) {
731     switch(*it) {
732     case 'C':
733       if (! hasclose && (decorations & Decor_Close)) {
734         hasclose = true;
735         parsed += *it;
736       }
737       break;
738     case 'I':
739       if (! hasiconify && (decorations & Decor_Iconify)) {
740         hasiconify = true;
741         parsed += *it;
742       }
743       break;
744     case 'M':
745       if (! hasmaximize && (decorations & Decor_Maximize)) {
746         hasmaximize = true;
747         parsed += *it;
748       }
749       break;
750     case 'L':
751       if (! haslabel) {
752         haslabel = true;
753         parsed += *it;
754       }
755     }
756   }
757   if (! hasclose && frame.close_button)
758     destroyCloseButton();
759   if (! hasiconify && frame.iconify_button)
760     destroyIconifyButton();
761   if (! hasmaximize && frame.maximize_button)
762     destroyMaximizeButton();
763   if (! haslabel)
764     parsed += 'L';      // require that the label be in the layout
765
766   const unsigned int bsep = frame.bevel_w + 1;  // separation between elements
767   const unsigned int by = frame.bevel_w + 1;
768   const unsigned int ty = frame.bevel_w;
769
770   frame.label_w = frame.inside_w - bsep * 2 -
771     (frame.button_w + bsep) * (parsed.size() - 1);
772
773   unsigned int x = bsep;
774   for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
775     switch(*it) {
776     case 'C':
777       if (! frame.close_button) createCloseButton();
778       XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
779                         frame.button_w, frame.button_w);
780       x += frame.button_w + bsep;
781       break;
782     case 'I':
783       if (! frame.iconify_button) createIconifyButton();
784       XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
785                         frame.button_w, frame.button_w);
786       x += frame.button_w + bsep;
787       break;
788     case 'M':
789       if (! frame.maximize_button) createMaximizeButton();
790       XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
791                         frame.button_w, frame.button_w);
792       x += frame.button_w + bsep;
793       break;
794     case 'L':
795       XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
796                         frame.label_w, frame.label_h);
797       x += frame.label_w + bsep;
798       break;
799     }
800   }
801
802   if (redecorate_label) decorateLabel();
803   redrawLabel();
804   redrawAllButtons();
805 }
806
807
808 void BlackboxWindow::reconfigure(void) {
809   restoreGravity(client.rect);
810   upsize();
811   applyGravity(frame.rect);
812   positionWindows();
813   decorate();
814   redrawWindowFrame();
815
816   ungrabButtons();
817   grabButtons();
818
819   if (windowmenu) {
820     windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
821     windowmenu->reconfigure();
822   }
823 }
824
825
826 void BlackboxWindow::grabButtons(void) {
827   if (! screen->isSloppyFocus() || screen->doClickRaise())
828     // grab button 1 for changing focus/raising
829     blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
830                          GrabModeSync, GrabModeSync, frame.plate, None,
831                          screen->allowScrollLock());
832   
833   if (functions & Func_Move)
834     blackbox->grabButton(Button1, ModMask, frame.window, True,
835                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
836                          GrabModeAsync, frame.window, None,
837                          screen->allowScrollLock());
838   if (functions & Func_Resize)
839     blackbox->grabButton(Button3, ModMask, frame.window, True,
840                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
841                          GrabModeAsync, frame.window, None,
842                          screen->allowScrollLock());
843   // alt+middle lowers the window
844   blackbox->grabButton(Button2, ModMask, frame.window, True,
845                        ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
846                        frame.window, None,
847                        screen->allowScrollLock());
848 }
849
850
851 void BlackboxWindow::ungrabButtons(void) {
852   if ((! screen->isSloppyFocus()) || screen->doClickRaise())
853     blackbox->ungrabButton(Button1, 0, frame.plate);
854
855   blackbox->ungrabButton(Button1, ModMask, frame.window);
856   blackbox->ungrabButton(Button2, ModMask, frame.window);
857   blackbox->ungrabButton(Button3, ModMask, frame.window);
858 }
859
860
861 void BlackboxWindow::positionWindows(void) {
862   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
863                     frame.rect.x(), frame.rect.y(), frame.inside_w,
864                     (flags.shaded) ? frame.title_h : frame.inside_h);
865   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
866                         frame.border_w);
867   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
868                         frame.mwm_border_w);
869   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
870                     frame.margin.left - frame.mwm_border_w - frame.border_w,
871                     frame.margin.top - frame.mwm_border_w - frame.border_w,
872                     client.rect.width(), client.rect.height());
873   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
874                     0, 0, client.rect.width(), client.rect.height());
875   // ensure client.rect contains the real location
876   client.rect.setPos(frame.rect.left() + frame.margin.left,
877                      frame.rect.top() + frame.margin.top);
878
879   if (decorations & Decor_Titlebar) {
880     if (frame.title == None) createTitlebar();
881
882     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
883                           frame.border_w);
884     XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
885                       -frame.border_w, frame.inside_w, frame.title_h);
886
887     positionButtons();
888     XMapSubwindows(blackbox->getXDisplay(), frame.title);
889     XMapWindow(blackbox->getXDisplay(), frame.title);
890   } else if (frame.title) {
891     destroyTitlebar();
892   }
893   if (decorations & Decor_Handle) {
894     if (frame.handle == None) createHandle();
895     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
896                           frame.border_w);
897     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
898                           frame.border_w);
899     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
900                           frame.border_w);
901
902     // use client.rect here so the value is correct even if shaded
903     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
904                       -frame.border_w,
905                       client.rect.height() + frame.margin.top +
906                       frame.mwm_border_w - frame.border_w,
907                       frame.inside_w, frame.handle_h);
908     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
909                       -frame.border_w, -frame.border_w,
910                       frame.grip_w, frame.handle_h);
911     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
912                       frame.inside_w - frame.grip_w - frame.border_w,
913                       -frame.border_w, frame.grip_w, frame.handle_h);
914
915     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
916     XMapWindow(blackbox->getXDisplay(), frame.handle);
917   } else if (frame.handle) {
918     destroyHandle();
919   }
920   XSync(blackbox->getXDisplay(), False);
921 }
922
923
924 void BlackboxWindow::updateStrut(void) {
925   unsigned long num = 4;
926   unsigned long *data;
927   if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
928                         num, &data))
929     return;
930  
931   if (num == 4) {
932     client.strut.left = data[0];
933     client.strut.right = data[1];
934     client.strut.top = data[2];
935     client.strut.bottom = data[3];
936
937     screen->updateAvailableArea();
938   }
939
940   delete [] data;
941 }
942
943
944 void BlackboxWindow::getWindowType(void) {
945   unsigned long val;
946   if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
947                       val)) {
948     if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop))
949       window_type = Type_Desktop;
950     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock))
951       window_type = Type_Dock;
952     else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
953       window_type = Type_Toolbar;
954     else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu))
955       window_type = Type_Menu;
956     else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility))
957       window_type = Type_Utility;
958     else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash))
959       window_type = Type_Splash;
960     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog))
961       window_type = Type_Dialog;
962     else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal))
963       window_type = Type_Normal;
964     return;
965   }
966
967   /*
968    * the window type hint was not set, which means we either classify ourself
969    * as a normal window or a dialog, depending on if we are a transient.
970    */
971   if (isTransient())
972     window_type = Type_Dialog;
973
974   window_type = Type_Normal;
975 }
976
977
978 void BlackboxWindow::getWMName(void) {
979   if (xatom->getValue(client.window, XAtom::net_wm_name,
980                       XAtom::utf8, client.title) &&
981       !client.title.empty()) {
982     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
983     return;
984   }
985   //fall through to using WM_NAME
986   if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
987       && !client.title.empty()) {
988     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
989     return;
990   }
991   // fall back to an internal default
992   client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
993   xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
994                   client.title);
995 }
996
997
998 void BlackboxWindow::getWMIconName(void) {
999   if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1000                       XAtom::utf8, client.icon_title) && 
1001       !client.icon_title.empty()) {
1002     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1003     return;
1004   }
1005   //fall through to using WM_ICON_NAME
1006   if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1007                       client.icon_title) && 
1008       !client.icon_title.empty()) {
1009     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1010     return;
1011   }
1012   // fall back to using the main name
1013   client.icon_title = client.title;
1014   xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1015                   client.icon_title);
1016 }
1017
1018
1019 /*
1020  * Retrieve which WM Protocols are supported by the client window.
1021  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1022  * window's decorations and allow the close behavior.
1023  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1024  * this.
1025  */
1026 void BlackboxWindow::getWMProtocols(void) {
1027   Atom *proto;
1028   int num_return = 0;
1029
1030   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1031                       &proto, &num_return)) {
1032     for (int i = 0; i < num_return; ++i) {
1033       if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1034         decorations |= Decor_Close;
1035         functions |= Func_Close;
1036       } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1037         flags.send_focus_message = True;
1038       else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1039         screen->addNetizen(new Netizen(screen, client.window));
1040     }
1041
1042     XFree(proto);
1043   }
1044 }
1045
1046
1047 /*
1048  * Gets the value of the WM_HINTS property.
1049  * If the property is not set, then use a set of default values.
1050  */
1051 void BlackboxWindow::getWMHints(void) {
1052   focus_mode = F_Passive;
1053   client.initial_state = NormalState;
1054
1055   // remove from current window group
1056   if (client.window_group) {
1057     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1058     if (group) group->removeWindow(this);
1059   }
1060   client.window_group = None;
1061
1062   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1063   if (! wmhint) {
1064     return;
1065   }
1066
1067   if (wmhint->flags & InputHint) {
1068     if (wmhint->input == True) {
1069       if (flags.send_focus_message)
1070         focus_mode = F_LocallyActive;
1071     } else {
1072       if (flags.send_focus_message)
1073         focus_mode = F_GloballyActive;
1074       else
1075         focus_mode = F_NoInput;
1076     }
1077   }
1078
1079   if (wmhint->flags & StateHint)
1080     client.initial_state = wmhint->initial_state;
1081
1082   if (wmhint->flags & WindowGroupHint) {
1083     client.window_group = wmhint->window_group;
1084
1085     // add window to the appropriate group
1086     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1087     if (! group) { // no group found, create it!
1088       new BWindowGroup(blackbox, client.window_group);
1089       group = blackbox->searchGroup(client.window_group);
1090     }
1091     if (group)
1092       group->addWindow(this);
1093   }
1094
1095   client.wm_hint_flags = wmhint->flags;
1096   XFree(wmhint);
1097 }
1098
1099
1100 /*
1101  * Gets the value of the WM_NORMAL_HINTS property.
1102  * If the property is not set, then use a set of default values.
1103  */
1104 void BlackboxWindow::getWMNormalHints(void) {
1105   long icccm_mask;
1106   XSizeHints sizehint;
1107
1108   client.min_width = client.min_height =
1109     client.width_inc = client.height_inc = 1;
1110   client.base_width = client.base_height = 0;
1111   client.win_gravity = NorthWestGravity;
1112 #if 0
1113   client.min_aspect_x = client.min_aspect_y =
1114     client.max_aspect_x = client.max_aspect_y = 1;
1115 #endif
1116
1117   /*
1118     use the full screen, not the strut modified size. otherwise when the
1119     availableArea changes max_width/height will be incorrect and lead to odd
1120     rendering bugs.
1121   */
1122   const Rect& screen_area = screen->getRect();
1123   client.max_width = screen_area.width();
1124   client.max_height = screen_area.height();
1125
1126   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1127                           &sizehint, &icccm_mask))
1128     return;
1129
1130   client.normal_hint_flags = sizehint.flags;
1131
1132   if (sizehint.flags & PMinSize) {
1133     if (sizehint.min_width >= 0)
1134       client.min_width = sizehint.min_width;
1135     if (sizehint.min_height >= 0)
1136       client.min_height = sizehint.min_height;
1137   }
1138
1139   if (sizehint.flags & PMaxSize) {
1140     if (sizehint.max_width > static_cast<signed>(client.min_width))
1141       client.max_width = sizehint.max_width;
1142     else
1143       client.max_width = client.min_width;
1144
1145     if (sizehint.max_height > static_cast<signed>(client.min_height))
1146       client.max_height = sizehint.max_height;
1147     else
1148       client.max_height = client.min_height;
1149   }
1150
1151   if (sizehint.flags & PResizeInc) {
1152     client.width_inc = sizehint.width_inc;
1153     client.height_inc = sizehint.height_inc;
1154   }
1155
1156 #if 0 // we do not support this at the moment
1157   if (sizehint.flags & PAspect) {
1158     client.min_aspect_x = sizehint.min_aspect.x;
1159     client.min_aspect_y = sizehint.min_aspect.y;
1160     client.max_aspect_x = sizehint.max_aspect.x;
1161     client.max_aspect_y = sizehint.max_aspect.y;
1162   }
1163 #endif
1164
1165   if (sizehint.flags & PBaseSize) {
1166     client.base_width = sizehint.base_width;
1167     client.base_height = sizehint.base_height;
1168   }
1169
1170   if (sizehint.flags & PWinGravity)
1171     client.win_gravity = sizehint.win_gravity;
1172 }
1173
1174
1175 /*
1176  * Gets the NETWM hints for the class' contained window.
1177  */
1178 void BlackboxWindow::getNetWMHints(void) {
1179   unsigned long workspace;
1180
1181   if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1182                       workspace)) {
1183     if (workspace == 0xffffffff)
1184       flags.stuck = True;
1185     else
1186       blackbox_attrib.workspace = workspace;
1187   }
1188
1189   unsigned long *state;
1190   unsigned long num = (unsigned) -1;
1191   if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1192                       num, &state)) {
1193     bool vert = False,
1194          horz = False;
1195     for (unsigned long i = 0; i < num; ++i) {
1196       if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1197         flags.modal = True;
1198       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1199         flags.shaded = True;
1200       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1201         flags.skip_taskbar = True;
1202       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1203         flags.skip_pager = True;
1204       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1205         flags.fullscreen = True;
1206       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1207         setState(IconicState);
1208       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1209         vert = True;
1210       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1211         horz = True;
1212     }
1213     if (vert && horz)
1214       flags.maximized = 1;
1215     else if (vert)
1216       flags.maximized = 2;
1217     else if (horz)
1218       flags.maximized = 3;
1219
1220     delete [] state;
1221   }
1222 }
1223
1224
1225 /*
1226  * Gets the MWM hints for the class' contained window.
1227  * This is used while initializing the window to its first state, and not
1228  * thereafter.
1229  * Returns: true if the MWM hints are successfully retreived and applied;
1230  * false if they are not.
1231  */
1232 void BlackboxWindow::getMWMHints(void) {
1233   unsigned long num;
1234   MwmHints *mwm_hint;
1235
1236   num = PropMwmHintsElements;
1237   if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1238                         XAtom::motif_wm_hints, num,
1239                         (unsigned long **)&mwm_hint))
1240     return;
1241   if (num < PropMwmHintsElements) {
1242     delete [] mwm_hint;
1243     return;
1244   }
1245
1246   if (mwm_hint->flags & MwmHintsDecorations) {
1247     if (mwm_hint->decorations & MwmDecorAll) {
1248       decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1249                     Decor_Iconify | Decor_Maximize | Decor_Close;
1250     } else {
1251       decorations = 0;
1252
1253       if (mwm_hint->decorations & MwmDecorBorder)
1254         decorations |= Decor_Border;
1255       if (mwm_hint->decorations & MwmDecorHandle)
1256         decorations |= Decor_Handle;
1257       if (mwm_hint->decorations & MwmDecorTitle)
1258         decorations |= Decor_Titlebar;
1259       if (mwm_hint->decorations & MwmDecorIconify)
1260         decorations |= Decor_Iconify;
1261       if (mwm_hint->decorations & MwmDecorMaximize)
1262         decorations |= Decor_Maximize;
1263     }
1264   }
1265
1266   if (mwm_hint->flags & MwmHintsFunctions) {
1267     if (mwm_hint->functions & MwmFuncAll) {
1268       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1269                   Func_Close;
1270     } else {
1271       functions = 0;
1272
1273       if (mwm_hint->functions & MwmFuncResize)
1274         functions |= Func_Resize;
1275       if (mwm_hint->functions & MwmFuncMove)
1276         functions |= Func_Move;
1277       if (mwm_hint->functions & MwmFuncIconify)
1278         functions |= Func_Iconify;
1279       if (mwm_hint->functions & MwmFuncMaximize)
1280         functions |= Func_Maximize;
1281       if (mwm_hint->functions & MwmFuncClose)
1282         functions |= Func_Close;
1283     }
1284   }
1285   delete [] mwm_hint;
1286 }
1287
1288
1289 /*
1290  * Gets the blackbox hints from the class' contained window.
1291  * This is used while initializing the window to its first state, and not
1292  * thereafter.
1293  * Returns: true if the hints are successfully retreived and applied; false if
1294  * they are not.
1295  */
1296 bool BlackboxWindow::getBlackboxHints(void) {
1297   unsigned long num;
1298   BlackboxHints *blackbox_hint;
1299
1300   num = PropBlackboxHintsElements;
1301   if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1302                         XAtom::blackbox_hints, num,
1303                         (unsigned long **)&blackbox_hint))
1304     return False;
1305   if (num < PropBlackboxHintsElements) {
1306     delete [] blackbox_hint;
1307     return False;
1308   }
1309
1310   if (blackbox_hint->flags & AttribShaded)
1311     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1312
1313   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1314       (blackbox_hint->flags & AttribMaxVert))
1315     flags.maximized = (blackbox_hint->attrib &
1316                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1317   else if (blackbox_hint->flags & AttribMaxVert)
1318     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1319   else if (blackbox_hint->flags & AttribMaxHoriz)
1320     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1321
1322   if (blackbox_hint->flags & AttribOmnipresent)
1323     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1324
1325   if (blackbox_hint->flags & AttribWorkspace)
1326     blackbox_attrib.workspace = blackbox_hint->workspace;
1327
1328   // if (blackbox_hint->flags & AttribStack)
1329   //   don't yet have always on top/bottom for blackbox yet... working
1330   //   on that
1331
1332   if (blackbox_hint->flags & AttribDecoration) {
1333     switch (blackbox_hint->decoration) {
1334     case DecorNone:
1335       // clear all decorations except close
1336       decorations &= Decor_Close;
1337       // clear all functions except close
1338       functions &= Func_Close;
1339
1340       break;
1341
1342     case DecorTiny:
1343       decorations |= Decor_Titlebar | Decor_Iconify;
1344       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1345       functions |= Func_Move | Func_Iconify;
1346       functions &= ~(Func_Resize | Func_Maximize);
1347
1348       break;
1349
1350     case DecorTool:
1351       decorations |= Decor_Titlebar;
1352       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1353       functions |= Func_Move;
1354       functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1355
1356       break;
1357
1358     case DecorNormal:
1359     default:
1360       decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1361                      Decor_Iconify | Decor_Maximize;
1362       functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
1363
1364       break;
1365     }
1366
1367     reconfigure();
1368   }
1369   
1370   delete [] blackbox_hint;
1371
1372   return True;
1373 }
1374
1375
1376 void BlackboxWindow::getTransientInfo(void) {
1377   if (client.transient_for &&
1378       client.transient_for != (BlackboxWindow *) ~0ul) {
1379     // the transient for hint was removed, so we need to tell our
1380     // previous transient_for that we are going away
1381     client.transient_for->client.transientList.remove(this);
1382   }
1383
1384   // we have no transient_for until we find a new one
1385   client.transient_for = 0;
1386
1387   Window trans_for;
1388   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1389                              &trans_for)) {
1390     // transient_for hint not set
1391     return;
1392   }
1393
1394   if (trans_for == client.window) {
1395     // wierd client... treat this window as a normal window
1396     return;
1397   }
1398
1399   if (trans_for == None || trans_for == screen->getRootWindow()) {
1400     // this is an undocumented interpretation of the ICCCM. a transient
1401     // associated with None/Root/itself is assumed to be a modal root
1402     // transient.  we don't support the concept of a global transient,
1403     // so we just associate this transient with nothing, and perhaps
1404     // we will add support later for global modality.
1405     client.transient_for = (BlackboxWindow *) ~0ul;
1406     flags.modal = True;
1407     return;
1408   }
1409
1410   client.transient_for = blackbox->searchWindow(trans_for);
1411   if (! client.transient_for &&
1412       client.window_group && trans_for == client.window_group) {
1413     // no direct transient_for, perhaps this is a group transient?
1414     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1415     if (group) client.transient_for = group->find(screen);
1416   }
1417
1418   if (! client.transient_for || client.transient_for == this) {
1419     // no transient_for found, or we have a wierd client that wants to be
1420     // a transient for itself, so we treat this window as a normal window
1421     client.transient_for = (BlackboxWindow*) 0;
1422     return;
1423   }
1424
1425   // register ourselves with our new transient_for
1426   client.transient_for->client.transientList.push_back(this);
1427   flags.stuck = client.transient_for->flags.stuck;
1428 }
1429
1430
1431 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1432   if (client.transient_for &&
1433       client.transient_for != (BlackboxWindow*) ~0ul)
1434     return client.transient_for;
1435   return 0;
1436 }
1437
1438
1439 /*
1440  * This function is responsible for updating both the client and the frame
1441  * rectangles.
1442  * According to the ICCCM a client message is not sent for a resize, only a
1443  * move.
1444  */
1445 void BlackboxWindow::configure(int dx, int dy,
1446                                unsigned int dw, unsigned int dh) {
1447   bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1448                      ! flags.moving);
1449
1450   if (dw != frame.rect.width() || dh != frame.rect.height()) {
1451     frame.rect.setRect(dx, dy, dw, dh);
1452     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1453     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1454
1455     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1456       frame.rect.setPos(0, 0);
1457
1458     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1459                           frame.rect.top() + frame.margin.top,
1460                           frame.rect.right() - frame.margin.right,
1461                           frame.rect.bottom() - frame.margin.bottom);
1462
1463 #ifdef    SHAPE
1464     if (blackbox->hasShapeExtensions() && flags.shaped) {
1465       configureShape();
1466     }
1467 #endif // SHAPE
1468
1469     positionWindows();
1470     decorate();
1471     redrawWindowFrame();
1472   } else {
1473     frame.rect.setPos(dx, dy);
1474
1475     XMoveWindow(blackbox->getXDisplay(), frame.window,
1476                 frame.rect.x(), frame.rect.y());
1477     /*
1478       we may have been called just after an opaque window move, so even though
1479       the old coords match the new ones no ConfigureNotify has been sent yet.
1480       There are likely other times when this will be relevant as well.
1481     */
1482     if (! flags.moving) send_event = True;
1483   }
1484
1485   if (send_event) {
1486     // if moving, the update and event will occur when the move finishes
1487     client.rect.setPos(frame.rect.left() + frame.margin.left,
1488                        frame.rect.top() + frame.margin.top);
1489
1490     XEvent event;
1491     event.type = ConfigureNotify;
1492
1493     event.xconfigure.display = blackbox->getXDisplay();
1494     event.xconfigure.event = client.window;
1495     event.xconfigure.window = client.window;
1496     event.xconfigure.x = client.rect.x();
1497     event.xconfigure.y = client.rect.y();
1498     event.xconfigure.width = client.rect.width();
1499     event.xconfigure.height = client.rect.height();
1500     event.xconfigure.border_width = client.old_bw;
1501     event.xconfigure.above = frame.window;
1502     event.xconfigure.override_redirect = False;
1503
1504     XSendEvent(blackbox->getXDisplay(), client.window, False,
1505                StructureNotifyMask, &event);
1506     screen->updateNetizenConfigNotify(&event);
1507     XFlush(blackbox->getXDisplay());
1508   }
1509 }
1510
1511
1512 #ifdef SHAPE
1513 void BlackboxWindow::configureShape(void) {
1514   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1515                      frame.margin.left - frame.border_w,
1516                      frame.margin.top - frame.border_w,
1517                      client.window, ShapeBounding, ShapeSet);
1518
1519   int num = 0;
1520   XRectangle xrect[2];
1521
1522   if (decorations & Decor_Titlebar) {
1523     xrect[0].x = xrect[0].y = -frame.border_w;
1524     xrect[0].width = frame.rect.width();
1525     xrect[0].height = frame.title_h + (frame.border_w * 2);
1526     ++num;
1527   }
1528
1529   if (decorations & Decor_Handle) {
1530     xrect[1].x = -frame.border_w;
1531     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1532                  frame.mwm_border_w - frame.border_w;
1533     xrect[1].width = frame.rect.width();
1534     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1535     ++num;
1536   }
1537
1538   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1539                           ShapeBounding, 0, 0, xrect, num,
1540                           ShapeUnion, Unsorted);
1541 }
1542 #endif // SHAPE
1543
1544
1545 bool BlackboxWindow::setInputFocus(void) {
1546   if (flags.focused) return True;
1547
1548   assert(! flags.iconic &&
1549          (flags.stuck ||  // window must be on the current workspace or sticky
1550           blackbox_attrib.workspace == screen->getCurrentWorkspaceID()));
1551 #if 0
1552   // if the window is not visible, mark the window as wanting focus rather
1553   // than give it focus.
1554   if (! flags.visible) {
1555     Workspace *wkspc = screen->getWorkspace(blackbox_attrib.workspace);
1556     wkspc->setLastFocusedWindow(this);
1557     return True;
1558   }
1559 #endif
1560   /*
1561      We only do this check for normal windows and dialogs because other windows
1562      do this on purpose, such as kde's kicker, and we don't want to go moving
1563      it.
1564   */
1565   if (window_type == Type_Normal || window_type == Type_Dialog)
1566     if (! frame.rect.intersects(screen->getRect())) {
1567       // client is outside the screen, move it to the center
1568       configure((screen->getWidth() - frame.rect.width()) / 2,
1569                 (screen->getHeight() - frame.rect.height()) / 2,
1570                 frame.rect.width(), frame.rect.height());
1571     }
1572
1573   if (client.transientList.size() > 0) {
1574     // transfer focus to any modal transients
1575     BlackboxWindowList::iterator it, end = client.transientList.end();
1576     for (it = client.transientList.begin(); it != end; ++it) {
1577       if ((*it)->flags.modal) return (*it)->setInputFocus();
1578     }
1579   }
1580
1581   bool ret = True;
1582   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1583     XSetInputFocus(blackbox->getXDisplay(), client.window,
1584                    RevertToPointerRoot, CurrentTime);
1585
1586     blackbox->setFocusedWindow(this);
1587   } else {
1588     /* we could set the focus to none, since the window doesn't accept focus,
1589      * but we shouldn't set focus to nothing since this would surely make
1590      * someone angry
1591      */
1592     ret = False;
1593   }
1594
1595   if (flags.send_focus_message) {
1596     XEvent ce;
1597     ce.xclient.type = ClientMessage;
1598     ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1599     ce.xclient.display = blackbox->getXDisplay();
1600     ce.xclient.window = client.window;
1601     ce.xclient.format = 32;
1602     ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1603     ce.xclient.data.l[1] = blackbox->getLastTime();
1604     ce.xclient.data.l[2] = 0l;
1605     ce.xclient.data.l[3] = 0l;
1606     ce.xclient.data.l[4] = 0l;
1607     XSendEvent(blackbox->getXDisplay(), client.window, False,
1608                NoEventMask, &ce);
1609     XFlush(blackbox->getXDisplay());
1610   }
1611
1612   return ret;
1613 }
1614
1615
1616 void BlackboxWindow::iconify(void) {
1617   if (flags.iconic) return;
1618
1619   // We don't need to worry about resizing because resizing always grabs the X
1620   // server. This should only ever happen if using opaque moving.
1621   if (flags.moving)
1622     endMove();
1623     
1624   if (windowmenu) windowmenu->hide();
1625
1626   /*
1627    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1628    * we need to clear the event mask on client.window for a split second.
1629    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1630    * split second, leaving us with a ghost window... so, we need to do this
1631    * while the X server is grabbed
1632    */
1633   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1634                              StructureNotifyMask;
1635   XGrabServer(blackbox->getXDisplay());
1636   XSelectInput(blackbox->getXDisplay(), client.window,
1637                event_mask & ~StructureNotifyMask);
1638   XUnmapWindow(blackbox->getXDisplay(), client.window);
1639   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1640   XUngrabServer(blackbox->getXDisplay());
1641
1642   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1643   flags.visible = False;
1644   flags.iconic = True;
1645
1646   setState(IconicState);
1647
1648   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1649
1650   if (isTransient()) {
1651     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1652         ! client.transient_for->flags.iconic) {
1653       // iconify our transient_for
1654       client.transient_for->iconify();
1655     }
1656   }
1657
1658   screen->addIcon(this);
1659
1660   if (client.transientList.size() > 0) {
1661     // iconify all transients
1662     BlackboxWindowList::iterator it, end = client.transientList.end();
1663     for (it = client.transientList.begin(); it != end; ++it) {
1664       if (! (*it)->flags.iconic) (*it)->iconify();
1665     }
1666   }
1667   screen->updateStackingList();
1668 }
1669
1670
1671 void BlackboxWindow::show(void) {
1672   flags.visible = True;
1673   flags.iconic = False;
1674
1675   current_state = (flags.shaded) ? IconicState : NormalState;
1676   setState(current_state);
1677
1678   XMapWindow(blackbox->getXDisplay(), client.window);
1679   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1680   XMapWindow(blackbox->getXDisplay(), frame.window);
1681
1682 #ifdef DEBUG
1683   int real_x, real_y;
1684   Window child;
1685   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1686                         screen->getRootWindow(),
1687                         0, 0, &real_x, &real_y, &child);
1688   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1689           client.rect.left(), client.rect.top(), real_x, real_y);
1690   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1691 #endif
1692 }
1693
1694
1695 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1696   if (flags.iconic || reassoc)
1697     screen->reassociateWindow(this, BSENTINEL, False);
1698   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1699     return;
1700
1701   show();
1702
1703   // reassociate and deiconify all transients
1704   if (reassoc && client.transientList.size() > 0) {
1705     BlackboxWindowList::iterator it, end = client.transientList.end();
1706     for (it = client.transientList.begin(); it != end; ++it) {
1707       (*it)->deiconify(True, False);
1708     }
1709   }
1710
1711   if (raise)
1712     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1713 }
1714
1715
1716 void BlackboxWindow::close(void) {
1717   XEvent ce;
1718   ce.xclient.type = ClientMessage;
1719   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1720   ce.xclient.display = blackbox->getXDisplay();
1721   ce.xclient.window = client.window;
1722   ce.xclient.format = 32;
1723   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1724   ce.xclient.data.l[1] = CurrentTime;
1725   ce.xclient.data.l[2] = 0l;
1726   ce.xclient.data.l[3] = 0l;
1727   ce.xclient.data.l[4] = 0l;
1728   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1729   XFlush(blackbox->getXDisplay());
1730 }
1731
1732
1733 void BlackboxWindow::withdraw(void) {
1734   // We don't need to worry about resizing because resizing always grabs the X
1735   // server. This should only ever happen if using opaque moving.
1736   if (flags.moving)
1737     endMove();
1738     
1739   flags.visible = False;
1740   flags.iconic = False;
1741
1742   setState(current_state);
1743
1744   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1745
1746   XGrabServer(blackbox->getXDisplay());
1747
1748   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1749                              StructureNotifyMask;
1750   XSelectInput(blackbox->getXDisplay(), client.window,
1751                event_mask & ~StructureNotifyMask);
1752   XUnmapWindow(blackbox->getXDisplay(), client.window);
1753   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1754
1755   XUngrabServer(blackbox->getXDisplay());
1756
1757   if (windowmenu) windowmenu->hide();
1758 }
1759
1760
1761 void BlackboxWindow::maximize(unsigned int button) {
1762   // We don't need to worry about resizing because resizing always grabs the X
1763   // server. This should only ever happen if using opaque moving.
1764   if (flags.moving)
1765     endMove();
1766
1767   // handle case where menu is open then the max button is used instead
1768   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1769
1770   if (flags.maximized) {
1771     flags.maximized = 0;
1772
1773     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1774     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1775
1776     /*
1777       when a resize finishes, maximize(0) is called to clear any maximization
1778       flags currently set.  Otherwise it still thinks it is maximized.
1779       so we do not need to call configure() because resizing will handle it
1780     */
1781     if (! flags.resizing)
1782       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1783                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1784
1785     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1786     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1787
1788     redrawAllButtons(); // in case it is not called in configure()
1789     setState(current_state);
1790     return;
1791   }
1792
1793   blackbox_attrib.premax_x = frame.rect.x();
1794   blackbox_attrib.premax_y = frame.rect.y();
1795   blackbox_attrib.premax_w = frame.rect.width();
1796   // use client.rect so that clients can be restored even if shaded
1797   blackbox_attrib.premax_h =
1798     client.rect.height() + frame.margin.top + frame.margin.bottom;
1799
1800 #ifdef    XINERAMA
1801   if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1802     // find the area to use
1803     RectList availableAreas = screen->allAvailableAreas();
1804     RectList::iterator it, end = availableAreas.end();
1805
1806     for (it = availableAreas.begin(); it != end; ++it)
1807       if (it->intersects(frame.rect)) break;
1808     if (it == end) // the window isn't inside an area
1809       it = availableAreas.begin(); // so just default to the first one
1810
1811     frame.changing = *it;
1812   } else
1813 #endif // XINERAMA
1814   frame.changing = screen->availableArea();
1815
1816   switch(button) {
1817   case 1:
1818     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1819     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1820     break;
1821
1822   case 2:
1823     blackbox_attrib.flags |= AttribMaxVert;
1824     blackbox_attrib.attrib |= AttribMaxVert;
1825
1826     frame.changing.setX(frame.rect.x());
1827     frame.changing.setWidth(frame.rect.width());
1828     break;
1829
1830   case 3:
1831     blackbox_attrib.flags |= AttribMaxHoriz;
1832     blackbox_attrib.attrib |= AttribMaxHoriz;
1833
1834     frame.changing.setY(frame.rect.y());
1835     frame.changing.setHeight(frame.rect.height());
1836     break;
1837   }
1838
1839   constrain(TopLeft);
1840
1841   if (flags.shaded) {
1842     blackbox_attrib.flags ^= AttribShaded;
1843     blackbox_attrib.attrib ^= AttribShaded;
1844     flags.shaded = False;
1845   }
1846
1847   flags.maximized = button;
1848
1849   configure(frame.changing.x(), frame.changing.y(),
1850             frame.changing.width(), frame.changing.height());
1851   if (flags.focused)
1852     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1853   redrawAllButtons(); // in case it is not called in configure()
1854   setState(current_state);
1855 }
1856
1857
1858 // re-maximizes the window to take into account availableArea changes
1859 void BlackboxWindow::remaximize(void) {
1860   // save the original dimensions because maximize will wipe them out
1861   int premax_x = blackbox_attrib.premax_x,
1862     premax_y = blackbox_attrib.premax_y,
1863     premax_w = blackbox_attrib.premax_w,
1864     premax_h = blackbox_attrib.premax_h;
1865
1866   unsigned int button = flags.maximized;
1867   flags.maximized = 0; // trick maximize() into working
1868   maximize(button);
1869
1870   // restore saved values
1871   blackbox_attrib.premax_x = premax_x;
1872   blackbox_attrib.premax_y = premax_y;
1873   blackbox_attrib.premax_w = premax_w;
1874   blackbox_attrib.premax_h = premax_h;
1875 }
1876
1877
1878 void BlackboxWindow::setWorkspace(unsigned int n) {
1879   blackbox_attrib.flags |= AttribWorkspace;
1880   blackbox_attrib.workspace = n;
1881   if (n == BSENTINEL) { // iconified window
1882     /*
1883        we set the workspace to 'all workspaces' so that taskbars will show the
1884        window. otherwise, it made uniconifying a window imposible without the
1885        blackbox workspace menu
1886     */
1887     n = 0xffffffff;
1888   }
1889   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1890 }
1891
1892
1893 void BlackboxWindow::shade(void) {
1894   if (flags.shaded) {
1895     XResizeWindow(blackbox->getXDisplay(), frame.window,
1896                   frame.inside_w, frame.inside_h);
1897     flags.shaded = False;
1898     blackbox_attrib.flags ^= AttribShaded;
1899     blackbox_attrib.attrib ^= AttribShaded;
1900
1901     setState(NormalState);
1902
1903     // set the frame rect to the normal size
1904     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1905                          frame.margin.bottom);
1906   } else {
1907     if (! (decorations & Decor_Titlebar))
1908       return; // can't shade it without a titlebar!
1909
1910     XResizeWindow(blackbox->getXDisplay(), frame.window,
1911                   frame.inside_w, frame.title_h);
1912     flags.shaded = True;
1913     blackbox_attrib.flags |= AttribShaded;
1914     blackbox_attrib.attrib |= AttribShaded;
1915
1916     setState(IconicState);
1917
1918     // set the frame rect to the shaded size
1919     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1920   }
1921 }
1922
1923
1924 /*
1925  * (Un)Sticks a window and its relatives.
1926  */
1927 void BlackboxWindow::stick(void) {
1928   if (flags.stuck) {
1929     blackbox_attrib.flags ^= AttribOmnipresent;
1930     blackbox_attrib.attrib ^= AttribOmnipresent;
1931
1932     flags.stuck = False;
1933
1934     if (! flags.iconic)
1935       screen->reassociateWindow(this, BSENTINEL, True);
1936     // temporary fix since sticky windows suck. set the hint to what we
1937     // actually hold in our data.
1938     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1939                     blackbox_attrib.workspace);
1940
1941     setState(current_state);
1942   } else {
1943     flags.stuck = True;
1944
1945     blackbox_attrib.flags |= AttribOmnipresent;
1946     blackbox_attrib.attrib |= AttribOmnipresent;
1947
1948     // temporary fix since sticky windows suck. set the hint to a different
1949     // value than that contained in the class' data.
1950     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1951                     0xffffffff);
1952
1953     setState(current_state);
1954   }
1955   // go up the chain
1956   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1957       client.transient_for->isStuck() != flags.stuck)
1958     client.transient_for->stick();
1959   // go down the chain
1960   BlackboxWindowList::iterator it;
1961   const BlackboxWindowList::iterator end = client.transientList.end();
1962   for (it = client.transientList.begin(); it != end; ++it)
1963     if ((*it)->isStuck() != flags.stuck)
1964       (*it)->stick();
1965 }
1966
1967
1968 void BlackboxWindow::redrawWindowFrame(void) const {
1969   if (decorations & Decor_Titlebar) {
1970     if (flags.focused) {
1971       if (frame.ftitle)
1972         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1973                                    frame.title, frame.ftitle);
1974       else
1975         XSetWindowBackground(blackbox->getXDisplay(),
1976                              frame.title, frame.ftitle_pixel);
1977     } else {
1978       if (frame.utitle)
1979         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1980                                    frame.title, frame.utitle);
1981       else
1982         XSetWindowBackground(blackbox->getXDisplay(),
1983                              frame.title, frame.utitle_pixel);
1984     }
1985     XClearWindow(blackbox->getXDisplay(), frame.title);
1986
1987     redrawLabel();
1988     redrawAllButtons();
1989   }
1990
1991   if (decorations & Decor_Handle) {
1992     if (flags.focused) {
1993       if (frame.fhandle)
1994         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1995                                    frame.handle, frame.fhandle);
1996       else
1997         XSetWindowBackground(blackbox->getXDisplay(),
1998                              frame.handle, frame.fhandle_pixel);
1999
2000       if (frame.fgrip) {
2001         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2002                                    frame.left_grip, frame.fgrip);
2003         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2004                                    frame.right_grip, frame.fgrip);
2005       } else {
2006         XSetWindowBackground(blackbox->getXDisplay(),
2007                              frame.left_grip, frame.fgrip_pixel);
2008         XSetWindowBackground(blackbox->getXDisplay(),
2009                              frame.right_grip, frame.fgrip_pixel);
2010       }
2011     } else {
2012       if (frame.uhandle)
2013         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2014                                    frame.handle, frame.uhandle);
2015       else
2016         XSetWindowBackground(blackbox->getXDisplay(),
2017                              frame.handle, frame.uhandle_pixel);
2018
2019       if (frame.ugrip) {
2020         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2021                                    frame.left_grip, frame.ugrip);
2022         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2023                                    frame.right_grip, frame.ugrip);
2024       } else {
2025         XSetWindowBackground(blackbox->getXDisplay(),
2026                              frame.left_grip, frame.ugrip_pixel);
2027         XSetWindowBackground(blackbox->getXDisplay(),
2028                              frame.right_grip, frame.ugrip_pixel);
2029       }
2030     }
2031     XClearWindow(blackbox->getXDisplay(), frame.handle);
2032     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2033     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2034   }
2035
2036   if (decorations & Decor_Border) {
2037     if (flags.focused)
2038       XSetWindowBorder(blackbox->getXDisplay(),
2039                        frame.plate, frame.fborder_pixel);
2040     else
2041       XSetWindowBorder(blackbox->getXDisplay(),
2042                        frame.plate, frame.uborder_pixel);
2043   }
2044 }
2045
2046
2047 void BlackboxWindow::setFocusFlag(bool focus) {
2048   // only focus a window if it is visible
2049   if (focus && !flags.visible)
2050     return;
2051
2052   flags.focused = focus;
2053
2054   redrawWindowFrame();
2055
2056   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2057     if (isFocused()) timer->start();
2058     else timer->stop();
2059   }
2060
2061   if (isFocused())
2062     blackbox->setFocusedWindow(this);
2063   
2064   Clientmenu *menu = screen->getWorkspace(blackbox_attrib.workspace)->getMenu();
2065   menu->setItemSelected(window_number, isFocused());
2066 }
2067
2068
2069 void BlackboxWindow::installColormap(bool install) {
2070   int i = 0, ncmap = 0;
2071   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2072                                             client.window, &ncmap);
2073   if (cmaps) {
2074     XWindowAttributes wattrib;
2075     if (XGetWindowAttributes(blackbox->getXDisplay(),
2076                              client.window, &wattrib)) {
2077       if (install) {
2078         // install the window's colormap
2079         for (i = 0; i < ncmap; i++) {
2080           if (*(cmaps + i) == wattrib.colormap)
2081             // this window is using an installed color map... do not install
2082             install = False;
2083         }
2084         // otherwise, install the window's colormap
2085         if (install)
2086           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2087       } else {
2088         // uninstall the window's colormap
2089         for (i = 0; i < ncmap; i++) {
2090           if (*(cmaps + i) == wattrib.colormap)
2091             // we found the colormap to uninstall
2092             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2093         }
2094       }
2095     }
2096
2097     XFree(cmaps);
2098   }
2099 }
2100
2101
2102 void BlackboxWindow::setAllowedActions(void) {
2103   Atom actions[7];
2104   int num = 0;
2105   
2106   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2107   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2108   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2109
2110   if (functions & Func_Move)
2111     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2112   if (functions & Func_Resize)
2113     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2114   if (functions & Func_Maximize) {
2115     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2116     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2117   }
2118
2119   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2120                   actions, num);
2121 }
2122
2123
2124 void BlackboxWindow::setState(unsigned long new_state) {
2125   current_state = new_state;
2126
2127   unsigned long state[2];
2128   state[0] = current_state;
2129   state[1] = None;
2130   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2131  
2132   xatom->setValue(client.window, XAtom::blackbox_attributes,
2133                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2134                   PropBlackboxAttributesElements);
2135
2136   Atom netstate[8];
2137   int num = 0;
2138   if (flags.modal)
2139     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2140   if (flags.shaded)
2141     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2142   if (flags.iconic)
2143     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2144   if (flags.skip_taskbar)
2145     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2146   if (flags.skip_pager)
2147     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2148   if (flags.fullscreen)
2149     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2150   if (flags.maximized == 1 || flags.maximized == 2)
2151     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2152   if (flags.maximized == 1 || flags.maximized == 3)
2153     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2154   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2155                   netstate, num);
2156 }
2157
2158
2159 bool BlackboxWindow::getState(void) {
2160   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2161                              current_state);
2162   if (! ret) current_state = 0;
2163   return ret;
2164 }
2165
2166
2167 void BlackboxWindow::restoreAttributes(void) {
2168   unsigned long num = PropBlackboxAttributesElements;
2169   BlackboxAttributes *net;
2170   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2171                         XAtom::blackbox_attributes, num,
2172                         (unsigned long **)&net))
2173     return;
2174   if (num < PropBlackboxAttributesElements) {
2175     delete [] net;
2176     return;
2177   }
2178
2179   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2180     flags.shaded = False;
2181     unsigned long orig_state = current_state;
2182     shade();
2183
2184     /*
2185       At this point in the life of a window, current_state should only be set
2186       to IconicState if the window was an *icon*, not if it was shaded.
2187     */
2188     if (orig_state != IconicState)
2189       current_state = WithdrawnState;
2190  }
2191
2192   if (net->workspace != screen->getCurrentWorkspaceID() &&
2193       net->workspace < screen->getWorkspaceCount())
2194     screen->reassociateWindow(this, net->workspace, True);
2195
2196   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2197       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2198     // set to WithdrawnState so it will be mapped on the new workspace
2199     if (current_state == NormalState) current_state = WithdrawnState;
2200   } else if (current_state == WithdrawnState) {
2201     // the window is on this workspace and is Withdrawn, so it is waiting to
2202     // be mapped
2203     current_state = NormalState;
2204   }
2205
2206   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2207     flags.stuck = False;
2208     stick();
2209
2210     // if the window was on another workspace, it was going to be hidden. this
2211     // specifies that the window should be mapped since it is sticky.
2212     if (current_state == WithdrawnState) current_state = NormalState;
2213   }
2214
2215   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2216     int x = net->premax_x, y = net->premax_y;
2217     unsigned int w = net->premax_w, h = net->premax_h;
2218     flags.maximized = 0;
2219
2220     unsigned int m = 0;
2221     if ((net->flags & AttribMaxHoriz) &&
2222         (net->flags & AttribMaxVert))
2223       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2224     else if (net->flags & AttribMaxVert)
2225       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2226     else if (net->flags & AttribMaxHoriz)
2227       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2228
2229     if (m) maximize(m);
2230
2231     blackbox_attrib.premax_x = x;
2232     blackbox_attrib.premax_y = y;
2233     blackbox_attrib.premax_w = w;
2234     blackbox_attrib.premax_h = h;
2235   }
2236
2237   // with the state set it will then be the map event's job to read the
2238   // window's state and behave accordingly
2239
2240   delete [] net;
2241 }
2242
2243
2244 /*
2245  * Positions the Rect r according the the client window position and
2246  * window gravity.
2247  */
2248 void BlackboxWindow::applyGravity(Rect &r) {
2249   // apply horizontal window gravity
2250   switch (client.win_gravity) {
2251   default:
2252   case NorthWestGravity:
2253   case SouthWestGravity:
2254   case WestGravity:
2255     r.setX(client.rect.x());
2256     break;
2257
2258   case NorthGravity:
2259   case SouthGravity:
2260   case CenterGravity:
2261     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2262     break;
2263
2264   case NorthEastGravity:
2265   case SouthEastGravity:
2266   case EastGravity:
2267     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2268     break;
2269
2270   case ForgetGravity:
2271   case StaticGravity:
2272     r.setX(client.rect.x() - frame.margin.left);
2273     break;
2274   }
2275
2276   // apply vertical window gravity
2277   switch (client.win_gravity) {
2278   default:
2279   case NorthWestGravity:
2280   case NorthEastGravity:
2281   case NorthGravity:
2282     r.setY(client.rect.y());
2283     break;
2284
2285   case CenterGravity:
2286   case EastGravity:
2287   case WestGravity:
2288     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2289     break;
2290
2291   case SouthWestGravity:
2292   case SouthEastGravity:
2293   case SouthGravity:
2294     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2295     break;
2296
2297   case ForgetGravity:
2298   case StaticGravity:
2299     r.setY(client.rect.y() - frame.margin.top);
2300     break;
2301   }
2302 }
2303
2304
2305 /*
2306  * The reverse of the applyGravity function.
2307  *
2308  * Positions the Rect r according to the frame window position and
2309  * window gravity.
2310  */
2311 void BlackboxWindow::restoreGravity(Rect &r) {
2312   // restore horizontal window gravity
2313   switch (client.win_gravity) {
2314   default:
2315   case NorthWestGravity:
2316   case SouthWestGravity:
2317   case WestGravity:
2318     r.setX(frame.rect.x());
2319     break;
2320
2321   case NorthGravity:
2322   case SouthGravity:
2323   case CenterGravity:
2324     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2325     break;
2326
2327   case NorthEastGravity:
2328   case SouthEastGravity:
2329   case EastGravity:
2330     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2331     break;
2332
2333   case ForgetGravity:
2334   case StaticGravity:
2335     r.setX(frame.rect.x() + frame.margin.left);
2336     break;
2337   }
2338
2339   // restore vertical window gravity
2340   switch (client.win_gravity) {
2341   default:
2342   case NorthWestGravity:
2343   case NorthEastGravity:
2344   case NorthGravity:
2345     r.setY(frame.rect.y());
2346     break;
2347
2348   case CenterGravity:
2349   case EastGravity:
2350   case WestGravity:
2351     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2352     break;
2353
2354   case SouthWestGravity:
2355   case SouthEastGravity:
2356   case SouthGravity:
2357     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2358     break;
2359
2360   case ForgetGravity:
2361   case StaticGravity:
2362     r.setY(frame.rect.y() + frame.margin.top);
2363     break;
2364   }
2365 }
2366
2367
2368 void BlackboxWindow::redrawLabel(void) const {
2369   if (flags.focused) {
2370     if (frame.flabel)
2371       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2372                                  frame.label, frame.flabel);
2373     else
2374       XSetWindowBackground(blackbox->getXDisplay(),
2375                            frame.label, frame.flabel_pixel);
2376   } else {
2377     if (frame.ulabel)
2378       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2379                                  frame.label, frame.ulabel);
2380     else
2381       XSetWindowBackground(blackbox->getXDisplay(),
2382                            frame.label, frame.ulabel_pixel);
2383   }
2384   XClearWindow(blackbox->getXDisplay(), frame.label);
2385
2386   WindowStyle *style = screen->getWindowStyle();
2387
2388   int pos = frame.bevel_w * 2;
2389   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2390   style->font->drawString(frame.label, pos, 1,
2391                           (flags.focused ? style->l_text_focus :
2392                            style->l_text_unfocus),
2393                           client.title);
2394 }
2395
2396
2397 void BlackboxWindow::redrawAllButtons(void) const {
2398   if (frame.iconify_button) redrawIconifyButton(False);
2399   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2400   if (frame.close_button) redrawCloseButton(False);
2401 }
2402
2403
2404 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2405   if (! pressed) {
2406     if (flags.focused) {
2407       if (frame.fbutton)
2408         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2409                                    frame.iconify_button, frame.fbutton);
2410       else
2411         XSetWindowBackground(blackbox->getXDisplay(),
2412                              frame.iconify_button, frame.fbutton_pixel);
2413     } else {
2414       if (frame.ubutton)
2415         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2416                                    frame.iconify_button, frame.ubutton);
2417       else
2418         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2419                              frame.ubutton_pixel);
2420     }
2421   } else {
2422     if (frame.pbutton)
2423       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2424                                  frame.iconify_button, frame.pbutton);
2425     else
2426       XSetWindowBackground(blackbox->getXDisplay(),
2427                            frame.iconify_button, frame.pbutton_pixel);
2428   }
2429   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2430
2431   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2432            screen->getWindowStyle()->b_pic_unfocus);
2433   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2434                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2435 }
2436
2437
2438 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2439   if (! pressed) {
2440     if (flags.focused) {
2441       if (frame.fbutton)
2442         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2443                                    frame.maximize_button, frame.fbutton);
2444       else
2445         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2446                              frame.fbutton_pixel);
2447     } else {
2448       if (frame.ubutton)
2449         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2450                                    frame.maximize_button, frame.ubutton);
2451       else
2452         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2453                              frame.ubutton_pixel);
2454     }
2455   } else {
2456     if (frame.pbutton)
2457       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2458                                  frame.maximize_button, frame.pbutton);
2459     else
2460       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2461                            frame.pbutton_pixel);
2462   }
2463   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2464
2465   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2466            screen->getWindowStyle()->b_pic_unfocus);
2467   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2468                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2469   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2470             2, 3, (frame.button_w - 3), 3);
2471 }
2472
2473
2474 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2475   if (! pressed) {
2476     if (flags.focused) {
2477       if (frame.fbutton)
2478         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2479                                    frame.fbutton);
2480       else
2481         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2482                              frame.fbutton_pixel);
2483     } else {
2484       if (frame.ubutton)
2485         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2486                                    frame.ubutton);
2487       else
2488         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2489                              frame.ubutton_pixel);
2490     }
2491   } else {
2492     if (frame.pbutton)
2493       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2494                                  frame.close_button, frame.pbutton);
2495     else
2496       XSetWindowBackground(blackbox->getXDisplay(),
2497                            frame.close_button, frame.pbutton_pixel);
2498   }
2499   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2500
2501   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2502            screen->getWindowStyle()->b_pic_unfocus);
2503   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2504             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2505   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2506             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2507 }
2508
2509
2510 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2511   if (re->window != client.window)
2512     return;
2513
2514 #ifdef    DEBUG
2515   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2516           client.window);
2517 #endif // DEBUG
2518
2519   switch (current_state) {
2520   case IconicState:
2521     iconify();
2522     break;
2523
2524   case WithdrawnState:
2525     withdraw();
2526     break;
2527
2528   case NormalState:
2529   case InactiveState:
2530   case ZoomState:
2531   default:
2532     show();
2533     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2534     if (isNormal()) {
2535       if (! blackbox->isStartup()) {
2536         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2537         if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2538                                     getTransientFor()->isFocused())) {
2539           setInputFocus();
2540         }
2541         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2542           int x, y, rx, ry;
2543           Window c, r;
2544           unsigned int m;
2545           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2546                         &r, &c, &rx, &ry, &x, &y, &m);
2547           beginMove(rx, ry);
2548         }
2549       }
2550     }
2551     break;
2552   }
2553 }
2554
2555
2556 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2557   if (ue->window != client.window)
2558     return;
2559
2560 #ifdef    DEBUG
2561   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2562           client.window);
2563 #endif // DEBUG
2564
2565   screen->unmanageWindow(this, False);
2566 }
2567
2568
2569 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2570   if (de->window != client.window)
2571     return;
2572
2573 #ifdef    DEBUG
2574   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2575           client.window);
2576 #endif // DEBUG
2577
2578   screen->unmanageWindow(this, False);
2579 }
2580
2581
2582 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2583   if (re->window != client.window || re->parent == frame.plate)
2584     return;
2585
2586 #ifdef    DEBUG
2587   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2588           "0x%lx.\n", client.window, re->parent);
2589 #endif // DEBUG
2590
2591   XEvent ev;
2592   ev.xreparent = *re;
2593   XPutBackEvent(blackbox->getXDisplay(), &ev);
2594   screen->unmanageWindow(this, True);
2595 }
2596
2597
2598 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2599   if (pe->state == PropertyDelete)
2600     return;
2601
2602 #ifdef    DEBUG
2603   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2604           client.window);
2605 #endif
2606
2607   switch(pe->atom) {
2608   case XA_WM_CLASS:
2609   case XA_WM_CLIENT_MACHINE:
2610   case XA_WM_COMMAND:
2611     break;
2612
2613   case XA_WM_TRANSIENT_FOR: {
2614     // determine if this is a transient window
2615     getTransientInfo();
2616
2617     // adjust the window decorations based on transience
2618     if (isTransient()) {
2619       decorations &= ~(Decor_Maximize | Decor_Handle);
2620       functions &= ~Func_Maximize;
2621       setAllowedActions();
2622     }
2623
2624     reconfigure();
2625   }
2626     break;
2627
2628   case XA_WM_HINTS:
2629     getWMHints();
2630     break;
2631
2632   case XA_WM_ICON_NAME:
2633     getWMIconName();
2634     if (flags.iconic) screen->propagateWindowName(this);
2635     break;
2636
2637   case XAtom::net_wm_name:
2638   case XA_WM_NAME:
2639     getWMName();
2640
2641     if (decorations & Decor_Titlebar)
2642       redrawLabel();
2643
2644     screen->propagateWindowName(this);
2645     break;
2646
2647   case XA_WM_NORMAL_HINTS: {
2648     getWMNormalHints();
2649
2650     if ((client.normal_hint_flags & PMinSize) &&
2651         (client.normal_hint_flags & PMaxSize)) {
2652       // the window now can/can't resize itself, so the buttons need to be
2653       // regrabbed.
2654       ungrabButtons();
2655       if (client.max_width <= client.min_width &&
2656           client.max_height <= client.min_height) {
2657         decorations &= ~(Decor_Maximize | Decor_Handle);
2658         functions &= ~(Func_Resize | Func_Maximize);
2659       } else {
2660         if (! isTransient()) {
2661           decorations |= Decor_Maximize | Decor_Handle;
2662           functions |= Func_Maximize;
2663         }
2664         functions |= Func_Resize;
2665       }
2666       grabButtons();
2667       setAllowedActions();
2668     }
2669
2670     Rect old_rect = frame.rect;
2671
2672     upsize();
2673
2674     if (old_rect != frame.rect)
2675       reconfigure();
2676
2677     break;
2678   }
2679
2680   default:
2681     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2682       getWMProtocols();
2683
2684       if ((decorations & Decor_Close) && (! frame.close_button)) {
2685         createCloseButton();
2686         if (decorations & Decor_Titlebar) {
2687           positionButtons(True);
2688           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2689         }
2690         if (windowmenu) windowmenu->reconfigure();
2691       }
2692     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2693       updateStrut();
2694     }
2695
2696     break;
2697   }
2698 }
2699
2700
2701 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2702 #ifdef DEBUG
2703   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2704 #endif
2705
2706   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2707     redrawLabel();
2708   else if (frame.close_button == ee->window)
2709     redrawCloseButton(False);
2710   else if (frame.maximize_button == ee->window)
2711     redrawMaximizeButton(flags.maximized);
2712   else if (frame.iconify_button == ee->window)
2713     redrawIconifyButton(False);
2714 }
2715
2716
2717 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2718   if (cr->window != client.window || flags.iconic)
2719     return;
2720
2721   if (cr->value_mask & CWBorderWidth)
2722     client.old_bw = cr->border_width;
2723
2724   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2725     Rect req = frame.rect;
2726
2727     if (cr->value_mask & (CWX | CWY)) {
2728       if (cr->value_mask & CWX)
2729         client.rect.setX(cr->x);
2730       if (cr->value_mask & CWY)
2731         client.rect.setY(cr->y);
2732
2733       applyGravity(req);
2734     }
2735
2736     if (cr->value_mask & CWWidth)
2737       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2738
2739     if (cr->value_mask & CWHeight)
2740       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2741
2742     configure(req.x(), req.y(), req.width(), req.height());
2743   }
2744
2745   if (cr->value_mask & CWStackMode && !isDesktop()) {
2746     switch (cr->detail) {
2747     case Below:
2748     case BottomIf:
2749       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2750       break;
2751
2752     case Above:
2753     case TopIf:
2754     default:
2755       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2756       break;
2757     }
2758   }
2759 }
2760
2761
2762 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2763 #ifdef DEBUG
2764   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2765           client.window);
2766 #endif
2767
2768   if (frame.maximize_button == be->window && be->button <= 3) {
2769     redrawMaximizeButton(True);
2770   } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2771     if (! flags.focused)
2772       setInputFocus();
2773
2774     if (frame.iconify_button == be->window) {
2775       redrawIconifyButton(True);
2776     } else if (frame.close_button == be->window) {
2777       redrawCloseButton(True);
2778     } else if (frame.plate == be->window) {
2779       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2780
2781       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2782
2783       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2784     } else {
2785       if (frame.title == be->window || frame.label == be->window) {
2786         if (((be->time - lastButtonPressTime) <=
2787              blackbox->getDoubleClickInterval()) ||
2788             (be->state == ControlMask)) {
2789           lastButtonPressTime = 0;
2790           shade();
2791         } else {
2792           lastButtonPressTime = be->time;
2793         }
2794       }
2795
2796       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2797
2798       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2799     }
2800   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2801              (be->window != frame.close_button)) {
2802     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2803   } else if (windowmenu && be->button == 3 &&
2804              (frame.title == be->window || frame.label == be->window ||
2805               frame.handle == be->window || frame.window == be->window)) {
2806     if (windowmenu->isVisible()) {
2807       windowmenu->hide();
2808     } else {
2809       int mx = be->x_root - windowmenu->getWidth() / 2,
2810           my = be->y_root - windowmenu->getHeight() / 2;
2811
2812       // snap the window menu into a corner/side if necessary
2813       int left_edge, right_edge, top_edge, bottom_edge;
2814
2815       /*
2816          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2817          and height of the menu, as the sizes returned by it do not include
2818          the borders.
2819        */
2820       left_edge = frame.rect.x();
2821       right_edge = frame.rect.right() -
2822         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2823       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2824       bottom_edge = client.rect.bottom() -
2825         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2826         (frame.border_w + frame.mwm_border_w);
2827
2828       if (mx < left_edge)
2829         mx = left_edge;
2830       if (mx > right_edge)
2831         mx = right_edge;
2832       if (my < top_edge)
2833         my = top_edge;
2834       if (my > bottom_edge)
2835         my = bottom_edge;
2836
2837       windowmenu->move(mx, my);
2838       windowmenu->show();
2839       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2840       XRaiseWindow(blackbox->getXDisplay(),
2841                    windowmenu->getSendToMenu()->getWindowID());
2842     }
2843   // mouse wheel up
2844   } else if (be->button == 4) {
2845     if ((be->window == frame.label ||
2846          be->window == frame.title ||
2847          be->window == frame.maximize_button ||
2848          be->window == frame.iconify_button ||
2849          be->window == frame.close_button) &&
2850         ! flags.shaded)
2851       shade();
2852   // mouse wheel down
2853   } else if (be->button == 5) {
2854     if ((be->window == frame.label ||
2855          be->window == frame.title ||
2856          be->window == frame.maximize_button ||
2857          be->window == frame.iconify_button ||
2858          be->window == frame.close_button) &&
2859         flags.shaded)
2860       shade();
2861   }
2862 }
2863
2864
2865 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2866 #ifdef DEBUG
2867   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2868           client.window);
2869 #endif
2870
2871   if (re->window == frame.maximize_button &&
2872       re->button >= 1 && re->button <= 3) {
2873     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2874         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2875       maximize(re->button);
2876     } else {
2877       redrawMaximizeButton(flags.maximized);
2878     }
2879   } else if (re->window == frame.iconify_button && re->button == 1) {
2880     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2881         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2882       iconify();
2883     } else {
2884       redrawIconifyButton(False);
2885     }
2886   } else if (re->window == frame.close_button & re->button == 1) {
2887     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2888         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2889       close();
2890     redrawCloseButton(False);
2891   } else if (flags.moving) {
2892     endMove();
2893   } else if (flags.resizing) {
2894     endResize();
2895   } else if (re->window == frame.window) {
2896     if (re->button == 2 && re->state == ModMask)
2897       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2898   }
2899 }
2900
2901
2902
2903 void BlackboxWindow::beginMove(int x_root, int y_root) {
2904   assert(! (flags.resizing || flags.moving));
2905
2906   /*
2907     Only one window can be moved/resized at a time. If another window is already
2908     being moved or resized, then stop it before whating to work with this one.
2909   */
2910   BlackboxWindow *changing = blackbox->getChangingWindow();
2911   if (changing && changing != this) {
2912     if (changing->flags.moving)
2913       changing->endMove();
2914     else // if (changing->flags.resizing)
2915       changing->endResize();
2916   }
2917   
2918   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2919                PointerMotionMask | ButtonReleaseMask,
2920                GrabModeAsync, GrabModeAsync,
2921                None, blackbox->getMoveCursor(), CurrentTime);
2922
2923   if (windowmenu && windowmenu->isVisible())
2924     windowmenu->hide();
2925
2926   flags.moving = True;
2927   blackbox->setChangingWindow(this);
2928
2929   if (! screen->doOpaqueMove()) {
2930     XGrabServer(blackbox->getXDisplay());
2931
2932     frame.changing = frame.rect;
2933     screen->showPosition(frame.changing.x(), frame.changing.y());
2934
2935     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2936                    screen->getOpGC(),
2937                    frame.changing.x(),
2938                    frame.changing.y(),
2939                    frame.changing.width() - 1,
2940                    frame.changing.height() - 1);
2941   }
2942
2943   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
2944   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
2945 }
2946
2947
2948 void BlackboxWindow::doMove(int x_root, int y_root) {
2949   assert(flags.moving);
2950   assert(blackbox->getChangingWindow() == this);
2951
2952   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
2953   dx -= frame.border_w;
2954   dy -= frame.border_w;
2955
2956   const int snap_distance = screen->getEdgeSnapThreshold();
2957
2958   if (snap_distance) {
2959     // window corners
2960     const int wleft = dx,
2961               wright = dx + frame.rect.width() - 1,
2962               wtop = dy,
2963               wbottom = dy + frame.rect.height() - 1;
2964
2965     if (screen->getWindowToWindowSnap()) {
2966       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2967       assert(w);
2968
2969       // try snap to another window
2970       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2971         BlackboxWindow *snapwin = w->getWindow(i);
2972         if (snapwin == this)
2973           continue;   // don't snap to self
2974
2975         bool snapped = False;
2976         
2977         const Rect &winrect = snapwin->frameRect();
2978         int dleft = std::abs(wright - winrect.left()),
2979            dright = std::abs(wleft - winrect.right()),
2980              dtop = std::abs(wbottom - winrect.top()),
2981           dbottom = std::abs(wtop - winrect.bottom());
2982
2983         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2984             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2985
2986           // snap left of other window?
2987           if (dleft < snap_distance && dleft <= dright) {
2988             dx = winrect.left() - frame.rect.width();
2989             snapped = True;
2990           }
2991           // snap right of other window?
2992           else if (dright < snap_distance) {
2993             dx = winrect.right() + 1;
2994             snapped = True;
2995           }
2996
2997           if (snapped) {
2998             if (screen->getWindowCornerSnap()) {
2999               // try corner-snap to its other sides
3000               dtop = std::abs(wtop - winrect.top());
3001               dbottom = std::abs(wbottom - winrect.bottom());
3002               if (dtop < snap_distance && dtop <= dbottom)
3003                 dy = winrect.top();
3004               else if (dbottom < snap_distance)
3005                 dy = winrect.bottom() - frame.rect.height() + 1;
3006             }
3007
3008             continue;
3009           }
3010         }
3011
3012         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3013             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3014
3015           // snap top of other window?
3016           if (dtop < snap_distance && dtop <= dbottom) {
3017             dy = winrect.top() - frame.rect.height();
3018             snapped = True;
3019           }
3020           // snap bottom of other window?
3021           else if (dbottom < snap_distance) {
3022             dy = winrect.bottom() + 1;
3023             snapped = True;
3024           }
3025
3026           if (snapped) {
3027             if (screen->getWindowCornerSnap()) {
3028               // try corner-snap to its other sides
3029               dleft = std::abs(wleft - winrect.left());
3030               dright = std::abs(wright - winrect.right());
3031               if (dleft < snap_distance && dleft <= dright)
3032                 dx = winrect.left();
3033               else if (dright < snap_distance)
3034                 dx = winrect.right() - frame.rect.width() + 1;
3035             }
3036
3037             continue;
3038           }
3039         }
3040       }
3041     }
3042
3043     RectList snaplist; // the list of rects we will try to snap to
3044
3045     // snap to the strut (and screen boundaries for xinerama)
3046 #ifdef    XINERAMA
3047     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3048       if (! screen->doFullMax())
3049         snaplist.insert(snaplist.begin(),
3050                         screen->allAvailableAreas().begin(),
3051                         screen->allAvailableAreas().end());
3052
3053       // always snap to the screen edges
3054       snaplist.insert(snaplist.begin(),
3055                       screen->getXineramaAreas().begin(),
3056                       screen->getXineramaAreas().end());
3057     } else
3058 #endif // XINERAMA
3059     {
3060       if (! screen->doFullMax())
3061         snaplist.push_back(screen->availableArea());
3062
3063       // always snap to the screen edges
3064       snaplist.push_back(screen->getRect());
3065     }
3066
3067     RectList::const_iterator it, end = snaplist.end();
3068     for (it = snaplist.begin(); it != end; ++it) {
3069       const Rect &srect = *it;
3070
3071       // if we're not in the rectangle then don't snap to it.
3072       if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3073                                   frame.rect.height())))
3074         continue;
3075
3076       int dleft = std::abs(wleft - srect.left()),
3077          dright = std::abs(wright - srect.right()),
3078            dtop = std::abs(wtop - srect.top()),
3079         dbottom = std::abs(wbottom - srect.bottom());
3080
3081         // snap left?
3082         if (dleft < snap_distance && dleft <= dright)
3083           dx = srect.left();
3084         // snap right?
3085         else if (dright < snap_distance)
3086           dx = srect.right() - frame.rect.width() + 1;
3087
3088         // snap top?
3089         if (dtop < snap_distance && dtop <= dbottom)
3090           dy = srect.top();
3091         // snap bottom?
3092         else if (dbottom < snap_distance)
3093           dy = srect.bottom() - frame.rect.height() + 1;
3094     }
3095   }
3096
3097   if (screen->doOpaqueMove()) {
3098     configure(dx, dy, frame.rect.width(), frame.rect.height());
3099   } else {
3100     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3101                    screen->getOpGC(),
3102                    frame.changing.x(),
3103                    frame.changing.y(),
3104                    frame.changing.width() - 1,
3105                    frame.changing.height() - 1);
3106
3107     frame.changing.setPos(dx, dy);
3108
3109     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3110                    screen->getOpGC(),
3111                    frame.changing.x(),
3112                    frame.changing.y(),
3113                    frame.changing.width() - 1,
3114                    frame.changing.height() - 1);
3115   }
3116
3117   screen->showPosition(dx, dy);
3118 }
3119
3120
3121 void BlackboxWindow::endMove(void) {
3122   assert(flags.moving);
3123   assert(blackbox->getChangingWindow() == this);
3124
3125   flags.moving = False;
3126   blackbox->setChangingWindow(0);
3127
3128   if (! screen->doOpaqueMove()) {
3129     /* when drawing the rubber band, we need to make sure we only draw inside
3130      * the frame... frame.changing_* contain the new coords for the window,
3131      * so we need to subtract 1 from changing_w/changing_h every where we
3132      * draw the rubber band (for both moving and resizing)
3133      */
3134     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3135                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3136                    frame.changing.width() - 1, frame.changing.height() - 1);
3137       XUngrabServer(blackbox->getXDisplay());
3138   
3139       configure(frame.changing.x(), frame.changing.y(),
3140                 frame.changing.width(), frame.changing.height());
3141   } else {
3142     configure(frame.rect.x(), frame.rect.y(),
3143               frame.rect.width(), frame.rect.height());
3144   }
3145   screen->hideGeometry();
3146
3147   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3148
3149   // if there are any left over motions from the move, drop them now
3150   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3151   XEvent e;
3152   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3153                                 MotionNotify, &e));
3154 }
3155
3156
3157 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3158   assert(! (flags.resizing || flags.moving));
3159
3160   /*
3161     Only one window can be moved/resized at a time. If another window is already
3162     being moved or resized, then stop it before whating to work with this one.
3163   */
3164   BlackboxWindow *changing = blackbox->getChangingWindow();
3165   if (changing && changing != this) {
3166     if (changing->flags.moving)
3167       changing->endMove();
3168     else // if (changing->flags.resizing)
3169       changing->endResize();
3170   }
3171
3172   resize_dir = dir;
3173
3174   Cursor cursor;
3175   Corner anchor;
3176   
3177   switch (resize_dir) {
3178   case BottomLeft:
3179     anchor = TopRight;
3180     cursor = blackbox->getLowerLeftAngleCursor();
3181     break;
3182
3183   case BottomRight:
3184     anchor = TopLeft;
3185     cursor = blackbox->getLowerRightAngleCursor();
3186     break;
3187
3188   case TopLeft:
3189     anchor = BottomRight;
3190     cursor = blackbox->getUpperLeftAngleCursor();
3191     break;
3192
3193   case TopRight:
3194     anchor = BottomLeft;
3195     cursor = blackbox->getUpperRightAngleCursor();
3196     break;
3197
3198   default:
3199     assert(false); // unhandled Corner
3200     return;        // unreachable, for the compiler
3201   }
3202   
3203   XGrabServer(blackbox->getXDisplay());
3204   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3205                PointerMotionMask | ButtonReleaseMask,
3206                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3207
3208   flags.resizing = True;
3209   blackbox->setChangingWindow(this);
3210
3211   int gw, gh;
3212   frame.changing = frame.rect;
3213
3214   constrain(anchor,  &gw, &gh);
3215
3216   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3217                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3218                  frame.changing.width() - 1, frame.changing.height() - 1);
3219
3220   screen->showGeometry(gw, gh);
3221   
3222   frame.grab_x = x_root;
3223   frame.grab_y = y_root;
3224 }
3225
3226
3227 void BlackboxWindow::doResize(int x_root, int y_root) {
3228   assert(flags.resizing);
3229   assert(blackbox->getChangingWindow() == this);
3230
3231   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3232                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3233                  frame.changing.width() - 1, frame.changing.height() - 1);
3234
3235   int gw, gh;
3236   Corner anchor;
3237
3238   switch (resize_dir) {
3239   case BottomLeft:
3240     anchor = TopRight;
3241     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3242                            frame.rect.height() + (y_root - frame.grab_y));
3243     break;
3244   case BottomRight:
3245     anchor = TopLeft;
3246     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3247                            frame.rect.height() + (y_root - frame.grab_y));
3248     break;
3249   case TopLeft:
3250     anchor = BottomRight;
3251     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3252                            frame.rect.height() - (y_root - frame.grab_y));
3253     break;
3254   case TopRight:
3255     anchor = BottomLeft;
3256     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3257                            frame.rect.height() - (y_root - frame.grab_y));
3258     break;
3259
3260   default:
3261     assert(false); // unhandled Corner
3262     return;        // unreachable, for the compiler
3263   }
3264   
3265   constrain(anchor, &gw, &gh);
3266
3267   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3268                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3269                  frame.changing.width() - 1, frame.changing.height() - 1);
3270
3271   screen->showGeometry(gw, gh);
3272 }
3273
3274
3275 void BlackboxWindow::endResize(void) {
3276   assert(flags.resizing);
3277   assert(blackbox->getChangingWindow() == this);
3278
3279   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3280                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3281                  frame.changing.width() - 1, frame.changing.height() - 1);
3282   XUngrabServer(blackbox->getXDisplay());
3283
3284   // unset maximized state after resized when fully maximized
3285   if (flags.maximized == 1)
3286     maximize(0);
3287   
3288   flags.resizing = False;
3289   blackbox->setChangingWindow(0);
3290
3291   configure(frame.changing.x(), frame.changing.y(),
3292             frame.changing.width(), frame.changing.height());
3293   screen->hideGeometry();
3294
3295   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3296   
3297   // if there are any left over motions from the resize, drop them now
3298   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3299   XEvent e;
3300   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3301                                 MotionNotify, &e));
3302 }
3303
3304
3305 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3306 #ifdef DEBUG
3307   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3308           client.window);
3309 #endif
3310
3311   if (flags.moving) {
3312     doMove(me->x_root, me->y_root);
3313   } else if (flags.resizing) {
3314     doResize(me->x_root, me->y_root);
3315   } else {
3316     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3317         (frame.title == me->window || frame.label == me->window ||
3318          frame.handle == me->window || frame.window == me->window)) {
3319       beginMove(me->x_root, me->y_root);
3320     } else if ((functions & Func_Resize) &&
3321                (me->state & Button1Mask && (me->window == frame.right_grip ||
3322                                             me->window == frame.left_grip)) ||
3323                (me->state & Button3Mask && me->state & ModMask &&
3324                 me->window == frame.window)) {
3325       unsigned int zones = screen->getResizeZones();
3326       Corner corner;
3327       
3328       if (me->window == frame.left_grip) {
3329         corner = BottomLeft;
3330       } else if (me->window == frame.right_grip || zones == 1) {
3331         corner = BottomRight;
3332       } else {
3333         bool top;
3334         bool left = (me->x_root - frame.rect.x() <=
3335                      static_cast<signed>(frame.rect.width() / 2));
3336         if (zones == 2)
3337           top = False;
3338         else // (zones == 4)
3339           top = (me->y_root - frame.rect.y() <=
3340                  static_cast<signed>(frame.rect.height() / 2));
3341         corner = (top ? (left ? TopLeft : TopRight) :
3342                         (left ? BottomLeft : BottomRight));
3343       }
3344
3345       beginResize(me->x_root, me->y_root, corner);
3346     }
3347   }
3348 }
3349
3350
3351 #ifdef    SHAPE
3352 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3353   if (blackbox->hasShapeExtensions() && flags.shaped) {
3354     configureShape();
3355   }
3356 }
3357 #endif // SHAPE
3358
3359
3360 bool BlackboxWindow::validateClient(void) const {
3361   XSync(blackbox->getXDisplay(), False);
3362
3363   XEvent e;
3364   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3365                              DestroyNotify, &e) ||
3366       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3367                              UnmapNotify, &e)) {
3368     XPutBackEvent(blackbox->getXDisplay(), &e);
3369
3370     return False;
3371   }
3372
3373   return True;
3374 }
3375
3376
3377 void BlackboxWindow::restore(bool remap) {
3378   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3379   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3380   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3381
3382   // do not leave a shaded window as an icon unless it was an icon
3383   if (flags.shaded && ! flags.iconic) setState(NormalState);
3384
3385   restoreGravity(client.rect);
3386
3387   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3388   XUnmapWindow(blackbox->getXDisplay(), client.window);
3389
3390   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3391
3392   XEvent ev;
3393   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3394                              ReparentNotify, &ev)) {
3395     remap = True;
3396   } else {
3397     // according to the ICCCM - if the client doesn't reparent to
3398     // root, then we have to do it for them
3399     XReparentWindow(blackbox->getXDisplay(), client.window,
3400                     screen->getRootWindow(),
3401                     client.rect.x(), client.rect.y());
3402   }
3403
3404   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3405 }
3406
3407
3408 // timer for autoraise
3409 void BlackboxWindow::timeout(void) {
3410   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3411 }
3412
3413
3414 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3415   if ((net->flags & AttribShaded) &&
3416       ((blackbox_attrib.attrib & AttribShaded) !=
3417        (net->attrib & AttribShaded)))
3418     shade();
3419
3420   if (flags.visible && // watch out for requests when we can not be seen
3421       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3422       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3423        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3424     if (flags.maximized) {
3425       maximize(0);
3426     } else {
3427       int button = 0;
3428
3429       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3430         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3431       else if (net->flags & AttribMaxVert)
3432         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3433       else if (net->flags & AttribMaxHoriz)
3434         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3435
3436       maximize(button);
3437     }
3438   }
3439
3440   if ((net->flags & AttribOmnipresent) &&
3441       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3442        (net->attrib & AttribOmnipresent)))
3443     stick();
3444
3445   if ((net->flags & AttribWorkspace) &&
3446       (blackbox_attrib.workspace != net->workspace)) {
3447     screen->reassociateWindow(this, net->workspace, True);
3448
3449     if (screen->getCurrentWorkspaceID() != net->workspace) {
3450       withdraw();
3451     } else {
3452       show();
3453       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3454     }
3455   }
3456
3457   if (net->flags & AttribDecoration) {
3458     switch (net->decoration) {
3459     case DecorNone:
3460       // clear all decorations except close
3461       decorations &= Decor_Close;
3462
3463       break;
3464
3465     default:
3466     case DecorNormal:
3467       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3468   
3469       decorations = ((functions & Func_Resize) && !isTransient() ?
3470                      decorations | Decor_Handle :
3471                      decorations &= ~Decor_Handle);
3472       decorations = (functions & Func_Maximize ?
3473                      decorations | Decor_Maximize :
3474                      decorations &= ~Decor_Maximize);
3475
3476       break;
3477
3478     case DecorTiny:
3479       decorations |= Decor_Titlebar | Decor_Iconify;
3480       decorations &= ~(Decor_Border | Decor_Handle);
3481       
3482       decorations = (functions & Func_Maximize ?
3483                      decorations | Decor_Maximize :
3484                      decorations &= ~Decor_Maximize);
3485
3486       break;
3487
3488     case DecorTool:
3489       decorations |= Decor_Titlebar;
3490       decorations &= ~(Decor_Iconify | Decor_Border);
3491
3492       decorations = ((functions & Func_Resize) && !isTransient() ?
3493                      decorations | Decor_Handle :
3494                      decorations &= ~Decor_Handle);
3495       decorations = (functions & Func_Maximize ?
3496                      decorations | Decor_Maximize :
3497                      decorations &= ~Decor_Maximize);
3498
3499       break;
3500     }
3501
3502     // we can not be shaded if we lack a titlebar
3503     if (flags.shaded && ! (decorations & Decor_Titlebar))
3504       shade();
3505
3506     if (flags.visible && frame.window) {
3507       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3508       XMapWindow(blackbox->getXDisplay(), frame.window);
3509     }
3510
3511     reconfigure();
3512     setState(current_state);
3513   }
3514 }
3515
3516
3517 /*
3518  * Set the sizes of all components of the window frame
3519  * (the window decorations).
3520  * These values are based upon the current style settings and the client
3521  * window's dimensions.
3522  */
3523 void BlackboxWindow::upsize(void) {
3524   frame.bevel_w = screen->getBevelWidth();
3525
3526   if (decorations & Decor_Border) {
3527     frame.border_w = screen->getBorderWidth();
3528     if (! isTransient())
3529       frame.mwm_border_w = screen->getFrameWidth();
3530     else
3531       frame.mwm_border_w = 0;
3532   } else {
3533     frame.mwm_border_w = frame.border_w = 0;
3534   }
3535
3536   if (decorations & Decor_Titlebar) {
3537     // the height of the titlebar is based upon the height of the font being
3538     // used to display the window's title
3539     WindowStyle *style = screen->getWindowStyle();
3540     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3541
3542     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3543     frame.button_w = (frame.label_h - 2);
3544
3545     // set the top frame margin
3546     frame.margin.top = frame.border_w + frame.title_h +
3547                        frame.border_w + frame.mwm_border_w;
3548   } else {
3549     frame.title_h = 0;
3550     frame.label_h = 0;
3551     frame.button_w = 0;
3552
3553     // set the top frame margin
3554     frame.margin.top = frame.border_w + frame.mwm_border_w;
3555   }
3556
3557   // set the left/right frame margin
3558   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3559
3560   if (decorations & Decor_Handle) {
3561     frame.grip_w = frame.button_w * 2;
3562     frame.handle_h = screen->getHandleWidth();
3563
3564     // set the bottom frame margin
3565     frame.margin.bottom = frame.border_w + frame.handle_h +
3566                           frame.border_w + frame.mwm_border_w;
3567   } else {
3568     frame.handle_h = 0;
3569     frame.grip_w = 0;
3570
3571     // set the bottom frame margin
3572     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3573   }
3574
3575   /*
3576     We first get the normal dimensions and use this to define the inside_w/h
3577     then we modify the height if shading is in effect.
3578     If the shade state is not considered then frame.rect gets reset to the
3579     normal window size on a reconfigure() call resulting in improper
3580     dimensions appearing in move/resize and other events.
3581   */
3582   unsigned int
3583     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3584     width = client.rect.width() + frame.margin.left + frame.margin.right;
3585
3586   frame.inside_w = width - (frame.border_w * 2);
3587   frame.inside_h = height - (frame.border_w * 2);
3588
3589   if (flags.shaded)
3590     height = frame.title_h + (frame.border_w * 2);
3591   frame.rect.setSize(width, height);
3592 }
3593
3594
3595 /*
3596  * Calculate the size of the client window and constrain it to the
3597  * size specified by the size hints of the client window.
3598  *
3599  * The logical width and height are placed into pw and ph, if they
3600  * are non-zero.  Logical size refers to the users perception of
3601  * the window size (for example an xterm resizes in cells, not in pixels).
3602  *
3603  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3604  * Physical geometry refers to the geometry of the window in pixels.
3605  */
3606 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3607   // frame.changing represents the requested frame size, we need to
3608   // strip the frame margin off and constrain the client size
3609   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3610                            frame.changing.top() + frame.margin.top,
3611                            frame.changing.right() - frame.margin.right,
3612                            frame.changing.bottom() - frame.margin.bottom);
3613
3614   int dw = frame.changing.width(), dh = frame.changing.height(),
3615     base_width = (client.base_width) ? client.base_width : client.min_width,
3616     base_height = (client.base_height) ? client.base_height :
3617                                          client.min_height;
3618
3619   // constrain
3620   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3621   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3622   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3623   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3624
3625   dw -= base_width;
3626   dw /= client.width_inc;
3627   dh -= base_height;
3628   dh /= client.height_inc;
3629
3630   if (pw) {
3631     if (client.width_inc == 1)
3632       *pw = dw + base_width;
3633     else
3634       *pw = dw;
3635   }
3636   if (ph) {
3637     if (client.height_inc == 1)
3638       *ph = dh + base_height;
3639     else
3640       *ph = dh;
3641   }
3642
3643   dw *= client.width_inc;
3644   dw += base_width;
3645   dh *= client.height_inc;
3646   dh += base_height;
3647
3648   frame.changing.setSize(dw, dh);
3649
3650   // add the frame margin back onto frame.changing
3651   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3652                            frame.changing.top() - frame.margin.top,
3653                            frame.changing.right() + frame.margin.right,
3654                            frame.changing.bottom() + frame.margin.bottom);
3655
3656   // move frame.changing to the specified anchor
3657   int dx = 0,
3658       dy = 0;
3659   switch (anchor) {
3660   case TopLeft:
3661     break;
3662
3663   case TopRight:
3664     dx = frame.rect.right() - frame.changing.right();
3665     break;
3666
3667   case BottomLeft:
3668     dy = frame.rect.bottom() - frame.changing.bottom();
3669     break;
3670
3671   case BottomRight:
3672     dx = frame.rect.right() - frame.changing.right();
3673     dy = frame.rect.bottom() - frame.changing.bottom();
3674     break;
3675
3676   default:
3677     assert(false);  // unhandled corner
3678   }
3679   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3680 }
3681
3682
3683 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3684                             unsigned int max_length,
3685                             unsigned int modifier) const {
3686   size_t text_len = text.size();
3687   unsigned int length;
3688
3689   do {
3690     length = font->measureString(string(text, 0, text_len)) + modifier;
3691   } while (length > max_length && text_len-- > 0);
3692
3693   switch (justify) {
3694   case RightJustify:
3695     start_pos += max_length - length;
3696     break;
3697
3698   case CenterJustify:
3699     start_pos += (max_length - length) / 2;
3700     break;
3701
3702   case LeftJustify:
3703   default:
3704     break;
3705   }
3706 }
3707
3708
3709 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3710   : blackbox(b), group(_group) {
3711   XWindowAttributes wattrib;
3712   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3713     // group window doesn't seem to exist anymore
3714     delete this;
3715     return;
3716   }
3717
3718   XSelectInput(blackbox->getXDisplay(), group,
3719                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3720
3721   blackbox->saveGroupSearch(group, this);
3722 }
3723
3724
3725 BWindowGroup::~BWindowGroup(void) {
3726   blackbox->removeGroupSearch(group);
3727 }
3728
3729
3730 BlackboxWindow *
3731 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3732   BlackboxWindow *ret = blackbox->getFocusedWindow();
3733
3734   // does the focus window match (or any transient_fors)?
3735   while (ret) {
3736     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3737       if (ret->isTransient() && allow_transients) break;
3738       else if (! ret->isTransient()) break;
3739     }
3740
3741     ret = ret->getTransientFor();
3742   }
3743
3744   if (ret) return ret;
3745
3746   // the focus window didn't match, look in the group's window list
3747   BlackboxWindowList::const_iterator it, end = windowList.end();
3748   for (it = windowList.begin(); it != end; ++it) {
3749     ret = *it;
3750     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3751       if (ret->isTransient() && allow_transients) break;
3752       else if (! ret->isTransient()) break;
3753     }
3754   }
3755
3756   return ret;
3757 }