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