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