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