]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
was ending up using the wrong cache items. now i find and release it every time a...
[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   // alt+middle lowers the window
851   blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
852                        ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
853                        frame.window, None);
854 }
855
856
857 void BlackboxWindow::ungrabButtons(void) {
858   if ((! screen->isSloppyFocus()) || screen->doClickRaise())
859     blackbox->ungrabButton(Button1, 0, frame.plate);
860
861   blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
862   blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
863   blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
864 }
865
866
867 void BlackboxWindow::positionWindows(void) {
868   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
869                     frame.rect.x(), frame.rect.y(), frame.inside_w,
870                     (flags.shaded) ? frame.title_h : frame.inside_h);
871   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
872                         frame.border_w);
873   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
874                         frame.mwm_border_w);
875   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
876                     frame.margin.left - frame.mwm_border_w - frame.border_w,
877                     frame.margin.top - frame.mwm_border_w - frame.border_w,
878                     client.rect.width(), client.rect.height());
879   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
880                     0, 0, client.rect.width(), client.rect.height());
881   // ensure client.rect contains the real location
882   client.rect.setCoords(frame.rect.left() + frame.margin.left,
883                         frame.rect.top() + frame.margin.top,
884                         frame.rect.right() - frame.margin.right,
885                         frame.rect.bottom() - frame.margin.bottom);
886
887   if (decorations & Decor_Titlebar) {
888     if (frame.title == None) createTitlebar();
889
890     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
891                           frame.border_w);
892     XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
893                       -frame.border_w, frame.inside_w, frame.title_h);
894
895     positionButtons();
896     XMapSubwindows(blackbox->getXDisplay(), frame.title);
897     XMapWindow(blackbox->getXDisplay(), frame.title);
898   } else if (frame.title) {
899     destroyTitlebar();
900   }
901   if (decorations & Decor_Handle) {
902     if (frame.handle == None) createHandle();
903     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
904                           frame.border_w);
905     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
906                           frame.border_w);
907     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
908                           frame.border_w);
909
910     // use client.rect here so the value is correct even if shaded
911     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
912                       -frame.border_w,
913                       client.rect.height() + frame.margin.top +
914                       frame.mwm_border_w - frame.border_w,
915                       frame.inside_w, frame.handle_h);
916     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
917                       -frame.border_w, -frame.border_w,
918                       frame.grip_w, frame.handle_h);
919     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
920                       frame.inside_w - frame.grip_w - frame.border_w,
921                       -frame.border_w, frame.grip_w, frame.handle_h);
922
923     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
924     XMapWindow(blackbox->getXDisplay(), frame.handle);
925   } else if (frame.handle) {
926     destroyHandle();
927   }
928 }
929
930
931 void BlackboxWindow::updateStrut(void) {
932   unsigned long num = 4;
933   unsigned long *data;
934   if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
935                         num, &data))
936     return;
937  
938   if (num == 4) {
939     client.strut.left = data[0];
940     client.strut.right = data[1];
941     client.strut.top = data[2];
942     client.strut.bottom = data[3];
943
944     screen->updateAvailableArea();
945   }
946
947   delete [] data;
948 }
949
950
951 void BlackboxWindow::getWindowType(void) {
952   unsigned long val;
953   if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
954                       val)) {
955     if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop))
956       window_type = Type_Desktop;
957     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock))
958       window_type = Type_Dock;
959     else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
960       window_type = Type_Toolbar;
961     else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu))
962       window_type = Type_Menu;
963     else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility))
964       window_type = Type_Utility;
965     else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash))
966       window_type = Type_Splash;
967     else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog))
968       window_type = Type_Dialog;
969     else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal))
970       window_type = Type_Normal;
971     return;
972   }
973
974   /*
975    * the window type hint was not set, which means we either classify ourself
976    * as a normal window or a dialog, depending on if we are a transient.
977    */
978   if (isTransient())
979     window_type = Type_Dialog;
980
981   window_type = Type_Normal;
982 }
983
984
985 void BlackboxWindow::getWMName(void) {
986   if (xatom->getValue(client.window, XAtom::net_wm_name,
987                       XAtom::utf8, client.title) &&
988       !client.title.empty()) {
989     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
990     return;
991   }
992   //fall through to using WM_NAME
993   if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
994       && !client.title.empty()) {
995     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
996     return;
997   }
998   // fall back to an internal default
999   client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
1000   xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
1001                   client.title);
1002 }
1003
1004
1005 void BlackboxWindow::getWMIconName(void) {
1006   if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1007                       XAtom::utf8, client.icon_title) && 
1008       !client.icon_title.empty()) {
1009     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1010     return;
1011   }
1012   //fall through to using WM_ICON_NAME
1013   if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1014                       client.icon_title) && 
1015       !client.icon_title.empty()) {
1016     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1017     return;
1018   }
1019   // fall back to using the main name
1020   client.icon_title = client.title;
1021   xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1022                   client.icon_title);
1023 }
1024
1025
1026 /*
1027  * Retrieve which WM Protocols are supported by the client window.
1028  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1029  * window's decorations and allow the close behavior.
1030  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1031  * this.
1032  */
1033 void BlackboxWindow::getWMProtocols(void) {
1034   Atom *proto;
1035   int num_return = 0;
1036
1037   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1038                       &proto, &num_return)) {
1039     for (int i = 0; i < num_return; ++i) {
1040       if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1041         decorations |= Decor_Close;
1042         functions |= Func_Close;
1043       } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1044         flags.send_focus_message = True;
1045       else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1046         screen->addNetizen(new Netizen(screen, client.window));
1047     }
1048
1049     XFree(proto);
1050   }
1051 }
1052
1053
1054 /*
1055  * Gets the value of the WM_HINTS property.
1056  * If the property is not set, then use a set of default values.
1057  */
1058 void BlackboxWindow::getWMHints(void) {
1059   focus_mode = F_Passive;
1060   client.initial_state = NormalState;
1061
1062   // remove from current window group
1063   if (client.window_group) {
1064     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1065     if (group) group->removeWindow(this);
1066   }
1067   client.window_group = None;
1068
1069   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1070   if (! wmhint) {
1071     return;
1072   }
1073
1074   if (wmhint->flags & InputHint) {
1075     if (wmhint->input == True) {
1076       if (flags.send_focus_message)
1077         focus_mode = F_LocallyActive;
1078     } else {
1079       if (flags.send_focus_message)
1080         focus_mode = F_GloballyActive;
1081       else
1082         focus_mode = F_NoInput;
1083     }
1084   }
1085
1086   if (wmhint->flags & StateHint)
1087     client.initial_state = wmhint->initial_state;
1088
1089   if (wmhint->flags & WindowGroupHint) {
1090     client.window_group = wmhint->window_group;
1091
1092     // add window to the appropriate group
1093     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1094     if (! group) // no group found, create it!
1095       group = new BWindowGroup(blackbox, client.window_group);
1096     group->addWindow(this);
1097   }
1098
1099   client.wm_hint_flags = wmhint->flags;
1100   XFree(wmhint);
1101 }
1102
1103
1104 /*
1105  * Gets the value of the WM_NORMAL_HINTS property.
1106  * If the property is not set, then use a set of default values.
1107  */
1108 void BlackboxWindow::getWMNormalHints(void) {
1109   long icccm_mask;
1110   XSizeHints sizehint;
1111
1112   client.min_width = client.min_height =
1113     client.width_inc = client.height_inc = 1;
1114   client.base_width = client.base_height = 0;
1115
1116   /*
1117     use the full screen, not the strut modified size. otherwise when the
1118     availableArea changes max_width/height will be incorrect and lead to odd
1119     rendering bugs.
1120   */
1121   const Rect& screen_area = screen->getRect();
1122   client.max_width = screen_area.width();
1123
1124   client.max_height = screen_area.height();
1125   client.min_aspect_x = client.min_aspect_y =
1126     client.max_aspect_x = client.max_aspect_y = 1;
1127   client.win_gravity = NorthWestGravity;
1128
1129   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1130                           &sizehint, &icccm_mask))
1131     return;
1132
1133   client.normal_hint_flags = sizehint.flags;
1134
1135   if (sizehint.flags & PMinSize) {
1136     client.min_width = sizehint.min_width;
1137     client.min_height = sizehint.min_height;
1138   }
1139
1140   if (sizehint.flags & PMaxSize) {
1141     client.max_width = sizehint.max_width;
1142     client.max_height = sizehint.max_height;
1143   }
1144
1145   if (sizehint.flags & PResizeInc) {
1146     client.width_inc = sizehint.width_inc;
1147     client.height_inc = sizehint.height_inc;
1148   }
1149
1150   if (sizehint.flags & PAspect) {
1151     client.min_aspect_x = sizehint.min_aspect.x;
1152     client.min_aspect_y = sizehint.min_aspect.y;
1153     client.max_aspect_x = sizehint.max_aspect.x;
1154     client.max_aspect_y = sizehint.max_aspect.y;
1155   }
1156
1157   if (sizehint.flags & PBaseSize) {
1158     client.base_width = sizehint.base_width;
1159     client.base_height = sizehint.base_height;
1160   }
1161
1162   if (sizehint.flags & PWinGravity)
1163     client.win_gravity = sizehint.win_gravity;
1164 }
1165
1166
1167 /*
1168  * Gets the NETWM hints for the class' contained window.
1169  */
1170 void BlackboxWindow::getNetWMHints(void) {
1171   unsigned long workspace;
1172
1173   if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1174                       workspace)) {
1175     if (workspace == 0xffffffff)
1176       flags.stuck = True;
1177     else
1178       blackbox_attrib.workspace = workspace;
1179   }
1180
1181   unsigned long *state;
1182   unsigned long num = (unsigned) -1;
1183   if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1184                       num, &state)) {
1185     bool vert = False,
1186          horz = False;
1187     for (unsigned long i = 0; i < num; ++i) {
1188       if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1189         flags.modal = True;
1190       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1191         flags.shaded = True;
1192       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1193         flags.skip_taskbar = True;
1194       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1195         flags.skip_pager = True;
1196       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1197         flags.fullscreen = True;
1198       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1199         setState(IconicState);
1200       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1201         vert = True;
1202       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1203         horz = True;
1204     }
1205     if (vert && horz)
1206       flags.maximized = 1;
1207     else if (vert)
1208       flags.maximized = 2;
1209     else if (horz)
1210       flags.maximized = 3;
1211
1212     delete [] state;
1213   }
1214 }
1215
1216
1217 /*
1218  * Gets the MWM hints for the class' contained window.
1219  * This is used while initializing the window to its first state, and not
1220  * thereafter.
1221  * Returns: true if the MWM hints are successfully retreived and applied;
1222  * false if they are not.
1223  */
1224 void BlackboxWindow::getMWMHints(void) {
1225   unsigned long num;
1226   MwmHints *mwm_hint;
1227
1228   num = PropMwmHintsElements;
1229   if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1230                         XAtom::motif_wm_hints, num,
1231                         (unsigned long **)&mwm_hint))
1232     return;
1233   if (num < PropMwmHintsElements) {
1234     delete [] mwm_hint;
1235     return;
1236   }
1237
1238   if (mwm_hint->flags & MwmHintsDecorations) {
1239     if (mwm_hint->decorations & MwmDecorAll) {
1240       decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1241                     Decor_Iconify | Decor_Maximize | Decor_Close;
1242     } else {
1243       decorations = 0;
1244
1245       if (mwm_hint->decorations & MwmDecorBorder)
1246         decorations |= Decor_Border;
1247       if (mwm_hint->decorations & MwmDecorHandle)
1248         decorations |= Decor_Handle;
1249       if (mwm_hint->decorations & MwmDecorTitle)
1250         decorations |= Decor_Titlebar;
1251       if (mwm_hint->decorations & MwmDecorIconify)
1252         decorations |= Decor_Iconify;
1253       if (mwm_hint->decorations & MwmDecorMaximize)
1254         decorations |= Decor_Maximize;
1255     }
1256   }
1257
1258   if (mwm_hint->flags & MwmHintsFunctions) {
1259     if (mwm_hint->functions & MwmFuncAll) {
1260       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1261                   Func_Close;
1262     } else {
1263       functions = 0;
1264
1265       if (mwm_hint->functions & MwmFuncResize)
1266         functions |= Func_Resize;
1267       if (mwm_hint->functions & MwmFuncMove)
1268         functions |= Func_Move;
1269       if (mwm_hint->functions & MwmFuncIconify)
1270         functions |= Func_Iconify;
1271       if (mwm_hint->functions & MwmFuncMaximize)
1272         functions |= Func_Maximize;
1273       if (mwm_hint->functions & MwmFuncClose)
1274         functions |= Func_Close;
1275     }
1276   }
1277   delete [] mwm_hint;
1278 }
1279
1280
1281 /*
1282  * Gets the blackbox hints from the class' contained window.
1283  * This is used while initializing the window to its first state, and not
1284  * thereafter.
1285  * Returns: true if the hints are successfully retreived and applied; false if
1286  * they are not.
1287  */
1288 bool BlackboxWindow::getBlackboxHints(void) {
1289   unsigned long num;
1290   BlackboxHints *blackbox_hint;
1291
1292   num = PropBlackboxHintsElements;
1293   if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1294                         XAtom::blackbox_hints, num,
1295                         (unsigned long **)&blackbox_hint))
1296     return False;
1297   if (num < PropBlackboxHintsElements) {
1298     delete [] blackbox_hint;
1299     return False;
1300   }
1301
1302   if (blackbox_hint->flags & AttribShaded)
1303     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1304
1305   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1306       (blackbox_hint->flags & AttribMaxVert))
1307     flags.maximized = (blackbox_hint->attrib &
1308                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1309   else if (blackbox_hint->flags & AttribMaxVert)
1310     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1311   else if (blackbox_hint->flags & AttribMaxHoriz)
1312     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1313
1314   if (blackbox_hint->flags & AttribOmnipresent)
1315     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1316
1317   if (blackbox_hint->flags & AttribWorkspace)
1318     blackbox_attrib.workspace = blackbox_hint->workspace;
1319
1320   // if (blackbox_hint->flags & AttribStack)
1321   //   don't yet have always on top/bottom for blackbox yet... working
1322   //   on that
1323
1324   if (blackbox_hint->flags & AttribDecoration) {
1325     switch (blackbox_hint->decoration) {
1326     case DecorNone:
1327       // clear all decorations except close
1328       decorations &= Decor_Close;
1329       // clear all functions except close
1330       functions &= Func_Close;
1331
1332       break;
1333
1334     case DecorTiny:
1335       decorations |= Decor_Titlebar | Decor_Iconify;
1336       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1337       functions |= Func_Move | Func_Iconify;
1338       functions &= ~(Func_Resize | Func_Maximize);
1339
1340       break;
1341
1342     case DecorTool:
1343       decorations |= Decor_Titlebar;
1344       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1345       functions |= Func_Move;
1346       functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1347
1348       break;
1349
1350     case DecorNormal:
1351     default:
1352       decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1353                      Decor_Iconify | Decor_Maximize;
1354       functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
1355
1356       break;
1357     }
1358
1359     reconfigure();
1360   }
1361   
1362   delete [] blackbox_hint;
1363
1364   return True;
1365 }
1366
1367
1368 void BlackboxWindow::getTransientInfo(void) {
1369   if (client.transient_for &&
1370       client.transient_for != (BlackboxWindow *) ~0ul) {
1371     // the transient for hint was removed, so we need to tell our
1372     // previous transient_for that we are going away
1373     client.transient_for->client.transientList.remove(this);
1374   }
1375
1376   // we have no transient_for until we find a new one
1377   client.transient_for = 0;
1378
1379   Window trans_for;
1380   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1381                              &trans_for)) {
1382     // transient_for hint not set
1383     return;
1384   }
1385
1386   if (trans_for == client.window) {
1387     // wierd client... treat this window as a normal window
1388     return;
1389   }
1390
1391   if (trans_for == None || trans_for == screen->getRootWindow()) {
1392     // this is an undocumented interpretation of the ICCCM. a transient
1393     // associated with None/Root/itself is assumed to be a modal root
1394     // transient.  we don't support the concept of a global transient,
1395     // so we just associate this transient with nothing, and perhaps
1396     // we will add support later for global modality.
1397     client.transient_for = (BlackboxWindow *) ~0ul;
1398     flags.modal = True;
1399     return;
1400   }
1401
1402   client.transient_for = blackbox->searchWindow(trans_for);
1403   if (! client.transient_for &&
1404       client.window_group && trans_for == client.window_group) {
1405     // no direct transient_for, perhaps this is a group transient?
1406     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1407     if (group) client.transient_for = group->find(screen);
1408   }
1409
1410   if (! client.transient_for || client.transient_for == this) {
1411     // no transient_for found, or we have a wierd client that wants to be
1412     // a transient for itself, so we treat this window as a normal window
1413     client.transient_for = (BlackboxWindow*) 0;
1414     return;
1415   }
1416
1417   // register ourselves with our new transient_for
1418   client.transient_for->client.transientList.push_back(this);
1419   flags.stuck = client.transient_for->flags.stuck;
1420 }
1421
1422
1423 bool BlackboxWindow::isKDESystrayWindow(void) {
1424   Window systray;
1425   if (xatom->getValue(client.window, XAtom::kde_net_wm_system_tray_window_for,
1426                       XAtom::window, systray) && systray)
1427     return True;
1428   return False;
1429 }
1430
1431
1432 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1433   if (client.transient_for &&
1434       client.transient_for != (BlackboxWindow*) ~0ul)
1435     return client.transient_for;
1436   return 0;
1437 }
1438
1439
1440 /*
1441  * This function is responsible for updating both the client and the frame
1442  * rectangles.
1443  * According to the ICCCM a client message is not sent for a resize, only a
1444  * move.
1445  */
1446 void BlackboxWindow::configure(int dx, int dy,
1447                                unsigned int dw, unsigned int dh) {
1448   bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
1449
1450   if (dw != frame.rect.width() || dh != frame.rect.height()) {
1451     frame.rect.setRect(dx, dy, dw, dh);
1452     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1453     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1454
1455     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1456       frame.rect.setPos(0, 0);
1457
1458     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1459                           frame.rect.top() + frame.margin.top,
1460                           frame.rect.right() - frame.margin.right,
1461                           frame.rect.bottom() - frame.margin.bottom);
1462
1463 #ifdef    SHAPE
1464     if (blackbox->hasShapeExtensions() && flags.shaped) {
1465       configureShape();
1466     }
1467 #endif // SHAPE
1468
1469     positionWindows();
1470     decorate();
1471     redrawWindowFrame();
1472   } else {
1473     frame.rect.setPos(dx, dy);
1474
1475     XMoveWindow(blackbox->getXDisplay(), frame.window,
1476                 frame.rect.x(), frame.rect.y());
1477   }
1478
1479   if (send_event && ! flags.moving) {
1480     // if moving, the update and event will occur when the move finishes
1481     client.rect.setPos(frame.rect.left() + frame.margin.left,
1482                        frame.rect.top() + frame.margin.top);
1483
1484     XEvent event;
1485     event.type = ConfigureNotify;
1486
1487     event.xconfigure.display = blackbox->getXDisplay();
1488     event.xconfigure.event = client.window;
1489     event.xconfigure.window = client.window;
1490     event.xconfigure.x = client.rect.x();
1491     event.xconfigure.y = client.rect.y();
1492     event.xconfigure.width = client.rect.width();
1493     event.xconfigure.height = client.rect.height();
1494     event.xconfigure.border_width = client.old_bw;
1495     event.xconfigure.above = frame.window;
1496     event.xconfigure.override_redirect = False;
1497
1498     XSendEvent(blackbox->getXDisplay(), client.window, False,
1499                StructureNotifyMask, &event);
1500     screen->updateNetizenConfigNotify(&event);
1501   }
1502 }
1503
1504
1505 #ifdef SHAPE
1506 void BlackboxWindow::configureShape(void) {
1507   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1508                      frame.margin.left - frame.border_w,
1509                      frame.margin.top - frame.border_w,
1510                      client.window, ShapeBounding, ShapeSet);
1511
1512   int num = 0;
1513   XRectangle xrect[2];
1514
1515   if (decorations & Decor_Titlebar) {
1516     xrect[0].x = xrect[0].y = -frame.border_w;
1517     xrect[0].width = frame.rect.width();
1518     xrect[0].height = frame.title_h + (frame.border_w * 2);
1519     ++num;
1520   }
1521
1522   if (decorations & Decor_Handle) {
1523     xrect[1].x = -frame.border_w;
1524     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1525                  frame.mwm_border_w - frame.border_w;
1526     xrect[1].width = frame.rect.width();
1527     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1528     ++num;
1529   }
1530
1531   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1532                           ShapeBounding, 0, 0, xrect, num,
1533                           ShapeUnion, Unsorted);
1534 }
1535 #endif // SHAPE
1536
1537
1538 bool BlackboxWindow::setInputFocus(void) {
1539   if (flags.focused) return True;
1540
1541   assert(! flags.iconic &&
1542          (flags.stuck ||  // window must be on the current workspace or sticky
1543           blackbox_attrib.workspace == screen->getCurrentWorkspaceID()));
1544 #if 0
1545   // if the window is not visible, mark the window as wanting focus rather
1546   // than give it focus.
1547   if (! flags.visible) {
1548     Workspace *wkspc = screen->getWorkspace(blackbox_attrib.workspace);
1549     wkspc->setLastFocusedWindow(this);
1550     return True;
1551   }
1552 #endif
1553   if (! frame.rect.intersects(screen->getRect())) {
1554     // client is outside the screen, move it to the center
1555     configure((screen->getWidth() - frame.rect.width()) / 2,
1556               (screen->getHeight() - frame.rect.height()) / 2,
1557               frame.rect.width(), frame.rect.height());
1558   }
1559
1560   if (client.transientList.size() > 0) {
1561     // transfer focus to any modal transients
1562     BlackboxWindowList::iterator it, end = client.transientList.end();
1563     for (it = client.transientList.begin(); it != end; ++it) {
1564       if ((*it)->flags.modal) return (*it)->setInputFocus();
1565     }
1566   }
1567
1568   bool ret = True;
1569   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1570     XSetInputFocus(blackbox->getXDisplay(), client.window,
1571                    RevertToPointerRoot, CurrentTime);
1572
1573     blackbox->setFocusedWindow(this);
1574   } else {
1575     /* we could set the focus to none, since the window doesn't accept focus,
1576      * but we shouldn't set focus to nothing since this would surely make
1577      * someone angry
1578      */
1579     ret = False;
1580   }
1581
1582   if (flags.send_focus_message) {
1583     XEvent ce;
1584     ce.xclient.type = ClientMessage;
1585     ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1586     ce.xclient.display = blackbox->getXDisplay();
1587     ce.xclient.window = client.window;
1588     ce.xclient.format = 32;
1589     ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1590     ce.xclient.data.l[1] = blackbox->getLastTime();
1591     ce.xclient.data.l[2] = 0l;
1592     ce.xclient.data.l[3] = 0l;
1593     ce.xclient.data.l[4] = 0l;
1594     XSendEvent(blackbox->getXDisplay(), client.window, False,
1595                NoEventMask, &ce);
1596   }
1597
1598   return ret;
1599 }
1600
1601
1602 void BlackboxWindow::iconify(void) {
1603   if (flags.iconic) return;
1604
1605   // We don't need to worry about resizing because resizing always grabs the X
1606   // server. This should only ever happen if using opaque moving.
1607   if (flags.moving)
1608     endMove();
1609     
1610   if (windowmenu) windowmenu->hide();
1611
1612   /*
1613    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1614    * we need to clear the event mask on client.window for a split second.
1615    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1616    * split second, leaving us with a ghost window... so, we need to do this
1617    * while the X server is grabbed
1618    */
1619   XGrabServer(blackbox->getXDisplay());
1620   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1621   XUnmapWindow(blackbox->getXDisplay(), client.window);
1622   XSelectInput(blackbox->getXDisplay(), client.window,
1623                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1624   XUngrabServer(blackbox->getXDisplay());
1625
1626   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1627   flags.visible = False;
1628   flags.iconic = True;
1629
1630   setState(IconicState);
1631
1632   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1633
1634   if (isTransient()) {
1635     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1636         ! client.transient_for->flags.iconic) {
1637       // iconify our transient_for
1638       client.transient_for->iconify();
1639     }
1640   }
1641
1642   screen->addIcon(this);
1643
1644   if (client.transientList.size() > 0) {
1645     // iconify all transients
1646     BlackboxWindowList::iterator it, end = client.transientList.end();
1647     for (it = client.transientList.begin(); it != end; ++it) {
1648       if (! (*it)->flags.iconic) (*it)->iconify();
1649     }
1650   }
1651 }
1652
1653
1654 void BlackboxWindow::show(void) {
1655   flags.visible = True;
1656   flags.iconic = False;
1657
1658   current_state = (flags.shaded) ? IconicState : NormalState;
1659   setState(current_state);
1660
1661   XMapWindow(blackbox->getXDisplay(), client.window);
1662   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1663   XMapWindow(blackbox->getXDisplay(), frame.window);
1664
1665 #ifdef DEBUG
1666   int real_x, real_y;
1667   Window child;
1668   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1669                         screen->getRootWindow(),
1670                         0, 0, &real_x, &real_y, &child);
1671   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1672           client.rect.left(), client.rect.top(), real_x, real_y);
1673   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1674 #endif
1675 }
1676
1677
1678 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1679   if (flags.iconic || reassoc)
1680     screen->reassociateWindow(this, BSENTINEL, False);
1681   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1682     return;
1683
1684   show();
1685
1686   // reassociate and deiconify all transients
1687   if (reassoc && client.transientList.size() > 0) {
1688     BlackboxWindowList::iterator it, end = client.transientList.end();
1689     for (it = client.transientList.begin(); it != end; ++it) {
1690       (*it)->deiconify(True, False);
1691     }
1692   }
1693
1694   if (raise)
1695     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1696 }
1697
1698
1699 void BlackboxWindow::close(void) {
1700   XEvent ce;
1701   ce.xclient.type = ClientMessage;
1702   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1703   ce.xclient.display = blackbox->getXDisplay();
1704   ce.xclient.window = client.window;
1705   ce.xclient.format = 32;
1706   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1707   ce.xclient.data.l[1] = CurrentTime;
1708   ce.xclient.data.l[2] = 0l;
1709   ce.xclient.data.l[3] = 0l;
1710   ce.xclient.data.l[4] = 0l;
1711   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1712 }
1713
1714
1715 void BlackboxWindow::withdraw(void) {
1716   // We don't need to worry about resizing because resizing always grabs the X
1717   // server. This should only ever happen if using opaque moving.
1718   if (flags.moving)
1719     endMove();
1720     
1721   flags.visible = False;
1722   flags.iconic = False;
1723
1724   setState(current_state);
1725
1726   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1727
1728   XGrabServer(blackbox->getXDisplay());
1729
1730   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1731   XUnmapWindow(blackbox->getXDisplay(), client.window);
1732   XSelectInput(blackbox->getXDisplay(), client.window,
1733                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1734
1735   XUngrabServer(blackbox->getXDisplay());
1736
1737   if (windowmenu) windowmenu->hide();
1738 }
1739
1740
1741 void BlackboxWindow::maximize(unsigned int button) {
1742   // We don't need to worry about resizing because resizing always grabs the X
1743   // server. This should only ever happen if using opaque moving.
1744   if (flags.moving)
1745     endMove();
1746
1747   // handle case where menu is open then the max button is used instead
1748   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1749
1750   if (flags.maximized) {
1751     flags.maximized = 0;
1752
1753     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1754     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1755
1756     /*
1757       when a resize finishes, maximize(0) is called to clear any maximization
1758       flags currently set.  Otherwise it still thinks it is maximized.
1759       so we do not need to call configure() because resizing will handle it
1760     */
1761     if (! flags.resizing)
1762       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1763                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1764
1765     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1766     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1767
1768     redrawAllButtons(); // in case it is not called in configure()
1769     setState(current_state);
1770     return;
1771   }
1772
1773   blackbox_attrib.premax_x = frame.rect.x();
1774   blackbox_attrib.premax_y = frame.rect.y();
1775   blackbox_attrib.premax_w = frame.rect.width();
1776   // use client.rect so that clients can be restored even if shaded
1777   blackbox_attrib.premax_h =
1778     client.rect.height() + frame.margin.top + frame.margin.bottom;
1779
1780   const Rect &screen_area = screen->availableArea();
1781   frame.changing = screen_area;
1782
1783   switch(button) {
1784   case 1:
1785     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1786     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1787     break;
1788
1789   case 2:
1790     blackbox_attrib.flags |= AttribMaxVert;
1791     blackbox_attrib.attrib |= AttribMaxVert;
1792
1793     frame.changing.setX(frame.rect.x());
1794     frame.changing.setWidth(frame.rect.width());
1795     break;
1796
1797   case 3:
1798     blackbox_attrib.flags |= AttribMaxHoriz;
1799     blackbox_attrib.attrib |= AttribMaxHoriz;
1800
1801     frame.changing.setY(frame.rect.y());
1802     frame.changing.setHeight(frame.rect.height());
1803     break;
1804   }
1805
1806   constrain(TopLeft);
1807
1808   if (flags.shaded) {
1809     blackbox_attrib.flags ^= AttribShaded;
1810     blackbox_attrib.attrib ^= AttribShaded;
1811     flags.shaded = False;
1812   }
1813
1814   flags.maximized = button;
1815
1816   configure(frame.changing.x(), frame.changing.y(),
1817             frame.changing.width(), frame.changing.height());
1818   if (flags.focused)
1819     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1820   redrawAllButtons(); // in case it is not called in configure()
1821   setState(current_state);
1822 }
1823
1824
1825 // re-maximizes the window to take into account availableArea changes
1826 void BlackboxWindow::remaximize(void) {
1827   // save the original dimensions because maximize will wipe them out
1828   int premax_x = blackbox_attrib.premax_x,
1829     premax_y = blackbox_attrib.premax_y,
1830     premax_w = blackbox_attrib.premax_w,
1831     premax_h = blackbox_attrib.premax_h;
1832
1833   unsigned int button = flags.maximized;
1834   flags.maximized = 0; // trick maximize() into working
1835   maximize(button);
1836
1837   // restore saved values
1838   blackbox_attrib.premax_x = premax_x;
1839   blackbox_attrib.premax_y = premax_y;
1840   blackbox_attrib.premax_w = premax_w;
1841   blackbox_attrib.premax_h = premax_h;
1842 }
1843
1844
1845 void BlackboxWindow::setWorkspace(unsigned int n) {
1846   blackbox_attrib.flags |= AttribWorkspace;
1847   blackbox_attrib.workspace = n;
1848   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1849 }
1850
1851
1852 void BlackboxWindow::shade(void) {
1853   if (flags.shaded) {
1854     XResizeWindow(blackbox->getXDisplay(), frame.window,
1855                   frame.inside_w, frame.inside_h);
1856     flags.shaded = False;
1857     blackbox_attrib.flags ^= AttribShaded;
1858     blackbox_attrib.attrib ^= AttribShaded;
1859
1860     setState(NormalState);
1861
1862     // set the frame rect to the normal size
1863     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1864                          frame.margin.bottom);
1865   } else {
1866     if (! (decorations & Decor_Titlebar))
1867       return; // can't shade it without a titlebar!
1868
1869     XResizeWindow(blackbox->getXDisplay(), frame.window,
1870                   frame.inside_w, frame.title_h);
1871     flags.shaded = True;
1872     blackbox_attrib.flags |= AttribShaded;
1873     blackbox_attrib.attrib |= AttribShaded;
1874
1875     setState(IconicState);
1876
1877     // set the frame rect to the shaded size
1878     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1879   }
1880 }
1881
1882
1883 /*
1884  * (Un)Sticks a window and its relatives.
1885  */
1886 void BlackboxWindow::stick(void) {
1887   if (flags.stuck) {
1888     blackbox_attrib.flags ^= AttribOmnipresent;
1889     blackbox_attrib.attrib ^= AttribOmnipresent;
1890
1891     flags.stuck = False;
1892
1893     if (! flags.iconic)
1894       screen->reassociateWindow(this, BSENTINEL, True);
1895     else
1896       // temporary fix since sticky windows suck. set the hint to what we
1897       // actually hold in our data.
1898       xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1899                       blackbox_attrib.workspace);
1900
1901     setState(current_state);
1902   } else {
1903     flags.stuck = True;
1904
1905     blackbox_attrib.flags |= AttribOmnipresent;
1906     blackbox_attrib.attrib |= AttribOmnipresent;
1907
1908     // temporary fix since sticky windows suck. set the hint to a different
1909     // value than that contained in the class' data.
1910     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1911                     0xffffffff);
1912
1913     setState(current_state);
1914   }
1915   // go up the chain
1916   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1917       client.transient_for->isStuck() != flags.stuck)
1918     client.transient_for->stick();
1919   // go down the chain
1920   BlackboxWindowList::iterator it;
1921   const BlackboxWindowList::iterator end = client.transientList.end();
1922   for (it = client.transientList.begin(); it != end; ++it)
1923     if ((*it)->isStuck() != flags.stuck)
1924       (*it)->stick();
1925 }
1926
1927
1928 void BlackboxWindow::redrawWindowFrame(void) const {
1929   if (decorations & Decor_Titlebar) {
1930     if (flags.focused) {
1931       if (frame.ftitle)
1932         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1933                                    frame.title, frame.ftitle);
1934       else
1935         XSetWindowBackground(blackbox->getXDisplay(),
1936                              frame.title, frame.ftitle_pixel);
1937     } else {
1938       if (frame.utitle)
1939         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1940                                    frame.title, frame.utitle);
1941       else
1942         XSetWindowBackground(blackbox->getXDisplay(),
1943                              frame.title, frame.utitle_pixel);
1944     }
1945     XClearWindow(blackbox->getXDisplay(), frame.title);
1946
1947     redrawLabel();
1948     redrawAllButtons();
1949   }
1950
1951   if (decorations & Decor_Handle) {
1952     if (flags.focused) {
1953       if (frame.fhandle)
1954         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1955                                    frame.handle, frame.fhandle);
1956       else
1957         XSetWindowBackground(blackbox->getXDisplay(),
1958                              frame.handle, frame.fhandle_pixel);
1959
1960       if (frame.fgrip) {
1961         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1962                                    frame.left_grip, frame.fgrip);
1963         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1964                                    frame.right_grip, frame.fgrip);
1965       } else {
1966         XSetWindowBackground(blackbox->getXDisplay(),
1967                              frame.left_grip, frame.fgrip_pixel);
1968         XSetWindowBackground(blackbox->getXDisplay(),
1969                              frame.right_grip, frame.fgrip_pixel);
1970       }
1971     } else {
1972       if (frame.uhandle)
1973         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1974                                    frame.handle, frame.uhandle);
1975       else
1976         XSetWindowBackground(blackbox->getXDisplay(),
1977                              frame.handle, frame.uhandle_pixel);
1978
1979       if (frame.ugrip) {
1980         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1981                                    frame.left_grip, frame.ugrip);
1982         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1983                                    frame.right_grip, frame.ugrip);
1984       } else {
1985         XSetWindowBackground(blackbox->getXDisplay(),
1986                              frame.left_grip, frame.ugrip_pixel);
1987         XSetWindowBackground(blackbox->getXDisplay(),
1988                              frame.right_grip, frame.ugrip_pixel);
1989       }
1990     }
1991     XClearWindow(blackbox->getXDisplay(), frame.handle);
1992     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
1993     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
1994   }
1995
1996   if (decorations & Decor_Border) {
1997     if (flags.focused)
1998       XSetWindowBorder(blackbox->getXDisplay(),
1999                        frame.plate, frame.fborder_pixel);
2000     else
2001       XSetWindowBorder(blackbox->getXDisplay(),
2002                        frame.plate, frame.uborder_pixel);
2003   }
2004 }
2005
2006
2007 void BlackboxWindow::setFocusFlag(bool focus) {
2008   // only focus a window if it is visible
2009   if (focus && !flags.visible)
2010     return;
2011
2012   flags.focused = focus;
2013
2014   redrawWindowFrame();
2015
2016   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2017     if (isFocused()) timer->start();
2018     else timer->stop();
2019   }
2020
2021   if (isFocused())
2022     blackbox->setFocusedWindow(this);
2023 }
2024
2025
2026 void BlackboxWindow::installColormap(bool install) {
2027   int i = 0, ncmap = 0;
2028   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2029                                             client.window, &ncmap);
2030   if (cmaps) {
2031     XWindowAttributes wattrib;
2032     if (XGetWindowAttributes(blackbox->getXDisplay(),
2033                              client.window, &wattrib)) {
2034       if (install) {
2035         // install the window's colormap
2036         for (i = 0; i < ncmap; i++) {
2037           if (*(cmaps + i) == wattrib.colormap)
2038             // this window is using an installed color map... do not install
2039             install = False;
2040         }
2041         // otherwise, install the window's colormap
2042         if (install)
2043           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2044       } else {
2045         // uninstall the window's colormap
2046         for (i = 0; i < ncmap; i++) {
2047           if (*(cmaps + i) == wattrib.colormap)
2048             // we found the colormap to uninstall
2049             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2050         }
2051       }
2052     }
2053
2054     XFree(cmaps);
2055   }
2056 }
2057
2058
2059 void BlackboxWindow::setAllowedActions(void) {
2060   Atom actions[7];
2061   int num = 0;
2062   
2063   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2064   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2065   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2066
2067   if (functions & Func_Move)
2068     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2069   if (functions & Func_Resize)
2070     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2071   if (functions & Func_Maximize) {
2072     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2073     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2074   }
2075
2076   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2077                   actions, num);
2078 }
2079
2080
2081 void BlackboxWindow::setState(unsigned long new_state) {
2082   current_state = new_state;
2083
2084   unsigned long state[2];
2085   state[0] = current_state;
2086   state[1] = None;
2087   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2088  
2089   xatom->setValue(client.window, XAtom::blackbox_attributes,
2090                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2091                   PropBlackboxAttributesElements);
2092
2093   Atom netstate[8];
2094   int num = 0;
2095   if (flags.modal)
2096     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2097   if (flags.shaded)
2098     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2099   if (flags.iconic)
2100     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2101   if (flags.skip_taskbar)
2102     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2103   if (flags.skip_pager)
2104     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2105   if (flags.fullscreen)
2106     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2107   if (flags.maximized == 1 || flags.maximized == 2)
2108     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2109   if (flags.maximized == 1 || flags.maximized == 3)
2110     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2111   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2112                   netstate, num);
2113 }
2114
2115
2116 bool BlackboxWindow::getState(void) {
2117   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2118                              current_state);
2119   if (! ret) current_state = 0;
2120   return ret;
2121 }
2122
2123
2124 void BlackboxWindow::restoreAttributes(void) {
2125   unsigned long num = PropBlackboxAttributesElements;
2126   BlackboxAttributes *net;
2127   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2128                         XAtom::blackbox_attributes, num,
2129                         (unsigned long **)&net))
2130     return;
2131   if (num < PropBlackboxAttributesElements) {
2132     delete [] net;
2133     return;
2134   }
2135
2136   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2137     flags.shaded = False;
2138     unsigned long orig_state = current_state;
2139     shade();
2140
2141     /*
2142       At this point in the life of a window, current_state should only be set
2143       to IconicState if the window was an *icon*, not if it was shaded.
2144     */
2145     if (orig_state != IconicState)
2146       current_state = WithdrawnState;
2147  }
2148
2149   if (net->workspace != screen->getCurrentWorkspaceID() &&
2150       net->workspace < screen->getWorkspaceCount())
2151     screen->reassociateWindow(this, net->workspace, True);
2152
2153   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2154       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2155     // set to WithdrawnState so it will be mapped on the new workspace
2156     if (current_state == NormalState) current_state = WithdrawnState;
2157   } else if (current_state == WithdrawnState) {
2158     // the window is on this workspace and is Withdrawn, so it is waiting to
2159     // be mapped
2160     current_state = NormalState;
2161   }
2162
2163   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2164     flags.stuck = False;
2165     stick();
2166
2167     // if the window was on another workspace, it was going to be hidden. this
2168     // specifies that the window should be mapped since it is sticky.
2169     if (current_state == WithdrawnState) current_state = NormalState;
2170   }
2171
2172   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2173     int x = net->premax_x, y = net->premax_y;
2174     unsigned int w = net->premax_w, h = net->premax_h;
2175     flags.maximized = 0;
2176
2177     unsigned int m = 0;
2178     if ((net->flags & AttribMaxHoriz) &&
2179         (net->flags & AttribMaxVert))
2180       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2181     else if (net->flags & AttribMaxVert)
2182       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2183     else if (net->flags & AttribMaxHoriz)
2184       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2185
2186     if (m) maximize(m);
2187
2188     blackbox_attrib.premax_x = x;
2189     blackbox_attrib.premax_y = y;
2190     blackbox_attrib.premax_w = w;
2191     blackbox_attrib.premax_h = h;
2192   }
2193
2194   // with the state set it will then be the map event's job to read the
2195   // window's state and behave accordingly
2196
2197   delete [] net;
2198 }
2199
2200
2201 /*
2202  * Positions the Rect r according the the client window position and
2203  * window gravity.
2204  */
2205 void BlackboxWindow::applyGravity(Rect &r) {
2206   // apply horizontal window gravity
2207   switch (client.win_gravity) {
2208   default:
2209   case NorthWestGravity:
2210   case SouthWestGravity:
2211   case WestGravity:
2212     r.setX(client.rect.x());
2213     break;
2214
2215   case NorthGravity:
2216   case SouthGravity:
2217   case CenterGravity:
2218     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2219     break;
2220
2221   case NorthEastGravity:
2222   case SouthEastGravity:
2223   case EastGravity:
2224     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2225     break;
2226
2227   case ForgetGravity:
2228   case StaticGravity:
2229     r.setX(client.rect.x() - frame.margin.left);
2230     break;
2231   }
2232
2233   // apply vertical window gravity
2234   switch (client.win_gravity) {
2235   default:
2236   case NorthWestGravity:
2237   case NorthEastGravity:
2238   case NorthGravity:
2239     r.setY(client.rect.y());
2240     break;
2241
2242   case CenterGravity:
2243   case EastGravity:
2244   case WestGravity:
2245     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2246     break;
2247
2248   case SouthWestGravity:
2249   case SouthEastGravity:
2250   case SouthGravity:
2251     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2252     break;
2253
2254   case ForgetGravity:
2255   case StaticGravity:
2256     r.setY(client.rect.y() - frame.margin.top);
2257     break;
2258   }
2259 }
2260
2261
2262 /*
2263  * The reverse of the applyGravity function.
2264  *
2265  * Positions the Rect r according to the frame window position and
2266  * window gravity.
2267  */
2268 void BlackboxWindow::restoreGravity(Rect &r) {
2269   // restore horizontal window gravity
2270   switch (client.win_gravity) {
2271   default:
2272   case NorthWestGravity:
2273   case SouthWestGravity:
2274   case WestGravity:
2275     r.setX(frame.rect.x());
2276     break;
2277
2278   case NorthGravity:
2279   case SouthGravity:
2280   case CenterGravity:
2281     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2282     break;
2283
2284   case NorthEastGravity:
2285   case SouthEastGravity:
2286   case EastGravity:
2287     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2288     break;
2289
2290   case ForgetGravity:
2291   case StaticGravity:
2292     r.setX(frame.rect.x() + frame.margin.left);
2293     break;
2294   }
2295
2296   // restore vertical window gravity
2297   switch (client.win_gravity) {
2298   default:
2299   case NorthWestGravity:
2300   case NorthEastGravity:
2301   case NorthGravity:
2302     r.setY(frame.rect.y());
2303     break;
2304
2305   case CenterGravity:
2306   case EastGravity:
2307   case WestGravity:
2308     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2309     break;
2310
2311   case SouthWestGravity:
2312   case SouthEastGravity:
2313   case SouthGravity:
2314     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2315     break;
2316
2317   case ForgetGravity:
2318   case StaticGravity:
2319     r.setY(frame.rect.y() + frame.margin.top);
2320     break;
2321   }
2322 }
2323
2324
2325 void BlackboxWindow::redrawLabel(void) const {
2326   if (flags.focused) {
2327     if (frame.flabel)
2328       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2329                                  frame.label, frame.flabel);
2330     else
2331       XSetWindowBackground(blackbox->getXDisplay(),
2332                            frame.label, frame.flabel_pixel);
2333   } else {
2334     if (frame.ulabel)
2335       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2336                                  frame.label, frame.ulabel);
2337     else
2338       XSetWindowBackground(blackbox->getXDisplay(),
2339                            frame.label, frame.ulabel_pixel);
2340   }
2341   XClearWindow(blackbox->getXDisplay(), frame.label);
2342
2343   WindowStyle *style = screen->getWindowStyle();
2344
2345   int pos = frame.bevel_w * 2;
2346   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2347   style->font->drawString(frame.label, pos, 1,
2348                           (flags.focused ? style->l_text_focus :
2349                            style->l_text_unfocus),
2350                           client.title);
2351 }
2352
2353
2354 void BlackboxWindow::redrawAllButtons(void) const {
2355   if (frame.iconify_button) redrawIconifyButton(False);
2356   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2357   if (frame.close_button) redrawCloseButton(False);
2358 }
2359
2360
2361 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2362   if (! pressed) {
2363     if (flags.focused) {
2364       if (frame.fbutton)
2365         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2366                                    frame.iconify_button, frame.fbutton);
2367       else
2368         XSetWindowBackground(blackbox->getXDisplay(),
2369                              frame.iconify_button, frame.fbutton_pixel);
2370     } else {
2371       if (frame.ubutton)
2372         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2373                                    frame.iconify_button, frame.ubutton);
2374       else
2375         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2376                              frame.ubutton_pixel);
2377     }
2378   } else {
2379     if (frame.pbutton)
2380       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2381                                  frame.iconify_button, frame.pbutton);
2382     else
2383       XSetWindowBackground(blackbox->getXDisplay(),
2384                            frame.iconify_button, frame.pbutton_pixel);
2385   }
2386   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2387
2388   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2389            screen->getWindowStyle()->b_pic_unfocus);
2390   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2391                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2392 }
2393
2394
2395 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2396   if (! pressed) {
2397     if (flags.focused) {
2398       if (frame.fbutton)
2399         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2400                                    frame.maximize_button, frame.fbutton);
2401       else
2402         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2403                              frame.fbutton_pixel);
2404     } else {
2405       if (frame.ubutton)
2406         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2407                                    frame.maximize_button, frame.ubutton);
2408       else
2409         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2410                              frame.ubutton_pixel);
2411     }
2412   } else {
2413     if (frame.pbutton)
2414       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2415                                  frame.maximize_button, frame.pbutton);
2416     else
2417       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2418                            frame.pbutton_pixel);
2419   }
2420   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2421
2422   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2423            screen->getWindowStyle()->b_pic_unfocus);
2424   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2425                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2426   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2427             2, 3, (frame.button_w - 3), 3);
2428 }
2429
2430
2431 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2432   if (! pressed) {
2433     if (flags.focused) {
2434       if (frame.fbutton)
2435         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2436                                    frame.fbutton);
2437       else
2438         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2439                              frame.fbutton_pixel);
2440     } else {
2441       if (frame.ubutton)
2442         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2443                                    frame.ubutton);
2444       else
2445         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2446                              frame.ubutton_pixel);
2447     }
2448   } else {
2449     if (frame.pbutton)
2450       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2451                                  frame.close_button, frame.pbutton);
2452     else
2453       XSetWindowBackground(blackbox->getXDisplay(),
2454                            frame.close_button, frame.pbutton_pixel);
2455   }
2456   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2457
2458   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2459            screen->getWindowStyle()->b_pic_unfocus);
2460   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2461             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2462   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2463             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2464 }
2465
2466
2467 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2468   if (re->window != client.window)
2469     return;
2470
2471 #ifdef    DEBUG
2472   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2473           client.window);
2474 #endif // DEBUG
2475
2476   switch (current_state) {
2477   case IconicState:
2478     iconify();
2479     break;
2480
2481   case WithdrawnState:
2482     withdraw();
2483     break;
2484
2485   case NormalState:
2486   case InactiveState:
2487   case ZoomState:
2488   default:
2489     show();
2490     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2491     if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2492       XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2493       setInputFocus();
2494     }
2495     break;
2496   }
2497 }
2498
2499
2500 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2501   if (ue->window != client.window)
2502     return;
2503
2504 #ifdef    DEBUG
2505   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2506           client.window);
2507 #endif // DEBUG
2508
2509   screen->unmanageWindow(this, False);
2510 }
2511
2512
2513 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2514   if (de->window != client.window)
2515     return;
2516
2517 #ifdef    DEBUG
2518   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2519           client.window);
2520 #endif // DEBUG
2521
2522   screen->unmanageWindow(this, False);
2523 }
2524
2525
2526 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2527   if (re->window != client.window || re->parent == frame.plate)
2528     return;
2529
2530 #ifdef    DEBUG
2531   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2532           "0x%lx.\n", client.window, re->parent);
2533 #endif // DEBUG
2534
2535   XEvent ev;
2536   ev.xreparent = *re;
2537   XPutBackEvent(blackbox->getXDisplay(), &ev);
2538   screen->unmanageWindow(this, True);
2539 }
2540
2541
2542 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2543   if (pe->state == PropertyDelete)
2544     return;
2545
2546 #ifdef    DEBUG
2547   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2548           client.window);
2549 #endif
2550
2551   switch(pe->atom) {
2552   case XA_WM_CLASS:
2553   case XA_WM_CLIENT_MACHINE:
2554   case XA_WM_COMMAND:
2555     break;
2556
2557   case XA_WM_TRANSIENT_FOR: {
2558     // determine if this is a transient window
2559     getTransientInfo();
2560
2561     // adjust the window decorations based on transience
2562     if (isTransient()) {
2563       decorations &= ~(Decor_Maximize | Decor_Handle);
2564       functions &= ~Func_Maximize;
2565       setAllowedActions();
2566     }
2567
2568     reconfigure();
2569   }
2570     break;
2571
2572   case XA_WM_HINTS:
2573     getWMHints();
2574     break;
2575
2576   case XA_WM_ICON_NAME:
2577     getWMIconName();
2578     if (flags.iconic) screen->propagateWindowName(this);
2579     break;
2580
2581   case XAtom::net_wm_name:
2582   case XA_WM_NAME:
2583     getWMName();
2584
2585     if (decorations & Decor_Titlebar)
2586       redrawLabel();
2587
2588     screen->propagateWindowName(this);
2589     break;
2590
2591   case XA_WM_NORMAL_HINTS: {
2592     getWMNormalHints();
2593
2594     if ((client.normal_hint_flags & PMinSize) &&
2595         (client.normal_hint_flags & PMaxSize)) {
2596       // the window now can/can't resize itself, so the buttons need to be
2597       // regrabbed.
2598       ungrabButtons();
2599       if (client.max_width <= client.min_width &&
2600           client.max_height <= client.min_height) {
2601         decorations &= ~(Decor_Maximize | Decor_Handle);
2602         functions &= ~(Func_Resize | Func_Maximize);
2603       } else {
2604         decorations |= Decor_Maximize | Decor_Handle;
2605         functions |= Func_Resize | Func_Maximize;
2606       }
2607       grabButtons();
2608       setAllowedActions();
2609     }
2610
2611     Rect old_rect = frame.rect;
2612
2613     upsize();
2614
2615     if (old_rect != frame.rect)
2616       reconfigure();
2617
2618     break;
2619   }
2620
2621   default:
2622     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2623       getWMProtocols();
2624
2625       if ((decorations & Decor_Close) && (! frame.close_button)) {
2626         createCloseButton();
2627         if (decorations & Decor_Titlebar) {
2628           positionButtons(True);
2629           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2630         }
2631         if (windowmenu) windowmenu->reconfigure();
2632       }
2633     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2634       updateStrut();
2635     }
2636
2637     break;
2638   }
2639 }
2640
2641
2642 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2643 #ifdef DEBUG
2644   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2645 #endif
2646
2647   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2648     redrawLabel();
2649   else if (frame.close_button == ee->window)
2650     redrawCloseButton(False);
2651   else if (frame.maximize_button == ee->window)
2652     redrawMaximizeButton(flags.maximized);
2653   else if (frame.iconify_button == ee->window)
2654     redrawIconifyButton(False);
2655 }
2656
2657
2658 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2659   if (cr->window != client.window || flags.iconic)
2660     return;
2661
2662   if (cr->value_mask & CWBorderWidth)
2663     client.old_bw = cr->border_width;
2664
2665   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2666     Rect req = frame.rect;
2667
2668     if (cr->value_mask & (CWX | CWY)) {
2669       if (cr->value_mask & CWX)
2670         client.rect.setX(cr->x);
2671       if (cr->value_mask & CWY)
2672         client.rect.setY(cr->y);
2673
2674       applyGravity(req);
2675     }
2676
2677     if (cr->value_mask & CWWidth)
2678       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2679
2680     if (cr->value_mask & CWHeight)
2681       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2682
2683     configure(req.x(), req.y(), req.width(), req.height());
2684   }
2685
2686   if (cr->value_mask & CWStackMode) {
2687     switch (cr->detail) {
2688     case Below:
2689     case BottomIf:
2690       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2691       break;
2692
2693     case Above:
2694     case TopIf:
2695     default:
2696       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2697       break;
2698     }
2699   }
2700 }
2701
2702
2703 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2704 #ifdef DEBUG
2705   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2706           client.window);
2707 #endif
2708
2709   if (frame.maximize_button == be->window) {
2710     redrawMaximizeButton(True);
2711   } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2712     if (! flags.focused)
2713       setInputFocus();
2714
2715     if (frame.iconify_button == be->window) {
2716       redrawIconifyButton(True);
2717     } else if (frame.close_button == be->window) {
2718       redrawCloseButton(True);
2719     } else if (frame.plate == be->window) {
2720       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2721
2722       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2723
2724       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2725     } else {
2726       if (frame.title == be->window || frame.label == be->window) {
2727         if (((be->time - lastButtonPressTime) <=
2728              blackbox->getDoubleClickInterval()) ||
2729             (be->state & ControlMask)) {
2730           lastButtonPressTime = 0;
2731           shade();
2732         } else {
2733           lastButtonPressTime = be->time;
2734         }
2735       }
2736
2737       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2738
2739       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2740     }
2741   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2742              (be->window != frame.close_button)) {
2743     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2744   } else if (windowmenu && be->button == 3 &&
2745              (frame.title == be->window || frame.label == be->window ||
2746               frame.handle == be->window || frame.window == be->window)) {
2747     if (windowmenu->isVisible()) {
2748       windowmenu->hide();
2749     } else {
2750       int mx = be->x_root - windowmenu->getWidth() / 2,
2751           my = be->y_root - windowmenu->getHeight() / 2;
2752
2753       // snap the window menu into a corner/side if necessary
2754       int left_edge, right_edge, top_edge, bottom_edge;
2755
2756       /*
2757          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2758          and height of the menu, as the sizes returned by it do not include
2759          the borders.
2760        */
2761       left_edge = frame.rect.x();
2762       right_edge = frame.rect.right() -
2763         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2764       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2765       bottom_edge = client.rect.bottom() -
2766         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2767         (frame.border_w + frame.mwm_border_w);
2768
2769       if (mx < left_edge)
2770         mx = left_edge;
2771       if (mx > right_edge)
2772         mx = right_edge;
2773       if (my < top_edge)
2774         my = top_edge;
2775       if (my > bottom_edge)
2776         my = bottom_edge;
2777
2778       windowmenu->move(mx, my);
2779       windowmenu->show();
2780       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2781       XRaiseWindow(blackbox->getXDisplay(),
2782                    windowmenu->getSendToMenu()->getWindowID());
2783     }
2784   // mouse wheel up
2785   } else if (be->button == 4) {
2786     if ((be->window == frame.label ||
2787          be->window == frame.title) &&
2788         ! flags.shaded)
2789       shade();
2790   // mouse wheel down
2791   } else if (be->button == 5) {
2792     if ((be->window == frame.label ||
2793          be->window == frame.title) &&
2794         flags.shaded)
2795       shade();
2796   }
2797 }
2798
2799
2800 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2801 #ifdef DEBUG
2802   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2803           client.window);
2804 #endif
2805
2806   if (re->window == frame.maximize_button) {
2807     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2808         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2809       maximize(re->button);
2810     } else {
2811       redrawMaximizeButton(flags.maximized);
2812     }
2813   } else if (re->window == frame.iconify_button) {
2814     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2815         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2816       iconify();
2817     } else {
2818       redrawIconifyButton(False);
2819     }
2820   } else if (re->window == frame.close_button) {
2821     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2822         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2823       close();
2824     redrawCloseButton(False);
2825   } else if (flags.moving) {
2826     endMove();
2827   } else if (flags.resizing) {
2828     endResize();
2829   } else if (re->window == frame.window) {
2830     if (re->button == 2 && re->state == Mod1Mask)
2831       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2832   }
2833 }
2834
2835
2836
2837 void BlackboxWindow::beginMove(int x_root, int y_root) {
2838   assert(! (flags.resizing || flags.moving));
2839
2840   /*
2841     Only one window can be moved/resized at a time. If another window is already
2842     being moved or resized, then stop it before whating to work with this one.
2843   */
2844   BlackboxWindow *changing = blackbox->getChangingWindow();
2845   if (changing && changing != this) {
2846     if (changing->flags.moving)
2847       changing->endMove();
2848     else // if (changing->flags.resizing)
2849       changing->endResize();
2850   }
2851   
2852   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2853                PointerMotionMask | ButtonReleaseMask,
2854                GrabModeAsync, GrabModeAsync,
2855                None, blackbox->getMoveCursor(), CurrentTime);
2856
2857   if (windowmenu && windowmenu->isVisible())
2858     windowmenu->hide();
2859
2860   flags.moving = True;
2861   blackbox->setChangingWindow(this);
2862
2863   if (! screen->doOpaqueMove()) {
2864     XGrabServer(blackbox->getXDisplay());
2865
2866     frame.changing = frame.rect;
2867     screen->showPosition(frame.changing.x(), frame.changing.y());
2868
2869     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2870                    screen->getOpGC(),
2871                    frame.changing.x(),
2872                    frame.changing.y(),
2873                    frame.changing.width() - 1,
2874                    frame.changing.height() - 1);
2875   }
2876
2877   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
2878   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
2879 }
2880
2881
2882 void BlackboxWindow::doMove(int x_root, int y_root) {
2883   assert(flags.moving);
2884   assert(blackbox->getChangingWindow() == this);
2885
2886   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
2887   dx -= frame.border_w;
2888   dy -= frame.border_w;
2889
2890   const int snap_distance = screen->getEdgeSnapThreshold();
2891
2892   if (snap_distance) {
2893     // window corners
2894     const int wleft = dx,
2895               wright = dx + frame.rect.width() - 1,
2896               wtop = dy,
2897               wbottom = dy + frame.rect.height() - 1;
2898
2899     if (screen->getWindowToWindowSnap()) {
2900       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2901       assert(w);
2902
2903       // try snap to another window
2904       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2905         BlackboxWindow *snapwin = w->getWindow(i);
2906         if (snapwin == this)
2907           continue;   // don't snap to self
2908
2909         bool snapped = False;
2910         
2911         const Rect &winrect = snapwin->frameRect();
2912         int dleft = std::abs(wright - winrect.left()),
2913            dright = std::abs(wleft - winrect.right()),
2914              dtop = std::abs(wbottom - winrect.top()),
2915           dbottom = std::abs(wtop - winrect.bottom());
2916
2917         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2918             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2919
2920           // snap left of other window?
2921           if (dleft < snap_distance && dleft <= dright) {
2922             dx = winrect.left() - frame.rect.width();
2923             snapped = True;
2924           }
2925           // snap right of other window?
2926           else if (dright < snap_distance) {
2927             dx = winrect.right() + 1;
2928             snapped = True;
2929           }
2930
2931           if (snapped) {
2932             if (screen->getWindowCornerSnap()) {
2933               // try corner-snap to its other sides
2934               dtop = std::abs(wtop - winrect.top());
2935               dbottom = std::abs(wbottom - winrect.bottom());
2936               if (dtop < snap_distance && dtop <= dbottom)
2937                 dy = winrect.top();
2938               else if (dbottom < snap_distance)
2939                 dy = winrect.bottom() - frame.rect.height() + 1;
2940             }
2941
2942             continue;
2943           }
2944         }
2945
2946         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
2947             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
2948
2949           // snap top of other window?
2950           if (dtop < snap_distance && dtop <= dbottom) {
2951             dy = winrect.top() - frame.rect.height();
2952             snapped = True;
2953           }
2954           // snap bottom of other window?
2955           else if (dbottom < snap_distance) {
2956             dy = winrect.bottom() + 1;
2957             snapped = True;
2958           }
2959
2960           if (snapped) {
2961             if (screen->getWindowCornerSnap()) {
2962               // try corner-snap to its other sides
2963               dleft = std::abs(wleft - winrect.left());
2964               dright = std::abs(wright - winrect.right());
2965               if (dleft < snap_distance && dleft <= dright)
2966                 dx = winrect.left();
2967               else if (dright < snap_distance)
2968                 dx = winrect.right() - frame.rect.width() + 1;
2969             }
2970
2971             continue;
2972           }
2973         }
2974       }
2975     }
2976
2977     // try snap to the screen's available area
2978     Rect srect = screen->availableArea();
2979
2980     int dleft = std::abs(wleft - srect.left()),
2981        dright = std::abs(wright - srect.right()),
2982          dtop = std::abs(wtop - srect.top()),
2983       dbottom = std::abs(wbottom - srect.bottom());
2984
2985     // snap left?
2986     if (dleft < snap_distance && dleft <= dright)
2987       dx = srect.left();
2988     // snap right?
2989     else if (dright < snap_distance)
2990       dx = srect.right() - frame.rect.width() + 1;
2991
2992     // snap top?
2993     if (dtop < snap_distance && dtop <= dbottom)
2994       dy = srect.top();
2995     // snap bottom?
2996     else if (dbottom < snap_distance)
2997       dy = srect.bottom() - frame.rect.height() + 1;
2998
2999     srect = screen->getRect(); // now get the full screen
3000
3001     dleft = std::abs(wleft - srect.left()),
3002       dright = std::abs(wright - srect.right()),
3003       dtop = std::abs(wtop - srect.top()),
3004       dbottom = std::abs(wbottom - srect.bottom());
3005
3006     // snap left?
3007     if (dleft < snap_distance && dleft <= dright)
3008       dx = srect.left();
3009     // snap right?
3010     else if (dright < snap_distance)
3011       dx = srect.right() - frame.rect.width() + 1;
3012
3013     // snap top?
3014     if (dtop < snap_distance && dtop <= dbottom)
3015       dy = srect.top();
3016     // snap bottom?
3017     else if (dbottom < snap_distance)
3018       dy = srect.bottom() - frame.rect.height() + 1;
3019   }
3020
3021   if (screen->doOpaqueMove()) {
3022     configure(dx, dy, frame.rect.width(), frame.rect.height());
3023   } else {
3024     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3025                    screen->getOpGC(),
3026                    frame.changing.x(),
3027                    frame.changing.y(),
3028                    frame.changing.width() - 1,
3029                    frame.changing.height() - 1);
3030
3031     frame.changing.setPos(dx, dy);
3032
3033     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3034                    screen->getOpGC(),
3035                    frame.changing.x(),
3036                    frame.changing.y(),
3037                    frame.changing.width() - 1,
3038                    frame.changing.height() - 1);
3039   }
3040
3041   screen->showPosition(dx, dy);
3042 }
3043
3044
3045 void BlackboxWindow::endMove(void) {
3046   assert(flags.moving);
3047   assert(blackbox->getChangingWindow() == this);
3048
3049   flags.moving = False;
3050   blackbox->setChangingWindow(0);
3051
3052   if (! screen->doOpaqueMove()) {
3053     /* when drawing the rubber band, we need to make sure we only draw inside
3054      * the frame... frame.changing_* contain the new coords for the window,
3055      * so we need to subtract 1 from changing_w/changing_h every where we
3056      * draw the rubber band (for both moving and resizing)
3057      */
3058     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3059                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3060                    frame.changing.width() - 1, frame.changing.height() - 1);
3061       XUngrabServer(blackbox->getXDisplay());
3062   
3063       configure(frame.changing.x(), frame.changing.y(),
3064                 frame.changing.width(), frame.changing.height());
3065   } else {
3066     configure(frame.rect.x(), frame.rect.y(),
3067               frame.rect.width(), frame.rect.height());
3068   }
3069   screen->hideGeometry();
3070
3071   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3072
3073   // if there are any left over motions from the move, drop them now
3074   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3075   XEvent e;
3076   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3077                                 MotionNotify, &e));
3078 }
3079
3080
3081 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3082   assert(! (flags.resizing || flags.moving));
3083
3084   /*
3085     Only one window can be moved/resized at a time. If another window is already
3086     being moved or resized, then stop it before whating to work with this one.
3087   */
3088   BlackboxWindow *changing = blackbox->getChangingWindow();
3089   if (changing && changing != this) {
3090     if (changing->flags.moving)
3091       changing->endMove();
3092     else // if (changing->flags.resizing)
3093       changing->endResize();
3094   }
3095
3096   resize_dir = dir;
3097
3098   Cursor cursor;
3099   Corner anchor;
3100   
3101   switch (resize_dir) {
3102   case BottomLeft:
3103     anchor = TopRight;
3104     cursor = blackbox->getLowerLeftAngleCursor();
3105     break;
3106
3107   case BottomRight:
3108     anchor = TopLeft;
3109     cursor = blackbox->getLowerRightAngleCursor();
3110     break;
3111
3112   case TopLeft:
3113     anchor = BottomRight;
3114     cursor = blackbox->getUpperLeftAngleCursor();
3115     break;
3116
3117   case TopRight:
3118     anchor = BottomLeft;
3119     cursor = blackbox->getUpperRightAngleCursor();
3120     break;
3121
3122   default:
3123     assert(false); // unhandled Corner
3124     return;        // unreachable, for the compiler
3125   }
3126   
3127   XGrabServer(blackbox->getXDisplay());
3128   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3129                PointerMotionMask | ButtonReleaseMask,
3130                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3131
3132   flags.resizing = True;
3133   blackbox->setChangingWindow(this);
3134
3135   int gw, gh;
3136   frame.changing = frame.rect;
3137
3138   constrain(anchor,  &gw, &gh);
3139
3140   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3141                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3142                  frame.changing.width() - 1, frame.changing.height() - 1);
3143
3144   screen->showGeometry(gw, gh);
3145   
3146   frame.grab_x = x_root;
3147   frame.grab_y = y_root;
3148 }
3149
3150
3151 void BlackboxWindow::doResize(int x_root, int y_root) {
3152   assert(flags.resizing);
3153   assert(blackbox->getChangingWindow() == this);
3154
3155   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3156                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3157                  frame.changing.width() - 1, frame.changing.height() - 1);
3158
3159   int gw, gh;
3160   Corner anchor;
3161
3162   switch (resize_dir) {
3163   case BottomLeft:
3164     anchor = TopRight;
3165     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3166                            frame.rect.height() + (y_root - frame.grab_y));
3167     break;
3168   case BottomRight:
3169     anchor = TopLeft;
3170     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3171                            frame.rect.height() + (y_root - frame.grab_y));
3172     break;
3173   case TopLeft:
3174     anchor = BottomRight;
3175     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3176                            frame.rect.height() - (y_root - frame.grab_y));
3177     break;
3178   case TopRight:
3179     anchor = BottomLeft;
3180     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3181                            frame.rect.height() - (y_root - frame.grab_y));
3182     break;
3183
3184   default:
3185     assert(false); // unhandled Corner
3186     return;        // unreachable, for the compiler
3187   }
3188   
3189   constrain(anchor, &gw, &gh);
3190
3191   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3192                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3193                  frame.changing.width() - 1, frame.changing.height() - 1);
3194
3195   screen->showGeometry(gw, gh);
3196 }
3197
3198
3199 void BlackboxWindow::endResize(void) {
3200   assert(flags.resizing);
3201   assert(blackbox->getChangingWindow() == this);
3202
3203   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3204                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3205                  frame.changing.width() - 1, frame.changing.height() - 1);
3206   XUngrabServer(blackbox->getXDisplay());
3207
3208   // unset maximized state after resized when fully maximized
3209   if (flags.maximized == 1)
3210     maximize(0);
3211   
3212   flags.resizing = False;
3213   blackbox->setChangingWindow(0);
3214
3215   configure(frame.changing.x(), frame.changing.y(),
3216             frame.changing.width(), frame.changing.height());
3217   screen->hideGeometry();
3218
3219   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3220   
3221   // if there are any left over motions from the resize, drop them now
3222   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3223   XEvent e;
3224   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3225                                 MotionNotify, &e));
3226 }
3227
3228
3229 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3230 #ifdef DEBUG
3231   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3232           client.window);
3233 #endif
3234
3235   if (flags.moving) {
3236     doMove(me->x_root, me->y_root);
3237   } else if (flags.resizing) {
3238     doResize(me->x_root, me->y_root);
3239   } else {
3240     if (! flags.resizing && (me->state & Button1Mask) &&
3241         (functions & Func_Move) &&
3242         (frame.title == me->window || frame.label == me->window ||
3243          frame.handle == me->window || frame.window == me->window)) {
3244       beginMove(me->x_root, me->y_root);
3245     } else if ((functions & Func_Resize) &&
3246                (((me->state & Button1Mask) &&
3247                  (me->window == frame.right_grip ||
3248                   me->window == frame.left_grip)) ||
3249                 (me->state & (Mod1Mask | Button3Mask) &&
3250                  me->window == frame.window))) {
3251       beginResize(me->x_root, me->y_root,
3252                   (me->window == frame.left_grip) ? BottomLeft : BottomRight);
3253     }
3254   }
3255 }
3256
3257
3258 #ifdef    SHAPE
3259 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3260   if (blackbox->hasShapeExtensions() && flags.shaped) {
3261     configureShape();
3262   }
3263 }
3264 #endif // SHAPE
3265
3266
3267 bool BlackboxWindow::validateClient(void) const {
3268   XSync(blackbox->getXDisplay(), False);
3269
3270   XEvent e;
3271   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3272                              DestroyNotify, &e) ||
3273       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3274                              UnmapNotify, &e)) {
3275     XPutBackEvent(blackbox->getXDisplay(), &e);
3276
3277     return False;
3278   }
3279
3280   return True;
3281 }
3282
3283
3284 void BlackboxWindow::restore(bool remap) {
3285   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3286   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3287   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3288
3289   // do not leave a shaded window as an icon unless it was an icon
3290   if (flags.shaded && ! flags.iconic) setState(NormalState);
3291
3292   restoreGravity(client.rect);
3293
3294   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3295   XUnmapWindow(blackbox->getXDisplay(), client.window);
3296
3297   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3298
3299   XEvent ev;
3300   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3301                              ReparentNotify, &ev)) {
3302     remap = True;
3303   } else {
3304     // according to the ICCCM - if the client doesn't reparent to
3305     // root, then we have to do it for them
3306     XReparentWindow(blackbox->getXDisplay(), client.window,
3307                     screen->getRootWindow(),
3308                     client.rect.x(), client.rect.y());
3309   }
3310
3311   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3312 }
3313
3314
3315 // timer for autoraise
3316 void BlackboxWindow::timeout(void) {
3317   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3318 }
3319
3320
3321 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3322   if ((net->flags & AttribShaded) &&
3323       ((blackbox_attrib.attrib & AttribShaded) !=
3324        (net->attrib & AttribShaded)))
3325     shade();
3326
3327   if (flags.visible && // watch out for requests when we can not be seen
3328       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3329       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3330        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3331     if (flags.maximized) {
3332       maximize(0);
3333     } else {
3334       int button = 0;
3335
3336       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3337         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3338       else if (net->flags & AttribMaxVert)
3339         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3340       else if (net->flags & AttribMaxHoriz)
3341         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3342
3343       maximize(button);
3344     }
3345   }
3346
3347   if ((net->flags & AttribOmnipresent) &&
3348       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3349        (net->attrib & AttribOmnipresent)))
3350     stick();
3351
3352   if ((net->flags & AttribWorkspace) &&
3353       (blackbox_attrib.workspace != net->workspace)) {
3354     screen->reassociateWindow(this, net->workspace, True);
3355
3356     if (screen->getCurrentWorkspaceID() != net->workspace) {
3357       withdraw();
3358     } else {
3359       show();
3360       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3361     }
3362   }
3363
3364   if (net->flags & AttribDecoration) {
3365     switch (net->decoration) {
3366     case DecorNone:
3367       // clear all decorations except close
3368       decorations &= Decor_Close;
3369
3370       break;
3371
3372     default:
3373     case DecorNormal:
3374       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3375   
3376       decorations = ((functions & Func_Resize) && !isTransient() ?
3377                      decorations | Decor_Handle :
3378                      decorations &= ~Decor_Handle);
3379       decorations = (functions & Func_Maximize ?
3380                      decorations | Decor_Maximize :
3381                      decorations &= ~Decor_Maximize);
3382
3383       break;
3384
3385     case DecorTiny:
3386       decorations |= Decor_Titlebar | Decor_Iconify;
3387       decorations &= ~(Decor_Border | Decor_Handle);
3388       
3389       decorations = (functions & Func_Maximize ?
3390                      decorations | Decor_Maximize :
3391                      decorations &= ~Decor_Maximize);
3392
3393       break;
3394
3395     case DecorTool:
3396       decorations |= Decor_Titlebar;
3397       decorations &= ~(Decor_Iconify | Decor_Border);
3398
3399       decorations = ((functions & Func_Resize) && !isTransient() ?
3400                      decorations | Decor_Handle :
3401                      decorations &= ~Decor_Handle);
3402       decorations = (functions & Func_Maximize ?
3403                      decorations | Decor_Maximize :
3404                      decorations &= ~Decor_Maximize);
3405
3406       break;
3407     }
3408
3409     // we can not be shaded if we lack a titlebar
3410     if (flags.shaded && ! (decorations & Decor_Titlebar))
3411       shade();
3412
3413     if (flags.visible && frame.window) {
3414       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3415       XMapWindow(blackbox->getXDisplay(), frame.window);
3416     }
3417
3418     reconfigure();
3419     setState(current_state);
3420   }
3421 }
3422
3423
3424 /*
3425  * Set the sizes of all components of the window frame
3426  * (the window decorations).
3427  * These values are based upon the current style settings and the client
3428  * window's dimensions.
3429  */
3430 void BlackboxWindow::upsize(void) {
3431   frame.bevel_w = screen->getBevelWidth();
3432
3433   if (decorations & Decor_Border) {
3434     frame.border_w = screen->getBorderWidth();
3435     if (! isTransient())
3436       frame.mwm_border_w = screen->getFrameWidth();
3437     else
3438       frame.mwm_border_w = 0;
3439   } else {
3440     frame.mwm_border_w = frame.border_w = 0;
3441   }
3442
3443   if (decorations & Decor_Titlebar) {
3444     // the height of the titlebar is based upon the height of the font being
3445     // used to display the window's title
3446     WindowStyle *style = screen->getWindowStyle();
3447     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3448
3449     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3450     frame.button_w = (frame.label_h - 2);
3451
3452     // set the top frame margin
3453     frame.margin.top = frame.border_w + frame.title_h +
3454                        frame.border_w + frame.mwm_border_w;
3455   } else {
3456     frame.title_h = 0;
3457     frame.label_h = 0;
3458     frame.button_w = 0;
3459
3460     // set the top frame margin
3461     frame.margin.top = frame.border_w + frame.mwm_border_w;
3462   }
3463
3464   // set the left/right frame margin
3465   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3466
3467   if (decorations & Decor_Handle) {
3468     frame.grip_w = frame.button_w * 2;
3469     frame.handle_h = screen->getHandleWidth();
3470
3471     // set the bottom frame margin
3472     frame.margin.bottom = frame.border_w + frame.handle_h +
3473                           frame.border_w + frame.mwm_border_w;
3474   } else {
3475     frame.handle_h = 0;
3476     frame.grip_w = 0;
3477
3478     // set the bottom frame margin
3479     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3480   }
3481
3482   /*
3483     We first get the normal dimensions and use this to define the inside_w/h
3484     then we modify the height if shading is in effect.
3485     If the shade state is not considered then frame.rect gets reset to the
3486     normal window size on a reconfigure() call resulting in improper
3487     dimensions appearing in move/resize and other events.
3488   */
3489   unsigned int
3490     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3491     width = client.rect.width() + frame.margin.left + frame.margin.right;
3492
3493   frame.inside_w = width - (frame.border_w * 2);
3494   frame.inside_h = height - (frame.border_w * 2);
3495
3496   if (flags.shaded)
3497     height = frame.title_h + (frame.border_w * 2);
3498   frame.rect.setSize(width, height);
3499 }
3500
3501
3502 /*
3503  * Calculate the size of the client window and constrain it to the
3504  * size specified by the size hints of the client window.
3505  *
3506  * The logical width and height are placed into pw and ph, if they
3507  * are non-zero.  Logical size refers to the users perception of
3508  * the window size (for example an xterm resizes in cells, not in pixels).
3509  *
3510  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3511  * Physical geometry refers to the geometry of the window in pixels.
3512  */
3513 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3514   // frame.changing represents the requested frame size, we need to
3515   // strip the frame margin off and constrain the client size
3516   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3517                            frame.changing.top() + frame.margin.top,
3518                            frame.changing.right() - frame.margin.right,
3519                            frame.changing.bottom() - frame.margin.bottom);
3520
3521   int dw = frame.changing.width(), dh = frame.changing.height(),
3522     base_width = (client.base_width) ? client.base_width : client.min_width,
3523     base_height = (client.base_height) ? client.base_height :
3524                                          client.min_height;
3525
3526   // constrain
3527   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3528   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3529   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3530   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3531
3532   dw -= base_width;
3533   dw /= client.width_inc;
3534   dh -= base_height;
3535   dh /= client.height_inc;
3536
3537   if (pw) {
3538     if (client.width_inc == 1)
3539       *pw = dw + base_width;
3540     else
3541       *pw = dw;
3542   }
3543   if (ph) {
3544     if (client.height_inc == 1)
3545       *ph = dh + base_height;
3546     else
3547       *ph = dh;
3548   }
3549
3550   dw *= client.width_inc;
3551   dw += base_width;
3552   dh *= client.height_inc;
3553   dh += base_height;
3554
3555   frame.changing.setSize(dw, dh);
3556
3557   // add the frame margin back onto frame.changing
3558   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3559                            frame.changing.top() - frame.margin.top,
3560                            frame.changing.right() + frame.margin.right,
3561                            frame.changing.bottom() + frame.margin.bottom);
3562
3563   // move frame.changing to the specified anchor
3564   int dx = 0,
3565       dy = 0;
3566   switch (anchor) {
3567   case TopLeft:
3568     break;
3569
3570   case TopRight:
3571     dx = frame.rect.right() - frame.changing.right();
3572     break;
3573
3574   case BottomLeft:
3575     dy = frame.rect.bottom() - frame.changing.bottom();
3576     break;
3577
3578   case BottomRight:
3579     dx = frame.rect.right() - frame.changing.right();
3580     dy = frame.rect.bottom() - frame.changing.bottom();
3581     break;
3582
3583   default:
3584     assert(false);  // unhandled corner
3585   }
3586   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3587 }
3588
3589
3590 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3591                             unsigned int max_length,
3592                             unsigned int modifier) const {
3593   size_t text_len = text.size();
3594   unsigned int length;
3595
3596   do {
3597     length = font->measureString(string(text, 0, text_len)) + modifier;
3598   } while (length > max_length && text_len-- > 0);
3599
3600   switch (justify) {
3601   case RightJustify:
3602     start_pos += max_length - length;
3603     break;
3604
3605   case CenterJustify:
3606     start_pos += (max_length - length) / 2;
3607     break;
3608
3609   case LeftJustify:
3610   default:
3611     break;
3612   }
3613 }
3614
3615
3616 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3617   : blackbox(b), group(_group) {
3618   XWindowAttributes wattrib;
3619   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3620     // group window doesn't seem to exist anymore
3621     delete this;
3622     return;
3623   }
3624
3625   /*
3626     watch for destroy notify on the group window (in addition to
3627     any other events we are looking for)
3628
3629     since some managed windows can also be window group controllers,
3630     we need to make sure that we don't clobber the event mask for the
3631     managed window
3632   */
3633   XSelectInput(blackbox->getXDisplay(), group,
3634                wattrib.your_event_mask | StructureNotifyMask);
3635
3636   blackbox->saveGroupSearch(group, this);
3637 }
3638
3639
3640 BWindowGroup::~BWindowGroup(void) {
3641   blackbox->removeGroupSearch(group);
3642 }
3643
3644
3645 BlackboxWindow *
3646 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3647   BlackboxWindow *ret = blackbox->getFocusedWindow();
3648
3649   // does the focus window match (or any transient_fors)?
3650   while (ret) {
3651     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3652       if (ret->isTransient() && allow_transients) break;
3653       else if (! ret->isTransient()) break;
3654     }
3655
3656     ret = ret->getTransientFor();
3657   }
3658
3659   if (ret) return ret;
3660
3661   // the focus window didn't match, look in the group's window list
3662   BlackboxWindowList::const_iterator it, end = windowList.end();
3663   for (it = windowList.begin(); it != end; ++it) {
3664     ret = *it;
3665     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3666       if (ret->isTransient() && allow_transients) break;
3667       else if (! ret->isTransient()) break;
3668     }
3669   }
3670
3671   return ret;
3672 }