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