]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
modal transients wouldnt throw their focus up to their arent cuz the parent didnt...
[dana/openbox.git] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 #  include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef    DEBUG
37 #  ifdef    HAVE_STDIO_H
38 #    include <stdio.h>
39 #  endif // HAVE_STDIO_H
40 #endif // DEBUG
41 }
42
43 #include <cstdlib>
44
45 #include "i18n.hh"
46 #include "blackbox.hh"
47 #include "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   delete timer;
351
352   delete windowmenu;
353
354   if (client.window_group) {
355     BWindowGroup *group = blackbox->searchGroup(client.window_group);
356     if (group) group->removeWindow(this);
357   }
358
359   // remove ourselves from our transient_for
360   if (isTransient()) {
361     if (client.transient_for != (BlackboxWindow *) ~0ul) {
362       client.transient_for->client.transientList.remove(this);
363     }
364     // we save our transient_for though because the workspace will use it
365     // when determining the next window to get focus
366   }
367
368   if (blackbox_attrib.workspace != BSENTINEL &&
369       window_number != BSENTINEL)
370     screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
371   else if (flags.iconic)
372     screen->removeIcon(this);
373
374   client.transient_for = (BlackboxWindow*) 0;
375
376   if (client.transientList.size() > 0) {
377     // reset transient_for for all transients
378     BlackboxWindowList::iterator it, end = client.transientList.end();
379     for (it = client.transientList.begin(); it != end; ++it) {
380       (*it)->client.transient_for = (BlackboxWindow*) 0;
381     }
382   }
383
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   const int snap_distance = screen->getEdgeSnapThreshold();
2966
2967   if (snap_distance) {
2968     // window corners
2969     const int wleft = dx,
2970               wright = dx + frame.rect.width() - 1,
2971               wtop = dy,
2972               wbottom = dy + frame.rect.height() - 1;
2973
2974     if (screen->getWindowToWindowSnap()) {
2975       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2976       assert(w);
2977
2978       // try snap to another window
2979       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2980         BlackboxWindow *snapwin = w->getWindow(i);
2981         if (snapwin == this)
2982           continue;   // don't snap to self
2983
2984         bool snapped = False;
2985         
2986         const Rect &winrect = snapwin->frameRect();
2987         int dleft = std::abs(wright - winrect.left()),
2988            dright = std::abs(wleft - winrect.right()),
2989              dtop = std::abs(wbottom - winrect.top()),
2990           dbottom = std::abs(wtop - winrect.bottom());
2991
2992         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2993             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2994
2995           // snap left of other window?
2996           if (dleft < snap_distance && dleft <= dright) {
2997             dx = winrect.left() - frame.rect.width();
2998             snapped = True;
2999           }
3000           // snap right of other window?
3001           else if (dright < snap_distance) {
3002             dx = winrect.right() + 1;
3003             snapped = True;
3004           }
3005
3006           if (snapped) {
3007             if (screen->getWindowCornerSnap()) {
3008               // try corner-snap to its other sides
3009               dtop = std::abs(wtop - winrect.top());
3010               dbottom = std::abs(wbottom - winrect.bottom());
3011               if (dtop < snap_distance && dtop <= dbottom)
3012                 dy = winrect.top();
3013               else if (dbottom < snap_distance)
3014                 dy = winrect.bottom() - frame.rect.height() + 1;
3015             }
3016
3017             continue;
3018           }
3019         }
3020
3021         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3022             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3023
3024           // snap top of other window?
3025           if (dtop < snap_distance && dtop <= dbottom) {
3026             dy = winrect.top() - frame.rect.height();
3027             snapped = True;
3028           }
3029           // snap bottom of other window?
3030           else if (dbottom < snap_distance) {
3031             dy = winrect.bottom() + 1;
3032             snapped = True;
3033           }
3034
3035           if (snapped) {
3036             if (screen->getWindowCornerSnap()) {
3037               // try corner-snap to its other sides
3038               dleft = std::abs(wleft - winrect.left());
3039               dright = std::abs(wright - winrect.right());
3040               if (dleft < snap_distance && dleft <= dright)
3041                 dx = winrect.left();
3042               else if (dright < snap_distance)
3043                 dx = winrect.right() - frame.rect.width() + 1;
3044             }
3045
3046             continue;
3047           }
3048         }
3049       }
3050     }
3051
3052     RectList snaplist; // the list of rects we will try to snap to
3053
3054     // snap to the strut (and screen boundaries for xinerama)
3055 #ifdef    XINERAMA
3056     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3057       if (! screen->doFullMax())
3058         snaplist.insert(snaplist.begin(),
3059                         screen->allAvailableAreas().begin(),
3060                         screen->allAvailableAreas().end());
3061
3062       // always snap to the screen edges
3063       snaplist.insert(snaplist.begin(),
3064                       screen->getXineramaAreas().begin(),
3065                       screen->getXineramaAreas().end());
3066     } else
3067 #endif // XINERAMA
3068     {
3069       if (! screen->doFullMax())
3070         snaplist.push_back(screen->availableArea());
3071
3072       // always snap to the screen edges
3073       snaplist.push_back(screen->getRect());
3074     }
3075
3076     RectList::const_iterator it, end = snaplist.end();
3077     for (it = snaplist.begin(); it != end; ++it) {
3078       const Rect &srect = *it;
3079
3080       // if we're not in the rectangle then don't snap to it.
3081       if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3082                                   frame.rect.height())))
3083         continue;
3084
3085       int dleft = std::abs(wleft - srect.left()),
3086          dright = std::abs(wright - srect.right()),
3087            dtop = std::abs(wtop - srect.top()),
3088         dbottom = std::abs(wbottom - srect.bottom());
3089
3090         // snap left?
3091         if (dleft < snap_distance && dleft <= dright)
3092           dx = srect.left();
3093         // snap right?
3094         else if (dright < snap_distance)
3095           dx = srect.right() - frame.rect.width() + 1;
3096
3097         // snap top?
3098         if (dtop < snap_distance && dtop <= dbottom)
3099           dy = srect.top();
3100         // snap bottom?
3101         else if (dbottom < snap_distance)
3102           dy = srect.bottom() - frame.rect.height() + 1;
3103     }
3104   }
3105
3106   if (screen->doOpaqueMove()) {
3107     configure(dx, dy, frame.rect.width(), frame.rect.height());
3108   } else {
3109     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3110                    screen->getOpGC(),
3111                    frame.changing.x(),
3112                    frame.changing.y(),
3113                    frame.changing.width() - 1,
3114                    frame.changing.height() - 1);
3115
3116     frame.changing.setPos(dx, dy);
3117
3118     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3119                    screen->getOpGC(),
3120                    frame.changing.x(),
3121                    frame.changing.y(),
3122                    frame.changing.width() - 1,
3123                    frame.changing.height() - 1);
3124   }
3125
3126   screen->showPosition(dx, dy);
3127 }
3128
3129
3130 void BlackboxWindow::endMove(void) {
3131   assert(flags.moving);
3132   assert(blackbox->getChangingWindow() == this);
3133
3134   flags.moving = False;
3135   blackbox->setChangingWindow(0);
3136
3137   if (! screen->doOpaqueMove()) {
3138     /* when drawing the rubber band, we need to make sure we only draw inside
3139      * the frame... frame.changing_* contain the new coords for the window,
3140      * so we need to subtract 1 from changing_w/changing_h every where we
3141      * draw the rubber band (for both moving and resizing)
3142      */
3143     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3144                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3145                    frame.changing.width() - 1, frame.changing.height() - 1);
3146       XUngrabServer(blackbox->getXDisplay());
3147   
3148       configure(frame.changing.x(), frame.changing.y(),
3149                 frame.changing.width(), frame.changing.height());
3150   } else {
3151     configure(frame.rect.x(), frame.rect.y(),
3152               frame.rect.width(), frame.rect.height());
3153   }
3154   screen->hideGeometry();
3155
3156   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3157
3158   // if there are any left over motions from the move, drop them now
3159   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3160   XEvent e;
3161   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3162                                 MotionNotify, &e));
3163 }
3164
3165
3166 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3167   assert(! (flags.resizing || flags.moving));
3168
3169   /*
3170     Only one window can be moved/resized at a time. If another window is already
3171     being moved or resized, then stop it before whating to work with this one.
3172   */
3173   BlackboxWindow *changing = blackbox->getChangingWindow();
3174   if (changing && changing != this) {
3175     if (changing->flags.moving)
3176       changing->endMove();
3177     else // if (changing->flags.resizing)
3178       changing->endResize();
3179   }
3180
3181   resize_dir = dir;
3182
3183   Cursor cursor;
3184   Corner anchor;
3185   
3186   switch (resize_dir) {
3187   case BottomLeft:
3188     anchor = TopRight;
3189     cursor = blackbox->getLowerLeftAngleCursor();
3190     break;
3191
3192   case BottomRight:
3193     anchor = TopLeft;
3194     cursor = blackbox->getLowerRightAngleCursor();
3195     break;
3196
3197   case TopLeft:
3198     anchor = BottomRight;
3199     cursor = blackbox->getUpperLeftAngleCursor();
3200     break;
3201
3202   case TopRight:
3203     anchor = BottomLeft;
3204     cursor = blackbox->getUpperRightAngleCursor();
3205     break;
3206
3207   default:
3208     assert(false); // unhandled Corner
3209     return;        // unreachable, for the compiler
3210   }
3211   
3212   XGrabServer(blackbox->getXDisplay());
3213   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3214                PointerMotionMask | ButtonReleaseMask,
3215                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3216
3217   flags.resizing = True;
3218   blackbox->setChangingWindow(this);
3219
3220   int gw, gh;
3221   frame.changing = frame.rect;
3222
3223   constrain(anchor,  &gw, &gh);
3224
3225   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3226                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3227                  frame.changing.width() - 1, frame.changing.height() - 1);
3228
3229   screen->showGeometry(gw, gh);
3230   
3231   frame.grab_x = x_root;
3232   frame.grab_y = y_root;
3233 }
3234
3235
3236 void BlackboxWindow::doResize(int x_root, int y_root) {
3237   assert(flags.resizing);
3238   assert(blackbox->getChangingWindow() == this);
3239
3240   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3241                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3242                  frame.changing.width() - 1, frame.changing.height() - 1);
3243
3244   int gw, gh;
3245   Corner anchor;
3246
3247   switch (resize_dir) {
3248   case BottomLeft:
3249     anchor = TopRight;
3250     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3251                            frame.rect.height() + (y_root - frame.grab_y));
3252     break;
3253   case BottomRight:
3254     anchor = TopLeft;
3255     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3256                            frame.rect.height() + (y_root - frame.grab_y));
3257     break;
3258   case TopLeft:
3259     anchor = BottomRight;
3260     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3261                            frame.rect.height() - (y_root - frame.grab_y));
3262     break;
3263   case TopRight:
3264     anchor = BottomLeft;
3265     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3266                            frame.rect.height() - (y_root - frame.grab_y));
3267     break;
3268
3269   default:
3270     assert(false); // unhandled Corner
3271     return;        // unreachable, for the compiler
3272   }
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
3283
3284 void BlackboxWindow::endResize(void) {
3285   assert(flags.resizing);
3286   assert(blackbox->getChangingWindow() == this);
3287
3288   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3289                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3290                  frame.changing.width() - 1, frame.changing.height() - 1);
3291   XUngrabServer(blackbox->getXDisplay());
3292
3293   // unset maximized state after resized when fully maximized
3294   if (flags.maximized == 1)
3295     maximize(0);
3296   
3297   flags.resizing = False;
3298   blackbox->setChangingWindow(0);
3299
3300   configure(frame.changing.x(), frame.changing.y(),
3301             frame.changing.width(), frame.changing.height());
3302   screen->hideGeometry();
3303
3304   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3305   
3306   // if there are any left over motions from the resize, drop them now
3307   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3308   XEvent e;
3309   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3310                                 MotionNotify, &e));
3311 }
3312
3313
3314 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3315 #ifdef DEBUG
3316   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3317           client.window);
3318 #endif
3319
3320   if (flags.moving) {
3321     doMove(me->x_root, me->y_root);
3322   } else if (flags.resizing) {
3323     doResize(me->x_root, me->y_root);
3324   } else {
3325     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3326         (frame.title == me->window || frame.label == me->window ||
3327          frame.handle == me->window || frame.window == me->window)) {
3328       beginMove(me->x_root, me->y_root);
3329     } else if ((functions & Func_Resize) &&
3330                (me->state & Button1Mask && (me->window == frame.right_grip ||
3331                                             me->window == frame.left_grip)) ||
3332                (me->state & Button3Mask && me->state & ModMask &&
3333                 me->window == frame.window)) {
3334       unsigned int zones = screen->getResizeZones();
3335       Corner corner;
3336       
3337       if (me->window == frame.left_grip) {
3338         corner = BottomLeft;
3339       } else if (me->window == frame.right_grip || zones == 1) {
3340         corner = BottomRight;
3341       } else {
3342         bool top;
3343         bool left = (me->x_root - frame.rect.x() <=
3344                      static_cast<signed>(frame.rect.width() / 2));
3345         if (zones == 2)
3346           top = False;
3347         else // (zones == 4)
3348           top = (me->y_root - frame.rect.y() <=
3349                  static_cast<signed>(frame.rect.height() / 2));
3350         corner = (top ? (left ? TopLeft : TopRight) :
3351                         (left ? BottomLeft : BottomRight));
3352       }
3353
3354       beginResize(me->x_root, me->y_root, corner);
3355     }
3356   }
3357 }
3358
3359
3360 #ifdef    SHAPE
3361 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3362   if (blackbox->hasShapeExtensions() && flags.shaped) {
3363     configureShape();
3364   }
3365 }
3366 #endif // SHAPE
3367
3368
3369 bool BlackboxWindow::validateClient(void) const {
3370   XSync(blackbox->getXDisplay(), False);
3371
3372   XEvent e;
3373   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3374                              DestroyNotify, &e) ||
3375       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3376                              UnmapNotify, &e)) {
3377     XPutBackEvent(blackbox->getXDisplay(), &e);
3378
3379     return False;
3380   }
3381
3382   return True;
3383 }
3384
3385
3386 void BlackboxWindow::restore(bool remap) {
3387   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3388   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3389   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3390
3391   // do not leave a shaded window as an icon unless it was an icon
3392   if (flags.shaded && ! flags.iconic) setState(NormalState);
3393
3394   restoreGravity(client.rect);
3395
3396   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3397   XUnmapWindow(blackbox->getXDisplay(), client.window);
3398
3399   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3400
3401   XEvent ev;
3402   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3403                              ReparentNotify, &ev)) {
3404     remap = True;
3405   } else {
3406     // according to the ICCCM - if the client doesn't reparent to
3407     // root, then we have to do it for them
3408     XReparentWindow(blackbox->getXDisplay(), client.window,
3409                     screen->getRootWindow(),
3410                     client.rect.x(), client.rect.y());
3411   }
3412
3413   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3414 }
3415
3416
3417 // timer for autoraise
3418 void BlackboxWindow::timeout(void) {
3419   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3420 }
3421
3422
3423 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3424   if ((net->flags & AttribShaded) &&
3425       ((blackbox_attrib.attrib & AttribShaded) !=
3426        (net->attrib & AttribShaded)))
3427     shade();
3428
3429   if (flags.visible && // watch out for requests when we can not be seen
3430       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3431       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3432        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3433     if (flags.maximized) {
3434       maximize(0);
3435     } else {
3436       int button = 0;
3437
3438       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3439         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3440       else if (net->flags & AttribMaxVert)
3441         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3442       else if (net->flags & AttribMaxHoriz)
3443         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3444
3445       maximize(button);
3446     }
3447   }
3448
3449   if ((net->flags & AttribOmnipresent) &&
3450       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3451        (net->attrib & AttribOmnipresent)))
3452     stick();
3453
3454   if ((net->flags & AttribWorkspace) &&
3455       (blackbox_attrib.workspace != net->workspace)) {
3456     screen->reassociateWindow(this, net->workspace, True);
3457
3458     if (screen->getCurrentWorkspaceID() != net->workspace) {
3459       withdraw();
3460     } else {
3461       show();
3462       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3463     }
3464   }
3465
3466   if (net->flags & AttribDecoration) {
3467     switch (net->decoration) {
3468     case DecorNone:
3469       // clear all decorations except close
3470       decorations &= Decor_Close;
3471
3472       break;
3473
3474     default:
3475     case DecorNormal:
3476       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3477   
3478       decorations = ((functions & Func_Resize) && !isTransient() ?
3479                      decorations | Decor_Handle :
3480                      decorations &= ~Decor_Handle);
3481       decorations = (functions & Func_Maximize ?
3482                      decorations | Decor_Maximize :
3483                      decorations &= ~Decor_Maximize);
3484
3485       break;
3486
3487     case DecorTiny:
3488       decorations |= Decor_Titlebar | Decor_Iconify;
3489       decorations &= ~(Decor_Border | Decor_Handle);
3490       
3491       decorations = (functions & Func_Maximize ?
3492                      decorations | Decor_Maximize :
3493                      decorations &= ~Decor_Maximize);
3494
3495       break;
3496
3497     case DecorTool:
3498       decorations |= Decor_Titlebar;
3499       decorations &= ~(Decor_Iconify | Decor_Border);
3500
3501       decorations = ((functions & Func_Resize) && !isTransient() ?
3502                      decorations | Decor_Handle :
3503                      decorations &= ~Decor_Handle);
3504       decorations = (functions & Func_Maximize ?
3505                      decorations | Decor_Maximize :
3506                      decorations &= ~Decor_Maximize);
3507
3508       break;
3509     }
3510
3511     // we can not be shaded if we lack a titlebar
3512     if (flags.shaded && ! (decorations & Decor_Titlebar))
3513       shade();
3514
3515     if (flags.visible && frame.window) {
3516       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3517       XMapWindow(blackbox->getXDisplay(), frame.window);
3518     }
3519
3520     reconfigure();
3521     setState(current_state);
3522   }
3523 }
3524
3525
3526 /*
3527  * Set the sizes of all components of the window frame
3528  * (the window decorations).
3529  * These values are based upon the current style settings and the client
3530  * window's dimensions.
3531  */
3532 void BlackboxWindow::upsize(void) {
3533   frame.bevel_w = screen->getBevelWidth();
3534
3535   if (decorations & Decor_Border) {
3536     frame.border_w = screen->getBorderWidth();
3537     if (! isTransient())
3538       frame.mwm_border_w = screen->getFrameWidth();
3539     else
3540       frame.mwm_border_w = 0;
3541   } else {
3542     frame.mwm_border_w = frame.border_w = 0;
3543   }
3544
3545   if (decorations & Decor_Titlebar) {
3546     // the height of the titlebar is based upon the height of the font being
3547     // used to display the window's title
3548     WindowStyle *style = screen->getWindowStyle();
3549     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3550
3551     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3552     frame.button_w = (frame.label_h - 2);
3553
3554     // set the top frame margin
3555     frame.margin.top = frame.border_w + frame.title_h +
3556                        frame.border_w + frame.mwm_border_w;
3557   } else {
3558     frame.title_h = 0;
3559     frame.label_h = 0;
3560     frame.button_w = 0;
3561
3562     // set the top frame margin
3563     frame.margin.top = frame.border_w + frame.mwm_border_w;
3564   }
3565
3566   // set the left/right frame margin
3567   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3568
3569   if (decorations & Decor_Handle) {
3570     frame.grip_w = frame.button_w * 2;
3571     frame.handle_h = screen->getHandleWidth();
3572
3573     // set the bottom frame margin
3574     frame.margin.bottom = frame.border_w + frame.handle_h +
3575                           frame.border_w + frame.mwm_border_w;
3576   } else {
3577     frame.handle_h = 0;
3578     frame.grip_w = 0;
3579
3580     // set the bottom frame margin
3581     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3582   }
3583
3584   /*
3585     We first get the normal dimensions and use this to define the inside_w/h
3586     then we modify the height if shading is in effect.
3587     If the shade state is not considered then frame.rect gets reset to the
3588     normal window size on a reconfigure() call resulting in improper
3589     dimensions appearing in move/resize and other events.
3590   */
3591   unsigned int
3592     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3593     width = client.rect.width() + frame.margin.left + frame.margin.right;
3594
3595   frame.inside_w = width - (frame.border_w * 2);
3596   frame.inside_h = height - (frame.border_w * 2);
3597
3598   if (flags.shaded)
3599     height = frame.title_h + (frame.border_w * 2);
3600   frame.rect.setSize(width, height);
3601 }
3602
3603
3604 /*
3605  * Calculate the size of the client window and constrain it to the
3606  * size specified by the size hints of the client window.
3607  *
3608  * The logical width and height are placed into pw and ph, if they
3609  * are non-zero.  Logical size refers to the users perception of
3610  * the window size (for example an xterm resizes in cells, not in pixels).
3611  *
3612  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3613  * Physical geometry refers to the geometry of the window in pixels.
3614  */
3615 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3616   // frame.changing represents the requested frame size, we need to
3617   // strip the frame margin off and constrain the client size
3618   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3619                            frame.changing.top() + frame.margin.top,
3620                            frame.changing.right() - frame.margin.right,
3621                            frame.changing.bottom() - frame.margin.bottom);
3622
3623   int dw = frame.changing.width(), dh = frame.changing.height(),
3624     base_width = (client.base_width) ? client.base_width : client.min_width,
3625     base_height = (client.base_height) ? client.base_height :
3626                                          client.min_height;
3627
3628   // constrain
3629   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3630   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3631   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3632   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3633
3634   dw -= base_width;
3635   dw /= client.width_inc;
3636   dh -= base_height;
3637   dh /= client.height_inc;
3638
3639   if (pw) {
3640     if (client.width_inc == 1)
3641       *pw = dw + base_width;
3642     else
3643       *pw = dw;
3644   }
3645   if (ph) {
3646     if (client.height_inc == 1)
3647       *ph = dh + base_height;
3648     else
3649       *ph = dh;
3650   }
3651
3652   dw *= client.width_inc;
3653   dw += base_width;
3654   dh *= client.height_inc;
3655   dh += base_height;
3656
3657   frame.changing.setSize(dw, dh);
3658
3659   // add the frame margin back onto frame.changing
3660   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3661                            frame.changing.top() - frame.margin.top,
3662                            frame.changing.right() + frame.margin.right,
3663                            frame.changing.bottom() + frame.margin.bottom);
3664
3665   // move frame.changing to the specified anchor
3666   int dx = 0,
3667       dy = 0;
3668   switch (anchor) {
3669   case TopLeft:
3670     break;
3671
3672   case TopRight:
3673     dx = frame.rect.right() - frame.changing.right();
3674     break;
3675
3676   case BottomLeft:
3677     dy = frame.rect.bottom() - frame.changing.bottom();
3678     break;
3679
3680   case BottomRight:
3681     dx = frame.rect.right() - frame.changing.right();
3682     dy = frame.rect.bottom() - frame.changing.bottom();
3683     break;
3684
3685   default:
3686     assert(false);  // unhandled corner
3687   }
3688   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3689 }
3690
3691
3692 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3693                             unsigned int max_length,
3694                             unsigned int modifier) const {
3695   size_t text_len = text.size();
3696   unsigned int length;
3697
3698   do {
3699     length = font->measureString(string(text, 0, text_len)) + modifier;
3700   } while (length > max_length && text_len-- > 0);
3701
3702   switch (justify) {
3703   case RightJustify:
3704     start_pos += max_length - length;
3705     break;
3706
3707   case CenterJustify:
3708     start_pos += (max_length - length) / 2;
3709     break;
3710
3711   case LeftJustify:
3712   default:
3713     break;
3714   }
3715 }
3716
3717
3718 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3719   : blackbox(b), group(_group) {
3720   XWindowAttributes wattrib;
3721   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3722     // group window doesn't seem to exist anymore
3723     delete this;
3724     return;
3725   }
3726
3727   XSelectInput(blackbox->getXDisplay(), group,
3728                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3729
3730   blackbox->saveGroupSearch(group, this);
3731 }
3732
3733
3734 BWindowGroup::~BWindowGroup(void) {
3735   blackbox->removeGroupSearch(group);
3736 }
3737
3738
3739 BlackboxWindow *
3740 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3741   BlackboxWindow *ret = blackbox->getFocusedWindow();
3742
3743   // does the focus window match (or any transient_fors)?
3744   while (ret) {
3745     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3746       if (ret->isTransient() && allow_transients) break;
3747       else if (! ret->isTransient()) break;
3748     }
3749
3750     ret = ret->getTransientFor();
3751   }
3752
3753   if (ret) return ret;
3754
3755   // the focus window didn't match, look in the group's window list
3756   BlackboxWindowList::const_iterator it, end = windowList.end();
3757   for (it = windowList.begin(); it != end; ++it) {
3758     ret = *it;
3759     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3760       if (ret->isTransient() && allow_transients) break;
3761       else if (! ret->isTransient()) break;
3762     }
3763   }
3764
3765   return ret;
3766 }