]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
changes to reflect the improvements to the error reporting in the parser.
[mikachu/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 #ifdef    BITMAPBUTTONS
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 #endif // BITMAPBUTTONS
2637     XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2638                    2, (frame.button_w - 5), (frame.button_w - 5), 2);
2639 #ifdef    BITMAPBUTTONS
2640   }
2641 #endif // BITMAPBUTTONS
2642 }
2643
2644
2645 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2646   if (! pressed) {
2647     if (flags.focused) {
2648       if (frame.fbutton)
2649         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2650                                    frame.maximize_button, frame.fbutton);
2651       else
2652         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2653                              frame.fbutton_pixel);
2654     } else {
2655       if (frame.ubutton)
2656         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2657                                    frame.maximize_button, frame.ubutton);
2658       else
2659         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2660                              frame.ubutton_pixel);
2661     }
2662   } else {
2663     if (frame.pbutton)
2664       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2665                                  frame.maximize_button, frame.pbutton);
2666     else
2667       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2668                            frame.pbutton_pixel);
2669   }
2670   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2671
2672   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2673            screen->getWindowStyle()->b_pic_unfocus);
2674   
2675 #ifdef    BITMAPBUTTONS
2676   PixmapMask pm = screen->getWindowStyle()->max_button;
2677     
2678   if (pm.mask != None) {
2679     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2680     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2681                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2682
2683     XFillRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2684                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2685                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2686     
2687     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2688     XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2689   } else {
2690 #endif // BITMAPBUTTONS
2691     XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2692                    2, 2, (frame.button_w - 5), (frame.button_w - 5));
2693     XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2694               2, 3, (frame.button_w - 3), 3);
2695 #ifdef    BITMAPBUTTONS
2696   }
2697 #endif // BITMAPBUTTONS
2698 }
2699
2700
2701 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2702   if (! pressed) {
2703     if (flags.focused) {
2704       if (frame.fbutton)
2705         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2706                                    frame.close_button, frame.fbutton);
2707       else
2708         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2709                              frame.fbutton_pixel);
2710     } else {
2711       if (frame.ubutton)
2712         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2713                                    frame.ubutton);
2714       else
2715         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2716                              frame.ubutton_pixel);
2717     }
2718   } else {
2719     if (frame.pbutton)
2720       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2721                                  frame.close_button, frame.pbutton);
2722     else
2723       XSetWindowBackground(blackbox->getXDisplay(),
2724                            frame.close_button, frame.pbutton_pixel);
2725   }
2726   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2727
2728   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2729            screen->getWindowStyle()->b_pic_unfocus);
2730   
2731 #ifdef    BITMAPBUTTONS
2732   PixmapMask pm = screen->getWindowStyle()->close_button;
2733
2734   if (pm.mask != None) {
2735     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2736     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2737                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2738     
2739     XFillRectangle(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2740                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2741                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2742
2743   
2744     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2745     XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2746   } else {
2747 #endif // BITMAPBUTTONS
2748     XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2749               2, 2, (frame.button_w - 3), (frame.button_w - 3));
2750     XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2751               2, (frame.button_w - 3), (frame.button_w - 3), 2);
2752 #ifdef    BITMAPBUTTONS
2753   }
2754 #endif // BITMAPBUTTONS
2755 }
2756
2757 void BlackboxWindow::redrawStickyButton(bool pressed) const {
2758   if (! pressed) {
2759     if (flags.focused) {
2760       if (frame.fbutton)
2761         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2762                                    frame.stick_button, frame.fbutton);
2763       else
2764         XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2765                              frame.fbutton_pixel);
2766     } else {
2767       if (frame.ubutton)
2768         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2769                                    frame.stick_button, frame.ubutton);
2770       else
2771         XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2772                              frame.ubutton_pixel);
2773     }
2774   } else {
2775     if (frame.pbutton)
2776       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2777                                  frame.stick_button, frame.pbutton);
2778     else
2779       XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2780                            frame.pbutton_pixel);
2781   }
2782   XClearWindow(blackbox->getXDisplay(), frame.stick_button);
2783
2784   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2785            screen->getWindowStyle()->b_pic_unfocus);
2786   
2787 #ifdef    BITMAPBUTTONS
2788   PixmapMask pm = screen->getWindowStyle()->stick_button;
2789
2790   if (pm.mask != None) {
2791     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2792     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2793                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2794     
2795     XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2796                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2797                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2798
2799   
2800     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2801     XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2802   } else {
2803 #endif // BITMAPBUTTONS
2804     XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2805                    frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 );
2806 #ifdef    BITMAPBUTTONS
2807   }
2808 #endif
2809 }
2810
2811 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2812   if (re->window != client.window)
2813     return;
2814
2815 #ifdef    DEBUG
2816   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2817           client.window);
2818 #endif // DEBUG
2819
2820   /*
2821      Even though the window wants to be shown, if it is not on the current
2822      workspace, then it isn't going to be shown right now.
2823   */
2824   if (! flags.stuck &&
2825       blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2826       blackbox_attrib.workspace < screen->getWorkspaceCount())
2827     if (current_state == NormalState) current_state = WithdrawnState;
2828
2829   switch (current_state) {
2830   case IconicState:
2831     iconify();
2832     break;
2833
2834   case WithdrawnState:
2835     withdraw();
2836     break;
2837
2838   case NormalState:
2839   case InactiveState:
2840   case ZoomState:
2841   default:
2842     show();
2843     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2844     if (isNormal()) {
2845       if (! blackbox->isStartup()) {
2846         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2847         if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2848                                      getTransientFor()->isFocused())) {
2849           setInputFocus();
2850         }
2851         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2852           int x, y, rx, ry;
2853           Window c, r;
2854           unsigned int m;
2855           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2856                         &r, &c, &rx, &ry, &x, &y, &m);
2857           beginMove(rx, ry);
2858         }
2859       }
2860     }
2861     break;
2862   }
2863 }
2864
2865
2866 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2867   if (ue->window != client.window)
2868     return;
2869
2870 #ifdef    DEBUG
2871   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2872           client.window);
2873 #endif // DEBUG
2874
2875   screen->unmanageWindow(this, False);
2876 }
2877
2878
2879 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2880   if (de->window != client.window)
2881     return;
2882
2883 #ifdef    DEBUG
2884   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2885           client.window);
2886 #endif // DEBUG
2887
2888   screen->unmanageWindow(this, False);
2889 }
2890
2891
2892 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2893   if (re->window != client.window || re->parent == frame.plate)
2894     return;
2895
2896 #ifdef    DEBUG
2897   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2898           "0x%lx.\n", client.window, re->parent);
2899 #endif // DEBUG
2900
2901   XEvent ev;
2902   ev.xreparent = *re;
2903   XPutBackEvent(blackbox->getXDisplay(), &ev);
2904   screen->unmanageWindow(this, True);
2905 }
2906
2907
2908 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2909   if (pe->state == PropertyDelete || ! validateClient())
2910     return;
2911
2912 #if 0
2913   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2914           client.window);
2915 #endif
2916
2917   switch(pe->atom) {
2918   case XA_WM_CLASS:
2919   case XA_WM_CLIENT_MACHINE:
2920   case XA_WM_COMMAND:
2921     break;
2922
2923   case XA_WM_TRANSIENT_FOR: {
2924     bool s = flags.stuck;
2925     
2926     // determine if this is a transient window
2927     getTransientInfo();
2928
2929     if (flags.stuck != s) stick();
2930
2931     // adjust the window decorations based on transience
2932     if (isTransient()) {
2933       functions &= ~Func_Maximize;
2934       setAllowedActions();
2935       setupDecor();
2936     }
2937
2938     reconfigure();
2939   }
2940     break;
2941
2942   case XA_WM_HINTS:
2943     getWMHints();
2944     break;
2945
2946   case XA_WM_ICON_NAME:
2947     getWMIconName();
2948     if (flags.iconic) screen->propagateWindowName(this);
2949     break;
2950
2951   case XAtom::net_wm_name:
2952   case XA_WM_NAME:
2953     getWMName();
2954
2955     if (decorations & Decor_Titlebar)
2956       redrawLabel();
2957
2958     screen->propagateWindowName(this);
2959     break;
2960
2961   case XA_WM_NORMAL_HINTS: {
2962     getWMNormalHints();
2963
2964     if ((client.normal_hint_flags & PMinSize) &&
2965         (client.normal_hint_flags & PMaxSize)) {
2966       // the window now can/can't resize itself, so the buttons need to be
2967       // regrabbed.
2968       ungrabButtons();
2969       if (client.max_width <= client.min_width &&
2970           client.max_height <= client.min_height) {
2971         functions &= ~(Func_Resize | Func_Maximize);
2972       } else {
2973         if (! isTransient())
2974           functions |= Func_Maximize;
2975         functions |= Func_Resize;
2976       }
2977       grabButtons();
2978       setAllowedActions();
2979       setupDecor();
2980     }
2981
2982     Rect old_rect = frame.rect;
2983
2984     upsize();
2985
2986     if (old_rect != frame.rect)
2987       reconfigure();
2988
2989     break;
2990   }
2991
2992   default:
2993     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2994       getWMProtocols();
2995
2996       if ((decorations & Decor_Close) && (! frame.close_button)) {
2997         createCloseButton();
2998         if (decorations & Decor_Titlebar) {
2999           positionButtons(True);
3000           XMapSubwindows(blackbox->getXDisplay(), frame.title);
3001         }
3002         if (windowmenu) windowmenu->reconfigure();
3003       }
3004     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
3005       updateStrut();
3006     }
3007
3008     break;
3009   }
3010 }
3011
3012
3013 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
3014 #if 0
3015   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3016 #endif
3017
3018   if (frame.label == ee->window && (decorations & Decor_Titlebar))
3019     redrawLabel();
3020   else if (frame.close_button == ee->window)
3021     redrawCloseButton(False);
3022   else if (frame.maximize_button == ee->window)
3023     redrawMaximizeButton(flags.maximized);
3024   else if (frame.iconify_button == ee->window)
3025     redrawIconifyButton(False);
3026   else if (frame.stick_button == ee->window)
3027     redrawStickyButton(flags.stuck);
3028 }
3029
3030
3031 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
3032   if (cr->window != client.window || flags.iconic)
3033     return;
3034
3035   if (cr->value_mask & CWBorderWidth)
3036     client.old_bw = cr->border_width;
3037
3038   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3039     frame.changing = frame.rect;
3040
3041     if (cr->value_mask & (CWX | CWY)) {
3042       if (cr->value_mask & CWX)
3043         client.rect.setX(cr->x);
3044       if (cr->value_mask & CWY)
3045         client.rect.setY(cr->y);
3046
3047       applyGravity(frame.changing);
3048     }
3049
3050     if (cr->value_mask & (CWWidth | CWHeight)) {
3051       if (cr->value_mask & CWWidth)
3052         frame.changing.setWidth(cr->width +
3053                                 frame.margin.left + frame.margin.right);
3054
3055       if (cr->value_mask & CWHeight)
3056         frame.changing.setHeight(cr->height +
3057                                  frame.margin.top + frame.margin.bottom);
3058
3059       /*
3060         if a position change has been specified, then that position will be
3061         used instead of determining a position based on the window's gravity.
3062       */
3063       if (! (cr->value_mask & (CWX | CWY))) {
3064         Corner corner;
3065         switch (client.win_gravity) {
3066         case NorthEastGravity:
3067         case EastGravity:
3068           corner = TopRight;
3069           break;
3070         case SouthWestGravity:
3071         case SouthGravity:
3072           corner = BottomLeft;
3073           break;
3074         case SouthEastGravity:
3075           corner = BottomRight;
3076           break;
3077         default:     // NorthWest, Static, etc
3078           corner = TopLeft;
3079         }
3080         constrain(corner);
3081       }
3082     }
3083
3084     configure(frame.changing.x(), frame.changing.y(),
3085               frame.changing.width(), frame.changing.height());
3086   }
3087
3088   if (cr->value_mask & CWStackMode && !isDesktop()) {
3089     switch (cr->detail) {
3090     case Below:
3091     case BottomIf:
3092       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3093       break;
3094
3095     case Above:
3096     case TopIf:
3097     default:
3098       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3099       break;
3100     }
3101   }
3102 }
3103
3104
3105 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
3106 #ifdef DEBUG
3107   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3108           client.window);
3109 #endif
3110
3111   if (frame.maximize_button == be->window && be->button <= 3) {
3112     redrawMaximizeButton(True);
3113   } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
3114     if (! flags.focused)
3115       setInputFocus();
3116
3117     if (frame.iconify_button == be->window) {
3118       redrawIconifyButton(True);
3119     } else if (frame.close_button == be->window) {
3120       redrawCloseButton(True);
3121     } else if (frame.stick_button == be->window) {
3122       redrawStickyButton(True);
3123     } else if (frame.plate == be->window) {
3124       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3125
3126       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3127
3128       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
3129     } else {
3130       if (frame.title == be->window || frame.label == be->window) {
3131         if (((be->time - lastButtonPressTime) <=
3132              blackbox->getDoubleClickInterval()) ||
3133             (be->state == ControlMask)) {
3134           lastButtonPressTime = 0;
3135           shade();
3136         } else {
3137           lastButtonPressTime = be->time;
3138         }
3139       }
3140
3141       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3142
3143       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3144     }
3145   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3146              (be->window != frame.close_button) &&
3147              (be->window != frame.stick_button)) {
3148     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3149   } else if (windowmenu && be->button == 3 &&
3150              (frame.title == be->window || frame.label == be->window ||
3151               frame.handle == be->window || frame.window == be->window)) {
3152     if (windowmenu->isVisible()) {
3153       windowmenu->hide();
3154     } else {
3155       int mx = be->x_root - windowmenu->getWidth() / 2,
3156           my = be->y_root - windowmenu->getHeight() / 2;
3157
3158       // snap the window menu into a corner/side if necessary
3159       int left_edge, right_edge, top_edge, bottom_edge;
3160
3161       /*
3162          the " + (frame.border_w * 2) - 1" bits are to get the proper width
3163          and height of the menu, as the sizes returned by it do not include
3164          the borders.
3165        */
3166       left_edge = frame.rect.x();
3167       right_edge = frame.rect.right() -
3168         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
3169       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
3170       bottom_edge = client.rect.bottom() -
3171         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
3172         (frame.border_w + frame.mwm_border_w);
3173
3174       if (mx < left_edge)
3175         mx = left_edge;
3176       if (mx > right_edge)
3177         mx = right_edge;
3178       if (my < top_edge)
3179         my = top_edge;
3180       if (my > bottom_edge)
3181         my = bottom_edge;
3182
3183       windowmenu->move(mx, my);
3184       windowmenu->show();
3185       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
3186       XRaiseWindow(blackbox->getXDisplay(),
3187                    windowmenu->getSendToMenu()->getWindowID());
3188     }
3189   // mouse wheel up
3190   } else if (be->button == 4) {
3191     if ((be->window == frame.label ||
3192          be->window == frame.title ||
3193          be->window == frame.maximize_button ||
3194          be->window == frame.iconify_button ||
3195          be->window == frame.close_button ||
3196          be->window == frame.stick_button) &&
3197         ! flags.shaded)
3198       shade();
3199   // mouse wheel down
3200   } else if (be->button == 5) {
3201     if ((be->window == frame.label ||
3202          be->window == frame.title ||
3203          be->window == frame.maximize_button ||
3204          be->window == frame.iconify_button ||
3205          be->window == frame.close_button ||
3206          be->window == frame.stick_button) &&
3207         flags.shaded)
3208       shade();
3209   }
3210 }
3211
3212
3213 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
3214 #ifdef DEBUG
3215   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3216           client.window);
3217 #endif
3218
3219   if (re->window == frame.maximize_button &&
3220       re->button >= 1 && re->button <= 3) {
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       maximize(re->button);
3224     } else {
3225       redrawMaximizeButton(flags.maximized);
3226     }
3227   } else if (re->window == frame.iconify_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       iconify();
3231     } else {
3232       redrawIconifyButton(False);
3233     }
3234   } else if (re->window == frame.stick_button && re->button == 1) {
3235     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3236         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3237       stick();
3238     } else {
3239       redrawStickyButton(False);
3240     }
3241   } else if (re->window == frame.close_button & re->button == 1) {
3242     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3243         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3244       close();
3245     redrawCloseButton(False);
3246   } else if (flags.moving) {
3247     endMove();
3248   } else if (flags.resizing) {
3249     endResize();
3250   } else if (re->window == frame.window) {
3251     if (re->button == 2 && re->state == mod_mask)
3252       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3253   }
3254 }
3255
3256
3257
3258 void BlackboxWindow::beginMove(int x_root, int y_root) {
3259   if (! (functions & Func_Move)) return;
3260
3261   assert(! (flags.resizing || flags.moving));
3262
3263   /*
3264     Only one window can be moved/resized at a time. If another window is already
3265     being moved or resized, then stop it before whating to work with this one.
3266   */
3267   BlackboxWindow *changing = blackbox->getChangingWindow();
3268   if (changing && changing != this) {
3269     if (changing->flags.moving)
3270       changing->endMove();
3271     else // if (changing->flags.resizing)
3272       changing->endResize();
3273   }
3274   
3275   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3276                PointerMotionMask | ButtonReleaseMask,
3277                GrabModeAsync, GrabModeAsync,
3278                None, blackbox->getMoveCursor(), CurrentTime);
3279
3280   if (windowmenu && windowmenu->isVisible())
3281     windowmenu->hide();
3282
3283   flags.moving = True;
3284   blackbox->setChangingWindow(this);
3285
3286   if (! screen->doOpaqueMove()) {
3287     XGrabServer(blackbox->getXDisplay());
3288
3289     frame.changing = frame.rect;
3290     screen->showPosition(frame.changing.x(), frame.changing.y());
3291
3292     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3293                    screen->getOpGC(),
3294                    frame.changing.x(),
3295                    frame.changing.y(),
3296                    frame.changing.width() - 1,
3297                    frame.changing.height() - 1);
3298   }
3299
3300   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3301   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3302 }
3303
3304
3305 void BlackboxWindow::doMove(int x_root, int y_root) {
3306   assert(flags.moving);
3307   assert(blackbox->getChangingWindow() == this);
3308
3309   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3310   dx -= frame.border_w;
3311   dy -= frame.border_w;
3312
3313   doWindowSnapping(dx, dy);
3314
3315   if (screen->doOpaqueMove()) {
3316     if (screen->doWorkspaceWarping())
3317       doWorkspaceWarping(x_root, y_root, dx);
3318
3319     configure(dx, dy, frame.rect.width(), frame.rect.height());
3320   } else {
3321     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3322                    screen->getOpGC(),
3323                    frame.changing.x(),
3324                    frame.changing.y(),
3325                    frame.changing.width() - 1,
3326                    frame.changing.height() - 1);
3327
3328     if (screen->doWorkspaceWarping())
3329       doWorkspaceWarping(x_root, y_root, dx);
3330
3331     frame.changing.setPos(dx, dy);
3332
3333     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3334                    screen->getOpGC(),
3335                    frame.changing.x(),
3336                    frame.changing.y(),
3337                    frame.changing.width() - 1,
3338                    frame.changing.height() - 1);
3339   }
3340
3341   screen->showPosition(dx, dy);
3342 }
3343
3344
3345 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
3346   // workspace warping
3347   bool warp = False;
3348   unsigned int dest = screen->getCurrentWorkspaceID();
3349   if (x_root <= 0) {
3350     warp = True;
3351
3352     if (dest > 0) dest--;
3353     else dest = screen->getNumberOfWorkspaces() - 1;
3354
3355   } else if (x_root >= screen->getRect().right()) {
3356     warp = True;
3357
3358     if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3359     else dest = 0;
3360   }
3361   if (! warp)
3362     return;
3363
3364   bool focus = flags.focused; // had focus while moving?
3365
3366   int dest_x = x_root;
3367   if (x_root < 0) {
3368     dest_x += screen->getRect().width() - 1;
3369     dx += screen->getRect().width() - 1;
3370   } else {
3371     dest_x -= screen->getRect().width() - 1;
3372     dx -= screen->getRect().width() - 1;
3373   }
3374
3375   if (! flags.stuck)
3376     screen->reassociateWindow(this, dest, False);
3377   screen->changeWorkspaceID(dest);
3378
3379   if (screen->doOpaqueMove())
3380     XGrabServer(blackbox->getXDisplay());
3381
3382   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3383   XWarpPointer(blackbox->getXDisplay(), None, 
3384                screen->getRootWindow(), 0, 0, 0, 0,
3385                dest_x, y_root);
3386   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3387                PointerMotionMask | ButtonReleaseMask,
3388                GrabModeAsync, GrabModeAsync,
3389                None, blackbox->getMoveCursor(), CurrentTime);
3390
3391   if (screen->doOpaqueMove())
3392     XUngrabServer(blackbox->getXDisplay());
3393
3394   if (focus)
3395     setInputFocus();
3396
3397 }
3398
3399
3400 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3401   // how much resistance to edges to provide
3402   const int resistance_size = screen->getResistanceSize();
3403
3404   // how far away to snap
3405   const int snap_distance = screen->getSnapThreshold();
3406
3407   // how to snap windows
3408   const int snap_to_windows = screen->getWindowToWindowSnap();
3409   const int snap_to_edges = screen->getWindowToEdgeSnap();
3410   // the amount of space away from the edge to provide resistance/snap
3411   const int snap_offset = screen->getSnapOffset();
3412
3413   // find the geomeetery where the moving window currently is
3414   const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3415
3416   // window corners
3417   const int wleft = dx,
3418            wright = dx + frame.rect.width() - 1,
3419              wtop = dy,
3420           wbottom = dy + frame.rect.height() - 1;
3421
3422   if (snap_to_windows) {
3423     RectList rectlist;
3424
3425     Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3426     assert(w);
3427
3428     // add windows on the workspace to the rect list
3429     const BlackboxWindowList& stack_list = w->getStackingList();
3430     BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3431     for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3432       if (*st_it != this) // don't snap to ourself
3433         rectlist.push_back( (*st_it)->frameRect() );
3434
3435     // add the toolbar and the slit to the rect list.
3436     // (only if they are not hidden)
3437     Toolbar *tbar = screen->getToolbar();
3438     Slit *slit = screen->getSlit();
3439     Rect tbar_rect, slit_rect;
3440     unsigned int bwidth = screen->getBorderWidth() * 2;
3441
3442     if (! (screen->doHideToolbar() || tbar->isHidden())) {
3443       tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3444                         tbar->getHeight() + bwidth);
3445       rectlist.push_back(tbar_rect);
3446     }
3447
3448     if (! slit->isHidden()) {
3449       slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3450                         slit->getHeight() + bwidth);
3451       rectlist.push_back(slit_rect);
3452     }
3453
3454     RectList::const_iterator it, end = rectlist.end();
3455     for (it = rectlist.begin(); it != end; ++it) {
3456       bool snapped = False;
3457       const Rect &winrect = *it;
3458       Rect offsetrect;
3459       offsetrect.setCoords(winrect.left() - snap_offset,
3460                            winrect.top() - snap_offset,
3461                            winrect.right() + snap_offset,
3462                            winrect.bottom() + snap_offset);
3463
3464       if (snap_to_windows == BScreen::WindowResistance)
3465         // if the window is already over top of this snap target, then
3466         // resistance is futile, so just ignore it
3467         if (winrect.intersects(moving))
3468           continue;
3469
3470       int dleft, dright, dtop, dbottom;
3471
3472       // if the windows are in the same plane vertically
3473       if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3474           wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3475
3476         if (snap_to_windows == BScreen::WindowResistance) {
3477           dleft = wright - offsetrect.left();
3478           dright = offsetrect.right() - wleft;
3479
3480           // snap left of other window?
3481           if (dleft >= 0 && dleft < resistance_size &&
3482               dleft < (wright - wleft)) {
3483             dx = offsetrect.left() - frame.rect.width();
3484             snapped = True;
3485           }
3486           // snap right of other window?
3487           else if (dright >= 0 && dright < resistance_size &&
3488                    dright < (wright - wleft)) {
3489             dx = offsetrect.right() + 1;
3490             snapped = True;
3491           }
3492         } else { // BScreen::WindowSnap
3493           dleft = abs(wright - offsetrect.left());
3494           dright = abs(wleft - offsetrect.right());
3495
3496           // snap left of other window?
3497           if (dleft < snap_distance && dleft <= dright) {
3498             dx = offsetrect.left() - frame.rect.width();
3499             snapped = True;
3500           }
3501           // snap right of other window?
3502           else if (dright < snap_distance) {
3503             dx = offsetrect.right() + 1;
3504             snapped = True;
3505           }            
3506         }
3507
3508         if (snapped) {
3509           if (screen->getWindowCornerSnap()) {
3510             // try corner-snap to its other sides
3511             if (snap_to_windows == BScreen::WindowResistance) {
3512               dtop = winrect.top() - wtop;
3513               dbottom = wbottom - winrect.bottom();
3514               if (dtop > 0 && dtop < resistance_size) {
3515                 // if we're already past the top edge, then don't provide
3516                 // resistance
3517                 if (moving.top() >= winrect.top())
3518                   dy = winrect.top();
3519               } else if (dbottom > 0 && dbottom < resistance_size) {
3520                 // if we're already past the bottom edge, then don't provide
3521                 // resistance
3522                 if (moving.bottom() <= winrect.bottom())
3523                   dy = winrect.bottom() - frame.rect.height() + 1;
3524               }
3525             } else { // BScreen::WindowSnap
3526               dtop = abs(wtop - winrect.top());
3527               dbottom = abs(wbottom - winrect.bottom());
3528               if (dtop < snap_distance && dtop <= dbottom)
3529                 dy = winrect.top();
3530               else if (dbottom < snap_distance)
3531                 dy = winrect.bottom() - frame.rect.height() + 1;
3532             }
3533           }
3534
3535           continue;
3536         }
3537       }
3538
3539       // if the windows are on the same plane horizontally
3540       if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3541           wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3542
3543         if (snap_to_windows == BScreen::WindowResistance) {
3544           dtop = wbottom - offsetrect.top();
3545           dbottom = offsetrect.bottom() - wtop;
3546
3547           // snap top of other window?
3548           if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3549             dy = offsetrect.top() - frame.rect.height();
3550             snapped = True;
3551           }
3552           // snap bottom of other window?
3553           else if (dbottom >= 0 && dbottom < resistance_size &&
3554                    dbottom < (wbottom - wtop)) {
3555             dy = offsetrect.bottom() + 1;
3556             snapped = True;
3557           }
3558         } else { // BScreen::WindowSnap
3559           dtop = abs(wbottom - offsetrect.top());
3560           dbottom = abs(wtop - offsetrect.bottom());
3561
3562           // snap top of other window?
3563           if (dtop < snap_distance && dtop <= dbottom) {
3564             dy = offsetrect.top() - frame.rect.height();
3565             snapped = True;
3566           }
3567           // snap bottom of other window?
3568           else if (dbottom < snap_distance) {
3569             dy = offsetrect.bottom() + 1;
3570             snapped = True;
3571           }
3572
3573         }
3574
3575         if (snapped) {
3576           if (screen->getWindowCornerSnap()) {
3577             // try corner-snap to its other sides
3578             if (snap_to_windows == BScreen::WindowResistance) {
3579               dleft = winrect.left() - wleft;
3580               dright = wright - winrect.right();
3581               if (dleft > 0 && dleft < resistance_size) {
3582                 // if we're already past the left edge, then don't provide
3583                 // resistance
3584                 if (moving.left() >= winrect.left())
3585                   dx = winrect.left();
3586               } else if (dright > 0 && dright < resistance_size) {
3587                 // if we're already past the right edge, then don't provide
3588                 // resistance
3589                 if (moving.right() <= winrect.right())
3590                   dx = winrect.right() - frame.rect.width() + 1;
3591               }
3592             } else { // BScreen::WindowSnap
3593               dleft = abs(wleft - winrect.left());
3594               dright = abs(wright - winrect.right());
3595               if (dleft < snap_distance && dleft <= dright)
3596                 dx = winrect.left();
3597               else if (dright < snap_distance)
3598                 dx = winrect.right() - frame.rect.width() + 1;
3599             }
3600           }
3601
3602           continue;
3603         }
3604       }
3605     }
3606   }
3607
3608   if (snap_to_edges) {
3609     RectList rectlist;
3610
3611     // snap to the screen edges (and screen boundaries for xinerama)
3612 #ifdef    XINERAMA
3613     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3614       rectlist.insert(rectlist.begin(),
3615                       screen->getXineramaAreas().begin(),
3616                       screen->getXineramaAreas().end());
3617     } else
3618 #endif // XINERAMA
3619       rectlist.push_back(screen->getRect());
3620
3621     RectList::const_iterator it, end = rectlist.end();
3622     for (it = rectlist.begin(); it != end; ++it) {
3623       const Rect &srect = *it;
3624       Rect offsetrect;
3625       offsetrect.setCoords(srect.left() + snap_offset,
3626                            srect.top() + snap_offset,
3627                            srect.right() - snap_offset,
3628                            srect.bottom() - snap_offset);
3629
3630       if (snap_to_edges == BScreen::WindowResistance) {
3631         // if we're not in the rectangle then don't snap to it.
3632         if (! srect.contains(moving))
3633           continue;
3634       } else { // BScreen::WindowSnap
3635         // if we're not in the rectangle then don't snap to it.
3636         if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3637                                     frame.rect.height())))
3638           continue;
3639       }
3640
3641       if (snap_to_edges == BScreen::WindowResistance) {
3642       int dleft = offsetrect.left() - wleft,
3643          dright = wright - offsetrect.right(),
3644            dtop = offsetrect.top() - wtop,
3645         dbottom = wbottom - offsetrect.bottom();
3646
3647         // snap left?
3648         if (dleft > 0 && dleft < resistance_size)
3649           dx = offsetrect.left();
3650         // snap right?
3651         else if (dright > 0 && dright < resistance_size)
3652           dx = offsetrect.right() - frame.rect.width() + 1;
3653
3654         // snap top?
3655         if (dtop > 0 && dtop < resistance_size)
3656           dy = offsetrect.top();
3657         // snap bottom?
3658         else if (dbottom > 0 && dbottom < resistance_size)
3659           dy = offsetrect.bottom() - frame.rect.height() + 1;
3660       } else { // BScreen::WindowSnap
3661         int dleft = abs(wleft - offsetrect.left()),
3662            dright = abs(wright - offsetrect.right()),
3663              dtop = abs(wtop - offsetrect.top()),
3664           dbottom = abs(wbottom - offsetrect.bottom());
3665
3666         // snap left?
3667         if (dleft < snap_distance && dleft <= dright)
3668           dx = offsetrect.left();
3669         // snap right?
3670         else if (dright < snap_distance)
3671           dx = offsetrect.right() - frame.rect.width() + 1;
3672
3673         // snap top?
3674         if (dtop < snap_distance && dtop <= dbottom)
3675           dy = offsetrect.top();
3676         // snap bottom?
3677         else if (dbottom < snap_distance)
3678           dy = offsetrect.bottom() - frame.rect.height() + 1;
3679       }
3680     }
3681   }
3682 }
3683
3684
3685 void BlackboxWindow::endMove(void) {
3686   assert(flags.moving);
3687   assert(blackbox->getChangingWindow() == this);
3688
3689   flags.moving = False;
3690   blackbox->setChangingWindow(0);
3691
3692   if (! screen->doOpaqueMove()) {
3693     /* when drawing the rubber band, we need to make sure we only draw inside
3694      * the frame... frame.changing_* contain the new coords for the window,
3695      * so we need to subtract 1 from changing_w/changing_h every where we
3696      * draw the rubber band (for both moving and resizing)
3697      */
3698     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3699                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3700                    frame.changing.width() - 1, frame.changing.height() - 1);
3701       XUngrabServer(blackbox->getXDisplay());
3702   
3703       configure(frame.changing.x(), frame.changing.y(),
3704                 frame.changing.width(), frame.changing.height());
3705   } else {
3706     configure(frame.rect.x(), frame.rect.y(),
3707               frame.rect.width(), frame.rect.height());
3708   }
3709   screen->hideGeometry();
3710
3711   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3712
3713   // if there are any left over motions from the move, drop them now
3714   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3715   XEvent e;
3716   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3717                                 MotionNotify, &e));
3718 }
3719
3720
3721 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3722   if (! (functions & Func_Resize)) return;
3723
3724   assert(! (flags.resizing || flags.moving));
3725
3726   /*
3727     Only one window can be moved/resized at a time. If another window is
3728     already being moved or resized, then stop it before whating to work with
3729     this one.
3730   */
3731   BlackboxWindow *changing = blackbox->getChangingWindow();
3732   if (changing && changing != this) {
3733     if (changing->flags.moving)
3734       changing->endMove();
3735     else // if (changing->flags.resizing)
3736       changing->endResize();
3737   }
3738
3739   resize_dir = dir;
3740
3741   Cursor cursor;
3742   Corner anchor;
3743   
3744   switch (resize_dir) {
3745   case BottomLeft:
3746     anchor = TopRight;
3747     cursor = blackbox->getLowerLeftAngleCursor();
3748     break;
3749
3750   case BottomRight:
3751     anchor = TopLeft;
3752     cursor = blackbox->getLowerRightAngleCursor();
3753     break;
3754
3755   case TopLeft:
3756     anchor = BottomRight;
3757     cursor = blackbox->getUpperLeftAngleCursor();
3758     break;
3759
3760   case TopRight:
3761     anchor = BottomLeft;
3762     cursor = blackbox->getUpperRightAngleCursor();
3763     break;
3764
3765   default:
3766     assert(false); // unhandled Corner
3767     return;        // unreachable, for the compiler
3768   }
3769   
3770   XGrabServer(blackbox->getXDisplay());
3771   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3772                PointerMotionMask | ButtonReleaseMask,
3773                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3774
3775   flags.resizing = True;
3776   blackbox->setChangingWindow(this);
3777
3778   unsigned int gw, gh;
3779   frame.changing = frame.rect;
3780
3781   constrain(anchor,  &gw, &gh);
3782
3783   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3784                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3785                  frame.changing.width() - 1, frame.changing.height() - 1);
3786
3787   screen->showGeometry(gw, gh);
3788   
3789   frame.grab_x = x_root;
3790   frame.grab_y = y_root;
3791 }
3792
3793
3794 void BlackboxWindow::doResize(int x_root, int y_root) {
3795   assert(flags.resizing);
3796   assert(blackbox->getChangingWindow() == this);
3797
3798   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3799                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3800                  frame.changing.width() - 1, frame.changing.height() - 1);
3801
3802   unsigned int gw, gh;
3803   Corner anchor;
3804   int dx, dy; // the amount of change in the size of the window
3805
3806   switch (resize_dir) {
3807   case BottomLeft:
3808     anchor = TopRight;
3809     dx = - (x_root - frame.grab_x);
3810     dy = + (y_root - frame.grab_y);
3811     break;
3812   case BottomRight:
3813     anchor = TopLeft;
3814     dx = + (x_root - frame.grab_x);
3815     dy = + (y_root - frame.grab_y);
3816     break;
3817   case TopLeft:
3818     anchor = BottomRight;
3819     dx = - (x_root - frame.grab_x);
3820     dy = - (y_root - frame.grab_y);
3821     break;
3822   case TopRight:
3823     anchor = BottomLeft;
3824     dx = + (x_root - frame.grab_x);
3825     dy = - (y_root - frame.grab_y);
3826     break;
3827
3828   default:
3829     assert(false); // unhandled Corner
3830     return;        // unreachable, for the compiler
3831   }
3832
3833   // make sure the user cant resize the window smaller than 0, which makes it
3834   // wrap around and become huge
3835   if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width();
3836   if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height();
3837
3838   frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy);
3839
3840   constrain(anchor, &gw, &gh);
3841
3842   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3843                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3844                  frame.changing.width() - 1, frame.changing.height() - 1);
3845
3846   screen->showGeometry(gw, gh);
3847 }
3848
3849
3850 void BlackboxWindow::endResize(void) {
3851   assert(flags.resizing);
3852   assert(blackbox->getChangingWindow() == this);
3853
3854   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3855                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3856                  frame.changing.width() - 1, frame.changing.height() - 1);
3857   XUngrabServer(blackbox->getXDisplay());
3858
3859   // unset maximized state after resized when fully maximized
3860   if (flags.maximized == 1)
3861     maximize(0);
3862   
3863   flags.resizing = False;
3864   blackbox->setChangingWindow(0);
3865
3866   configure(frame.changing.x(), frame.changing.y(),
3867             frame.changing.width(), frame.changing.height());
3868   screen->hideGeometry();
3869
3870   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3871   
3872   // if there are any left over motions from the resize, drop them now
3873   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3874   XEvent e;
3875   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3876                                 MotionNotify, &e));
3877 }
3878
3879
3880 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3881 #if 0
3882   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3883           client.window);
3884 #endif
3885
3886   if (flags.moving) {
3887     doMove(me->x_root, me->y_root);
3888   } else if (flags.resizing) {
3889     doResize(me->x_root, me->y_root);
3890   } else {
3891     if ((functions & Func_Move) &&
3892        (me->state & Button1Mask) &&
3893         (frame.title == me->window || frame.label == me->window ||
3894          frame.handle == me->window || frame.window == me->window)) {
3895       beginMove(me->x_root, me->y_root);
3896     } else if ((functions & Func_Resize) &&
3897                ((me->state & Button1Mask) &&
3898                 (me->window == frame.right_grip ||
3899                  me->window == frame.left_grip)) ||
3900                ((me->state & Button3Mask) && (me->state & mod_mask) &&
3901                 (frame.title == me->window || frame.label == me->window ||
3902                  frame.handle == me->window || frame.window == me->window ||
3903                  frame.right_grip == me->window ||
3904                  frame.left_grip == me->window))) {
3905       unsigned int zones = screen->getResizeZones();
3906       Corner corner;
3907       
3908       if (me->window == frame.left_grip) {
3909         corner = BottomLeft;
3910       } else if (me->window == frame.right_grip || zones == 1) {
3911         corner = BottomRight;
3912       } else {
3913         bool top;
3914         bool left = (me->x_root - frame.rect.x() <=
3915                      static_cast<signed>(frame.rect.width() / 2));
3916         if (zones == 2)
3917           top = False;
3918         else // (zones == 4)
3919           top = (me->y_root - frame.rect.y() <=
3920                  static_cast<signed>(frame.rect.height() / 2));
3921         corner = (top ? (left ? TopLeft : TopRight) :
3922                         (left ? BottomLeft : BottomRight));
3923       }
3924
3925       beginResize(me->x_root, me->y_root, corner);
3926     }
3927   }
3928 }
3929
3930
3931 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3932   if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3933     return;
3934
3935   XEvent e;
3936   bool leave = False, inferior = False;
3937
3938   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3939                                 LeaveNotify, &e)) {
3940     if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3941       leave = True;
3942       inferior = (e.xcrossing.detail == NotifyInferior);
3943     }
3944   }
3945
3946   if (! leave || inferior) {
3947     if (! isFocused()) {
3948       bool success = setInputFocus();
3949       if (success)    // if focus succeeded install the colormap
3950         installColormap(True); // XXX: shouldnt we honour no install?
3951
3952       /*
3953         We only auto-raise when the window wasn't focused because otherwise
3954         we run into problems with gtk+ drop-down lists. The window ends up
3955         raising over the list.
3956       */
3957       if (screen->doAutoRaise())
3958         timer->start();
3959     }
3960   }
3961 }
3962
3963
3964 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3965   if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3966     return;
3967
3968   installColormap(False);
3969
3970   if (timer->isTiming())
3971     timer->stop();
3972 }
3973
3974
3975 #ifdef    SHAPE
3976 void BlackboxWindow::shapeEvent(XShapeEvent *e) {
3977   if (blackbox->hasShapeExtensions()) {
3978     if (! e->shaped && flags.shaped) {
3979       clearShape();
3980       flags.shaped = False;
3981     } else if (e->shaped) {
3982       configureShape();
3983       flags.shaped = True;
3984     }
3985   }
3986 }
3987 #endif // SHAPE
3988
3989
3990 bool BlackboxWindow::validateClient(void) const {
3991   XSync(blackbox->getXDisplay(), False);
3992
3993   XEvent e;
3994   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3995                              DestroyNotify, &e) ||
3996       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3997                              UnmapNotify, &e)) {
3998     XPutBackEvent(blackbox->getXDisplay(), &e);
3999
4000     return False;
4001   }
4002
4003   return True;
4004 }
4005
4006
4007 void BlackboxWindow::restore(bool remap) {
4008   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
4009   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
4010   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
4011
4012   // do not leave a shaded window as an icon unless it was an icon
4013   if (flags.shaded && ! flags.iconic)
4014     setState(NormalState);
4015
4016   // erase the netwm stuff that we read when a window maps, so that it
4017   // doesn't persist between mappings.
4018   // (these are the ones read in getNetWMFlags().)
4019   xatom->eraseValue(client.window, XAtom::net_wm_desktop);
4020   xatom->eraseValue(client.window, XAtom::net_wm_state);
4021
4022   restoreGravity(client.rect);
4023
4024   XUnmapWindow(blackbox->getXDisplay(), frame.window);
4025   XUnmapWindow(blackbox->getXDisplay(), client.window);
4026
4027   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
4028
4029   XEvent ev;
4030   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
4031                              ReparentNotify, &ev)) {
4032     remap = True;
4033   } else {
4034     // according to the ICCCM - if the client doesn't reparent to
4035     // root, then we have to do it for them
4036     XReparentWindow(blackbox->getXDisplay(), client.window,
4037                     screen->getRootWindow(),
4038                     client.rect.x(), client.rect.y());
4039   }
4040
4041   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
4042 }
4043
4044
4045 // timer for autoraise
4046 void BlackboxWindow::timeout(void) {
4047   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
4048 }
4049
4050
4051 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
4052   if ((net->flags & AttribShaded) &&
4053       ((blackbox_attrib.attrib & AttribShaded) !=
4054        (net->attrib & AttribShaded)))
4055     shade();
4056
4057   if (flags.visible && // watch out for requests when we can not be seen
4058       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
4059       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
4060        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
4061     if (flags.maximized) {
4062       maximize(0);
4063     } else {
4064       int button = 0;
4065
4066       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
4067         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
4068       else if (net->flags & AttribMaxVert)
4069         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
4070       else if (net->flags & AttribMaxHoriz)
4071         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
4072
4073       maximize(button);
4074     }
4075   }
4076
4077   if ((net->flags & AttribOmnipresent) &&
4078       ((blackbox_attrib.attrib & AttribOmnipresent) !=
4079        (net->attrib & AttribOmnipresent)))
4080     stick();
4081
4082   if ((net->flags & AttribWorkspace) &&
4083       (blackbox_attrib.workspace != net->workspace)) {
4084     screen->reassociateWindow(this, net->workspace, True);
4085
4086     if (screen->getCurrentWorkspaceID() != net->workspace) {
4087       withdraw();
4088     } else {
4089       show();
4090       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
4091     }
4092   }
4093
4094   if (net->flags & AttribDecoration) {
4095     switch (net->decoration) {
4096     case DecorNone:
4097       enableDecor(False);
4098       break;
4099
4100     default:
4101     case DecorNormal:
4102     case DecorTiny:
4103     case DecorTool:
4104       enableDecor(True);
4105       break;
4106     }
4107   }
4108 }
4109
4110
4111 /*
4112  * Set the sizes of all components of the window frame
4113  * (the window decorations).
4114  * These values are based upon the current style settings and the client
4115  * window's dimensions.
4116  */
4117 void BlackboxWindow::upsize(void) {
4118   frame.bevel_w = screen->getBevelWidth();
4119
4120   if (decorations & Decor_Border) {
4121     frame.border_w = screen->getBorderWidth();
4122     if (! isTransient())
4123       frame.mwm_border_w = screen->getFrameWidth();
4124     else
4125       frame.mwm_border_w = 0;
4126   } else {
4127     frame.mwm_border_w = frame.border_w = 0;
4128   }
4129
4130   if (decorations & Decor_Titlebar) {
4131     // the height of the titlebar is based upon the height of the font being
4132     // used to display the window's title
4133     WindowStyle *style = screen->getWindowStyle();
4134     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
4135
4136     frame.label_h = frame.title_h - (frame.bevel_w * 2);
4137     frame.button_w = (frame.label_h - 2);
4138
4139     // set the top frame margin
4140     frame.margin.top = frame.border_w + frame.title_h +
4141                        frame.border_w + frame.mwm_border_w;
4142   } else {
4143     frame.title_h = 0;
4144     frame.label_h = 0;
4145     frame.button_w = 0;
4146
4147     // set the top frame margin
4148     frame.margin.top = frame.border_w + frame.mwm_border_w;
4149   }
4150
4151   // set the left/right frame margin
4152   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
4153
4154   if (decorations & Decor_Handle) {
4155     frame.grip_w = frame.button_w * 2;
4156     frame.handle_h = screen->getHandleWidth();
4157
4158     // set the bottom frame margin
4159     frame.margin.bottom = frame.border_w + frame.handle_h +
4160                           frame.border_w + frame.mwm_border_w;
4161   } else {
4162     frame.handle_h = 0;
4163     frame.grip_w = 0;
4164
4165     // set the bottom frame margin
4166     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
4167   }
4168
4169   /*
4170     We first get the normal dimensions and use this to define the inside_w/h
4171     then we modify the height if shading is in effect.
4172     If the shade state is not considered then frame.rect gets reset to the
4173     normal window size on a reconfigure() call resulting in improper
4174     dimensions appearing in move/resize and other events.
4175   */
4176   unsigned int
4177     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
4178     width = client.rect.width() + frame.margin.left + frame.margin.right;
4179
4180   frame.inside_w = width - (frame.border_w * 2);
4181   frame.inside_h = height - (frame.border_w * 2);
4182
4183   if (flags.shaded)
4184     height = frame.title_h + (frame.border_w * 2);
4185   frame.rect.setSize(width, height);
4186 }
4187
4188
4189 /*
4190  * Calculate the size of the client window and constrain it to the
4191  * size specified by the size hints of the client window.
4192  *
4193  * The logical width and height are placed into pw and ph, if they
4194  * are non-zero.  Logical size refers to the users perception of
4195  * the window size (for example an xterm resizes in cells, not in pixels).
4196  * pw and ph are then used to display the geometry during window moves, resize,
4197  * etc.
4198  *
4199  * The physical geometry is placed into frame.changing_{x,y,width,height}.
4200  * Physical geometry refers to the geometry of the window in pixels.
4201  */
4202 void BlackboxWindow::constrain(Corner anchor,
4203                                unsigned int *pw, unsigned int *ph) {
4204   // frame.changing represents the requested frame size, we need to
4205   // strip the frame margin off and constrain the client size
4206   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
4207                            frame.changing.top() + frame.margin.top,
4208                            frame.changing.right() - frame.margin.right,
4209                            frame.changing.bottom() - frame.margin.bottom);
4210
4211   unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
4212     base_width = (client.base_width) ? client.base_width : client.min_width,
4213     base_height = (client.base_height) ? client.base_height :
4214                                          client.min_height;
4215
4216   // constrain, but only if the min/max are being used. if they aren't, then
4217   // this resize is going to be from a ConfigureRequest because the window
4218   // isn't allowed to be resized by the user. And in that case, we don't want
4219   // to limit what the app can do
4220   if (client.max_width > client.min_width ||
4221       client.max_height > client.min_height) {
4222     if (dw < client.min_width) dw = client.min_width;
4223     if (dh < client.min_height) dh = client.min_height;
4224     if (dw > client.max_width) dw = client.max_width;
4225     if (dh > client.max_height) dh = client.max_height;
4226   }
4227
4228   assert(dw >= base_width && dh >= base_height);
4229
4230   if (client.width_inc > 1) {
4231     dw -= base_width;
4232     dw /= client.width_inc;
4233   }
4234   if (client.height_inc > 1) {
4235     dh -= base_height;
4236     dh /= client.height_inc;
4237   }
4238
4239   if (pw)
4240     *pw = dw;
4241
4242   if (ph)
4243     *ph = dh;
4244
4245   if (client.width_inc > 1) {
4246     dw *= client.width_inc;
4247     dw += base_width;
4248   }
4249   if (client.height_inc > 1) {
4250     dh *= client.height_inc;
4251     dh += base_height;
4252   }
4253
4254   frame.changing.setSize(dw, dh);
4255
4256   // add the frame margin back onto frame.changing
4257   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4258                            frame.changing.top() - frame.margin.top,
4259                            frame.changing.right() + frame.margin.right,
4260                            frame.changing.bottom() + frame.margin.bottom);
4261
4262   // move frame.changing to the specified anchor
4263   int dx = 0,
4264       dy = 0;
4265   switch (anchor) {
4266   case TopLeft:
4267     break;
4268
4269   case TopRight:
4270     dx = frame.rect.right() - frame.changing.right();
4271     break;
4272
4273   case BottomLeft:
4274     dy = frame.rect.bottom() - frame.changing.bottom();
4275     break;
4276
4277   case BottomRight:
4278     dx = frame.rect.right() - frame.changing.right();
4279     dy = frame.rect.bottom() - frame.changing.bottom();
4280     break;
4281
4282   default:
4283     assert(false);  // unhandled corner
4284   }
4285   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4286 }
4287
4288
4289 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4290                             unsigned int max_length,
4291                             unsigned int modifier) const {
4292   size_t text_len = text.size();
4293   unsigned int length;
4294
4295   do {
4296     length = font->measureString(string(text, 0, text_len)) + modifier;
4297   } while (length > max_length && text_len-- > 0);
4298
4299   switch (justify) {
4300   case RightJustify:
4301     start_pos += max_length - length;
4302     break;
4303
4304   case CenterJustify:
4305     start_pos += (max_length - length) / 2;
4306     break;
4307
4308   case LeftJustify:
4309   default:
4310     break;
4311   }
4312 }
4313
4314
4315 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4316   : blackbox(b), group(_group) {
4317   XWindowAttributes wattrib;
4318   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4319     // group window doesn't seem to exist anymore
4320     delete this;
4321     return;
4322   }
4323
4324   XSelectInput(blackbox->getXDisplay(), group,
4325                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4326
4327   blackbox->saveGroupSearch(group, this);
4328 }
4329
4330
4331 BWindowGroup::~BWindowGroup(void) {
4332   blackbox->removeGroupSearch(group);
4333 }
4334
4335
4336 BlackboxWindow *
4337 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4338   BlackboxWindow *ret = blackbox->getFocusedWindow();
4339
4340   // does the focus window match (or any transient_fors)?
4341   for (; ret; ret = ret->getTransientFor()) {
4342     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4343         (! ret->isTransient() || allow_transients))
4344       break;
4345   }
4346
4347   if (ret) return ret;
4348
4349   // the focus window didn't match, look in the group's window list
4350   BlackboxWindowList::const_iterator it, end = windowList.end();
4351   for (it = windowList.begin(); it != end; ++it) {
4352     ret = *it;
4353     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4354         (! ret->isTransient() || allow_transients))
4355       break;
4356   }
4357
4358   return ret;
4359 }