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