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