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