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