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