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