]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
no more menus, at last. woop
[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.uborder_pixel =
138     frame.fborder_pixel = frame.ugrip_pixel = frame.fgrip_pixel = 0;
139   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
140   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
141   frame.ugrip = frame.fgrip = None;
142
143   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
144   mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
145                     Decor_Iconify | Decor_Maximize;
146
147   client.normal_hint_flags = 0;
148   client.window_group = None;
149   client.transient_for = 0;
150
151   current_state = NormalState;
152
153   windowmenu = 0;
154
155   /*
156     set the initial size and location of client window (relative to the
157     _root window_). This position is the reference point used with the
158     window's gravity to find the window's initial position.
159   */
160   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
161   client.old_bw = wattrib.border_width;
162
163   lastButtonPressTime = 0;
164
165   timer = new BTimer(blackbox, this);
166   timer->setTimeout(blackbox->getAutoRaiseDelay());
167
168   // get size, aspect, minimum/maximum size and other hints set by the
169   // client
170
171   if (! getBlackboxHints())
172     getNetWMHints();
173
174   getWMProtocols();
175   getWMHints();
176   getWMNormalHints();
177
178   frame.window = createToplevelWindow();
179
180   blackbox->saveWindowSearch(frame.window, this);
181   
182   frame.plate = createChildWindow(frame.window, ExposureMask);
183   blackbox->saveWindowSearch(frame.plate, this);
184
185   // determine if this is a transient window
186   getTransientInfo();
187
188   // determine the window's type, so we can decide its decorations and
189   // functionality, or if we should not manage it at all
190   if (getWindowType()) {
191     // adjust the window decorations/behavior based on the window type
192     switch (window_type) {
193     case Type_Desktop:
194     case Type_Dock:
195     case Type_Menu:
196       blackbox_attrib.workspace = 0;  // we do need to belong to a workspace
197       flags.stuck = True;             // we show up on all workspaces
198     case Type_Splash:
199       // none of these windows are manipulated by the window manager
200       functions = 0;
201       break;
202
203     case Type_Toolbar:
204     case Type_Utility:
205       // these windows get less functionality
206       functions &= ~(Func_Maximize | Func_Resize | Func_Iconify);
207       break;
208
209     case Type_Dialog:
210       // dialogs cannot be maximized
211       functions &= ~Func_Maximize;
212       break;
213
214     case Type_Normal:
215       // normal windows retain all of the possible decorations and
216       // functionality
217       break;
218     }
219   } else {
220     getMWMHints();
221   }
222   
223   // further adjeust the window's decorations/behavior based on window sizes
224   if ((client.normal_hint_flags & PMinSize) &&
225       (client.normal_hint_flags & PMaxSize) &&
226       client.max_width <= client.min_width &&
227       client.max_height <= client.min_height) {
228     functions &= ~(Func_Resize | Func_Maximize);
229   }
230   
231   setAllowedActions();
232
233   setupDecor();
234   
235   if (decorations & Decor_Titlebar)
236     createTitlebar();
237
238   if (decorations & Decor_Handle)
239     createHandle();
240
241   // apply the size and gravity hint to the frame
242
243   upsize();
244
245   bool place_window = True;
246   if (blackbox->isStartup() || isTransient() ||
247       client.normal_hint_flags & (PPosition|USPosition)) {
248     applyGravity(frame.rect);
249
250     if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
251       place_window = False;
252   }
253
254   // add the window's strut. note this is done *after* placing the window.
255   screen->addStrut(&client.strut);
256   updateStrut();
257   
258   /*
259     the server needs to be grabbed here to prevent client's from sending
260     events while we are in the process of configuring their window.
261     We hold the grab until after we are done moving the window around.
262   */
263
264   XGrabServer(blackbox->getXDisplay());
265
266   associateClientWindow();
267
268   blackbox->saveWindowSearch(client.window, this);
269
270   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
271     screen->getCurrentWorkspace()->addWindow(this, place_window);
272   else
273     screen->getWorkspace(blackbox_attrib.workspace)->
274       addWindow(this, place_window);
275
276   if (! place_window) {
277     // don't need to call configure if we are letting the workspace
278     // place the window
279     configure(frame.rect.x(), frame.rect.y(),
280               frame.rect.width(), frame.rect.height());
281
282   }
283
284   positionWindows();
285
286   XUngrabServer(blackbox->getXDisplay());
287
288 #ifdef    SHAPE
289   if (blackbox->hasShapeExtensions() && flags.shaped)
290     configureShape();
291 #endif // SHAPE
292
293   // now that we know where to put the window and what it should look like
294   // we apply the decorations
295   decorate();
296
297   grabButtons();
298
299   XMapSubwindows(blackbox->getXDisplay(), frame.window);
300
301   // this ensures the title, buttons, and other decor are properly displayed
302   redrawWindowFrame();
303
304   // preserve the window's initial state on first map, and its current state
305   // across a restart
306   unsigned long initial_state = current_state;
307   if (! getState())
308     current_state = initial_state;
309
310   // get sticky state from our parent window if we've got one
311   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
312       client.transient_for->isStuck() != flags.stuck)
313     flags.stuck = True;
314
315   if (flags.shaded) {
316     flags.shaded = False;
317     initial_state = current_state;
318     shade();
319
320     /*
321       At this point in the life of a window, current_state should only be set
322       to IconicState if the window was an *icon*, not if it was shaded.
323     */
324     if (initial_state != IconicState)
325       current_state = NormalState;
326   }
327
328   if (flags.stuck) {
329     flags.stuck = False;
330     stick();
331   }
332
333   if (flags.maximized && (functions & Func_Maximize))
334     remaximize();
335
336   // create this last so it only needs to be configured once
337   windowmenu = new Windowmenu(this);
338 }
339
340
341 BlackboxWindow::~BlackboxWindow(void) {
342 #ifdef    DEBUG
343   fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
344           client.window);
345 #endif // DEBUG
346
347   if (! timer) // window not managed...
348     return;
349
350   if (flags.moving)
351     endMove();
352
353   screen->removeStrut(&client.strut);
354   screen->updateAvailableArea();
355
356   // We don't need to worry about resizing because resizing always grabs the X
357   // server. This should only ever happen if using opaque moving.
358   if (flags.moving)
359     endMove();
360
361   delete timer;
362
363   delete windowmenu;
364
365   if (client.window_group) {
366     BWindowGroup *group = blackbox->searchGroup(client.window_group);
367     if (group) group->removeWindow(this);
368   }
369
370   // remove ourselves from our transient_for
371   if (isTransient()) {
372     if (client.transient_for != (BlackboxWindow *) ~0ul)
373       client.transient_for->client.transientList.remove(this);
374     client.transient_for = (BlackboxWindow*) 0;
375   }
376
377   if (client.transientList.size() > 0) {
378     // reset transient_for for all transients
379     BlackboxWindowList::iterator it, end = client.transientList.end();
380     for (it = client.transientList.begin(); it != end; ++it)
381       (*it)->client.transient_for = (BlackboxWindow*) 0;
382   }
383
384   if (frame.title)
385     destroyTitlebar();
386
387   if (frame.handle)
388     destroyHandle();
389
390   if (frame.plate) {
391     blackbox->removeWindowSearch(frame.plate);
392     XDestroyWindow(blackbox->getXDisplay(), frame.plate);
393   }
394
395   if (frame.window) {
396     blackbox->removeWindowSearch(frame.window);
397     XDestroyWindow(blackbox->getXDisplay(), frame.window);
398   }
399
400   blackbox->removeWindowSearch(client.window);
401 }
402
403
404 void BlackboxWindow::enableDecor(bool enable) {
405   blackbox_attrib.flags |= AttribDecoration;
406   blackbox_attrib.decoration = enable ? DecorNormal : DecorNone;
407   setupDecor();
408     
409   // we can not be shaded if we lack a titlebar
410   if (! (decorations & Decor_Titlebar) && flags.shaded)
411     shade();
412     
413   if (flags.visible && frame.window) {
414     XMapSubwindows(blackbox->getXDisplay(), frame.window);
415     XMapWindow(blackbox->getXDisplay(), frame.window);
416   }
417
418   reconfigure();
419   setState(current_state);
420 }
421
422
423 void BlackboxWindow::setupDecor() {
424   if (blackbox_attrib.decoration != DecorNone) {
425     // start with everything on
426     decorations = Decor_Close |
427       (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) |
428       (mwm_decorations & Decor_Border ? Decor_Border : 0) |
429       (mwm_decorations & Decor_Handle ? Decor_Handle : 0) |
430       (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) |
431       (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0);
432
433     if (! (functions & Func_Close)) decorations &= ~Decor_Close;
434     if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize;
435     if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify;
436     if (! (functions & Func_Resize)) decorations &= ~Decor_Handle;
437
438     switch (window_type) {
439     case Type_Desktop:
440     case Type_Dock:
441     case Type_Menu:
442     case Type_Splash:
443       // none of these windows are decorated by the window manager at all
444       decorations = 0;
445       break;
446
447     case Type_Toolbar:
448     case Type_Utility:
449       decorations &= ~(Decor_Border);
450       break;
451
452     case Type_Dialog:
453       decorations &= ~Decor_Handle;
454       break;
455
456     case Type_Normal:
457       break;
458     }
459   } else {
460     decorations = 0;
461   }
462 }
463
464 /*
465  * Creates a new top level window, with a given location, size, and border
466  * width.
467  * Returns: the newly created window
468  */
469 Window BlackboxWindow::createToplevelWindow(void) {
470   XSetWindowAttributes attrib_create;
471   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
472                               CWOverrideRedirect | CWEventMask;
473
474   attrib_create.background_pixmap = None;
475   attrib_create.colormap = screen->getColormap();
476   attrib_create.override_redirect = True;
477   attrib_create.event_mask = EnterWindowMask | LeaveWindowMask |
478                              ButtonPress;
479   /*
480     We catch button presses because other wise they get passed down to the
481     root window, which will then cause root menus to show when you click the
482     window's frame.
483   */
484
485   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
486                        0, 0, 1, 1, frame.border_w, screen->getDepth(),
487                        InputOutput, screen->getVisual(), create_mask,
488                        &attrib_create);
489 }
490
491
492 /*
493  * Creates a child window, and optionally associates a given cursor with
494  * the new window.
495  */
496 Window BlackboxWindow::createChildWindow(Window parent,
497                                          unsigned long event_mask,
498                                          Cursor cursor) {
499   XSetWindowAttributes attrib_create;
500   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
501                               CWEventMask;
502
503   attrib_create.background_pixmap = None;
504   attrib_create.event_mask = event_mask;
505
506   if (cursor) {
507     create_mask |= CWCursor;
508     attrib_create.cursor = cursor;
509   }
510
511   return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
512                        screen->getDepth(), InputOutput, screen->getVisual(),
513                        create_mask, &attrib_create);
514 }
515
516
517 void BlackboxWindow::associateClientWindow(void) {
518   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
519   getWMName();
520   getWMIconName();
521
522   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
523
524   XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
525
526   /*
527     note we used to grab around this call to XReparentWindow however the
528     server is now grabbed before this method is called
529   */
530   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
531                              StructureNotifyMask;
532   XSelectInput(blackbox->getXDisplay(), client.window,
533                event_mask & ~StructureNotifyMask);
534   XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
535   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
536
537   XRaiseWindow(blackbox->getXDisplay(), frame.plate);
538   XMapSubwindows(blackbox->getXDisplay(), frame.plate);
539
540 #ifdef    SHAPE
541   if (blackbox->hasShapeExtensions()) {
542     XShapeSelectInput(blackbox->getXDisplay(), client.window,
543                       ShapeNotifyMask);
544
545     Bool shaped = False;
546     int foo;
547     unsigned int ufoo;
548
549     XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
550                        &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
551                        &ufoo, &ufoo);
552     flags.shaped = shaped;
553   }
554 #endif // SHAPE
555 }
556
557
558 void BlackboxWindow::decorate(void) {
559   BTexture* texture;
560
561   texture = &(screen->getWindowStyle()->b_focus);
562   frame.fbutton = texture->render(frame.button_w, frame.button_w,
563                                   frame.fbutton);
564   if (! frame.fbutton)
565     frame.fbutton_pixel = texture->color().pixel();
566
567   texture = &(screen->getWindowStyle()->b_unfocus);
568   frame.ubutton = texture->render(frame.button_w, frame.button_w,
569                                   frame.ubutton);
570   if (! frame.ubutton)
571     frame.ubutton_pixel = texture->color().pixel();
572
573   unsigned char needsPressed = 0;
574
575   texture = &(screen->getWindowStyle()->b_pressed_focus);
576   
577   if (texture->texture() != BTexture::NoTexture) {
578     frame.pfbutton = texture->render(frame.button_w, frame.button_w,
579                                      frame.pfbutton);
580     if (! frame.pfbutton)
581       frame.pfbutton_pixel = texture->color().pixel();
582   } else {
583     needsPressed = 0x1;
584   }
585
586   texture = &(screen->getWindowStyle()->b_pressed_unfocus);
587   
588   if (texture->texture() != BTexture::NoTexture) {
589     frame.pubutton = texture->render(frame.button_w, frame.button_w,
590                                      frame.pubutton);
591     if (! frame.pubutton)
592       frame.pubutton = texture->color().pixel();
593   } else {
594     needsPressed |= 0x2;
595   }
596
597   // if we either pressed unfocused, or pressed focused were undefined,
598   // make them inherit from the old resource. It's a hack for sure, but
599   // it allows for some backwards and forwards compatibility.
600   if (needsPressed) {
601     texture = &(screen->getWindowStyle()->b_pressed);
602     
603     if (needsPressed & 0x1) {
604       frame.pfbutton = texture->render(frame.button_w, frame.button_w,
605                                        frame.pfbutton);
606       if (! frame.pfbutton)
607         frame.pfbutton_pixel = texture->color().pixel();
608     }
609     if (needsPressed & 0x2) {
610       frame.pubutton = texture->render(frame.button_w, frame.button_w,
611                                        frame.pubutton);
612       if (! frame.pubutton)
613         frame.pubutton = texture->color().pixel();
614     }
615     
616   }
617
618   if (decorations & Decor_Titlebar) {
619     texture = &(screen->getWindowStyle()->t_focus);
620     frame.ftitle = texture->render(frame.inside_w, frame.title_h,
621                                    frame.ftitle);
622     if (! frame.ftitle)
623       frame.ftitle_pixel = texture->color().pixel();
624
625     texture = &(screen->getWindowStyle()->t_unfocus);
626     frame.utitle = texture->render(frame.inside_w, frame.title_h,
627                                    frame.utitle);
628     if (! frame.utitle)
629       frame.utitle_pixel = texture->color().pixel();
630
631     XSetWindowBorder(blackbox->getXDisplay(), frame.title,
632                      screen->getBorderColor()->pixel());
633
634     decorateLabel();
635   }
636
637   if (decorations & Decor_Border) {
638     frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel();
639     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel();
640   }
641
642   if (decorations & Decor_Handle) {
643     texture = &(screen->getWindowStyle()->h_focus);
644     frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
645                                     frame.fhandle);
646     if (! frame.fhandle)
647       frame.fhandle_pixel = texture->color().pixel();
648
649     texture = &(screen->getWindowStyle()->h_unfocus);
650     frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
651                                     frame.uhandle);
652     if (! frame.uhandle)
653       frame.uhandle_pixel = texture->color().pixel();
654
655     texture = &(screen->getWindowStyle()->g_focus);
656     frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
657     if (! frame.fgrip)
658       frame.fgrip_pixel = texture->color().pixel();
659
660     texture = &(screen->getWindowStyle()->g_unfocus);
661     frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
662     if (! frame.ugrip)
663       frame.ugrip_pixel = texture->color().pixel();
664
665     XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
666                      screen->getBorderColor()->pixel());
667     XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
668                      screen->getBorderColor()->pixel());
669     XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
670                      screen->getBorderColor()->pixel());
671   }
672
673   XSetWindowBorder(blackbox->getXDisplay(), frame.window,
674                    screen->getBorderColor()->pixel());
675 }
676
677
678 void BlackboxWindow::decorateLabel(void) {
679   BTexture *texture;
680
681   texture = &(screen->getWindowStyle()->l_focus);
682   frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
683   if (! frame.flabel)
684     frame.flabel_pixel = texture->color().pixel();
685
686   texture = &(screen->getWindowStyle()->l_unfocus);
687   frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
688   if (! frame.ulabel)
689     frame.ulabel_pixel = texture->color().pixel();
690 }
691
692
693 void BlackboxWindow::createHandle(void) {
694   frame.handle = createChildWindow(frame.window,
695                                    ButtonPressMask | ButtonReleaseMask |
696                                    ButtonMotionMask | ExposureMask);
697   blackbox->saveWindowSearch(frame.handle, this);
698
699   frame.left_grip =
700     createChildWindow(frame.handle,
701                       ButtonPressMask | ButtonReleaseMask |
702                       ButtonMotionMask | ExposureMask,
703                       blackbox->getLowerLeftAngleCursor());
704   blackbox->saveWindowSearch(frame.left_grip, this);
705
706   frame.right_grip =
707     createChildWindow(frame.handle,
708                       ButtonPressMask | ButtonReleaseMask |
709                       ButtonMotionMask | ExposureMask,
710                       blackbox->getLowerRightAngleCursor());
711   blackbox->saveWindowSearch(frame.right_grip, this);
712 }
713
714
715 void BlackboxWindow::destroyHandle(void) {
716   if (frame.fhandle)
717     screen->getImageControl()->removeImage(frame.fhandle);
718
719   if (frame.uhandle)
720     screen->getImageControl()->removeImage(frame.uhandle);
721
722   if (frame.fgrip)
723     screen->getImageControl()->removeImage(frame.fgrip);
724
725   if (frame.ugrip)
726     screen->getImageControl()->removeImage(frame.ugrip);
727
728   blackbox->removeWindowSearch(frame.left_grip);
729   blackbox->removeWindowSearch(frame.right_grip);
730
731   XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
732   XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
733   frame.left_grip = frame.right_grip = None;
734
735   blackbox->removeWindowSearch(frame.handle);
736   XDestroyWindow(blackbox->getXDisplay(), frame.handle);
737   frame.handle = None;
738 }
739
740
741 void BlackboxWindow::createTitlebar(void) {
742   frame.title = createChildWindow(frame.window,
743                                   ButtonPressMask | ButtonReleaseMask |
744                                   ButtonMotionMask | ExposureMask);
745   frame.label = createChildWindow(frame.title,
746                                   ButtonPressMask | ButtonReleaseMask |
747                                   ButtonMotionMask | ExposureMask);
748   blackbox->saveWindowSearch(frame.title, this);
749   blackbox->saveWindowSearch(frame.label, this);
750
751   if (decorations & Decor_Iconify) createIconifyButton();
752   if (decorations & Decor_Maximize) createMaximizeButton();
753   if (decorations & Decor_Close) createCloseButton();
754 }
755
756
757 void BlackboxWindow::destroyTitlebar(void) {
758   if (frame.close_button)
759     destroyCloseButton();
760
761   if (frame.iconify_button)
762     destroyIconifyButton();
763
764   if (frame.maximize_button)
765     destroyMaximizeButton();
766
767   if (frame.stick_button)
768     destroyStickyButton();
769
770   if (frame.ftitle)
771     screen->getImageControl()->removeImage(frame.ftitle);
772
773   if (frame.utitle)
774     screen->getImageControl()->removeImage(frame.utitle);
775
776   if (frame.flabel)
777     screen->getImageControl()->removeImage(frame.flabel);
778
779   if( frame.ulabel)
780     screen->getImageControl()->removeImage(frame.ulabel);
781
782   if (frame.fbutton)
783     screen->getImageControl()->removeImage(frame.fbutton);
784
785   if (frame.ubutton)
786     screen->getImageControl()->removeImage(frame.ubutton);
787
788   blackbox->removeWindowSearch(frame.title);
789   blackbox->removeWindowSearch(frame.label);
790
791   XDestroyWindow(blackbox->getXDisplay(), frame.label);
792   XDestroyWindow(blackbox->getXDisplay(), frame.title);
793   frame.title = frame.label = None;
794 }
795
796
797 void BlackboxWindow::createCloseButton(void) {
798   if (frame.title != None) {
799     frame.close_button = createChildWindow(frame.title,
800                                            ButtonPressMask |
801                                            ButtonReleaseMask |
802                                            ButtonMotionMask | ExposureMask);
803     blackbox->saveWindowSearch(frame.close_button, this);
804   }
805 }
806
807
808 void BlackboxWindow::destroyCloseButton(void) {
809   blackbox->removeWindowSearch(frame.close_button);
810   XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
811   frame.close_button = None;
812 }
813
814
815 void BlackboxWindow::createIconifyButton(void) {
816   if (frame.title != None) {
817     frame.iconify_button = createChildWindow(frame.title,
818                                              ButtonPressMask |
819                                              ButtonReleaseMask |
820                                              ButtonMotionMask | ExposureMask);
821     blackbox->saveWindowSearch(frame.iconify_button, this);
822   }
823 }
824
825
826 void BlackboxWindow::destroyIconifyButton(void) {
827   blackbox->removeWindowSearch(frame.iconify_button);
828   XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
829   frame.iconify_button = None;
830 }
831
832
833 void BlackboxWindow::createMaximizeButton(void) {
834   if (frame.title != None) {
835     frame.maximize_button = createChildWindow(frame.title,
836                                               ButtonPressMask |
837                                               ButtonReleaseMask |
838                                               ButtonMotionMask | ExposureMask);
839     blackbox->saveWindowSearch(frame.maximize_button, this);
840   }
841 }
842
843
844 void BlackboxWindow::destroyMaximizeButton(void) {
845   blackbox->removeWindowSearch(frame.maximize_button);
846   XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
847   frame.maximize_button = None;
848 }
849
850 void BlackboxWindow::createStickyButton(void) {
851   if (frame.title != None) {
852     frame.stick_button = createChildWindow(frame.title,
853                                            ButtonPressMask |
854                                            ButtonReleaseMask |
855                                            ButtonMotionMask | ExposureMask);
856     blackbox->saveWindowSearch(frame.stick_button, this);
857   }
858 }
859
860 void BlackboxWindow::destroyStickyButton(void) {
861   blackbox->removeWindowSearch(frame.stick_button);
862   XDestroyWindow(blackbox->getXDisplay(), frame.stick_button);
863   frame.stick_button = None;
864 }
865
866 void BlackboxWindow::positionButtons(bool redecorate_label) {
867   string layout = blackbox->getTitlebarLayout();
868   string parsed;
869
870   bool hasclose, hasiconify, hasmaximize, haslabel, hasstick;
871   hasclose = hasiconify = hasmaximize = haslabel = hasstick = false;
872
873   string::const_iterator it, end;
874   for (it = layout.begin(), end = layout.end(); it != end; ++it) {
875     switch(*it) {
876     case 'C':
877       if (! hasclose && (decorations & Decor_Close)) {
878         hasclose = true;
879         parsed += *it;
880       }
881       break;
882     case 'I':
883       if (! hasiconify && (decorations & Decor_Iconify)) {
884         hasiconify = true;
885         parsed += *it;
886       }
887       break;
888     case 'S':
889       if (!hasstick) {
890         hasstick = true;
891         parsed += *it;
892       }
893       break;
894     case 'M':
895       if (! hasmaximize && (decorations & Decor_Maximize)) {
896         hasmaximize = true;
897         parsed += *it;
898       }
899       break;
900     case 'L':
901       if (! haslabel) {
902         haslabel = true;
903         parsed += *it;
904       }
905       break;
906     }
907   }
908   
909   if (! hasclose && frame.close_button)
910     destroyCloseButton();
911   if (! hasiconify && frame.iconify_button)
912     destroyIconifyButton();
913   if (! hasmaximize && frame.maximize_button)
914     destroyMaximizeButton();
915   if (! hasstick && frame.stick_button)
916     destroyStickyButton();
917   if (! haslabel)
918     parsed += 'L';      // require that the label be in the layout
919
920   const unsigned int bsep = frame.bevel_w + 1;  // separation between elements
921   const unsigned int by = frame.bevel_w + 1;
922   const unsigned int ty = frame.bevel_w;
923
924   frame.label_w = frame.inside_w - bsep * 2 -
925     (frame.button_w + bsep) * (parsed.size() - 1);
926
927   unsigned int x = bsep;
928   for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
929     switch(*it) {
930     case 'C':
931       if (! frame.close_button) createCloseButton();
932       XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
933                         frame.button_w, frame.button_w);
934       x += frame.button_w + bsep;
935       break;
936     case 'I':
937       if (! frame.iconify_button) createIconifyButton();
938       XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
939                         frame.button_w, frame.button_w);
940       x += frame.button_w + bsep;
941       break;
942     case 'S':
943       if (! frame.stick_button) createStickyButton();
944       XMoveResizeWindow(blackbox->getXDisplay(), frame.stick_button, x, by,
945                         frame.button_w, frame.button_w);
946       x += frame.button_w + bsep;
947       break;
948     case 'M':
949       if (! frame.maximize_button) createMaximizeButton();
950       XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
951                         frame.button_w, frame.button_w);
952       x += frame.button_w + bsep;
953       break;
954     case 'L':
955       XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
956                         frame.label_w, frame.label_h);
957       x += frame.label_w + bsep;
958       break;
959     }
960   }
961
962   if (redecorate_label) decorateLabel();
963   redrawLabel();
964   redrawAllButtons();
965 }
966
967
968 void BlackboxWindow::reconfigure(void) {
969   restoreGravity(client.rect);
970   upsize();
971   applyGravity(frame.rect);
972   positionWindows();
973   decorate();
974   redrawWindowFrame();
975
976   ungrabButtons();
977   grabButtons();
978
979   if (windowmenu) {
980     windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
981     windowmenu->reconfigure();
982   }
983 }
984
985
986 void BlackboxWindow::grabButtons(void) {
987   mod_mask = blackbox->getMouseModMask();
988
989   if (! screen->isSloppyFocus() || screen->doClickRaise())
990     // grab button 1 for changing focus/raising
991     blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
992                          GrabModeSync, GrabModeSync, frame.plate, None,
993                          screen->allowScrollLock());
994   
995   if (functions & Func_Move)
996     blackbox->grabButton(Button1, mod_mask, frame.window, True,
997                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
998                          GrabModeAsync, frame.window, None,
999                          screen->allowScrollLock());
1000   if (functions & Func_Resize)
1001     blackbox->grabButton(Button3, mod_mask, frame.window, True,
1002                          ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
1003                          GrabModeAsync, frame.window, None,
1004                          screen->allowScrollLock());
1005   // alt+middle lowers the window
1006   blackbox->grabButton(Button2, mod_mask, frame.window, True,
1007                        ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
1008                        frame.window, None, screen->allowScrollLock());
1009 }
1010
1011
1012 void BlackboxWindow::ungrabButtons(void) {
1013   blackbox->ungrabButton(Button1, 0, frame.plate);
1014   blackbox->ungrabButton(Button1, mod_mask, frame.window);
1015   blackbox->ungrabButton(Button2, mod_mask, frame.window);
1016   blackbox->ungrabButton(Button3, mod_mask, frame.window);
1017 }
1018
1019
1020 void BlackboxWindow::positionWindows(void) {
1021   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
1022                     frame.rect.x(), frame.rect.y(), frame.inside_w,
1023                     (flags.shaded) ? frame.title_h : frame.inside_h);
1024   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
1025                         frame.border_w);
1026   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
1027                         frame.mwm_border_w);
1028   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
1029                     frame.margin.left - frame.mwm_border_w - frame.border_w,
1030                     frame.margin.top - frame.mwm_border_w - frame.border_w,
1031                     client.rect.width(), client.rect.height());
1032   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
1033                     0, 0, client.rect.width(), client.rect.height());
1034   // ensure client.rect contains the real location
1035   client.rect.setPos(frame.rect.left() + frame.margin.left,
1036                      frame.rect.top() + frame.margin.top);
1037
1038   if (decorations & Decor_Titlebar) {
1039     if (frame.title == None) createTitlebar();
1040
1041     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
1042                           frame.border_w);
1043     XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
1044                       -frame.border_w, frame.inside_w, frame.title_h);
1045
1046     positionButtons();
1047     XMapSubwindows(blackbox->getXDisplay(), frame.title);
1048     XMapWindow(blackbox->getXDisplay(), frame.title);
1049   } else if (frame.title) {
1050     destroyTitlebar();
1051   }
1052   if (decorations & Decor_Handle) {
1053     if (frame.handle == None) createHandle();
1054     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
1055                           frame.border_w);
1056     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
1057                           frame.border_w);
1058     XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
1059                           frame.border_w);
1060
1061     // use client.rect here so the value is correct even if shaded
1062     XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
1063                       -frame.border_w,
1064                       client.rect.height() + frame.margin.top +
1065                       frame.mwm_border_w - frame.border_w,
1066                       frame.inside_w, frame.handle_h);
1067     XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
1068                       -frame.border_w, -frame.border_w,
1069                       frame.grip_w, frame.handle_h);
1070     XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
1071                       frame.inside_w - frame.grip_w - frame.border_w,
1072                       -frame.border_w, frame.grip_w, frame.handle_h);
1073
1074     XMapSubwindows(blackbox->getXDisplay(), frame.handle);
1075     XMapWindow(blackbox->getXDisplay(), frame.handle);
1076   } else if (frame.handle) {
1077     destroyHandle();
1078   }
1079   XSync(blackbox->getXDisplay(), False);
1080 }
1081
1082
1083 void BlackboxWindow::updateStrut(void) {
1084   unsigned long num = 4;
1085   unsigned long *data;
1086   if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
1087                         num, &data))
1088     return;
1089  
1090   if (num == 4) {
1091     client.strut.left = data[0];
1092     client.strut.right = data[1];
1093     client.strut.top = data[2];
1094     client.strut.bottom = data[3];
1095
1096     screen->updateAvailableArea();
1097   }
1098
1099   delete [] data;
1100 }
1101
1102
1103 bool BlackboxWindow::getWindowType(void) {
1104   window_type = (WindowType) -1;
1105
1106   unsigned long *val;
1107   unsigned long num = (unsigned) -1;
1108   if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
1109                         num, &val)) {
1110     for (unsigned long i = 0; i < num; ++i) {
1111       if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_desktop))
1112         window_type = Type_Desktop;
1113       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dock))
1114         window_type = Type_Dock;
1115       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
1116         window_type = Type_Toolbar;
1117       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_menu))
1118         window_type = Type_Menu;
1119       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_utility))
1120         window_type = Type_Utility;
1121       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_splash))
1122         window_type = Type_Splash;
1123       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dialog))
1124         window_type = Type_Dialog;
1125       else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_normal))
1126         window_type = Type_Normal;
1127       else if (val[i] ==
1128                xatom->getAtom(XAtom::kde_net_wm_window_type_override))
1129         mwm_decorations = 0; // prevent this window from getting any decor
1130     }
1131     delete val;
1132   }
1133     
1134   if (window_type == (WindowType) -1) {
1135     /*
1136      * the window type hint was not set, which means we either classify ourself
1137      * as a normal window or a dialog, depending on if we are a transient.
1138      */
1139     if (isTransient())
1140       window_type = Type_Dialog;
1141     else
1142       window_type = Type_Normal;
1143
1144     return False;
1145   }
1146
1147   return True;
1148 }
1149
1150
1151 void BlackboxWindow::getWMName(void) {
1152   if (xatom->getValue(client.window, XAtom::net_wm_name,
1153                       XAtom::utf8, client.title) &&
1154       !client.title.empty()) {
1155     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1156     return;
1157   }
1158   //fall through to using WM_NAME
1159   if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
1160       && !client.title.empty()) {
1161     xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1162     return;
1163   }
1164   // fall back to an internal default
1165   client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
1166   xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
1167                   client.title);
1168
1169 #ifdef DEBUG_WITH_ID
1170   // the 16 is the 8 chars of the debug text plus the number
1171   char *tmp = new char[client.title.length() + 16];
1172   sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window);
1173   client.title = tmp;
1174   delete tmp;
1175 #endif
1176 }
1177
1178
1179 void BlackboxWindow::getWMIconName(void) {
1180   if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1181                       XAtom::utf8, client.icon_title) && 
1182       !client.icon_title.empty()) {
1183     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1184     return;
1185   }
1186   //fall through to using WM_ICON_NAME
1187   if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1188                       client.icon_title) && 
1189       !client.icon_title.empty()) {
1190     xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1191     return;
1192   }
1193   // fall back to using the main name
1194   client.icon_title = client.title;
1195   xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1196                   client.icon_title);
1197 }
1198
1199
1200 /*
1201  * Retrieve which WM Protocols are supported by the client window.
1202  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1203  * window's decorations and allow the close behavior.
1204  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1205  * this.
1206  */
1207 void BlackboxWindow::getWMProtocols(void) {
1208   Atom *proto;
1209   int num_return = 0;
1210
1211   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1212                       &proto, &num_return)) {
1213     for (int i = 0; i < num_return; ++i) {
1214       if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1215         decorations |= Decor_Close;
1216         functions |= Func_Close;
1217       } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1218         flags.send_focus_message = True;
1219       else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1220         screen->addNetizen(new Netizen(screen, client.window));
1221     }
1222
1223     XFree(proto);
1224   }
1225 }
1226
1227
1228 /*
1229  * Gets the value of the WM_HINTS property.
1230  * If the property is not set, then use a set of default values.
1231  */
1232 void BlackboxWindow::getWMHints(void) {
1233   focus_mode = F_Passive;
1234
1235   // remove from current window group
1236   if (client.window_group) {
1237     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1238     if (group) group->removeWindow(this);
1239   }
1240   client.window_group = None;
1241
1242   XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1243   if (! wmhint) {
1244     return;
1245   }
1246
1247   if (wmhint->flags & InputHint) {
1248     if (wmhint->input == True) {
1249       if (flags.send_focus_message)
1250         focus_mode = F_LocallyActive;
1251     } else {
1252       if (flags.send_focus_message)
1253         focus_mode = F_GloballyActive;
1254       else
1255         focus_mode = F_NoInput;
1256     }
1257   }
1258
1259   if (wmhint->flags & StateHint)
1260     current_state = wmhint->initial_state;
1261
1262   if (wmhint->flags & WindowGroupHint) {
1263     client.window_group = wmhint->window_group;
1264
1265     // add window to the appropriate group
1266     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1267     if (! group) { // no group found, create it!
1268       new BWindowGroup(blackbox, client.window_group);
1269       group = blackbox->searchGroup(client.window_group);
1270     }
1271     if (group)
1272       group->addWindow(this);
1273   }
1274
1275   XFree(wmhint);
1276 }
1277
1278
1279 /*
1280  * Gets the value of the WM_NORMAL_HINTS property.
1281  * If the property is not set, then use a set of default values.
1282  */
1283 void BlackboxWindow::getWMNormalHints(void) {
1284   long icccm_mask;
1285   XSizeHints sizehint;
1286
1287   client.min_width = client.min_height =
1288     client.width_inc = client.height_inc = 1;
1289   client.base_width = client.base_height = 0;
1290   client.win_gravity = NorthWestGravity;
1291 #if 0
1292   client.min_aspect_x = client.min_aspect_y =
1293     client.max_aspect_x = client.max_aspect_y = 1;
1294 #endif
1295
1296   // don't limit the size of a window, the default max width is the biggest
1297   // possible
1298   client.max_width = (unsigned) -1;
1299   client.max_height = (unsigned) -1;
1300
1301
1302   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1303                           &sizehint, &icccm_mask))
1304     return;
1305
1306   client.normal_hint_flags = sizehint.flags;
1307
1308   if (sizehint.flags & PMinSize) {
1309     if (sizehint.min_width >= 0)
1310       client.min_width = sizehint.min_width;
1311     if (sizehint.min_height >= 0)
1312       client.min_height = sizehint.min_height;
1313   }
1314
1315   if (sizehint.flags & PMaxSize) {
1316     if (sizehint.max_width > static_cast<signed>(client.min_width))
1317       client.max_width = sizehint.max_width;
1318     else
1319       client.max_width = client.min_width;
1320
1321     if (sizehint.max_height > static_cast<signed>(client.min_height))
1322       client.max_height = sizehint.max_height;
1323     else
1324       client.max_height = client.min_height;
1325   }
1326
1327   if (sizehint.flags & PResizeInc) {
1328     client.width_inc = sizehint.width_inc;
1329     client.height_inc = sizehint.height_inc;
1330   }
1331
1332 #if 0 // we do not support this at the moment
1333   if (sizehint.flags & PAspect) {
1334     client.min_aspect_x = sizehint.min_aspect.x;
1335     client.min_aspect_y = sizehint.min_aspect.y;
1336     client.max_aspect_x = sizehint.max_aspect.x;
1337     client.max_aspect_y = sizehint.max_aspect.y;
1338   }
1339 #endif
1340
1341   if (sizehint.flags & PBaseSize) {
1342     client.base_width = sizehint.base_width;
1343     client.base_height = sizehint.base_height;
1344   }
1345
1346   if (sizehint.flags & PWinGravity)
1347     client.win_gravity = sizehint.win_gravity;
1348 }
1349
1350
1351 /*
1352  * Gets the NETWM hints for the class' contained window.
1353  */
1354 void BlackboxWindow::getNetWMHints(void) {
1355   unsigned long workspace;
1356
1357   if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1358                       workspace)) {
1359     if (workspace == 0xffffffff)
1360       flags.stuck = True;
1361     else
1362       blackbox_attrib.workspace = workspace;
1363   }
1364
1365   unsigned long *state;
1366   unsigned long num = (unsigned) -1;
1367   if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1368                       num, &state)) {
1369     bool vert = False,
1370          horz = False;
1371     for (unsigned long i = 0; i < num; ++i) {
1372       if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1373         flags.modal = True;
1374       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1375         flags.shaded = True;
1376       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1377         flags.skip_taskbar = True;
1378       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1379         flags.skip_pager = True;
1380       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1381         flags.fullscreen = True;
1382       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1383         setState(IconicState);
1384       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1385         vert = True;
1386       else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1387         horz = True;
1388     }
1389     if (vert && horz)
1390       flags.maximized = 1;
1391     else if (vert)
1392       flags.maximized = 2;
1393     else if (horz)
1394       flags.maximized = 3;
1395
1396     delete [] state;
1397   }
1398 }
1399
1400
1401 /*
1402  * Gets the MWM hints for the class' contained window.
1403  * This is used while initializing the window to its first state, and not
1404  * thereafter.
1405  * Returns: true if the MWM hints are successfully retreived and applied;
1406  * false if they are not.
1407  */
1408 void BlackboxWindow::getMWMHints(void) {
1409   unsigned long num;
1410   MwmHints *mwm_hint;
1411
1412   num = PropMwmHintsElements;
1413   if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1414                         XAtom::motif_wm_hints, num,
1415                         (unsigned long **)&mwm_hint))
1416     return;
1417   if (num < PropMwmHintsElements) {
1418     delete [] mwm_hint;
1419     return;
1420   }
1421
1422   if (mwm_hint->flags & MwmHintsDecorations) {
1423     if (mwm_hint->decorations & MwmDecorAll) {
1424       mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1425                         Decor_Iconify | Decor_Maximize;
1426     } else {
1427       mwm_decorations = 0;
1428
1429       if (mwm_hint->decorations & MwmDecorBorder)
1430         mwm_decorations |= Decor_Border;
1431       if (mwm_hint->decorations & MwmDecorHandle)
1432         mwm_decorations |= Decor_Handle;
1433       if (mwm_hint->decorations & MwmDecorTitle)
1434         mwm_decorations |= Decor_Titlebar;
1435       if (mwm_hint->decorations & MwmDecorIconify)
1436         mwm_decorations |= Decor_Iconify;
1437       if (mwm_hint->decorations & MwmDecorMaximize)
1438         mwm_decorations |= Decor_Maximize;
1439     }
1440   }
1441
1442   if (mwm_hint->flags & MwmHintsFunctions) {
1443     if (mwm_hint->functions & MwmFuncAll) {
1444       functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1445                   Func_Close;
1446     } else {
1447       functions = 0;
1448
1449       if (mwm_hint->functions & MwmFuncResize)
1450         functions |= Func_Resize;
1451       if (mwm_hint->functions & MwmFuncMove)
1452         functions |= Func_Move;
1453       if (mwm_hint->functions & MwmFuncIconify)
1454         functions |= Func_Iconify;
1455       if (mwm_hint->functions & MwmFuncMaximize)
1456         functions |= Func_Maximize;
1457       if (mwm_hint->functions & MwmFuncClose)
1458         functions |= Func_Close;
1459     }
1460   }
1461   delete [] mwm_hint;
1462 }
1463
1464
1465 /*
1466  * Gets the blackbox hints from the class' contained window.
1467  * This is used while initializing the window to its first state, and not
1468  * thereafter.
1469  * Returns: true if the hints are successfully retreived and applied; false if
1470  * they are not.
1471  */
1472 bool BlackboxWindow::getBlackboxHints(void) {
1473   unsigned long num;
1474   BlackboxHints *blackbox_hint;
1475
1476   num = PropBlackboxHintsElements;
1477   if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1478                         XAtom::blackbox_hints, num,
1479                         (unsigned long **)&blackbox_hint))
1480     return False;
1481   if (num < PropBlackboxHintsElements) {
1482     delete [] blackbox_hint;
1483     return False;
1484   }
1485
1486   if (blackbox_hint->flags & AttribShaded)
1487     flags.shaded = (blackbox_hint->attrib & AttribShaded);
1488
1489   if ((blackbox_hint->flags & AttribMaxHoriz) &&
1490       (blackbox_hint->flags & AttribMaxVert))
1491     flags.maximized = (blackbox_hint->attrib &
1492                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1493   else if (blackbox_hint->flags & AttribMaxVert)
1494     flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1495   else if (blackbox_hint->flags & AttribMaxHoriz)
1496     flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1497
1498   if (blackbox_hint->flags & AttribOmnipresent)
1499     flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1500
1501   if (blackbox_hint->flags & AttribWorkspace)
1502     blackbox_attrib.workspace = blackbox_hint->workspace;
1503
1504   // if (blackbox_hint->flags & AttribStack)
1505   //   don't yet have always on top/bottom for blackbox yet... working
1506   //   on that
1507
1508   if (blackbox_hint->flags & AttribDecoration) {
1509     switch (blackbox_hint->decoration) {
1510     case DecorNone:
1511       blackbox_attrib.decoration = DecorNone;
1512       break;
1513
1514     case DecorTiny:
1515     case DecorTool:
1516     case DecorNormal:
1517     default:
1518       // blackbox_attrib.decoration defaults to DecorNormal
1519       break;
1520     }
1521   }
1522   
1523   delete [] blackbox_hint;
1524
1525   return True;
1526 }
1527
1528
1529 void BlackboxWindow::getTransientInfo(void) {
1530   if (client.transient_for &&
1531       client.transient_for != (BlackboxWindow *) ~0ul) {
1532     // reset transient_for in preparation of looking for a new owner
1533     client.transient_for->client.transientList.remove(this);
1534   }
1535
1536   // we have no transient_for until we find a new one
1537   client.transient_for = (BlackboxWindow *) 0;
1538
1539   Window trans_for;
1540   if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1541                              &trans_for)) {
1542     // transient_for hint not set
1543     return;
1544   }
1545
1546   if (trans_for == client.window) {
1547     // wierd client... treat this window as a normal window
1548     return;
1549   }
1550
1551   if (trans_for == None || trans_for == screen->getRootWindow()) {
1552     // this is an undocumented interpretation of the ICCCM. a transient
1553     // associated with None/Root/itself is assumed to be a modal root
1554     // transient.  we don't support the concept of a global transient,
1555     // so we just associate this transient with nothing, and perhaps
1556     // we will add support later for global modality.
1557     client.transient_for = (BlackboxWindow *) ~0ul;
1558     flags.modal = True;
1559     return;
1560   }
1561
1562   client.transient_for = blackbox->searchWindow(trans_for);
1563   if (! client.transient_for &&
1564       client.window_group && trans_for == client.window_group) {
1565     // no direct transient_for, perhaps this is a group transient?
1566     BWindowGroup *group = blackbox->searchGroup(client.window_group);
1567     if (group) client.transient_for = group->find(screen);
1568   }
1569
1570   if (! client.transient_for || client.transient_for == this) {
1571     // no transient_for found, or we have a wierd client that wants to be
1572     // a transient for itself, so we treat this window as a normal window
1573     client.transient_for = (BlackboxWindow*) 0;
1574     return;
1575   }
1576
1577   // Check for a circular transient state: this can lock up Blackbox
1578   // when it tries to find the non-transient window for a transient.
1579   BlackboxWindow *w = this;
1580   while(w->client.transient_for &&
1581         w->client.transient_for != (BlackboxWindow *) ~0ul) {
1582     if(w->client.transient_for == this) {
1583       client.transient_for = (BlackboxWindow*) 0;
1584       break;
1585     }
1586     w = w->client.transient_for;
1587   }
1588
1589   if (client.transient_for &&
1590       client.transient_for != (BlackboxWindow *) ~0ul) {
1591     // register ourselves with our new transient_for
1592     client.transient_for->client.transientList.push_back(this);
1593     flags.stuck = client.transient_for->flags.stuck;
1594   }
1595 }
1596
1597
1598 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1599   if (client.transient_for &&
1600       client.transient_for != (BlackboxWindow*) ~0ul)
1601     return client.transient_for;
1602   return 0;
1603 }
1604
1605
1606 /*
1607  * This function is responsible for updating both the client and the frame
1608  * rectangles.
1609  * According to the ICCCM a client message is not sent for a resize, only a
1610  * move.
1611  */
1612 void BlackboxWindow::configure(int dx, int dy,
1613                                unsigned int dw, unsigned int dh) {
1614   bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1615                      ! flags.moving);
1616
1617   if (dw != frame.rect.width() || dh != frame.rect.height()) {
1618     frame.rect.setRect(dx, dy, dw, dh);
1619     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1620     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1621
1622     if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1623       frame.rect.setPos(0, 0);
1624
1625     client.rect.setCoords(frame.rect.left() + frame.margin.left,
1626                           frame.rect.top() + frame.margin.top,
1627                           frame.rect.right() - frame.margin.right,
1628                           frame.rect.bottom() - frame.margin.bottom);
1629
1630 #ifdef    SHAPE
1631     if (blackbox->hasShapeExtensions() && flags.shaped) {
1632       configureShape();
1633     }
1634 #endif // SHAPE
1635
1636     positionWindows();
1637     decorate();
1638     redrawWindowFrame();
1639   } else {
1640     frame.rect.setPos(dx, dy);
1641
1642     XMoveWindow(blackbox->getXDisplay(), frame.window,
1643                 frame.rect.x(), frame.rect.y());
1644     /*
1645       we may have been called just after an opaque window move, so even though
1646       the old coords match the new ones no ConfigureNotify has been sent yet.
1647       There are likely other times when this will be relevant as well.
1648     */
1649     if (! flags.moving) send_event = True;
1650   }
1651
1652   if (send_event) {
1653     // if moving, the update and event will occur when the move finishes
1654     client.rect.setPos(frame.rect.left() + frame.margin.left,
1655                        frame.rect.top() + frame.margin.top);
1656
1657     XEvent event;
1658     event.type = ConfigureNotify;
1659
1660     event.xconfigure.display = blackbox->getXDisplay();
1661     event.xconfigure.event = client.window;
1662     event.xconfigure.window = client.window;
1663     event.xconfigure.x = client.rect.x();
1664     event.xconfigure.y = client.rect.y();
1665     event.xconfigure.width = client.rect.width();
1666     event.xconfigure.height = client.rect.height();
1667     event.xconfigure.border_width = client.old_bw;
1668     event.xconfigure.above = frame.window;
1669     event.xconfigure.override_redirect = False;
1670
1671     XSendEvent(blackbox->getXDisplay(), client.window, False,
1672                StructureNotifyMask, &event);
1673     screen->updateNetizenConfigNotify(&event);
1674     XFlush(blackbox->getXDisplay());
1675   }
1676 }
1677
1678
1679 #ifdef SHAPE
1680 void BlackboxWindow::configureShape(void) {
1681   XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1682                      frame.margin.left - frame.border_w,
1683                      frame.margin.top - frame.border_w,
1684                      client.window, ShapeBounding, ShapeSet);
1685
1686   int num = 0;
1687   XRectangle xrect[2];
1688
1689   if (decorations & Decor_Titlebar) {
1690     xrect[0].x = xrect[0].y = -frame.border_w;
1691     xrect[0].width = frame.rect.width();
1692     xrect[0].height = frame.title_h + (frame.border_w * 2);
1693     ++num;
1694   }
1695
1696   if (decorations & Decor_Handle) {
1697     xrect[1].x = -frame.border_w;
1698     xrect[1].y = frame.rect.height() - frame.margin.bottom +
1699                  frame.mwm_border_w - frame.border_w;
1700     xrect[1].width = frame.rect.width();
1701     xrect[1].height = frame.handle_h + (frame.border_w * 2);
1702     ++num;
1703   }
1704
1705   XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1706                           ShapeBounding, 0, 0, xrect, num,
1707                           ShapeUnion, Unsorted);
1708 }
1709
1710
1711 void BlackboxWindow::clearShape(void) {
1712   XShapeCombineMask(blackbox->getXDisplay(), frame.window, ShapeBounding,
1713                     frame.margin.left - frame.border_w,
1714                     frame.margin.top - frame.border_w,
1715                     None, ShapeSet);
1716 }
1717 #endif // SHAPE
1718
1719
1720 bool BlackboxWindow::setInputFocus(void) {
1721   if (flags.focused) return True;
1722
1723   assert(flags.stuck ||  // window must be on the current workspace or sticky
1724          blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1725
1726   /*
1727      We only do this check for normal windows and dialogs because other windows
1728      do this on purpose, such as kde's kicker, and we don't want to go moving
1729      it.
1730   */
1731   if (window_type == Type_Normal || window_type == Type_Dialog)
1732     if (! frame.rect.intersects(screen->getRect())) {
1733       // client is outside the screen, move it to the center
1734       configure((screen->getWidth() - frame.rect.width()) / 2,
1735                 (screen->getHeight() - frame.rect.height()) / 2,
1736                 frame.rect.width(), frame.rect.height());
1737     }
1738
1739   if (client.transientList.size() > 0) {
1740     // transfer focus to any modal transients
1741     BlackboxWindowList::iterator it, end = client.transientList.end();
1742     for (it = client.transientList.begin(); it != end; ++it)
1743       if ((*it)->flags.modal) return (*it)->setInputFocus();
1744   }
1745
1746   bool ret = True;
1747   if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1748     XSetInputFocus(blackbox->getXDisplay(), client.window,
1749                    RevertToPointerRoot, CurrentTime);
1750   } else {
1751     /* we could set the focus to none, since the window doesn't accept focus,
1752      * but we shouldn't set focus to nothing since this would surely make
1753      * someone angry
1754      */
1755     ret = False;
1756   }
1757
1758   if (flags.send_focus_message) {
1759     XEvent ce;
1760     ce.xclient.type = ClientMessage;
1761     ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1762     ce.xclient.display = blackbox->getXDisplay();
1763     ce.xclient.window = client.window;
1764     ce.xclient.format = 32;
1765     ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1766     ce.xclient.data.l[1] = blackbox->getLastTime();
1767     ce.xclient.data.l[2] = 0l;
1768     ce.xclient.data.l[3] = 0l;
1769     ce.xclient.data.l[4] = 0l;
1770     XSendEvent(blackbox->getXDisplay(), client.window, False,
1771                NoEventMask, &ce);
1772     XFlush(blackbox->getXDisplay());
1773   }
1774
1775   return ret;
1776 }
1777
1778
1779 void BlackboxWindow::iconify(void) {
1780   if (flags.iconic || ! (functions & Func_Iconify)) return;
1781
1782   // We don't need to worry about resizing because resizing always grabs the X
1783   // server. This should only ever happen if using opaque moving.
1784   if (flags.moving)
1785     endMove();
1786     
1787   if (windowmenu) windowmenu->hide();
1788
1789   /*
1790    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1791    * we need to clear the event mask on client.window for a split second.
1792    * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1793    * split second, leaving us with a ghost window... so, we need to do this
1794    * while the X server is grabbed
1795    */
1796   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1797                              StructureNotifyMask;
1798   XGrabServer(blackbox->getXDisplay());
1799   XSelectInput(blackbox->getXDisplay(), client.window,
1800                event_mask & ~StructureNotifyMask);
1801   XUnmapWindow(blackbox->getXDisplay(), client.window);
1802   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1803   XUngrabServer(blackbox->getXDisplay());
1804
1805   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1806   flags.visible = False;
1807   flags.iconic = True;
1808
1809   setState(IconicState);
1810
1811   screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1812   if (flags.stuck) {
1813     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1814       if (i != blackbox_attrib.workspace)
1815         screen->getWorkspace(i)->removeWindow(this, True);
1816   }
1817
1818   if (isTransient()) {
1819     if (client.transient_for != (BlackboxWindow *) ~0ul &&
1820         ! client.transient_for->flags.iconic) {
1821       // iconify our transient_for
1822       client.transient_for->iconify();
1823     }
1824   }
1825
1826   screen->addIcon(this);
1827
1828   if (client.transientList.size() > 0) {
1829     // iconify all transients
1830     BlackboxWindowList::iterator it, end = client.transientList.end();
1831     for (it = client.transientList.begin(); it != end; ++it) {
1832       if (! (*it)->flags.iconic) (*it)->iconify();
1833     }
1834   }
1835   screen->updateStackingList();
1836 }
1837
1838
1839 void BlackboxWindow::show(void) {
1840   flags.visible = True;
1841   flags.iconic = False;
1842
1843   current_state = (flags.shaded) ? IconicState : NormalState;
1844   setState(current_state);
1845
1846   XMapWindow(blackbox->getXDisplay(), client.window);
1847   XMapSubwindows(blackbox->getXDisplay(), frame.window);
1848   XMapWindow(blackbox->getXDisplay(), frame.window);
1849
1850 #if 0
1851   int real_x, real_y;
1852   Window child;
1853   XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1854                         screen->getRootWindow(),
1855                         0, 0, &real_x, &real_y, &child);
1856   fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1857           client.rect.left(), client.rect.top(), real_x, real_y);
1858   assert(client.rect.left() == real_x && client.rect.top() == real_y);
1859 #endif
1860 }
1861
1862
1863 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1864   if (flags.iconic || reassoc)
1865     screen->reassociateWindow(this, BSENTINEL, False);
1866   else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1867     return;
1868
1869   show();
1870
1871   // reassociate and deiconify all transients
1872   if (reassoc && client.transientList.size() > 0) {
1873     BlackboxWindowList::iterator it, end = client.transientList.end();
1874     for (it = client.transientList.begin(); it != end; ++it)
1875       (*it)->deiconify(True, False);
1876   }
1877
1878   if (raise)
1879     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1880 }
1881
1882
1883 void BlackboxWindow::close(void) {
1884   if (! (functions & Func_Close)) return;
1885
1886   XEvent ce;
1887   ce.xclient.type = ClientMessage;
1888   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1889   ce.xclient.display = blackbox->getXDisplay();
1890   ce.xclient.window = client.window;
1891   ce.xclient.format = 32;
1892   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1893   ce.xclient.data.l[1] = CurrentTime;
1894   ce.xclient.data.l[2] = 0l;
1895   ce.xclient.data.l[3] = 0l;
1896   ce.xclient.data.l[4] = 0l;
1897   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1898   XFlush(blackbox->getXDisplay());
1899 }
1900
1901
1902 void BlackboxWindow::withdraw(void) {
1903   // We don't need to worry about resizing because resizing always grabs the X
1904   // server. This should only ever happen if using opaque moving.
1905   if (flags.moving)
1906     endMove();
1907     
1908   flags.visible = False;
1909   flags.iconic = False;
1910
1911   setState(current_state);
1912
1913   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1914
1915   XGrabServer(blackbox->getXDisplay());
1916
1917   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1918                              StructureNotifyMask;
1919   XSelectInput(blackbox->getXDisplay(), client.window,
1920                event_mask & ~StructureNotifyMask);
1921   XUnmapWindow(blackbox->getXDisplay(), client.window);
1922   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1923
1924   XUngrabServer(blackbox->getXDisplay());
1925
1926   if (windowmenu) windowmenu->hide();
1927 }
1928
1929
1930 void BlackboxWindow::maximize(unsigned int button) {
1931   if (! (functions & Func_Maximize)) return;
1932
1933   // We don't need to worry about resizing because resizing always grabs the X
1934   // server. This should only ever happen if using opaque moving.
1935   if (flags.moving)
1936     endMove();
1937
1938   // handle case where menu is open then the max button is used instead
1939   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1940
1941   if (flags.maximized) {
1942     flags.maximized = 0;
1943
1944     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1945     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1946
1947     /*
1948       when a resize finishes, maximize(0) is called to clear any maximization
1949       flags currently set.  Otherwise it still thinks it is maximized.
1950       so we do not need to call configure() because resizing will handle it
1951     */
1952     if (! flags.resizing)
1953       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1954                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1955
1956     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1957     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1958
1959     redrawAllButtons(); // in case it is not called in configure()
1960     setState(current_state);
1961     return;
1962   }
1963
1964   blackbox_attrib.premax_x = frame.rect.x();
1965   blackbox_attrib.premax_y = frame.rect.y();
1966   blackbox_attrib.premax_w = frame.rect.width();
1967   // use client.rect so that clients can be restored even if shaded
1968   blackbox_attrib.premax_h =
1969     client.rect.height() + frame.margin.top + frame.margin.bottom;
1970
1971 #ifdef    XINERAMA
1972   if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1973     // find the area to use
1974     RectList availableAreas = screen->allAvailableAreas();
1975     RectList::iterator it, end = availableAreas.end();
1976
1977     for (it = availableAreas.begin(); it != end; ++it)
1978       if (it->intersects(frame.rect)) break;
1979     if (it == end) // the window isn't inside an area
1980       it = availableAreas.begin(); // so just default to the first one
1981
1982     frame.changing = *it;
1983   } else
1984 #endif // XINERAMA
1985   frame.changing = screen->availableArea();
1986
1987   switch(button) {
1988   case 1:
1989     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1990     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1991     break;
1992
1993   case 2:
1994     blackbox_attrib.flags |= AttribMaxVert;
1995     blackbox_attrib.attrib |= AttribMaxVert;
1996
1997     frame.changing.setX(frame.rect.x());
1998     frame.changing.setWidth(frame.rect.width());
1999     break;
2000
2001   case 3:
2002     blackbox_attrib.flags |= AttribMaxHoriz;
2003     blackbox_attrib.attrib |= AttribMaxHoriz;
2004
2005     frame.changing.setY(frame.rect.y());
2006     frame.changing.setHeight(frame.rect.height());
2007     break;
2008   }
2009
2010   constrain(TopLeft);
2011
2012   if (flags.shaded) {
2013     blackbox_attrib.flags ^= AttribShaded;
2014     blackbox_attrib.attrib ^= AttribShaded;
2015     flags.shaded = False;
2016   }
2017
2018   flags.maximized = button;
2019
2020   configure(frame.changing.x(), frame.changing.y(),
2021             frame.changing.width(), frame.changing.height());
2022   if (flags.focused)
2023     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2024   redrawAllButtons(); // in case it is not called in configure()
2025   setState(current_state);
2026 }
2027
2028
2029 // re-maximizes the window to take into account availableArea changes
2030 void BlackboxWindow::remaximize(void) {
2031   if (flags.shaded) {
2032     // we only update the window's attributes otherwise we lose the shade bit
2033     switch(flags.maximized) {
2034     case 1:
2035       blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
2036       blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
2037       break;
2038
2039     case 2:
2040       blackbox_attrib.flags |= AttribMaxVert;
2041       blackbox_attrib.attrib |= AttribMaxVert;
2042       break;
2043
2044     case 3:
2045       blackbox_attrib.flags |= AttribMaxHoriz;
2046       blackbox_attrib.attrib |= AttribMaxHoriz;
2047       break;
2048     }
2049     return;
2050   }
2051
2052   // save the original dimensions because maximize will wipe them out
2053   int premax_x = blackbox_attrib.premax_x,
2054     premax_y = blackbox_attrib.premax_y,
2055     premax_w = blackbox_attrib.premax_w,
2056     premax_h = blackbox_attrib.premax_h;
2057
2058   unsigned int button = flags.maximized;
2059   flags.maximized = 0; // trick maximize() into working
2060   maximize(button);
2061
2062   // restore saved values
2063   blackbox_attrib.premax_x = premax_x;
2064   blackbox_attrib.premax_y = premax_y;
2065   blackbox_attrib.premax_w = premax_w;
2066   blackbox_attrib.premax_h = premax_h;
2067 }
2068
2069
2070 void BlackboxWindow::setWorkspace(unsigned int n) {
2071   blackbox_attrib.flags |= AttribWorkspace;
2072   blackbox_attrib.workspace = n;
2073   if (n == BSENTINEL) { // iconified window
2074     /*
2075        we set the workspace to 'all workspaces' so that taskbars will show the
2076        window. otherwise, it made uniconifying a window imposible without the
2077        blackbox workspace menu
2078     */
2079     n = 0xffffffff;
2080   }
2081   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
2082 }
2083
2084
2085 void BlackboxWindow::shade(void) {
2086   if (flags.shaded) {
2087     XResizeWindow(blackbox->getXDisplay(), frame.window,
2088                   frame.inside_w, frame.inside_h);
2089     flags.shaded = False;
2090     blackbox_attrib.flags ^= AttribShaded;
2091     blackbox_attrib.attrib ^= AttribShaded;
2092
2093     setState(NormalState);
2094
2095     // set the frame rect to the normal size
2096     frame.rect.setHeight(client.rect.height() + frame.margin.top +
2097                          frame.margin.bottom);
2098   } else {
2099     if (! (decorations & Decor_Titlebar))
2100       return; // can't shade it without a titlebar!
2101
2102     XResizeWindow(blackbox->getXDisplay(), frame.window,
2103                   frame.inside_w, frame.title_h);
2104     flags.shaded = True;
2105     blackbox_attrib.flags |= AttribShaded;
2106     blackbox_attrib.attrib |= AttribShaded;
2107
2108     setState(IconicState);
2109
2110     // set the frame rect to the shaded size
2111     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
2112   }
2113 }
2114
2115
2116 /*
2117  * (Un)Sticks a window and its relatives.
2118  */
2119 void BlackboxWindow::stick(void) {
2120   if (flags.stuck) {
2121     blackbox_attrib.flags ^= AttribOmnipresent;
2122     blackbox_attrib.attrib ^= AttribOmnipresent;
2123
2124     flags.stuck = False;
2125     
2126     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2127       if (i != blackbox_attrib.workspace)
2128         screen->getWorkspace(i)->removeWindow(this, True);
2129
2130     if (! flags.iconic)
2131       screen->reassociateWindow(this, BSENTINEL, True);
2132     // temporary fix since sticky windows suck. set the hint to what we
2133     // actually hold in our data.
2134     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2135                     blackbox_attrib.workspace);
2136
2137     setState(current_state);
2138   } else {
2139     flags.stuck = True;
2140
2141     blackbox_attrib.flags |= AttribOmnipresent;
2142     blackbox_attrib.attrib |= AttribOmnipresent;
2143
2144     // temporary fix since sticky windows suck. set the hint to a different
2145     // value than that contained in the class' data.
2146     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2147                     0xffffffff);
2148     
2149     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2150       if (i != blackbox_attrib.workspace)
2151         screen->getWorkspace(i)->addWindow(this, False, True);
2152
2153     setState(current_state);
2154   }
2155
2156   redrawAllButtons();
2157   
2158   // go up the chain
2159   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2160       client.transient_for->isStuck() != flags.stuck)
2161     client.transient_for->stick();
2162   // go down the chain
2163   BlackboxWindowList::iterator it;
2164   const BlackboxWindowList::iterator end = client.transientList.end();
2165   for (it = client.transientList.begin(); it != end; ++it)
2166     if ((*it)->isStuck() != flags.stuck)
2167       (*it)->stick();
2168 }
2169
2170
2171 void BlackboxWindow::redrawWindowFrame(void) const {
2172   if (decorations & Decor_Titlebar) {
2173     if (flags.focused) {
2174       if (frame.ftitle)
2175         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2176                                    frame.title, frame.ftitle);
2177       else
2178         XSetWindowBackground(blackbox->getXDisplay(),
2179                              frame.title, frame.ftitle_pixel);
2180     } else {
2181       if (frame.utitle)
2182         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2183                                    frame.title, frame.utitle);
2184       else
2185         XSetWindowBackground(blackbox->getXDisplay(),
2186                              frame.title, frame.utitle_pixel);
2187     }
2188     XClearWindow(blackbox->getXDisplay(), frame.title);
2189
2190     redrawLabel();
2191     redrawAllButtons();
2192   }
2193
2194   if (decorations & Decor_Handle) {
2195     if (flags.focused) {
2196       if (frame.fhandle)
2197         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2198                                    frame.handle, frame.fhandle);
2199       else
2200         XSetWindowBackground(blackbox->getXDisplay(),
2201                              frame.handle, frame.fhandle_pixel);
2202
2203       if (frame.fgrip) {
2204         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2205                                    frame.left_grip, frame.fgrip);
2206         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2207                                    frame.right_grip, frame.fgrip);
2208       } else {
2209         XSetWindowBackground(blackbox->getXDisplay(),
2210                              frame.left_grip, frame.fgrip_pixel);
2211         XSetWindowBackground(blackbox->getXDisplay(),
2212                              frame.right_grip, frame.fgrip_pixel);
2213       }
2214     } else {
2215       if (frame.uhandle)
2216         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2217                                    frame.handle, frame.uhandle);
2218       else
2219         XSetWindowBackground(blackbox->getXDisplay(),
2220                              frame.handle, frame.uhandle_pixel);
2221
2222       if (frame.ugrip) {
2223         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2224                                    frame.left_grip, frame.ugrip);
2225         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2226                                    frame.right_grip, frame.ugrip);
2227       } else {
2228         XSetWindowBackground(blackbox->getXDisplay(),
2229                              frame.left_grip, frame.ugrip_pixel);
2230         XSetWindowBackground(blackbox->getXDisplay(),
2231                              frame.right_grip, frame.ugrip_pixel);
2232       }
2233     }
2234     XClearWindow(blackbox->getXDisplay(), frame.handle);
2235     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2236     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2237   }
2238
2239   if (decorations & Decor_Border) {
2240     if (flags.focused)
2241       XSetWindowBorder(blackbox->getXDisplay(),
2242                        frame.plate, frame.fborder_pixel);
2243     else
2244       XSetWindowBorder(blackbox->getXDisplay(),
2245                        frame.plate, frame.uborder_pixel);
2246   }
2247 }
2248
2249
2250 void BlackboxWindow::setFocusFlag(bool focus) {
2251   // only focus a window if it is visible
2252   if (focus && ! flags.visible)
2253     return;
2254
2255   flags.focused = focus;
2256
2257   redrawWindowFrame();
2258
2259   if (flags.focused)
2260     blackbox->setFocusedWindow(this);
2261  
2262   if (! flags.iconic) {
2263     // iconic windows arent in a workspace menu!
2264     if (flags.stuck)
2265       screen->getCurrentWorkspace()->setFocused(this, isFocused());
2266     else
2267       screen->getWorkspace(blackbox_attrib.workspace)->
2268         setFocused(this, flags.focused);
2269   }
2270 }
2271
2272
2273 void BlackboxWindow::installColormap(bool install) {
2274   int i = 0, ncmap = 0;
2275   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2276                                             client.window, &ncmap);
2277   if (cmaps) {
2278     XWindowAttributes wattrib;
2279     if (XGetWindowAttributes(blackbox->getXDisplay(),
2280                              client.window, &wattrib)) {
2281       if (install) {
2282         // install the window's colormap
2283         for (i = 0; i < ncmap; i++) {
2284           if (*(cmaps + i) == wattrib.colormap)
2285             // this window is using an installed color map... do not install
2286             install = False;
2287         }
2288         // otherwise, install the window's colormap
2289         if (install)
2290           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2291       } else {
2292         // uninstall the window's colormap
2293         for (i = 0; i < ncmap; i++) {
2294           if (*(cmaps + i) == wattrib.colormap)
2295             // we found the colormap to uninstall
2296             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2297         }
2298       }
2299     }
2300
2301     XFree(cmaps);
2302   }
2303 }
2304
2305
2306 void BlackboxWindow::setAllowedActions(void) {
2307   Atom actions[7];
2308   int num = 0;
2309   
2310   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2311   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2312   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2313
2314   if (functions & Func_Move)
2315     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2316   if (functions & Func_Resize)
2317     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2318   if (functions & Func_Maximize) {
2319     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2320     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2321   }
2322
2323   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2324                   actions, num);
2325 }
2326
2327
2328 void BlackboxWindow::setState(unsigned long new_state) {
2329   current_state = new_state;
2330
2331   unsigned long state[2];
2332   state[0] = current_state;
2333   state[1] = None;
2334   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2335  
2336   xatom->setValue(client.window, XAtom::blackbox_attributes,
2337                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2338                   PropBlackboxAttributesElements);
2339
2340   Atom netstate[8];
2341   int num = 0;
2342   if (flags.modal)
2343     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2344   if (flags.shaded)
2345     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2346   if (flags.iconic)
2347     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2348   if (flags.skip_taskbar)
2349     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2350   if (flags.skip_pager)
2351     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2352   if (flags.fullscreen)
2353     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2354   if (flags.maximized == 1 || flags.maximized == 2)
2355     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2356   if (flags.maximized == 1 || flags.maximized == 3)
2357     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2358   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2359                   netstate, num);
2360 }
2361
2362
2363 bool BlackboxWindow::getState(void) {
2364   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2365                              current_state);
2366   if (! ret) current_state = 0;
2367   return ret;
2368 }
2369
2370
2371 void BlackboxWindow::restoreAttributes(void) {
2372   unsigned long num = PropBlackboxAttributesElements;
2373   BlackboxAttributes *net;
2374   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2375                         XAtom::blackbox_attributes, num,
2376                         (unsigned long **)&net))
2377     return;
2378   if (num < PropBlackboxAttributesElements) {
2379     delete [] net;
2380     return;
2381   }
2382
2383   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2384     flags.shaded = False;
2385     unsigned long orig_state = current_state;
2386     shade();
2387
2388     /*
2389       At this point in the life of a window, current_state should only be set
2390       to IconicState if the window was an *icon*, not if it was shaded.
2391     */
2392     if (orig_state != IconicState)
2393       current_state = WithdrawnState;
2394  }
2395
2396   if (net->workspace != screen->getCurrentWorkspaceID() &&
2397       net->workspace < screen->getWorkspaceCount())
2398     screen->reassociateWindow(this, net->workspace, True);
2399
2400   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2401       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2402     // set to WithdrawnState so it will be mapped on the new workspace
2403     if (current_state == NormalState) current_state = WithdrawnState;
2404   } else if (current_state == WithdrawnState) {
2405     // the window is on this workspace and is Withdrawn, so it is waiting to
2406     // be mapped
2407     current_state = NormalState;
2408   }
2409
2410   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2411       ! flags.stuck) {
2412     stick();
2413
2414     // if the window was on another workspace, it was going to be hidden. this
2415     // specifies that the window should be mapped since it is sticky.
2416     if (current_state == WithdrawnState) current_state = NormalState;
2417   }
2418
2419   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2420     int x = net->premax_x, y = net->premax_y;
2421     unsigned int w = net->premax_w, h = net->premax_h;
2422     flags.maximized = 0;
2423
2424     unsigned int m = 0;
2425     if ((net->flags & AttribMaxHoriz) &&
2426         (net->flags & AttribMaxVert))
2427       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2428     else if (net->flags & AttribMaxVert)
2429       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2430     else if (net->flags & AttribMaxHoriz)
2431       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2432
2433     if (m) maximize(m);
2434
2435     blackbox_attrib.premax_x = x;
2436     blackbox_attrib.premax_y = y;
2437     blackbox_attrib.premax_w = w;
2438     blackbox_attrib.premax_h = h;
2439   }
2440
2441   if (net->flags & AttribDecoration) {
2442     switch (net->decoration) {
2443     case DecorNone:
2444       enableDecor(False);
2445       break;
2446
2447     /* since tools only let you toggle this anyways, we'll just make that all
2448        it supports for now.
2449      */
2450     default:
2451     case DecorNormal:
2452     case DecorTiny:
2453     case DecorTool:
2454       enableDecor(True);
2455       break;
2456     }
2457   }
2458
2459   // with the state set it will then be the map event's job to read the
2460   // window's state and behave accordingly
2461
2462   delete [] net;
2463 }
2464
2465
2466 /*
2467  * Positions the Rect r according the the client window position and
2468  * window gravity.
2469  */
2470 void BlackboxWindow::applyGravity(Rect &r) {
2471   // apply horizontal window gravity
2472   switch (client.win_gravity) {
2473   default:
2474   case NorthWestGravity:
2475   case SouthWestGravity:
2476   case WestGravity:
2477     r.setX(client.rect.x());
2478     break;
2479
2480   case NorthGravity:
2481   case SouthGravity:
2482   case CenterGravity:
2483     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2484     break;
2485
2486   case NorthEastGravity:
2487   case SouthEastGravity:
2488   case EastGravity:
2489     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2490     break;
2491
2492   case ForgetGravity:
2493   case StaticGravity:
2494     r.setX(client.rect.x() - frame.margin.left);
2495     break;
2496   }
2497
2498   // apply vertical window gravity
2499   switch (client.win_gravity) {
2500   default:
2501   case NorthWestGravity:
2502   case NorthEastGravity:
2503   case NorthGravity:
2504     r.setY(client.rect.y());
2505     break;
2506
2507   case CenterGravity:
2508   case EastGravity:
2509   case WestGravity:
2510     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2511     break;
2512
2513   case SouthWestGravity:
2514   case SouthEastGravity:
2515   case SouthGravity:
2516     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2517     break;
2518
2519   case ForgetGravity:
2520   case StaticGravity:
2521     r.setY(client.rect.y() - frame.margin.top);
2522     break;
2523   }
2524 }
2525
2526
2527 /*
2528  * The reverse of the applyGravity function.
2529  *
2530  * Positions the Rect r according to the frame window position and
2531  * window gravity.
2532  */
2533 void BlackboxWindow::restoreGravity(Rect &r) {
2534   // restore horizontal window gravity
2535   switch (client.win_gravity) {
2536   default:
2537   case NorthWestGravity:
2538   case SouthWestGravity:
2539   case WestGravity:
2540     r.setX(frame.rect.x());
2541     break;
2542
2543   case NorthGravity:
2544   case SouthGravity:
2545   case CenterGravity:
2546     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2547     break;
2548
2549   case NorthEastGravity:
2550   case SouthEastGravity:
2551   case EastGravity:
2552     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2553     break;
2554
2555   case ForgetGravity:
2556   case StaticGravity:
2557     r.setX(frame.rect.x() + frame.margin.left);
2558     break;
2559   }
2560
2561   // restore vertical window gravity
2562   switch (client.win_gravity) {
2563   default:
2564   case NorthWestGravity:
2565   case NorthEastGravity:
2566   case NorthGravity:
2567     r.setY(frame.rect.y());
2568     break;
2569
2570   case CenterGravity:
2571   case EastGravity:
2572   case WestGravity:
2573     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2574     break;
2575
2576   case SouthWestGravity:
2577   case SouthEastGravity:
2578   case SouthGravity:
2579     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2580     break;
2581
2582   case ForgetGravity:
2583   case StaticGravity:
2584     r.setY(frame.rect.y() + frame.margin.top);
2585     break;
2586   }
2587 }
2588
2589
2590 void BlackboxWindow::redrawLabel(void) const {
2591   if (flags.focused) {
2592     if (frame.flabel)
2593       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2594                                  frame.label, frame.flabel);
2595     else
2596       XSetWindowBackground(blackbox->getXDisplay(),
2597                            frame.label, frame.flabel_pixel);
2598   } else {
2599     if (frame.ulabel)
2600       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2601                                  frame.label, frame.ulabel);
2602     else
2603       XSetWindowBackground(blackbox->getXDisplay(),
2604                            frame.label, frame.ulabel_pixel);
2605   }
2606   XClearWindow(blackbox->getXDisplay(), frame.label);
2607
2608   WindowStyle *style = screen->getWindowStyle();
2609
2610   int pos = frame.bevel_w * 2;
2611   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2612   style->font->drawString(frame.label, pos, 1,
2613                           (flags.focused ? style->l_text_focus :
2614                            style->l_text_unfocus),
2615                           client.title);
2616 }
2617
2618
2619 void BlackboxWindow::redrawAllButtons(void) const {
2620   if (frame.iconify_button) redrawIconifyButton(False);
2621   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2622   if (frame.close_button) redrawCloseButton(False);
2623   if (frame.stick_button) redrawStickyButton(flags.stuck);
2624 }
2625
2626
2627 void BlackboxWindow::redrawButton(bool pressed, Window win,
2628                                   Pixmap fppix, unsigned long fppixel,
2629                                   Pixmap uppix, unsigned long uppixel,
2630                                   Pixmap fpix, unsigned long fpixel,
2631                                   Pixmap upix, unsigned long upixel) const {
2632   Pixmap p;
2633   unsigned long pix;
2634   
2635   if (pressed) {
2636     if (flags.focused) {
2637       p = fppix;
2638       pix = fppixel;
2639     } else {
2640       p = uppix;
2641       pix = uppixel;
2642     }
2643   } else {
2644     if (flags.focused) {
2645       p = fpix;
2646       pix = fpixel;
2647     } else {
2648       p = upix;
2649       pix = upixel;
2650     }
2651   }
2652   
2653   if (p)
2654     XSetWindowBackgroundPixmap(blackbox->getXDisplay(), win, p);
2655   else
2656     XSetWindowBackground(blackbox->getXDisplay(), win, pix);
2657
2658 }
2659
2660 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2661   redrawButton(pressed, frame.iconify_button, 
2662                frame.pfbutton, frame.pfbutton_pixel,
2663                frame.pubutton, frame.pubutton_pixel,
2664                frame.fbutton, frame.fbutton_pixel,
2665                frame.ubutton, frame.ubutton_pixel);
2666
2667   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2668   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2669              screen->getWindowStyle()->b_pic_unfocus);
2670
2671 #ifdef    BITMAPBUTTONS
2672   PixmapMask pm = screen->getWindowStyle()->icon_button;
2673   
2674   if (screen->getWindowStyle()->icon_button.mask != None) {
2675     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2676     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2677                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2678
2679     XFillRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2680                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2681                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2682
2683     XSetClipMask(blackbox->getXDisplay(), pen.gc(), None);
2684     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0);
2685   } else {
2686 #endif // BITMAPBUTTONS
2687     XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2688                    2, (frame.button_w - 5), (frame.button_w - 5), 2);
2689 #ifdef    BITMAPBUTTONS
2690   }
2691 #endif // BITMAPBUTTONS
2692 }
2693
2694
2695 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2696   redrawButton(pressed, frame.maximize_button, 
2697                frame.pfbutton, frame.pfbutton_pixel,
2698                frame.pubutton, frame.pubutton_pixel,
2699                frame.fbutton, frame.fbutton_pixel,
2700                frame.ubutton, frame.ubutton_pixel);
2701
2702   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2703
2704   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2705            screen->getWindowStyle()->b_pic_unfocus);
2706   
2707 #ifdef    BITMAPBUTTONS
2708   PixmapMask pm = screen->getWindowStyle()->max_button;
2709     
2710   if (pm.mask != None) {
2711     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2712     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2713                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2714
2715     XFillRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2716                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2717                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2718     
2719     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2720     XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2721   } else {
2722 #endif // BITMAPBUTTONS
2723     XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2724                    2, 2, (frame.button_w - 5), (frame.button_w - 5));
2725     XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2726               2, 3, (frame.button_w - 3), 3);
2727 #ifdef    BITMAPBUTTONS
2728   }
2729 #endif // BITMAPBUTTONS
2730 }
2731
2732
2733 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2734   redrawButton(pressed, frame.close_button, 
2735                frame.pfbutton, frame.pfbutton_pixel,
2736                frame.pubutton, frame.pubutton_pixel,
2737                frame.fbutton, frame.fbutton_pixel,
2738                frame.ubutton, frame.ubutton_pixel);
2739
2740   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2741
2742   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2743            screen->getWindowStyle()->b_pic_unfocus);
2744   
2745 #ifdef    BITMAPBUTTONS
2746   PixmapMask pm = screen->getWindowStyle()->close_button;
2747
2748   if (pm.mask != None) {
2749     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2750     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2751                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2752     
2753     XFillRectangle(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2754                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2755                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2756
2757   
2758     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2759     XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2760   } else {
2761 #endif // BITMAPBUTTONS
2762     XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2763               2, 2, (frame.button_w - 3), (frame.button_w - 3));
2764     XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2765               2, (frame.button_w - 3), (frame.button_w - 3), 2);
2766 #ifdef    BITMAPBUTTONS
2767   }
2768 #endif // BITMAPBUTTONS
2769 }
2770
2771 void BlackboxWindow::redrawStickyButton(bool pressed) const {
2772   redrawButton(pressed, frame.stick_button, 
2773                frame.pfbutton, frame.pfbutton_pixel,
2774                frame.pubutton, frame.pubutton_pixel,
2775                frame.fbutton, frame.fbutton_pixel,
2776                frame.ubutton, frame.ubutton_pixel);
2777
2778   XClearWindow(blackbox->getXDisplay(), frame.stick_button);
2779
2780   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2781            screen->getWindowStyle()->b_pic_unfocus);
2782   
2783 #ifdef    BITMAPBUTTONS
2784   PixmapMask pm = screen->getWindowStyle()->stick_button;
2785
2786   if (pm.mask != None) {
2787     XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2788     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2789                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2790     
2791     XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2792                    (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2793                    (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2794
2795   
2796     XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2797     XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2798   } else {
2799 #endif // BITMAPBUTTONS
2800     XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2801                    frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 );
2802 #ifdef    BITMAPBUTTONS
2803   }
2804 #endif
2805 }
2806
2807 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2808   if (re->window != client.window)
2809     return;
2810
2811 #ifdef    DEBUG
2812   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2813           client.window);
2814 #endif // DEBUG
2815
2816   /*
2817      Even though the window wants to be shown, if it is not on the current
2818      workspace, then it isn't going to be shown right now.
2819   */
2820   if (! flags.stuck &&
2821       blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2822       blackbox_attrib.workspace < screen->getWorkspaceCount())
2823     if (current_state == NormalState) current_state = WithdrawnState;
2824
2825   switch (current_state) {
2826   case IconicState:
2827     iconify();
2828     break;
2829
2830   case WithdrawnState:
2831     withdraw();
2832     break;
2833
2834   case NormalState:
2835   case InactiveState:
2836   case ZoomState:
2837   default:
2838     show();
2839     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2840     if (isNormal()) {
2841       if (! blackbox->isStartup()) {
2842         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2843         if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2844                                      getTransientFor()->isFocused())) {
2845           setInputFocus();
2846         }
2847         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2848           int x, y, rx, ry;
2849           Window c, r;
2850           unsigned int m;
2851           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2852                         &r, &c, &rx, &ry, &x, &y, &m);
2853           beginMove(rx, ry);
2854         }
2855       }
2856     }
2857     break;
2858   }
2859 }
2860
2861
2862 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2863   if (ue->window != client.window)
2864     return;
2865
2866 #ifdef    DEBUG
2867   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2868           client.window);
2869 #endif // DEBUG
2870
2871   screen->unmanageWindow(this, False);
2872 }
2873
2874
2875 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2876   if (de->window != client.window)
2877     return;
2878
2879 #ifdef    DEBUG
2880   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2881           client.window);
2882 #endif // DEBUG
2883
2884   screen->unmanageWindow(this, False);
2885 }
2886
2887
2888 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2889   if (re->window != client.window || re->parent == frame.plate)
2890     return;
2891
2892 #ifdef    DEBUG
2893   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2894           "0x%lx.\n", client.window, re->parent);
2895 #endif // DEBUG
2896
2897   XEvent ev;
2898   ev.xreparent = *re;
2899   XPutBackEvent(blackbox->getXDisplay(), &ev);
2900   screen->unmanageWindow(this, True);
2901 }
2902
2903
2904 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2905   if (pe->state == PropertyDelete || ! validateClient())
2906     return;
2907
2908 #if 0
2909   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2910           client.window);
2911 #endif
2912
2913   switch(pe->atom) {
2914   case XA_WM_CLASS:
2915   case XA_WM_CLIENT_MACHINE:
2916   case XA_WM_COMMAND:
2917     break;
2918
2919   case XA_WM_TRANSIENT_FOR: {
2920     bool s = flags.stuck;
2921     
2922     // determine if this is a transient window
2923     getTransientInfo();
2924
2925     if (flags.stuck != s) stick();
2926
2927     // adjust the window decorations based on transience
2928     if (isTransient()) {
2929       functions &= ~Func_Maximize;
2930       setAllowedActions();
2931       setupDecor();
2932     }
2933
2934     reconfigure();
2935   }
2936     break;
2937
2938   case XA_WM_HINTS:
2939     getWMHints();
2940     break;
2941
2942   case XA_WM_ICON_NAME:
2943     getWMIconName();
2944     if (flags.iconic) screen->propagateWindowName(this);
2945     break;
2946
2947   case XAtom::net_wm_name:
2948   case XA_WM_NAME:
2949     getWMName();
2950
2951     if (decorations & Decor_Titlebar)
2952       redrawLabel();
2953
2954     screen->propagateWindowName(this);
2955     break;
2956
2957   case XA_WM_NORMAL_HINTS: {
2958     getWMNormalHints();
2959
2960     if ((client.normal_hint_flags & PMinSize) &&
2961         (client.normal_hint_flags & PMaxSize)) {
2962       // the window now can/can't resize itself, so the buttons need to be
2963       // regrabbed.
2964       ungrabButtons();
2965       if (client.max_width <= client.min_width &&
2966           client.max_height <= client.min_height) {
2967         functions &= ~(Func_Resize | Func_Maximize);
2968       } else {
2969         if (! isTransient())
2970           functions |= Func_Maximize;
2971         functions |= Func_Resize;
2972       }
2973       grabButtons();
2974       setAllowedActions();
2975       setupDecor();
2976     }
2977
2978     Rect old_rect = frame.rect;
2979
2980     upsize();
2981
2982     if (old_rect != frame.rect)
2983       reconfigure();
2984
2985     break;
2986   }
2987
2988   default:
2989     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2990       getWMProtocols();
2991
2992       if ((decorations & Decor_Close) && (! frame.close_button)) {
2993         createCloseButton();
2994         if (decorations & Decor_Titlebar) {
2995           positionButtons(True);
2996           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2997         }
2998         if (windowmenu) windowmenu->reconfigure();
2999       }
3000     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
3001       updateStrut();
3002     }
3003
3004     break;
3005   }
3006 }
3007
3008
3009 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
3010 #if 0
3011   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3012 #endif
3013
3014   if (frame.label == ee->window && (decorations & Decor_Titlebar))
3015     redrawLabel();
3016   else if (frame.close_button == ee->window)
3017     redrawCloseButton(False);
3018   else if (frame.maximize_button == ee->window)
3019     redrawMaximizeButton(flags.maximized);
3020   else if (frame.iconify_button == ee->window)
3021     redrawIconifyButton(False);
3022   else if (frame.stick_button == ee->window)
3023     redrawStickyButton(flags.stuck);
3024 }
3025
3026
3027 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
3028   if (cr->window != client.window || flags.iconic)
3029     return;
3030
3031   if (cr->value_mask & CWBorderWidth)
3032     client.old_bw = cr->border_width;
3033
3034   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3035     frame.changing = frame.rect;
3036
3037     if (cr->value_mask & (CWX | CWY)) {
3038       if (cr->value_mask & CWX)
3039         client.rect.setX(cr->x);
3040       if (cr->value_mask & CWY)
3041         client.rect.setY(cr->y);
3042
3043       applyGravity(frame.changing);
3044     }
3045
3046     if (cr->value_mask & (CWWidth | CWHeight)) {
3047       if (cr->value_mask & CWWidth)
3048         frame.changing.setWidth(cr->width +
3049                                 frame.margin.left + frame.margin.right);
3050
3051       if (cr->value_mask & CWHeight)
3052         frame.changing.setHeight(cr->height +
3053                                  frame.margin.top + frame.margin.bottom);
3054
3055       /*
3056         if a position change has been specified, then that position will be
3057         used instead of determining a position based on the window's gravity.
3058       */
3059       if (! (cr->value_mask & (CWX | CWY))) {
3060         Corner corner;
3061         switch (client.win_gravity) {
3062         case NorthEastGravity:
3063         case EastGravity:
3064           corner = TopRight;
3065           break;
3066         case SouthWestGravity:
3067         case SouthGravity:
3068           corner = BottomLeft;
3069           break;
3070         case SouthEastGravity:
3071           corner = BottomRight;
3072           break;
3073         default:     // NorthWest, Static, etc
3074           corner = TopLeft;
3075         }
3076         constrain(corner);
3077       }
3078     }
3079
3080     configure(frame.changing.x(), frame.changing.y(),
3081               frame.changing.width(), frame.changing.height());
3082   }
3083
3084   if (cr->value_mask & CWStackMode && !isDesktop()) {
3085     switch (cr->detail) {
3086     case Below:
3087     case BottomIf:
3088       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3089       break;
3090
3091     case Above:
3092     case TopIf:
3093     default:
3094       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3095       break;
3096     }
3097   }
3098 }
3099
3100
3101 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
3102 #ifdef DEBUG
3103   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3104           client.window);
3105 #endif
3106
3107   if (frame.maximize_button == be->window && be->button <= 3) {
3108     redrawMaximizeButton(True);
3109   } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
3110     if (! flags.focused)
3111       setInputFocus();
3112
3113     if (frame.iconify_button == be->window) {
3114       redrawIconifyButton(True);
3115     } else if (frame.close_button == be->window) {
3116       redrawCloseButton(True);
3117     } else if (frame.stick_button == be->window) {
3118       redrawStickyButton(True);
3119     } else if (frame.plate == be->window) {
3120       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3121
3122       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3123
3124       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
3125     } else {
3126       if (frame.title == be->window || frame.label == be->window) {
3127         if (((be->time - lastButtonPressTime) <=
3128              blackbox->getDoubleClickInterval()) ||
3129             (be->state == ControlMask)) {
3130           lastButtonPressTime = 0;
3131           shade();
3132         } else {
3133           lastButtonPressTime = be->time;
3134         }
3135       }
3136
3137       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3138
3139       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3140     }
3141   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3142              (be->window != frame.close_button) &&
3143              (be->window != frame.stick_button)) {
3144     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3145   } else if (windowmenu && be->button == 3 &&
3146              (frame.title == be->window || frame.label == be->window ||
3147               frame.handle == be->window || frame.window == be->window)) {
3148     if (windowmenu->isVisible()) {
3149       windowmenu->hide();
3150     } else {
3151       int mx = be->x_root - windowmenu->getWidth() / 2,
3152           my = be->y_root - windowmenu->getHeight() / 2;
3153
3154       // snap the window menu into a corner/side if necessary
3155       int left_edge, right_edge, top_edge, bottom_edge;
3156
3157       /*
3158          the " + (frame.border_w * 2) - 1" bits are to get the proper width
3159          and height of the menu, as the sizes returned by it do not include
3160          the borders.
3161        */
3162       left_edge = frame.rect.x();
3163       right_edge = frame.rect.right() -
3164         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
3165       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
3166       bottom_edge = client.rect.bottom() -
3167         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
3168         (frame.border_w + frame.mwm_border_w);
3169
3170       if (mx < left_edge)
3171         mx = left_edge;
3172       else if (mx > right_edge)
3173         mx = right_edge;
3174       if (my < top_edge)
3175         my = top_edge;
3176       else if (my > bottom_edge)
3177         my = bottom_edge;
3178       
3179       if (my + windowmenu->getHeight() > screen->getHeight())
3180         my = screen->getHeight() - windowmenu->getHeight() -
3181           (screen->getBorderWidth() * 2);
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   if (client.width_inc > 1) {
4229     dw -= base_width;
4230     dw /= client.width_inc;
4231   }
4232   if (client.height_inc > 1) {
4233     dh -= base_height;
4234     dh /= client.height_inc;
4235   }
4236
4237   if (pw)
4238     *pw = dw;
4239
4240   if (ph)
4241     *ph = dh;
4242
4243   if (client.width_inc > 1) {
4244     dw *= client.width_inc;
4245     dw += base_width;
4246   }
4247   if (client.height_inc > 1) {
4248     dh *= client.height_inc;
4249     dh += base_height;
4250   }
4251
4252   frame.changing.setSize(dw, dh);
4253
4254   // add the frame margin back onto frame.changing
4255   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4256                            frame.changing.top() - frame.margin.top,
4257                            frame.changing.right() + frame.margin.right,
4258                            frame.changing.bottom() + frame.margin.bottom);
4259
4260   // move frame.changing to the specified anchor
4261   int dx = 0,
4262       dy = 0;
4263   switch (anchor) {
4264   case TopLeft:
4265     break;
4266
4267   case TopRight:
4268     dx = frame.rect.right() - frame.changing.right();
4269     break;
4270
4271   case BottomLeft:
4272     dy = frame.rect.bottom() - frame.changing.bottom();
4273     break;
4274
4275   case BottomRight:
4276     dx = frame.rect.right() - frame.changing.right();
4277     dy = frame.rect.bottom() - frame.changing.bottom();
4278     break;
4279
4280   default:
4281     assert(false);  // unhandled corner
4282   }
4283   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4284 }
4285
4286
4287 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4288                             unsigned int max_length,
4289                             unsigned int modifier) const {
4290   size_t text_len = text.size();
4291   unsigned int length;
4292
4293   do {
4294     length = font->measureString(string(text, 0, text_len)) + modifier;
4295   } while (length > max_length && text_len-- > 0);
4296
4297   switch (justify) {
4298   case RightJustify:
4299     start_pos += max_length - length;
4300     break;
4301
4302   case CenterJustify:
4303     start_pos += (max_length - length) / 2;
4304     break;
4305
4306   case LeftJustify:
4307   default:
4308     break;
4309   }
4310 }
4311
4312
4313 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4314   : blackbox(b), group(_group) {
4315   XWindowAttributes wattrib;
4316   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4317     // group window doesn't seem to exist anymore
4318     delete this;
4319     return;
4320   }
4321
4322   XSelectInput(blackbox->getXDisplay(), group,
4323                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4324
4325   blackbox->saveGroupSearch(group, this);
4326 }
4327
4328
4329 BWindowGroup::~BWindowGroup(void) {
4330   blackbox->removeGroupSearch(group);
4331 }
4332
4333
4334 BlackboxWindow *
4335 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4336   BlackboxWindow *ret = blackbox->getFocusedWindow();
4337
4338   // does the focus window match (or any transient_fors)?
4339   for (; ret; ret = ret->getTransientFor()) {
4340     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4341         (! ret->isTransient() || allow_transients))
4342       break;
4343   }
4344
4345   if (ret) return ret;
4346
4347   // the focus window didn't match, look in the group's window list
4348   BlackboxWindowList::const_iterator it, end = windowList.end();
4349   for (it = windowList.begin(); it != end; ++it) {
4350     ret = *it;
4351     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4352         (! ret->isTransient() || allow_transients))
4353       break;
4354   }
4355
4356   return ret;
4357 }