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