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