]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
add support for the kde-override hint, making the windows get no decorations.
[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.pixel();
594     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.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) 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   XEvent ce;
1776   ce.xclient.type = ClientMessage;
1777   ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
1778   ce.xclient.display = blackbox->getXDisplay();
1779   ce.xclient.window = client.window;
1780   ce.xclient.format = 32;
1781   ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1782   ce.xclient.data.l[1] = CurrentTime;
1783   ce.xclient.data.l[2] = 0l;
1784   ce.xclient.data.l[3] = 0l;
1785   ce.xclient.data.l[4] = 0l;
1786   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1787   XFlush(blackbox->getXDisplay());
1788 }
1789
1790
1791 void BlackboxWindow::withdraw(void) {
1792   // We don't need to worry about resizing because resizing always grabs the X
1793   // server. This should only ever happen if using opaque moving.
1794   if (flags.moving)
1795     endMove();
1796     
1797   flags.visible = False;
1798   flags.iconic = False;
1799
1800   setState(current_state);
1801
1802   XUnmapWindow(blackbox->getXDisplay(), frame.window);
1803
1804   XGrabServer(blackbox->getXDisplay());
1805
1806   unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1807                              StructureNotifyMask;
1808   XSelectInput(blackbox->getXDisplay(), client.window,
1809                event_mask & ~StructureNotifyMask);
1810   XUnmapWindow(blackbox->getXDisplay(), client.window);
1811   XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1812
1813   XUngrabServer(blackbox->getXDisplay());
1814
1815   if (windowmenu) windowmenu->hide();
1816 }
1817
1818
1819 void BlackboxWindow::maximize(unsigned int button) {
1820   // We don't need to worry about resizing because resizing always grabs the X
1821   // server. This should only ever happen if using opaque moving.
1822   if (flags.moving)
1823     endMove();
1824
1825   // handle case where menu is open then the max button is used instead
1826   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1827
1828   if (flags.maximized) {
1829     flags.maximized = 0;
1830
1831     blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1832     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1833
1834     /*
1835       when a resize finishes, maximize(0) is called to clear any maximization
1836       flags currently set.  Otherwise it still thinks it is maximized.
1837       so we do not need to call configure() because resizing will handle it
1838     */
1839     if (! flags.resizing)
1840       configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1841                 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1842
1843     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1844     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1845
1846     redrawAllButtons(); // in case it is not called in configure()
1847     setState(current_state);
1848     return;
1849   }
1850
1851   blackbox_attrib.premax_x = frame.rect.x();
1852   blackbox_attrib.premax_y = frame.rect.y();
1853   blackbox_attrib.premax_w = frame.rect.width();
1854   // use client.rect so that clients can be restored even if shaded
1855   blackbox_attrib.premax_h =
1856     client.rect.height() + frame.margin.top + frame.margin.bottom;
1857
1858 #ifdef    XINERAMA
1859   if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1860     // find the area to use
1861     RectList availableAreas = screen->allAvailableAreas();
1862     RectList::iterator it, end = availableAreas.end();
1863
1864     for (it = availableAreas.begin(); it != end; ++it)
1865       if (it->intersects(frame.rect)) break;
1866     if (it == end) // the window isn't inside an area
1867       it = availableAreas.begin(); // so just default to the first one
1868
1869     frame.changing = *it;
1870   } else
1871 #endif // XINERAMA
1872   frame.changing = screen->availableArea();
1873
1874   switch(button) {
1875   case 1:
1876     blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1877     blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1878     break;
1879
1880   case 2:
1881     blackbox_attrib.flags |= AttribMaxVert;
1882     blackbox_attrib.attrib |= AttribMaxVert;
1883
1884     frame.changing.setX(frame.rect.x());
1885     frame.changing.setWidth(frame.rect.width());
1886     break;
1887
1888   case 3:
1889     blackbox_attrib.flags |= AttribMaxHoriz;
1890     blackbox_attrib.attrib |= AttribMaxHoriz;
1891
1892     frame.changing.setY(frame.rect.y());
1893     frame.changing.setHeight(frame.rect.height());
1894     break;
1895   }
1896
1897   constrain(TopLeft);
1898
1899   if (flags.shaded) {
1900     blackbox_attrib.flags ^= AttribShaded;
1901     blackbox_attrib.attrib ^= AttribShaded;
1902     flags.shaded = False;
1903   }
1904
1905   flags.maximized = button;
1906
1907   configure(frame.changing.x(), frame.changing.y(),
1908             frame.changing.width(), frame.changing.height());
1909   if (flags.focused)
1910     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1911   redrawAllButtons(); // in case it is not called in configure()
1912   setState(current_state);
1913 }
1914
1915
1916 // re-maximizes the window to take into account availableArea changes
1917 void BlackboxWindow::remaximize(void) {
1918   if (flags.shaded) {
1919     // we only update the window's attributes otherwise we lose the shade bit
1920     switch(flags.maximized) {
1921     case 1:
1922       blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1923       blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1924       break;
1925
1926     case 2:
1927       blackbox_attrib.flags |= AttribMaxVert;
1928       blackbox_attrib.attrib |= AttribMaxVert;
1929       break;
1930
1931     case 3:
1932       blackbox_attrib.flags |= AttribMaxHoriz;
1933       blackbox_attrib.attrib |= AttribMaxHoriz;
1934       break;
1935     }
1936     return;
1937   }
1938
1939   // save the original dimensions because maximize will wipe them out
1940   int premax_x = blackbox_attrib.premax_x,
1941     premax_y = blackbox_attrib.premax_y,
1942     premax_w = blackbox_attrib.premax_w,
1943     premax_h = blackbox_attrib.premax_h;
1944
1945   unsigned int button = flags.maximized;
1946   flags.maximized = 0; // trick maximize() into working
1947   maximize(button);
1948
1949   // restore saved values
1950   blackbox_attrib.premax_x = premax_x;
1951   blackbox_attrib.premax_y = premax_y;
1952   blackbox_attrib.premax_w = premax_w;
1953   blackbox_attrib.premax_h = premax_h;
1954 }
1955
1956
1957 void BlackboxWindow::setWorkspace(unsigned int n) {
1958   blackbox_attrib.flags |= AttribWorkspace;
1959   blackbox_attrib.workspace = n;
1960   if (n == BSENTINEL) { // iconified window
1961     /*
1962        we set the workspace to 'all workspaces' so that taskbars will show the
1963        window. otherwise, it made uniconifying a window imposible without the
1964        blackbox workspace menu
1965     */
1966     n = 0xffffffff;
1967   }
1968   xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1969 }
1970
1971
1972 void BlackboxWindow::shade(void) {
1973   if (flags.shaded) {
1974     XResizeWindow(blackbox->getXDisplay(), frame.window,
1975                   frame.inside_w, frame.inside_h);
1976     flags.shaded = False;
1977     blackbox_attrib.flags ^= AttribShaded;
1978     blackbox_attrib.attrib ^= AttribShaded;
1979
1980     setState(NormalState);
1981
1982     // set the frame rect to the normal size
1983     frame.rect.setHeight(client.rect.height() + frame.margin.top +
1984                          frame.margin.bottom);
1985   } else {
1986     if (! (decorations & Decor_Titlebar))
1987       return; // can't shade it without a titlebar!
1988
1989     XResizeWindow(blackbox->getXDisplay(), frame.window,
1990                   frame.inside_w, frame.title_h);
1991     flags.shaded = True;
1992     blackbox_attrib.flags |= AttribShaded;
1993     blackbox_attrib.attrib |= AttribShaded;
1994
1995     setState(IconicState);
1996
1997     // set the frame rect to the shaded size
1998     frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1999   }
2000 }
2001
2002
2003 /*
2004  * (Un)Sticks a window and its relatives.
2005  */
2006 void BlackboxWindow::stick(void) {
2007   if (flags.stuck) {
2008     blackbox_attrib.flags ^= AttribOmnipresent;
2009     blackbox_attrib.attrib ^= AttribOmnipresent;
2010
2011     flags.stuck = False;
2012     
2013     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2014       if (i != blackbox_attrib.workspace)
2015         screen->getWorkspace(i)->removeWindow(this, True);
2016
2017     if (! flags.iconic)
2018       screen->reassociateWindow(this, BSENTINEL, True);
2019     // temporary fix since sticky windows suck. set the hint to what we
2020     // actually hold in our data.
2021     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2022                     blackbox_attrib.workspace);
2023
2024     setState(current_state);
2025   } else {
2026     flags.stuck = True;
2027
2028     blackbox_attrib.flags |= AttribOmnipresent;
2029     blackbox_attrib.attrib |= AttribOmnipresent;
2030
2031     // temporary fix since sticky windows suck. set the hint to a different
2032     // value than that contained in the class' data.
2033     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2034                     0xffffffff);
2035     
2036     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2037       if (i != blackbox_attrib.workspace)
2038         screen->getWorkspace(i)->addWindow(this, False, True);
2039
2040     setState(current_state);
2041   }
2042   // go up the chain
2043   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2044       client.transient_for->isStuck() != flags.stuck)
2045     client.transient_for->stick();
2046   // go down the chain
2047   BlackboxWindowList::iterator it;
2048   const BlackboxWindowList::iterator end = client.transientList.end();
2049   for (it = client.transientList.begin(); it != end; ++it)
2050     if ((*it)->isStuck() != flags.stuck)
2051       (*it)->stick();
2052 }
2053
2054
2055 void BlackboxWindow::redrawWindowFrame(void) const {
2056   if (decorations & Decor_Titlebar) {
2057     if (flags.focused) {
2058       if (frame.ftitle)
2059         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2060                                    frame.title, frame.ftitle);
2061       else
2062         XSetWindowBackground(blackbox->getXDisplay(),
2063                              frame.title, frame.ftitle_pixel);
2064     } else {
2065       if (frame.utitle)
2066         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2067                                    frame.title, frame.utitle);
2068       else
2069         XSetWindowBackground(blackbox->getXDisplay(),
2070                              frame.title, frame.utitle_pixel);
2071     }
2072     XClearWindow(blackbox->getXDisplay(), frame.title);
2073
2074     redrawLabel();
2075     redrawAllButtons();
2076   }
2077
2078   if (decorations & Decor_Handle) {
2079     if (flags.focused) {
2080       if (frame.fhandle)
2081         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2082                                    frame.handle, frame.fhandle);
2083       else
2084         XSetWindowBackground(blackbox->getXDisplay(),
2085                              frame.handle, frame.fhandle_pixel);
2086
2087       if (frame.fgrip) {
2088         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2089                                    frame.left_grip, frame.fgrip);
2090         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2091                                    frame.right_grip, frame.fgrip);
2092       } else {
2093         XSetWindowBackground(blackbox->getXDisplay(),
2094                              frame.left_grip, frame.fgrip_pixel);
2095         XSetWindowBackground(blackbox->getXDisplay(),
2096                              frame.right_grip, frame.fgrip_pixel);
2097       }
2098     } else {
2099       if (frame.uhandle)
2100         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2101                                    frame.handle, frame.uhandle);
2102       else
2103         XSetWindowBackground(blackbox->getXDisplay(),
2104                              frame.handle, frame.uhandle_pixel);
2105
2106       if (frame.ugrip) {
2107         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2108                                    frame.left_grip, frame.ugrip);
2109         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2110                                    frame.right_grip, frame.ugrip);
2111       } else {
2112         XSetWindowBackground(blackbox->getXDisplay(),
2113                              frame.left_grip, frame.ugrip_pixel);
2114         XSetWindowBackground(blackbox->getXDisplay(),
2115                              frame.right_grip, frame.ugrip_pixel);
2116       }
2117     }
2118     XClearWindow(blackbox->getXDisplay(), frame.handle);
2119     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2120     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2121   }
2122
2123   if (decorations & Decor_Border) {
2124     if (flags.focused)
2125       XSetWindowBorder(blackbox->getXDisplay(),
2126                        frame.plate, frame.fborder_pixel);
2127     else
2128       XSetWindowBorder(blackbox->getXDisplay(),
2129                        frame.plate, frame.uborder_pixel);
2130   }
2131 }
2132
2133
2134 void BlackboxWindow::setFocusFlag(bool focus) {
2135   // only focus a window if it is visible
2136   if (focus && ! flags.visible)
2137     return;
2138
2139   flags.focused = focus;
2140
2141   redrawWindowFrame();
2142
2143   if (flags.focused)
2144     blackbox->setFocusedWindow(this);
2145  
2146   if (! flags.iconic) {
2147     // iconic windows arent in a workspace menu!
2148     if (flags.stuck)
2149       screen->getCurrentWorkspace()->setFocused(this, isFocused());
2150     else
2151       screen->getWorkspace(blackbox_attrib.workspace)->
2152         setFocused(this, flags.focused);
2153   }
2154 }
2155
2156
2157 void BlackboxWindow::installColormap(bool install) {
2158   int i = 0, ncmap = 0;
2159   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2160                                             client.window, &ncmap);
2161   if (cmaps) {
2162     XWindowAttributes wattrib;
2163     if (XGetWindowAttributes(blackbox->getXDisplay(),
2164                              client.window, &wattrib)) {
2165       if (install) {
2166         // install the window's colormap
2167         for (i = 0; i < ncmap; i++) {
2168           if (*(cmaps + i) == wattrib.colormap)
2169             // this window is using an installed color map... do not install
2170             install = False;
2171         }
2172         // otherwise, install the window's colormap
2173         if (install)
2174           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2175       } else {
2176         // uninstall the window's colormap
2177         for (i = 0; i < ncmap; i++) {
2178           if (*(cmaps + i) == wattrib.colormap)
2179             // we found the colormap to uninstall
2180             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2181         }
2182       }
2183     }
2184
2185     XFree(cmaps);
2186   }
2187 }
2188
2189
2190 void BlackboxWindow::setAllowedActions(void) {
2191   Atom actions[7];
2192   int num = 0;
2193   
2194   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2195   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2196   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2197
2198   if (functions & Func_Move)
2199     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2200   if (functions & Func_Resize)
2201     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2202   if (functions & Func_Maximize) {
2203     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2204     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2205   }
2206
2207   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2208                   actions, num);
2209 }
2210
2211
2212 void BlackboxWindow::setState(unsigned long new_state) {
2213   current_state = new_state;
2214
2215   unsigned long state[2];
2216   state[0] = current_state;
2217   state[1] = None;
2218   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2219  
2220   xatom->setValue(client.window, XAtom::blackbox_attributes,
2221                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2222                   PropBlackboxAttributesElements);
2223
2224   Atom netstate[8];
2225   int num = 0;
2226   if (flags.modal)
2227     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2228   if (flags.shaded)
2229     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2230   if (flags.iconic)
2231     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2232   if (flags.skip_taskbar)
2233     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2234   if (flags.skip_pager)
2235     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2236   if (flags.fullscreen)
2237     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2238   if (flags.maximized == 1 || flags.maximized == 2)
2239     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2240   if (flags.maximized == 1 || flags.maximized == 3)
2241     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2242   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2243                   netstate, num);
2244 }
2245
2246
2247 bool BlackboxWindow::getState(void) {
2248   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2249                              current_state);
2250   if (! ret) current_state = 0;
2251   return ret;
2252 }
2253
2254
2255 void BlackboxWindow::restoreAttributes(void) {
2256   unsigned long num = PropBlackboxAttributesElements;
2257   BlackboxAttributes *net;
2258   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2259                         XAtom::blackbox_attributes, num,
2260                         (unsigned long **)&net))
2261     return;
2262   if (num < PropBlackboxAttributesElements) {
2263     delete [] net;
2264     return;
2265   }
2266
2267   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2268     flags.shaded = False;
2269     unsigned long orig_state = current_state;
2270     shade();
2271
2272     /*
2273       At this point in the life of a window, current_state should only be set
2274       to IconicState if the window was an *icon*, not if it was shaded.
2275     */
2276     if (orig_state != IconicState)
2277       current_state = WithdrawnState;
2278  }
2279
2280   if (net->workspace != screen->getCurrentWorkspaceID() &&
2281       net->workspace < screen->getWorkspaceCount())
2282     screen->reassociateWindow(this, net->workspace, True);
2283
2284   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2285       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2286     // set to WithdrawnState so it will be mapped on the new workspace
2287     if (current_state == NormalState) current_state = WithdrawnState;
2288   } else if (current_state == WithdrawnState) {
2289     // the window is on this workspace and is Withdrawn, so it is waiting to
2290     // be mapped
2291     current_state = NormalState;
2292   }
2293
2294   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2295       ! flags.stuck) {
2296     stick();
2297
2298     // if the window was on another workspace, it was going to be hidden. this
2299     // specifies that the window should be mapped since it is sticky.
2300     if (current_state == WithdrawnState) current_state = NormalState;
2301   }
2302
2303   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2304     int x = net->premax_x, y = net->premax_y;
2305     unsigned int w = net->premax_w, h = net->premax_h;
2306     flags.maximized = 0;
2307
2308     unsigned int m = 0;
2309     if ((net->flags & AttribMaxHoriz) &&
2310         (net->flags & AttribMaxVert))
2311       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2312     else if (net->flags & AttribMaxVert)
2313       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2314     else if (net->flags & AttribMaxHoriz)
2315       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2316
2317     if (m) maximize(m);
2318
2319     blackbox_attrib.premax_x = x;
2320     blackbox_attrib.premax_y = y;
2321     blackbox_attrib.premax_w = w;
2322     blackbox_attrib.premax_h = h;
2323   }
2324
2325   if (net->flags & AttribDecoration) {
2326     switch (net->decoration) {
2327     case DecorNone:
2328       enableDecor(False);
2329       break;
2330
2331     /* since tools only let you toggle this anyways, we'll just make that all
2332        it supports for now.
2333      */
2334     default:
2335     case DecorNormal:
2336     case DecorTiny:
2337     case DecorTool:
2338       enableDecor(True);
2339       break;
2340     }
2341   }
2342
2343   // with the state set it will then be the map event's job to read the
2344   // window's state and behave accordingly
2345
2346   delete [] net;
2347 }
2348
2349
2350 /*
2351  * Positions the Rect r according the the client window position and
2352  * window gravity.
2353  */
2354 void BlackboxWindow::applyGravity(Rect &r) {
2355   // apply horizontal window gravity
2356   switch (client.win_gravity) {
2357   default:
2358   case NorthWestGravity:
2359   case SouthWestGravity:
2360   case WestGravity:
2361     r.setX(client.rect.x());
2362     break;
2363
2364   case NorthGravity:
2365   case SouthGravity:
2366   case CenterGravity:
2367     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2368     break;
2369
2370   case NorthEastGravity:
2371   case SouthEastGravity:
2372   case EastGravity:
2373     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2374     break;
2375
2376   case ForgetGravity:
2377   case StaticGravity:
2378     r.setX(client.rect.x() - frame.margin.left);
2379     break;
2380   }
2381
2382   // apply vertical window gravity
2383   switch (client.win_gravity) {
2384   default:
2385   case NorthWestGravity:
2386   case NorthEastGravity:
2387   case NorthGravity:
2388     r.setY(client.rect.y());
2389     break;
2390
2391   case CenterGravity:
2392   case EastGravity:
2393   case WestGravity:
2394     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2395     break;
2396
2397   case SouthWestGravity:
2398   case SouthEastGravity:
2399   case SouthGravity:
2400     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2401     break;
2402
2403   case ForgetGravity:
2404   case StaticGravity:
2405     r.setY(client.rect.y() - frame.margin.top);
2406     break;
2407   }
2408 }
2409
2410
2411 /*
2412  * The reverse of the applyGravity function.
2413  *
2414  * Positions the Rect r according to the frame window position and
2415  * window gravity.
2416  */
2417 void BlackboxWindow::restoreGravity(Rect &r) {
2418   // restore horizontal window gravity
2419   switch (client.win_gravity) {
2420   default:
2421   case NorthWestGravity:
2422   case SouthWestGravity:
2423   case WestGravity:
2424     r.setX(frame.rect.x());
2425     break;
2426
2427   case NorthGravity:
2428   case SouthGravity:
2429   case CenterGravity:
2430     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2431     break;
2432
2433   case NorthEastGravity:
2434   case SouthEastGravity:
2435   case EastGravity:
2436     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2437     break;
2438
2439   case ForgetGravity:
2440   case StaticGravity:
2441     r.setX(frame.rect.x() + frame.margin.left);
2442     break;
2443   }
2444
2445   // restore vertical window gravity
2446   switch (client.win_gravity) {
2447   default:
2448   case NorthWestGravity:
2449   case NorthEastGravity:
2450   case NorthGravity:
2451     r.setY(frame.rect.y());
2452     break;
2453
2454   case CenterGravity:
2455   case EastGravity:
2456   case WestGravity:
2457     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2458     break;
2459
2460   case SouthWestGravity:
2461   case SouthEastGravity:
2462   case SouthGravity:
2463     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2464     break;
2465
2466   case ForgetGravity:
2467   case StaticGravity:
2468     r.setY(frame.rect.y() + frame.margin.top);
2469     break;
2470   }
2471 }
2472
2473
2474 void BlackboxWindow::redrawLabel(void) const {
2475   if (flags.focused) {
2476     if (frame.flabel)
2477       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2478                                  frame.label, frame.flabel);
2479     else
2480       XSetWindowBackground(blackbox->getXDisplay(),
2481                            frame.label, frame.flabel_pixel);
2482   } else {
2483     if (frame.ulabel)
2484       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2485                                  frame.label, frame.ulabel);
2486     else
2487       XSetWindowBackground(blackbox->getXDisplay(),
2488                            frame.label, frame.ulabel_pixel);
2489   }
2490   XClearWindow(blackbox->getXDisplay(), frame.label);
2491
2492   WindowStyle *style = screen->getWindowStyle();
2493
2494   int pos = frame.bevel_w * 2;
2495   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2496   style->font->drawString(frame.label, pos, 1,
2497                           (flags.focused ? style->l_text_focus :
2498                            style->l_text_unfocus),
2499                           client.title);
2500 }
2501
2502
2503 void BlackboxWindow::redrawAllButtons(void) const {
2504   if (frame.iconify_button) redrawIconifyButton(False);
2505   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2506   if (frame.close_button) redrawCloseButton(False);
2507 }
2508
2509
2510 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2511   if (! pressed) {
2512     if (flags.focused) {
2513       if (frame.fbutton)
2514         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2515                                    frame.iconify_button, frame.fbutton);
2516       else
2517         XSetWindowBackground(blackbox->getXDisplay(),
2518                              frame.iconify_button, frame.fbutton_pixel);
2519     } else {
2520       if (frame.ubutton)
2521         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2522                                    frame.iconify_button, frame.ubutton);
2523       else
2524         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2525                              frame.ubutton_pixel);
2526     }
2527   } else {
2528     if (frame.pbutton)
2529       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2530                                  frame.iconify_button, frame.pbutton);
2531     else
2532       XSetWindowBackground(blackbox->getXDisplay(),
2533                            frame.iconify_button, frame.pbutton_pixel);
2534   }
2535   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2536
2537   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2538            screen->getWindowStyle()->b_pic_unfocus);
2539   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2540                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2541 }
2542
2543
2544 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2545   if (! pressed) {
2546     if (flags.focused) {
2547       if (frame.fbutton)
2548         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2549                                    frame.maximize_button, frame.fbutton);
2550       else
2551         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2552                              frame.fbutton_pixel);
2553     } else {
2554       if (frame.ubutton)
2555         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2556                                    frame.maximize_button, frame.ubutton);
2557       else
2558         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2559                              frame.ubutton_pixel);
2560     }
2561   } else {
2562     if (frame.pbutton)
2563       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2564                                  frame.maximize_button, frame.pbutton);
2565     else
2566       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2567                            frame.pbutton_pixel);
2568   }
2569   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2570
2571   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2572            screen->getWindowStyle()->b_pic_unfocus);
2573   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2574                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2575   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2576             2, 3, (frame.button_w - 3), 3);
2577 }
2578
2579
2580 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2581   if (! pressed) {
2582     if (flags.focused) {
2583       if (frame.fbutton)
2584         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2585                                    frame.fbutton);
2586       else
2587         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2588                              frame.fbutton_pixel);
2589     } else {
2590       if (frame.ubutton)
2591         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2592                                    frame.ubutton);
2593       else
2594         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2595                              frame.ubutton_pixel);
2596     }
2597   } else {
2598     if (frame.pbutton)
2599       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2600                                  frame.close_button, frame.pbutton);
2601     else
2602       XSetWindowBackground(blackbox->getXDisplay(),
2603                            frame.close_button, frame.pbutton_pixel);
2604   }
2605   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2606
2607   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2608            screen->getWindowStyle()->b_pic_unfocus);
2609   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2610             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2611   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2612             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2613 }
2614
2615
2616 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2617   if (re->window != client.window)
2618     return;
2619
2620 #ifdef    DEBUG
2621   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2622           client.window);
2623 #endif // DEBUG
2624
2625   /*
2626      Even though the window wants to be shown, if it is not on the current
2627      workspace, then it isn't going to be shown right now.
2628   */
2629   if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2630       blackbox_attrib.workspace < screen->getWorkspaceCount())
2631     if (current_state == NormalState) current_state = WithdrawnState;
2632
2633   switch (current_state) {
2634   case IconicState:
2635     iconify();
2636     break;
2637
2638   case WithdrawnState:
2639     withdraw();
2640     break;
2641
2642   case NormalState:
2643   case InactiveState:
2644   case ZoomState:
2645   default:
2646     show();
2647     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2648     if (isNormal()) {
2649       if (! blackbox->isStartup()) {
2650         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2651         if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2652                                      getTransientFor()->isFocused())) {
2653           setInputFocus();
2654         }
2655         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2656           int x, y, rx, ry;
2657           Window c, r;
2658           unsigned int m;
2659           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2660                         &r, &c, &rx, &ry, &x, &y, &m);
2661           beginMove(rx, ry);
2662         }
2663       }
2664     }
2665     break;
2666   }
2667 }
2668
2669
2670 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2671   if (ue->window != client.window)
2672     return;
2673
2674 #ifdef    DEBUG
2675   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2676           client.window);
2677 #endif // DEBUG
2678
2679   screen->unmanageWindow(this, False);
2680 }
2681
2682
2683 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2684   if (de->window != client.window)
2685     return;
2686
2687 #ifdef    DEBUG
2688   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2689           client.window);
2690 #endif // DEBUG
2691
2692   screen->unmanageWindow(this, False);
2693 }
2694
2695
2696 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2697   if (re->window != client.window || re->parent == frame.plate)
2698     return;
2699
2700 #ifdef    DEBUG
2701   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2702           "0x%lx.\n", client.window, re->parent);
2703 #endif // DEBUG
2704
2705   XEvent ev;
2706   ev.xreparent = *re;
2707   XPutBackEvent(blackbox->getXDisplay(), &ev);
2708   screen->unmanageWindow(this, True);
2709 }
2710
2711
2712 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2713   if (pe->state == PropertyDelete)
2714     return;
2715
2716 #if 0
2717   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2718           client.window);
2719 #endif
2720
2721   switch(pe->atom) {
2722   case XA_WM_CLASS:
2723   case XA_WM_CLIENT_MACHINE:
2724   case XA_WM_COMMAND:
2725     break;
2726
2727   case XA_WM_TRANSIENT_FOR: {
2728     bool s = flags.stuck;
2729     
2730     // determine if this is a transient window
2731     getTransientInfo();
2732
2733     if (flags.stuck != s) stick();
2734
2735     // adjust the window decorations based on transience
2736     if (isTransient()) {
2737       functions &= ~Func_Maximize;
2738       setAllowedActions();
2739       setupDecor();
2740     }
2741
2742     reconfigure();
2743   }
2744     break;
2745
2746   case XA_WM_HINTS:
2747     getWMHints();
2748     break;
2749
2750   case XA_WM_ICON_NAME:
2751     getWMIconName();
2752     if (flags.iconic) screen->propagateWindowName(this);
2753     break;
2754
2755   case XAtom::net_wm_name:
2756   case XA_WM_NAME:
2757     getWMName();
2758
2759     if (decorations & Decor_Titlebar)
2760       redrawLabel();
2761
2762     screen->propagateWindowName(this);
2763     break;
2764
2765   case XA_WM_NORMAL_HINTS: {
2766     getWMNormalHints();
2767
2768     if ((client.normal_hint_flags & PMinSize) &&
2769         (client.normal_hint_flags & PMaxSize)) {
2770       // the window now can/can't resize itself, so the buttons need to be
2771       // regrabbed.
2772       ungrabButtons();
2773       if (client.max_width <= client.min_width &&
2774           client.max_height <= client.min_height) {
2775         functions &= ~(Func_Resize | Func_Maximize);
2776       } else {
2777         if (! isTransient())
2778           functions |= Func_Maximize;
2779         functions |= Func_Resize;
2780       }
2781       grabButtons();
2782       setAllowedActions();
2783       setupDecor();
2784     }
2785
2786     Rect old_rect = frame.rect;
2787
2788     upsize();
2789
2790     if (old_rect != frame.rect)
2791       reconfigure();
2792
2793     break;
2794   }
2795
2796   default:
2797     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2798       getWMProtocols();
2799
2800       if ((decorations & Decor_Close) && (! frame.close_button)) {
2801         createCloseButton();
2802         if (decorations & Decor_Titlebar) {
2803           positionButtons(True);
2804           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2805         }
2806         if (windowmenu) windowmenu->reconfigure();
2807       }
2808     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2809       updateStrut();
2810     }
2811
2812     break;
2813   }
2814 }
2815
2816
2817 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2818 #if 0
2819   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2820 #endif
2821
2822   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2823     redrawLabel();
2824   else if (frame.close_button == ee->window)
2825     redrawCloseButton(False);
2826   else if (frame.maximize_button == ee->window)
2827     redrawMaximizeButton(flags.maximized);
2828   else if (frame.iconify_button == ee->window)
2829     redrawIconifyButton(False);
2830 }
2831
2832
2833 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2834   if (cr->window != client.window || flags.iconic)
2835     return;
2836
2837   if (cr->value_mask & CWBorderWidth)
2838     client.old_bw = cr->border_width;
2839
2840   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2841     Rect req = frame.rect;
2842
2843     if (cr->value_mask & (CWX | CWY)) {
2844       if (cr->value_mask & CWX)
2845         client.rect.setX(cr->x);
2846       if (cr->value_mask & CWY)
2847         client.rect.setY(cr->y);
2848
2849       applyGravity(req);
2850     }
2851
2852     if (cr->value_mask & CWWidth)
2853       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2854
2855     if (cr->value_mask & CWHeight)
2856       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2857
2858     configure(req.x(), req.y(), req.width(), req.height());
2859   }
2860
2861   if (cr->value_mask & CWStackMode && !isDesktop()) {
2862     switch (cr->detail) {
2863     case Below:
2864     case BottomIf:
2865       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2866       break;
2867
2868     case Above:
2869     case TopIf:
2870     default:
2871       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2872       break;
2873     }
2874   }
2875 }
2876
2877
2878 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2879 #ifdef DEBUG
2880   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2881           client.window);
2882 #endif
2883
2884   if (frame.maximize_button == be->window && be->button <= 3) {
2885     redrawMaximizeButton(True);
2886   } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
2887     if (! flags.focused)
2888       setInputFocus();
2889
2890     if (frame.iconify_button == be->window) {
2891       redrawIconifyButton(True);
2892     } else if (frame.close_button == be->window) {
2893       redrawCloseButton(True);
2894     } else if (frame.plate == be->window) {
2895       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2896
2897       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2898
2899       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2900     } else {
2901       if (frame.title == be->window || frame.label == be->window) {
2902         if (((be->time - lastButtonPressTime) <=
2903              blackbox->getDoubleClickInterval()) ||
2904             (be->state == ControlMask)) {
2905           lastButtonPressTime = 0;
2906           shade();
2907         } else {
2908           lastButtonPressTime = be->time;
2909         }
2910       }
2911
2912       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2913
2914       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2915     }
2916   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2917              (be->window != frame.close_button)) {
2918     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2919   } else if (windowmenu && be->button == 3 &&
2920              (frame.title == be->window || frame.label == be->window ||
2921               frame.handle == be->window || frame.window == be->window)) {
2922     if (windowmenu->isVisible()) {
2923       windowmenu->hide();
2924     } else {
2925       int mx = be->x_root - windowmenu->getWidth() / 2,
2926           my = be->y_root - windowmenu->getHeight() / 2;
2927
2928       // snap the window menu into a corner/side if necessary
2929       int left_edge, right_edge, top_edge, bottom_edge;
2930
2931       /*
2932          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2933          and height of the menu, as the sizes returned by it do not include
2934          the borders.
2935        */
2936       left_edge = frame.rect.x();
2937       right_edge = frame.rect.right() -
2938         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2939       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2940       bottom_edge = client.rect.bottom() -
2941         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2942         (frame.border_w + frame.mwm_border_w);
2943
2944       if (mx < left_edge)
2945         mx = left_edge;
2946       if (mx > right_edge)
2947         mx = right_edge;
2948       if (my < top_edge)
2949         my = top_edge;
2950       if (my > bottom_edge)
2951         my = bottom_edge;
2952
2953       windowmenu->move(mx, my);
2954       windowmenu->show();
2955       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2956       XRaiseWindow(blackbox->getXDisplay(),
2957                    windowmenu->getSendToMenu()->getWindowID());
2958     }
2959   // mouse wheel up
2960   } else if (be->button == 4) {
2961     if ((be->window == frame.label ||
2962          be->window == frame.title ||
2963          be->window == frame.maximize_button ||
2964          be->window == frame.iconify_button ||
2965          be->window == frame.close_button) &&
2966         ! flags.shaded)
2967       shade();
2968   // mouse wheel down
2969   } else if (be->button == 5) {
2970     if ((be->window == frame.label ||
2971          be->window == frame.title ||
2972          be->window == frame.maximize_button ||
2973          be->window == frame.iconify_button ||
2974          be->window == frame.close_button) &&
2975         flags.shaded)
2976       shade();
2977   }
2978 }
2979
2980
2981 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2982 #ifdef DEBUG
2983   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2984           client.window);
2985 #endif
2986
2987   if (re->window == frame.maximize_button &&
2988       re->button >= 1 && re->button <= 3) {
2989     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2990         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2991       maximize(re->button);
2992     } else {
2993       redrawMaximizeButton(flags.maximized);
2994     }
2995   } else if (re->window == frame.iconify_button && re->button == 1) {
2996     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2997         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2998       iconify();
2999     } else {
3000       redrawIconifyButton(False);
3001     }
3002   } else if (re->window == frame.close_button & re->button == 1) {
3003     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3004         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3005       close();
3006     redrawCloseButton(False);
3007   } else if (flags.moving) {
3008     endMove();
3009   } else if (flags.resizing) {
3010     endResize();
3011   } else if (re->window == frame.window) {
3012     if (re->button == 2 && re->state == mod_mask)
3013       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3014   }
3015 }
3016
3017
3018
3019 void BlackboxWindow::beginMove(int x_root, int y_root) {
3020   assert(! (flags.resizing || flags.moving));
3021
3022   /*
3023     Only one window can be moved/resized at a time. If another window is already
3024     being moved or resized, then stop it before whating to work with this one.
3025   */
3026   BlackboxWindow *changing = blackbox->getChangingWindow();
3027   if (changing && changing != this) {
3028     if (changing->flags.moving)
3029       changing->endMove();
3030     else // if (changing->flags.resizing)
3031       changing->endResize();
3032   }
3033   
3034   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3035                PointerMotionMask | ButtonReleaseMask,
3036                GrabModeAsync, GrabModeAsync,
3037                None, blackbox->getMoveCursor(), CurrentTime);
3038
3039   if (windowmenu && windowmenu->isVisible())
3040     windowmenu->hide();
3041
3042   flags.moving = True;
3043   blackbox->setChangingWindow(this);
3044
3045   if (! screen->doOpaqueMove()) {
3046     XGrabServer(blackbox->getXDisplay());
3047
3048     frame.changing = frame.rect;
3049     screen->showPosition(frame.changing.x(), frame.changing.y());
3050
3051     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3052                    screen->getOpGC(),
3053                    frame.changing.x(),
3054                    frame.changing.y(),
3055                    frame.changing.width() - 1,
3056                    frame.changing.height() - 1);
3057   }
3058
3059   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3060   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3061 }
3062
3063
3064 void BlackboxWindow::doMove(int x_root, int y_root) {
3065   assert(flags.moving);
3066   assert(blackbox->getChangingWindow() == this);
3067
3068   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3069   dx -= frame.border_w;
3070   dy -= frame.border_w;
3071
3072   doWindowSnapping(dx, dy);
3073
3074   if (screen->doOpaqueMove()) {
3075     configure(dx, dy, frame.rect.width(), frame.rect.height());
3076   } else {
3077     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3078                    screen->getOpGC(),
3079                    frame.changing.x(),
3080                    frame.changing.y(),
3081                    frame.changing.width() - 1,
3082                    frame.changing.height() - 1);
3083
3084     frame.changing.setPos(dx, dy);
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   if (screen->doWorkspaceWarping())
3095     doWorkspaceWarping(x_root, y_root, dx, dy);
3096
3097   screen->showPosition(dx, dy);
3098 }
3099
3100
3101 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root,
3102                                         int &dx, int dy) {
3103   // workspace warping
3104   bool warp = False;
3105   unsigned int dest = screen->getCurrentWorkspaceID();
3106   if (x_root <= 0) {
3107     warp = True;
3108
3109     if (dest > 0) dest--;
3110     else dest = screen->getNumberOfWorkspaces() - 1;
3111
3112   } else if (x_root >= screen->getRect().right()) {
3113     warp = True;
3114
3115     if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3116     else dest = 0;
3117   }
3118   if (! warp)
3119     return;
3120
3121   endMove();
3122   bool focus = flags.focused; // had focus while moving?
3123   if (! flags.stuck)
3124     screen->reassociateWindow(this, dest, False);
3125   screen->changeWorkspaceID(dest);
3126   if (focus)
3127     setInputFocus();
3128
3129   int dest_x = x_root;
3130   if (x_root <= 0) {
3131     dest_x += screen->getRect().width() - 1;
3132     dx += screen->getRect().width() - 1;
3133   } else {
3134     dest_x -= screen->getRect().width() - 1;
3135     dx -= screen->getRect().width() - 1;
3136   }
3137
3138   /*
3139      We grab the X server here because we are moving the window and then the
3140      mouse cursor. When one moves, it could end up putting the mouse cursor
3141      over another window for a moment. This can cause the warp to iniate a
3142      move on another window.
3143   */
3144   XGrabServer(blackbox->getXDisplay());
3145
3146   configure(dx, dy, frame.rect.width(), frame.rect.height());
3147   XWarpPointer(blackbox->getXDisplay(), None, 
3148                screen->getRootWindow(), 0, 0, 0, 0,
3149                dest_x, y_root);
3150
3151   XUngrabServer(blackbox->getXDisplay());
3152
3153   beginMove(dest_x, y_root);
3154 }
3155
3156
3157 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3158   // how much resistance to edges to provide
3159   const int resistance_size = screen->getResistanceSize();
3160
3161   // how far away to snap
3162   const int snap_distance = screen->getSnapThreshold();
3163
3164   // how to snap windows
3165   const int snap_to_windows = screen->getWindowToWindowSnap();
3166   const int snap_to_edges = screen->getWindowToEdgeSnap();
3167   // the amount of space away from the edge to provide resistance/snap
3168   const int snap_offset = screen->getSnapOffset();
3169
3170   // find the geomeetery where the moving window currently is
3171   const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3172
3173   // window corners
3174   const int wleft = dx,
3175            wright = dx + frame.rect.width() - 1,
3176              wtop = dy,
3177           wbottom = dy + frame.rect.height() - 1;
3178
3179   if (snap_to_windows) {
3180     RectList rectlist;
3181
3182     Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3183     assert(w);
3184
3185     // add windows on the workspace to the rect list
3186     const BlackboxWindowList& stack_list = w->getStackingList();
3187     BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3188     for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3189       if (*st_it != this) // don't snap to ourself
3190         rectlist.push_back( (*st_it)->frameRect() );
3191
3192     // add the toolbar and the slit to the rect list.
3193     // (only if they are not hidden)
3194     Toolbar *tbar = screen->getToolbar();
3195     Slit *slit = screen->getSlit();
3196     Rect tbar_rect, slit_rect;
3197     unsigned int bwidth = screen->getBorderWidth() * 2;
3198
3199     if (! (screen->doHideToolbar() || tbar->isHidden())) {
3200       tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3201                         tbar->getHeight() + bwidth);
3202       rectlist.push_back(tbar_rect);
3203     }
3204
3205     if (! slit->isHidden()) {
3206       slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3207                         slit->getHeight() + bwidth);
3208       rectlist.push_back(slit_rect);
3209     }
3210
3211     RectList::const_iterator it, end = rectlist.end();
3212     for (it = rectlist.begin(); it != end; ++it) {
3213       bool snapped = False;
3214       const Rect &winrect = *it;
3215       Rect offsetrect;
3216       offsetrect.setCoords(winrect.left() - snap_offset,
3217                            winrect.top() - snap_offset,
3218                            winrect.right() + snap_offset,
3219                            winrect.bottom() + snap_offset);
3220
3221       if (snap_to_windows == BScreen::WindowResistance)
3222         // if the window is already over top of this snap target, then
3223         // resistance is futile, so just ignore it
3224         if (winrect.intersects(moving))
3225           continue;
3226
3227       int dleft, dright, dtop, dbottom;
3228
3229       // if the windows are in the same plane vertically
3230       if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3231           wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3232
3233         if (snap_to_windows == BScreen::WindowResistance) {
3234           dleft = wright - offsetrect.left();
3235           dright = offsetrect.right() - wleft;
3236
3237           // snap left of other window?
3238           if (dleft >= 0 && dleft < resistance_size &&
3239               dleft < (wright - wleft)) {
3240             dx = offsetrect.left() - frame.rect.width();
3241             snapped = True;
3242           }
3243           // snap right of other window?
3244           else if (dright >= 0 && dright < resistance_size &&
3245                    dright < (wright - wleft)) {
3246             dx = offsetrect.right() + 1;
3247             snapped = True;
3248           }
3249         } else { // BScreen::WindowSnap
3250           dleft = abs(wright - offsetrect.left());
3251           dright = abs(wleft - offsetrect.right());
3252
3253           // snap left of other window?
3254           if (dleft < snap_distance && dleft <= dright) {
3255             dx = offsetrect.left() - frame.rect.width();
3256             snapped = True;
3257           }
3258           // snap right of other window?
3259           else if (dright < snap_distance) {
3260             dx = offsetrect.right() + 1;
3261             snapped = True;
3262           }            
3263         }
3264
3265         if (snapped) {
3266           if (screen->getWindowCornerSnap()) {
3267             // try corner-snap to its other sides
3268             if (snap_to_windows == BScreen::WindowResistance) {
3269               dtop = winrect.top() - wtop;
3270               dbottom = wbottom - winrect.bottom();
3271               if (dtop > 0 && dtop < resistance_size) {
3272                 // if we're already past the top edge, then don't provide
3273                 // resistance
3274                 if (moving.top() >= winrect.top())
3275                   dy = winrect.top();
3276               } else if (dbottom > 0 && dbottom < resistance_size) {
3277                 // if we're already past the bottom edge, then don't provide
3278                 // resistance
3279                 if (moving.bottom() <= winrect.bottom())
3280                   dy = winrect.bottom() - frame.rect.height() + 1;
3281               }
3282             } else { // BScreen::WindowSnap
3283               dtop = abs(wtop - winrect.top());
3284               dbottom = abs(wbottom - winrect.bottom());
3285               if (dtop < snap_distance && dtop <= dbottom)
3286                 dy = winrect.top();
3287               else if (dbottom < snap_distance)
3288                 dy = winrect.bottom() - frame.rect.height() + 1;
3289             }
3290           }
3291
3292           continue;
3293         }
3294       }
3295
3296       // if the windows are on the same plane horizontally
3297       if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3298           wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3299
3300         if (snap_to_windows == BScreen::WindowResistance) {
3301           dtop = wbottom - offsetrect.top();
3302           dbottom = offsetrect.bottom() - wtop;
3303
3304           // snap top of other window?
3305           if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3306             dy = offsetrect.top() - frame.rect.height();
3307             snapped = True;
3308           }
3309           // snap bottom of other window?
3310           else if (dbottom >= 0 && dbottom < resistance_size &&
3311                    dbottom < (wbottom - wtop)) {
3312             dy = offsetrect.bottom() + 1;
3313             snapped = True;
3314           }
3315         } else { // BScreen::WindowSnap
3316           dtop = abs(wbottom - offsetrect.top());
3317           dbottom = abs(wtop - offsetrect.bottom());
3318
3319           // snap top of other window?
3320           if (dtop < snap_distance && dtop <= dbottom) {
3321             dy = offsetrect.top() - frame.rect.height();
3322             snapped = True;
3323           }
3324           // snap bottom of other window?
3325           else if (dbottom < snap_distance) {
3326             dy = offsetrect.bottom() + 1;
3327             snapped = True;
3328           }
3329
3330         }
3331
3332         if (snapped) {
3333           if (screen->getWindowCornerSnap()) {
3334             // try corner-snap to its other sides
3335             if (snap_to_windows == BScreen::WindowResistance) {
3336               dleft = winrect.left() - wleft;
3337               dright = wright - winrect.right();
3338               if (dleft > 0 && dleft < resistance_size) {
3339                 // if we're already past the left edge, then don't provide
3340                 // resistance
3341                 if (moving.left() >= winrect.left())
3342                   dx = winrect.left();
3343               } else if (dright > 0 && dright < resistance_size) {
3344                 // if we're already past the right edge, then don't provide
3345                 // resistance
3346                 if (moving.right() <= winrect.right())
3347                   dx = winrect.right() - frame.rect.width() + 1;
3348               }
3349             } else { // BScreen::WindowSnap
3350               dleft = abs(wleft - winrect.left());
3351               dright = abs(wright - winrect.right());
3352               if (dleft < snap_distance && dleft <= dright)
3353                 dx = winrect.left();
3354               else if (dright < snap_distance)
3355                 dx = winrect.right() - frame.rect.width() + 1;
3356             }
3357           }
3358
3359           continue;
3360         }
3361       }
3362     }
3363   }
3364
3365   if (snap_to_edges) {
3366     RectList rectlist;
3367
3368     // snap to the screen edges (and screen boundaries for xinerama)
3369 #ifdef    XINERAMA
3370     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3371       rectlist.insert(rectlist.begin(),
3372                       screen->getXineramaAreas().begin(),
3373                       screen->getXineramaAreas().end());
3374     } else
3375 #endif // XINERAMA
3376       rectlist.push_back(screen->getRect());
3377
3378     RectList::const_iterator it, end = rectlist.end();
3379     for (it = rectlist.begin(); it != end; ++it) {
3380       const Rect &srect = *it;
3381       Rect offsetrect;
3382       offsetrect.setCoords(srect.left() + snap_offset,
3383                            srect.top() + snap_offset,
3384                            srect.right() - snap_offset,
3385                            srect.bottom() - snap_offset);
3386
3387       if (snap_to_edges == BScreen::WindowResistance) {
3388         // if we're not in the rectangle then don't snap to it.
3389         if (! srect.contains(moving))
3390           continue;
3391       } else { // BScreen::WindowSnap
3392         // if we're not in the rectangle then don't snap to it.
3393         if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3394                                     frame.rect.height())))
3395           continue;
3396       }
3397
3398       if (snap_to_edges == BScreen::WindowResistance) {
3399       int dleft = offsetrect.left() - wleft,
3400          dright = wright - offsetrect.right(),
3401            dtop = offsetrect.top() - wtop,
3402         dbottom = wbottom - offsetrect.bottom();
3403
3404         // snap left?
3405         if (dleft > 0 && dleft < resistance_size)
3406           dx = offsetrect.left();
3407         // snap right?
3408         else if (dright > 0 && dright < resistance_size)
3409           dx = offsetrect.right() - frame.rect.width() + 1;
3410
3411         // snap top?
3412         if (dtop > 0 && dtop < resistance_size)
3413           dy = offsetrect.top();
3414         // snap bottom?
3415         else if (dbottom > 0 && dbottom < resistance_size)
3416           dy = offsetrect.bottom() - frame.rect.height() + 1;
3417       } else { // BScreen::WindowSnap
3418         int dleft = abs(wleft - offsetrect.left()),
3419            dright = abs(wright - offsetrect.right()),
3420              dtop = abs(wtop - offsetrect.top()),
3421           dbottom = abs(wbottom - offsetrect.bottom());
3422
3423         // snap left?
3424         if (dleft < snap_distance && dleft <= dright)
3425           dx = offsetrect.left();
3426         // snap right?
3427         else if (dright < snap_distance)
3428           dx = offsetrect.right() - frame.rect.width() + 1;
3429
3430         // snap top?
3431         if (dtop < snap_distance && dtop <= dbottom)
3432           dy = offsetrect.top();
3433         // snap bottom?
3434         else if (dbottom < snap_distance)
3435           dy = offsetrect.bottom() - frame.rect.height() + 1;
3436       }
3437     }
3438   }
3439 }
3440
3441
3442 void BlackboxWindow::endMove(void) {
3443   assert(flags.moving);
3444   assert(blackbox->getChangingWindow() == this);
3445
3446   flags.moving = False;
3447   blackbox->setChangingWindow(0);
3448
3449   if (! screen->doOpaqueMove()) {
3450     /* when drawing the rubber band, we need to make sure we only draw inside
3451      * the frame... frame.changing_* contain the new coords for the window,
3452      * so we need to subtract 1 from changing_w/changing_h every where we
3453      * draw the rubber band (for both moving and resizing)
3454      */
3455     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3456                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3457                    frame.changing.width() - 1, frame.changing.height() - 1);
3458       XUngrabServer(blackbox->getXDisplay());
3459   
3460       configure(frame.changing.x(), frame.changing.y(),
3461                 frame.changing.width(), frame.changing.height());
3462   } else {
3463     configure(frame.rect.x(), frame.rect.y(),
3464               frame.rect.width(), frame.rect.height());
3465   }
3466   screen->hideGeometry();
3467
3468   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3469
3470   // if there are any left over motions from the move, drop them now
3471   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3472   XEvent e;
3473   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3474                                 MotionNotify, &e));
3475 }
3476
3477
3478 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3479   assert(! (flags.resizing || flags.moving));
3480
3481   /*
3482     Only one window can be moved/resized at a time. If another window is already
3483     being moved or resized, then stop it before whating to work with this one.
3484   */
3485   BlackboxWindow *changing = blackbox->getChangingWindow();
3486   if (changing && changing != this) {
3487     if (changing->flags.moving)
3488       changing->endMove();
3489     else // if (changing->flags.resizing)
3490       changing->endResize();
3491   }
3492
3493   resize_dir = dir;
3494
3495   Cursor cursor;
3496   Corner anchor;
3497   
3498   switch (resize_dir) {
3499   case BottomLeft:
3500     anchor = TopRight;
3501     cursor = blackbox->getLowerLeftAngleCursor();
3502     break;
3503
3504   case BottomRight:
3505     anchor = TopLeft;
3506     cursor = blackbox->getLowerRightAngleCursor();
3507     break;
3508
3509   case TopLeft:
3510     anchor = BottomRight;
3511     cursor = blackbox->getUpperLeftAngleCursor();
3512     break;
3513
3514   case TopRight:
3515     anchor = BottomLeft;
3516     cursor = blackbox->getUpperRightAngleCursor();
3517     break;
3518
3519   default:
3520     assert(false); // unhandled Corner
3521     return;        // unreachable, for the compiler
3522   }
3523   
3524   XGrabServer(blackbox->getXDisplay());
3525   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3526                PointerMotionMask | ButtonReleaseMask,
3527                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3528
3529   flags.resizing = True;
3530   blackbox->setChangingWindow(this);
3531
3532   unsigned int gw, gh;
3533   frame.changing = frame.rect;
3534
3535   constrain(anchor,  &gw, &gh);
3536
3537   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3538                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3539                  frame.changing.width() - 1, frame.changing.height() - 1);
3540
3541   screen->showGeometry(gw, gh);
3542   
3543   frame.grab_x = x_root;
3544   frame.grab_y = y_root;
3545 }
3546
3547
3548 void BlackboxWindow::doResize(int x_root, int y_root) {
3549   assert(flags.resizing);
3550   assert(blackbox->getChangingWindow() == this);
3551
3552   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3553                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3554                  frame.changing.width() - 1, frame.changing.height() - 1);
3555
3556   unsigned int gw, gh;
3557   Corner anchor;
3558
3559   switch (resize_dir) {
3560   case BottomLeft:
3561     anchor = TopRight;
3562     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3563                            frame.rect.height() + (y_root - frame.grab_y));
3564     break;
3565   case BottomRight:
3566     anchor = TopLeft;
3567     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3568                            frame.rect.height() + (y_root - frame.grab_y));
3569     break;
3570   case TopLeft:
3571     anchor = BottomRight;
3572     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3573                            frame.rect.height() - (y_root - frame.grab_y));
3574     break;
3575   case TopRight:
3576     anchor = BottomLeft;
3577     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3578                            frame.rect.height() - (y_root - frame.grab_y));
3579     break;
3580
3581   default:
3582     assert(false); // unhandled Corner
3583     return;        // unreachable, for the compiler
3584   }
3585   
3586   constrain(anchor, &gw, &gh);
3587
3588   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3589                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3590                  frame.changing.width() - 1, frame.changing.height() - 1);
3591
3592   screen->showGeometry(gw, gh);
3593 }
3594
3595
3596 void BlackboxWindow::endResize(void) {
3597   assert(flags.resizing);
3598   assert(blackbox->getChangingWindow() == this);
3599
3600   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3601                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3602                  frame.changing.width() - 1, frame.changing.height() - 1);
3603   XUngrabServer(blackbox->getXDisplay());
3604
3605   // unset maximized state after resized when fully maximized
3606   if (flags.maximized == 1)
3607     maximize(0);
3608   
3609   flags.resizing = False;
3610   blackbox->setChangingWindow(0);
3611
3612   configure(frame.changing.x(), frame.changing.y(),
3613             frame.changing.width(), frame.changing.height());
3614   screen->hideGeometry();
3615
3616   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3617   
3618   // if there are any left over motions from the resize, drop them now
3619   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3620   XEvent e;
3621   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3622                                 MotionNotify, &e));
3623 }
3624
3625
3626 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3627 #if 0
3628   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3629           client.window);
3630 #endif
3631
3632   if (flags.moving) {
3633     doMove(me->x_root, me->y_root);
3634   } else if (flags.resizing) {
3635     doResize(me->x_root, me->y_root);
3636   } else {
3637     if ((functions & Func_Move) &&
3638        (me->state & Button1Mask) &&
3639         (frame.title == me->window || frame.label == me->window ||
3640          frame.handle == me->window || frame.window == me->window)) {
3641       beginMove(me->x_root, me->y_root);
3642     } else if ((functions & Func_Resize) &&
3643                ((me->state & Button1Mask) && (me->window == frame.right_grip ||
3644                                               me->window == frame.left_grip)) ||
3645                ((me->state & Button3Mask) && (me->state & mod_mask) &&
3646                 (frame.title == me->window || frame.label == me->window ||
3647                  frame.handle == me->window || frame.window == me->window))) {
3648       unsigned int zones = screen->getResizeZones();
3649       Corner corner;
3650       
3651       if (me->window == frame.left_grip) {
3652         corner = BottomLeft;
3653       } else if (me->window == frame.right_grip || zones == 1) {
3654         corner = BottomRight;
3655       } else {
3656         bool top;
3657         bool left = (me->x_root - frame.rect.x() <=
3658                      static_cast<signed>(frame.rect.width() / 2));
3659         if (zones == 2)
3660           top = False;
3661         else // (zones == 4)
3662           top = (me->y_root - frame.rect.y() <=
3663                  static_cast<signed>(frame.rect.height() / 2));
3664         corner = (top ? (left ? TopLeft : TopRight) :
3665                         (left ? BottomLeft : BottomRight));
3666       }
3667
3668       beginResize(me->x_root, me->y_root, corner);
3669     }
3670   }
3671 }
3672
3673
3674 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3675   if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3676     return;
3677
3678   XEvent e;
3679   bool leave = False, inferior = False;
3680
3681   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3682                                 LeaveNotify, &e)) {
3683     if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3684       leave = True;
3685       inferior = (e.xcrossing.detail == NotifyInferior);
3686     }
3687   }
3688
3689   if ((! leave || inferior) && ! isFocused()) {
3690     bool success = setInputFocus();
3691     if (success)    // if focus succeeded install the colormap
3692       installColormap(True); // XXX: shouldnt we honour no install?
3693   }
3694
3695   if (screen->doAutoRaise())
3696     timer->start();
3697 }
3698
3699
3700 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3701   if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3702     return;
3703
3704   installColormap(False);
3705
3706   if (timer->isTiming())
3707     timer->stop();
3708 }
3709
3710
3711 #ifdef    SHAPE
3712 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3713   if (blackbox->hasShapeExtensions() && flags.shaped) {
3714     configureShape();
3715   }
3716 }
3717 #endif // SHAPE
3718
3719
3720 bool BlackboxWindow::validateClient(void) const {
3721   XSync(blackbox->getXDisplay(), False);
3722
3723   XEvent e;
3724   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3725                              DestroyNotify, &e) ||
3726       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3727                              UnmapNotify, &e)) {
3728     XPutBackEvent(blackbox->getXDisplay(), &e);
3729
3730     return False;
3731   }
3732
3733   return True;
3734 }
3735
3736
3737 void BlackboxWindow::restore(bool remap) {
3738   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3739   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3740   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3741
3742   // do not leave a shaded window as an icon unless it was an icon
3743   if (flags.shaded && ! flags.iconic)
3744     setState(NormalState);
3745
3746   // erase the netwm stuff that we read when a window maps, so that it
3747   // doesn't persist between mappings.
3748   // (these are the ones read in getNetWMFlags().)
3749   xatom->eraseValue(client.window, XAtom::net_wm_desktop);
3750   xatom->eraseValue(client.window, XAtom::net_wm_state);
3751
3752   restoreGravity(client.rect);
3753
3754   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3755   XUnmapWindow(blackbox->getXDisplay(), client.window);
3756
3757   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3758
3759   XEvent ev;
3760   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3761                              ReparentNotify, &ev)) {
3762     remap = True;
3763   } else {
3764     // according to the ICCCM - if the client doesn't reparent to
3765     // root, then we have to do it for them
3766     XReparentWindow(blackbox->getXDisplay(), client.window,
3767                     screen->getRootWindow(),
3768                     client.rect.x(), client.rect.y());
3769   }
3770
3771   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3772 }
3773
3774
3775 // timer for autoraise
3776 void BlackboxWindow::timeout(void) {
3777   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3778 }
3779
3780
3781 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3782   if ((net->flags & AttribShaded) &&
3783       ((blackbox_attrib.attrib & AttribShaded) !=
3784        (net->attrib & AttribShaded)))
3785     shade();
3786
3787   if (flags.visible && // watch out for requests when we can not be seen
3788       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3789       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3790        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3791     if (flags.maximized) {
3792       maximize(0);
3793     } else {
3794       int button = 0;
3795
3796       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3797         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3798       else if (net->flags & AttribMaxVert)
3799         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3800       else if (net->flags & AttribMaxHoriz)
3801         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3802
3803       maximize(button);
3804     }
3805   }
3806
3807   if ((net->flags & AttribOmnipresent) &&
3808       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3809        (net->attrib & AttribOmnipresent)))
3810     stick();
3811
3812   if ((net->flags & AttribWorkspace) &&
3813       (blackbox_attrib.workspace != net->workspace)) {
3814     screen->reassociateWindow(this, net->workspace, True);
3815
3816     if (screen->getCurrentWorkspaceID() != net->workspace) {
3817       withdraw();
3818     } else {
3819       show();
3820       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3821     }
3822   }
3823
3824   if (net->flags & AttribDecoration) {
3825     switch (net->decoration) {
3826     case DecorNone:
3827       enableDecor(False);
3828       break;
3829
3830     default:
3831     case DecorNormal:
3832     case DecorTiny:
3833     case DecorTool:
3834       enableDecor(True);
3835       break;
3836     }
3837   }
3838 }
3839
3840
3841 /*
3842  * Set the sizes of all components of the window frame
3843  * (the window decorations).
3844  * These values are based upon the current style settings and the client
3845  * window's dimensions.
3846  */
3847 void BlackboxWindow::upsize(void) {
3848   frame.bevel_w = screen->getBevelWidth();
3849
3850   if (decorations & Decor_Border) {
3851     frame.border_w = screen->getBorderWidth();
3852     if (! isTransient())
3853       frame.mwm_border_w = screen->getFrameWidth();
3854     else
3855       frame.mwm_border_w = 0;
3856   } else {
3857     frame.mwm_border_w = frame.border_w = 0;
3858   }
3859
3860   if (decorations & Decor_Titlebar) {
3861     // the height of the titlebar is based upon the height of the font being
3862     // used to display the window's title
3863     WindowStyle *style = screen->getWindowStyle();
3864     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3865
3866     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3867     frame.button_w = (frame.label_h - 2);
3868
3869     // set the top frame margin
3870     frame.margin.top = frame.border_w + frame.title_h +
3871                        frame.border_w + frame.mwm_border_w;
3872   } else {
3873     frame.title_h = 0;
3874     frame.label_h = 0;
3875     frame.button_w = 0;
3876
3877     // set the top frame margin
3878     frame.margin.top = frame.border_w + frame.mwm_border_w;
3879   }
3880
3881   // set the left/right frame margin
3882   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3883
3884   if (decorations & Decor_Handle) {
3885     frame.grip_w = frame.button_w * 2;
3886     frame.handle_h = screen->getHandleWidth();
3887
3888     // set the bottom frame margin
3889     frame.margin.bottom = frame.border_w + frame.handle_h +
3890                           frame.border_w + frame.mwm_border_w;
3891   } else {
3892     frame.handle_h = 0;
3893     frame.grip_w = 0;
3894
3895     // set the bottom frame margin
3896     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3897   }
3898
3899   /*
3900     We first get the normal dimensions and use this to define the inside_w/h
3901     then we modify the height if shading is in effect.
3902     If the shade state is not considered then frame.rect gets reset to the
3903     normal window size on a reconfigure() call resulting in improper
3904     dimensions appearing in move/resize and other events.
3905   */
3906   unsigned int
3907     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3908     width = client.rect.width() + frame.margin.left + frame.margin.right;
3909
3910   frame.inside_w = width - (frame.border_w * 2);
3911   frame.inside_h = height - (frame.border_w * 2);
3912
3913   if (flags.shaded)
3914     height = frame.title_h + (frame.border_w * 2);
3915   frame.rect.setSize(width, height);
3916 }
3917
3918
3919 /*
3920  * Calculate the size of the client window and constrain it to the
3921  * size specified by the size hints of the client window.
3922  *
3923  * The logical width and height are placed into pw and ph, if they
3924  * are non-zero.  Logical size refers to the users perception of
3925  * the window size (for example an xterm resizes in cells, not in pixels).
3926  * pw and ph are then used to display the geometry during window moves, resize,
3927  * etc.
3928  *
3929  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3930  * Physical geometry refers to the geometry of the window in pixels.
3931  */
3932 void BlackboxWindow::constrain(Corner anchor,
3933                                unsigned int *pw, unsigned int *ph) {
3934   // frame.changing represents the requested frame size, we need to
3935   // strip the frame margin off and constrain the client size
3936   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3937                            frame.changing.top() + frame.margin.top,
3938                            frame.changing.right() - frame.margin.right,
3939                            frame.changing.bottom() - frame.margin.bottom);
3940
3941   unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
3942     base_width = (client.base_width) ? client.base_width : client.min_width,
3943     base_height = (client.base_height) ? client.base_height :
3944                                          client.min_height;
3945
3946   // constrain
3947   if (dw < client.min_width) dw = client.min_width;
3948   if (dh < client.min_height) dh = client.min_height;
3949   if (dw > client.max_width) dw = client.max_width;
3950   if (dh > client.max_height) dh = client.max_height;
3951
3952   assert(dw >= base_width && dh >= base_height);
3953
3954   if (client.width_inc > 1) {
3955     dw -= base_width;
3956     dw /= client.width_inc;
3957   }
3958   if (client.height_inc > 1) {
3959     dh -= base_height;
3960     dh /= client.height_inc;
3961   }
3962
3963   if (pw)
3964     *pw = dw;
3965
3966   if (ph)
3967     *ph = dh;
3968
3969   if (client.width_inc > 1) {
3970     dw *= client.width_inc;
3971     dw += base_width;
3972   }
3973   if (client.height_inc > 1) {
3974     dh *= client.height_inc;
3975     dh += base_height;
3976   }
3977
3978   frame.changing.setSize(dw, dh);
3979
3980   // add the frame margin back onto frame.changing
3981   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3982                            frame.changing.top() - frame.margin.top,
3983                            frame.changing.right() + frame.margin.right,
3984                            frame.changing.bottom() + frame.margin.bottom);
3985
3986   // move frame.changing to the specified anchor
3987   int dx = 0,
3988       dy = 0;
3989   switch (anchor) {
3990   case TopLeft:
3991     break;
3992
3993   case TopRight:
3994     dx = frame.rect.right() - frame.changing.right();
3995     break;
3996
3997   case BottomLeft:
3998     dy = frame.rect.bottom() - frame.changing.bottom();
3999     break;
4000
4001   case BottomRight:
4002     dx = frame.rect.right() - frame.changing.right();
4003     dy = frame.rect.bottom() - frame.changing.bottom();
4004     break;
4005
4006   default:
4007     assert(false);  // unhandled corner
4008   }
4009   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4010 }
4011
4012
4013 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4014                             unsigned int max_length,
4015                             unsigned int modifier) const {
4016   size_t text_len = text.size();
4017   unsigned int length;
4018
4019   do {
4020     length = font->measureString(string(text, 0, text_len)) + modifier;
4021   } while (length > max_length && text_len-- > 0);
4022
4023   switch (justify) {
4024   case RightJustify:
4025     start_pos += max_length - length;
4026     break;
4027
4028   case CenterJustify:
4029     start_pos += (max_length - length) / 2;
4030     break;
4031
4032   case LeftJustify:
4033   default:
4034     break;
4035   }
4036 }
4037
4038
4039 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4040   : blackbox(b), group(_group) {
4041   XWindowAttributes wattrib;
4042   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4043     // group window doesn't seem to exist anymore
4044     delete this;
4045     return;
4046   }
4047
4048   XSelectInput(blackbox->getXDisplay(), group,
4049                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4050
4051   blackbox->saveGroupSearch(group, this);
4052 }
4053
4054
4055 BWindowGroup::~BWindowGroup(void) {
4056   blackbox->removeGroupSearch(group);
4057 }
4058
4059
4060 BlackboxWindow *
4061 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4062   BlackboxWindow *ret = blackbox->getFocusedWindow();
4063
4064   // does the focus window match (or any transient_fors)?
4065   for (; ret; ret = ret->getTransientFor()) {
4066     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4067         (! ret->isTransient() || allow_transients))
4068       break;
4069   }
4070
4071   if (ret) return ret;
4072
4073   // the focus window didn't match, look in the group's window list
4074   BlackboxWindowList::const_iterator it, end = windowList.end();
4075   for (it = windowList.begin(); it != end; ++it) {
4076     ret = *it;
4077     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4078         (! ret->isTransient() || allow_transients))
4079       break;
4080   }
4081
4082   return ret;
4083 }