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