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