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