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