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