]> icculus.org git repositories - mikachu/openbox.git/blob - src/Window.cc
synch with bb-cvs.
[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) {
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         ! flags.shaded)
2822       shade();
2823   // mouse wheel down
2824   } else if (be->button == 5) {
2825     if ((be->window == frame.label ||
2826          be->window == frame.title) &&
2827         flags.shaded)
2828       shade();
2829   }
2830 }
2831
2832
2833 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2834 #ifdef DEBUG
2835   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2836           client.window);
2837 #endif
2838
2839   if (re->window == frame.maximize_button) {
2840     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2841         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2842       maximize(re->button);
2843     } else {
2844       redrawMaximizeButton(flags.maximized);
2845     }
2846   } else if (re->window == frame.iconify_button) {
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       iconify();
2850     } else {
2851       redrawIconifyButton(False);
2852     }
2853   } else if (re->window == frame.close_button) {
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       close();
2857     redrawCloseButton(False);
2858   } else if (flags.moving) {
2859     endMove();
2860   } else if (flags.resizing) {
2861     endResize();
2862   } else if (re->window == frame.window) {
2863     if (re->button == 2 && re->state == Mod1Mask)
2864       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2865   }
2866 }
2867
2868
2869
2870 void BlackboxWindow::beginMove(int x_root, int y_root) {
2871   assert(! (flags.resizing || flags.moving));
2872
2873   /*
2874     Only one window can be moved/resized at a time. If another window is already
2875     being moved or resized, then stop it before whating to work with this one.
2876   */
2877   BlackboxWindow *changing = blackbox->getChangingWindow();
2878   if (changing && changing != this) {
2879     if (changing->flags.moving)
2880       changing->endMove();
2881     else // if (changing->flags.resizing)
2882       changing->endResize();
2883   }
2884   
2885   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2886                PointerMotionMask | ButtonReleaseMask,
2887                GrabModeAsync, GrabModeAsync,
2888                None, blackbox->getMoveCursor(), CurrentTime);
2889
2890   if (windowmenu && windowmenu->isVisible())
2891     windowmenu->hide();
2892
2893   flags.moving = True;
2894   blackbox->setChangingWindow(this);
2895
2896   if (! screen->doOpaqueMove()) {
2897     XGrabServer(blackbox->getXDisplay());
2898
2899     frame.changing = frame.rect;
2900     screen->showPosition(frame.changing.x(), frame.changing.y());
2901
2902     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2903                    screen->getOpGC(),
2904                    frame.changing.x(),
2905                    frame.changing.y(),
2906                    frame.changing.width() - 1,
2907                    frame.changing.height() - 1);
2908   }
2909
2910   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
2911   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
2912 }
2913
2914
2915 void BlackboxWindow::doMove(int x_root, int y_root) {
2916   assert(flags.moving);
2917   assert(blackbox->getChangingWindow() == this);
2918
2919   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
2920   dx -= frame.border_w;
2921   dy -= frame.border_w;
2922
2923   const int snap_distance = screen->getEdgeSnapThreshold();
2924
2925   if (snap_distance) {
2926     // window corners
2927     const int wleft = dx,
2928               wright = dx + frame.rect.width() - 1,
2929               wtop = dy,
2930               wbottom = dy + frame.rect.height() - 1;
2931
2932     if (screen->getWindowToWindowSnap()) {
2933       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2934       assert(w);
2935
2936       // try snap to another window
2937       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2938         BlackboxWindow *snapwin = w->getWindow(i);
2939         if (snapwin == this)
2940           continue;   // don't snap to self
2941
2942         bool snapped = False;
2943         
2944         const Rect &winrect = snapwin->frameRect();
2945         int dleft = std::abs(wright - winrect.left()),
2946            dright = std::abs(wleft - winrect.right()),
2947              dtop = std::abs(wbottom - winrect.top()),
2948           dbottom = std::abs(wtop - winrect.bottom());
2949
2950         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2951             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2952
2953           // snap left of other window?
2954           if (dleft < snap_distance && dleft <= dright) {
2955             dx = winrect.left() - frame.rect.width();
2956             snapped = True;
2957           }
2958           // snap right of other window?
2959           else if (dright < snap_distance) {
2960             dx = winrect.right() + 1;
2961             snapped = True;
2962           }
2963
2964           if (snapped) {
2965             if (screen->getWindowCornerSnap()) {
2966               // try corner-snap to its other sides
2967               dtop = std::abs(wtop - winrect.top());
2968               dbottom = std::abs(wbottom - winrect.bottom());
2969               if (dtop < snap_distance && dtop <= dbottom)
2970                 dy = winrect.top();
2971               else if (dbottom < snap_distance)
2972                 dy = winrect.bottom() - frame.rect.height() + 1;
2973             }
2974
2975             continue;
2976           }
2977         }
2978
2979         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
2980             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
2981
2982           // snap top of other window?
2983           if (dtop < snap_distance && dtop <= dbottom) {
2984             dy = winrect.top() - frame.rect.height();
2985             snapped = True;
2986           }
2987           // snap bottom of other window?
2988           else if (dbottom < snap_distance) {
2989             dy = winrect.bottom() + 1;
2990             snapped = True;
2991           }
2992
2993           if (snapped) {
2994             if (screen->getWindowCornerSnap()) {
2995               // try corner-snap to its other sides
2996               dleft = std::abs(wleft - winrect.left());
2997               dright = std::abs(wright - winrect.right());
2998               if (dleft < snap_distance && dleft <= dright)
2999                 dx = winrect.left();
3000               else if (dright < snap_distance)
3001                 dx = winrect.right() - frame.rect.width() + 1;
3002             }
3003
3004             continue;
3005           }
3006         }
3007       }
3008     }
3009
3010     // try snap to the screen's available area
3011     Rect srect = screen->availableArea();
3012
3013     int dleft = std::abs(wleft - srect.left()),
3014        dright = std::abs(wright - srect.right()),
3015          dtop = std::abs(wtop - srect.top()),
3016       dbottom = std::abs(wbottom - srect.bottom());
3017
3018     // snap left?
3019     if (dleft < snap_distance && dleft <= dright)
3020       dx = srect.left();
3021     // snap right?
3022     else if (dright < snap_distance)
3023       dx = srect.right() - frame.rect.width() + 1;
3024
3025     // snap top?
3026     if (dtop < snap_distance && dtop <= dbottom)
3027       dy = srect.top();
3028     // snap bottom?
3029     else if (dbottom < snap_distance)
3030       dy = srect.bottom() - frame.rect.height() + 1;
3031
3032     srect = screen->getRect(); // now get the full screen
3033
3034     dleft = std::abs(wleft - srect.left()),
3035       dright = std::abs(wright - srect.right()),
3036       dtop = std::abs(wtop - srect.top()),
3037       dbottom = std::abs(wbottom - srect.bottom());
3038
3039     // snap left?
3040     if (dleft < snap_distance && dleft <= dright)
3041       dx = srect.left();
3042     // snap right?
3043     else if (dright < snap_distance)
3044       dx = srect.right() - frame.rect.width() + 1;
3045
3046     // snap top?
3047     if (dtop < snap_distance && dtop <= dbottom)
3048       dy = srect.top();
3049     // snap bottom?
3050     else if (dbottom < snap_distance)
3051       dy = srect.bottom() - frame.rect.height() + 1;
3052   }
3053
3054   if (screen->doOpaqueMove()) {
3055     configure(dx, dy, frame.rect.width(), frame.rect.height());
3056   } else {
3057     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3058                    screen->getOpGC(),
3059                    frame.changing.x(),
3060                    frame.changing.y(),
3061                    frame.changing.width() - 1,
3062                    frame.changing.height() - 1);
3063
3064     frame.changing.setPos(dx, dy);
3065
3066     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3067                    screen->getOpGC(),
3068                    frame.changing.x(),
3069                    frame.changing.y(),
3070                    frame.changing.width() - 1,
3071                    frame.changing.height() - 1);
3072   }
3073
3074   screen->showPosition(dx, dy);
3075 }
3076
3077
3078 void BlackboxWindow::endMove(void) {
3079   assert(flags.moving);
3080   assert(blackbox->getChangingWindow() == this);
3081
3082   flags.moving = False;
3083   blackbox->setChangingWindow(0);
3084
3085   if (! screen->doOpaqueMove()) {
3086     /* when drawing the rubber band, we need to make sure we only draw inside
3087      * the frame... frame.changing_* contain the new coords for the window,
3088      * so we need to subtract 1 from changing_w/changing_h every where we
3089      * draw the rubber band (for both moving and resizing)
3090      */
3091     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3092                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3093                    frame.changing.width() - 1, frame.changing.height() - 1);
3094       XUngrabServer(blackbox->getXDisplay());
3095   
3096       configure(frame.changing.x(), frame.changing.y(),
3097                 frame.changing.width(), frame.changing.height());
3098   } else {
3099     configure(frame.rect.x(), frame.rect.y(),
3100               frame.rect.width(), frame.rect.height());
3101   }
3102   screen->hideGeometry();
3103
3104   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3105
3106   // if there are any left over motions from the move, drop them now
3107   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3108   XEvent e;
3109   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3110                                 MotionNotify, &e));
3111 }
3112
3113
3114 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3115   assert(! (flags.resizing || flags.moving));
3116
3117   /*
3118     Only one window can be moved/resized at a time. If another window is already
3119     being moved or resized, then stop it before whating to work with this one.
3120   */
3121   BlackboxWindow *changing = blackbox->getChangingWindow();
3122   if (changing && changing != this) {
3123     if (changing->flags.moving)
3124       changing->endMove();
3125     else // if (changing->flags.resizing)
3126       changing->endResize();
3127   }
3128
3129   resize_dir = dir;
3130
3131   Cursor cursor;
3132   Corner anchor;
3133   
3134   switch (resize_dir) {
3135   case BottomLeft:
3136     anchor = TopRight;
3137     cursor = blackbox->getLowerLeftAngleCursor();
3138     break;
3139
3140   case BottomRight:
3141     anchor = TopLeft;
3142     cursor = blackbox->getLowerRightAngleCursor();
3143     break;
3144
3145   case TopLeft:
3146     anchor = BottomRight;
3147     cursor = blackbox->getUpperLeftAngleCursor();
3148     break;
3149
3150   case TopRight:
3151     anchor = BottomLeft;
3152     cursor = blackbox->getUpperRightAngleCursor();
3153     break;
3154
3155   default:
3156     assert(false); // unhandled Corner
3157     return;        // unreachable, for the compiler
3158   }
3159   
3160   XGrabServer(blackbox->getXDisplay());
3161   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3162                PointerMotionMask | ButtonReleaseMask,
3163                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3164
3165   flags.resizing = True;
3166   blackbox->setChangingWindow(this);
3167
3168   int gw, gh;
3169   frame.changing = frame.rect;
3170
3171   constrain(anchor,  &gw, &gh);
3172
3173   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3174                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3175                  frame.changing.width() - 1, frame.changing.height() - 1);
3176
3177   screen->showGeometry(gw, gh);
3178   
3179   frame.grab_x = x_root;
3180   frame.grab_y = y_root;
3181 }
3182
3183
3184 void BlackboxWindow::doResize(int x_root, int y_root) {
3185   assert(flags.resizing);
3186   assert(blackbox->getChangingWindow() == this);
3187
3188   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3189                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3190                  frame.changing.width() - 1, frame.changing.height() - 1);
3191
3192   int gw, gh;
3193   Corner anchor;
3194
3195   switch (resize_dir) {
3196   case BottomLeft:
3197     anchor = TopRight;
3198     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3199                            frame.rect.height() + (y_root - frame.grab_y));
3200     break;
3201   case BottomRight:
3202     anchor = TopLeft;
3203     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3204                            frame.rect.height() + (y_root - frame.grab_y));
3205     break;
3206   case TopLeft:
3207     anchor = BottomRight;
3208     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3209                            frame.rect.height() - (y_root - frame.grab_y));
3210     break;
3211   case TopRight:
3212     anchor = BottomLeft;
3213     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3214                            frame.rect.height() - (y_root - frame.grab_y));
3215     break;
3216
3217   default:
3218     assert(false); // unhandled Corner
3219     return;        // unreachable, for the compiler
3220   }
3221   
3222   constrain(anchor, &gw, &gh);
3223
3224   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3225                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3226                  frame.changing.width() - 1, frame.changing.height() - 1);
3227
3228   screen->showGeometry(gw, gh);
3229 }
3230
3231
3232 void BlackboxWindow::endResize(void) {
3233   assert(flags.resizing);
3234   assert(blackbox->getChangingWindow() == this);
3235
3236   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3237                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3238                  frame.changing.width() - 1, frame.changing.height() - 1);
3239   XUngrabServer(blackbox->getXDisplay());
3240
3241   // unset maximized state after resized when fully maximized
3242   if (flags.maximized == 1)
3243     maximize(0);
3244   
3245   flags.resizing = False;
3246   blackbox->setChangingWindow(0);
3247
3248   configure(frame.changing.x(), frame.changing.y(),
3249             frame.changing.width(), frame.changing.height());
3250   screen->hideGeometry();
3251
3252   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3253   
3254   // if there are any left over motions from the resize, drop them now
3255   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3256   XEvent e;
3257   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3258                                 MotionNotify, &e));
3259 }
3260
3261
3262 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3263 #ifdef DEBUG
3264   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3265           client.window);
3266 #endif
3267
3268   if (flags.moving) {
3269     doMove(me->x_root, me->y_root);
3270   } else if (flags.resizing) {
3271     doResize(me->x_root, me->y_root);
3272   } else {
3273     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3274         (frame.title == me->window || frame.label == me->window ||
3275          frame.handle == me->window || frame.window == me->window)) {
3276       beginMove(me->x_root, me->y_root);
3277     } else if ((functions & Func_Resize) &&
3278                (me->state & Button1Mask && (me->window == frame.right_grip ||
3279                                             me->window == frame.left_grip)) ||
3280                (me->state & Button3Mask && me->state & Mod1Mask &&
3281                 me->window == frame.window)) {
3282       unsigned int zones = screen->getResizeZones();
3283       Corner corner;
3284       
3285       if (me->window == frame.left_grip) {
3286         corner = BottomLeft;
3287       } else if (me->window == frame.right_grip || zones == 1) {
3288         corner = BottomRight;
3289       } else {
3290         bool top;
3291         bool left = (me->x_root - frame.rect.x() <=
3292                      static_cast<signed>(frame.rect.width() / 2));
3293         if (zones == 2)
3294           top = False;
3295         else // (zones == 4)
3296           top = (me->y_root - frame.rect.y() <=
3297                  static_cast<signed>(frame.rect.height() / 2));
3298         corner = (top ? (left ? TopLeft : TopRight) :
3299                         (left ? BottomLeft : BottomRight));
3300       }
3301
3302       beginResize(me->x_root, me->y_root, corner);
3303     }
3304   }
3305 }
3306
3307
3308 #ifdef    SHAPE
3309 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3310   if (blackbox->hasShapeExtensions() && flags.shaped) {
3311     configureShape();
3312   }
3313 }
3314 #endif // SHAPE
3315
3316
3317 bool BlackboxWindow::validateClient(void) const {
3318   XSync(blackbox->getXDisplay(), False);
3319
3320   XEvent e;
3321   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3322                              DestroyNotify, &e) ||
3323       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3324                              UnmapNotify, &e)) {
3325     XPutBackEvent(blackbox->getXDisplay(), &e);
3326
3327     return False;
3328   }
3329
3330   return True;
3331 }
3332
3333
3334 void BlackboxWindow::restore(bool remap) {
3335   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3336   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3337   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3338
3339   // do not leave a shaded window as an icon unless it was an icon
3340   if (flags.shaded && ! flags.iconic) setState(NormalState);
3341
3342   restoreGravity(client.rect);
3343
3344   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3345   XUnmapWindow(blackbox->getXDisplay(), client.window);
3346
3347   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3348
3349   XEvent ev;
3350   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3351                              ReparentNotify, &ev)) {
3352     remap = True;
3353   } else {
3354     // according to the ICCCM - if the client doesn't reparent to
3355     // root, then we have to do it for them
3356     XReparentWindow(blackbox->getXDisplay(), client.window,
3357                     screen->getRootWindow(),
3358                     client.rect.x(), client.rect.y());
3359   }
3360
3361   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3362 }
3363
3364
3365 // timer for autoraise
3366 void BlackboxWindow::timeout(void) {
3367   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3368 }
3369
3370
3371 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3372   if ((net->flags & AttribShaded) &&
3373       ((blackbox_attrib.attrib & AttribShaded) !=
3374        (net->attrib & AttribShaded)))
3375     shade();
3376
3377   if (flags.visible && // watch out for requests when we can not be seen
3378       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3379       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3380        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3381     if (flags.maximized) {
3382       maximize(0);
3383     } else {
3384       int button = 0;
3385
3386       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3387         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3388       else if (net->flags & AttribMaxVert)
3389         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3390       else if (net->flags & AttribMaxHoriz)
3391         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3392
3393       maximize(button);
3394     }
3395   }
3396
3397   if ((net->flags & AttribOmnipresent) &&
3398       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3399        (net->attrib & AttribOmnipresent)))
3400     stick();
3401
3402   if ((net->flags & AttribWorkspace) &&
3403       (blackbox_attrib.workspace != net->workspace)) {
3404     screen->reassociateWindow(this, net->workspace, True);
3405
3406     if (screen->getCurrentWorkspaceID() != net->workspace) {
3407       withdraw();
3408     } else {
3409       show();
3410       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3411     }
3412   }
3413
3414   if (net->flags & AttribDecoration) {
3415     switch (net->decoration) {
3416     case DecorNone:
3417       // clear all decorations except close
3418       decorations &= Decor_Close;
3419
3420       break;
3421
3422     default:
3423     case DecorNormal:
3424       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3425   
3426       decorations = ((functions & Func_Resize) && !isTransient() ?
3427                      decorations | Decor_Handle :
3428                      decorations &= ~Decor_Handle);
3429       decorations = (functions & Func_Maximize ?
3430                      decorations | Decor_Maximize :
3431                      decorations &= ~Decor_Maximize);
3432
3433       break;
3434
3435     case DecorTiny:
3436       decorations |= Decor_Titlebar | Decor_Iconify;
3437       decorations &= ~(Decor_Border | Decor_Handle);
3438       
3439       decorations = (functions & Func_Maximize ?
3440                      decorations | Decor_Maximize :
3441                      decorations &= ~Decor_Maximize);
3442
3443       break;
3444
3445     case DecorTool:
3446       decorations |= Decor_Titlebar;
3447       decorations &= ~(Decor_Iconify | Decor_Border);
3448
3449       decorations = ((functions & Func_Resize) && !isTransient() ?
3450                      decorations | Decor_Handle :
3451                      decorations &= ~Decor_Handle);
3452       decorations = (functions & Func_Maximize ?
3453                      decorations | Decor_Maximize :
3454                      decorations &= ~Decor_Maximize);
3455
3456       break;
3457     }
3458
3459     // we can not be shaded if we lack a titlebar
3460     if (flags.shaded && ! (decorations & Decor_Titlebar))
3461       shade();
3462
3463     if (flags.visible && frame.window) {
3464       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3465       XMapWindow(blackbox->getXDisplay(), frame.window);
3466     }
3467
3468     reconfigure();
3469     setState(current_state);
3470   }
3471 }
3472
3473
3474 /*
3475  * Set the sizes of all components of the window frame
3476  * (the window decorations).
3477  * These values are based upon the current style settings and the client
3478  * window's dimensions.
3479  */
3480 void BlackboxWindow::upsize(void) {
3481   frame.bevel_w = screen->getBevelWidth();
3482
3483   if (decorations & Decor_Border) {
3484     frame.border_w = screen->getBorderWidth();
3485     if (! isTransient())
3486       frame.mwm_border_w = screen->getFrameWidth();
3487     else
3488       frame.mwm_border_w = 0;
3489   } else {
3490     frame.mwm_border_w = frame.border_w = 0;
3491   }
3492
3493   if (decorations & Decor_Titlebar) {
3494     // the height of the titlebar is based upon the height of the font being
3495     // used to display the window's title
3496     WindowStyle *style = screen->getWindowStyle();
3497     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3498
3499     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3500     frame.button_w = (frame.label_h - 2);
3501
3502     // set the top frame margin
3503     frame.margin.top = frame.border_w + frame.title_h +
3504                        frame.border_w + frame.mwm_border_w;
3505   } else {
3506     frame.title_h = 0;
3507     frame.label_h = 0;
3508     frame.button_w = 0;
3509
3510     // set the top frame margin
3511     frame.margin.top = frame.border_w + frame.mwm_border_w;
3512   }
3513
3514   // set the left/right frame margin
3515   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3516
3517   if (decorations & Decor_Handle) {
3518     frame.grip_w = frame.button_w * 2;
3519     frame.handle_h = screen->getHandleWidth();
3520
3521     // set the bottom frame margin
3522     frame.margin.bottom = frame.border_w + frame.handle_h +
3523                           frame.border_w + frame.mwm_border_w;
3524   } else {
3525     frame.handle_h = 0;
3526     frame.grip_w = 0;
3527
3528     // set the bottom frame margin
3529     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3530   }
3531
3532   /*
3533     We first get the normal dimensions and use this to define the inside_w/h
3534     then we modify the height if shading is in effect.
3535     If the shade state is not considered then frame.rect gets reset to the
3536     normal window size on a reconfigure() call resulting in improper
3537     dimensions appearing in move/resize and other events.
3538   */
3539   unsigned int
3540     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3541     width = client.rect.width() + frame.margin.left + frame.margin.right;
3542
3543   frame.inside_w = width - (frame.border_w * 2);
3544   frame.inside_h = height - (frame.border_w * 2);
3545
3546   if (flags.shaded)
3547     height = frame.title_h + (frame.border_w * 2);
3548   frame.rect.setSize(width, height);
3549 }
3550
3551
3552 /*
3553  * Calculate the size of the client window and constrain it to the
3554  * size specified by the size hints of the client window.
3555  *
3556  * The logical width and height are placed into pw and ph, if they
3557  * are non-zero.  Logical size refers to the users perception of
3558  * the window size (for example an xterm resizes in cells, not in pixels).
3559  *
3560  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3561  * Physical geometry refers to the geometry of the window in pixels.
3562  */
3563 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3564   // frame.changing represents the requested frame size, we need to
3565   // strip the frame margin off and constrain the client size
3566   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3567                            frame.changing.top() + frame.margin.top,
3568                            frame.changing.right() - frame.margin.right,
3569                            frame.changing.bottom() - frame.margin.bottom);
3570
3571   int dw = frame.changing.width(), dh = frame.changing.height(),
3572     base_width = (client.base_width) ? client.base_width : client.min_width,
3573     base_height = (client.base_height) ? client.base_height :
3574                                          client.min_height;
3575
3576   // constrain
3577   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3578   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3579   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3580   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3581
3582   dw -= base_width;
3583   dw /= client.width_inc;
3584   dh -= base_height;
3585   dh /= client.height_inc;
3586
3587   if (pw) {
3588     if (client.width_inc == 1)
3589       *pw = dw + base_width;
3590     else
3591       *pw = dw;
3592   }
3593   if (ph) {
3594     if (client.height_inc == 1)
3595       *ph = dh + base_height;
3596     else
3597       *ph = dh;
3598   }
3599
3600   dw *= client.width_inc;
3601   dw += base_width;
3602   dh *= client.height_inc;
3603   dh += base_height;
3604
3605   frame.changing.setSize(dw, dh);
3606
3607   // add the frame margin back onto frame.changing
3608   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3609                            frame.changing.top() - frame.margin.top,
3610                            frame.changing.right() + frame.margin.right,
3611                            frame.changing.bottom() + frame.margin.bottom);
3612
3613   // move frame.changing to the specified anchor
3614   int dx = 0,
3615       dy = 0;
3616   switch (anchor) {
3617   case TopLeft:
3618     break;
3619
3620   case TopRight:
3621     dx = frame.rect.right() - frame.changing.right();
3622     break;
3623
3624   case BottomLeft:
3625     dy = frame.rect.bottom() - frame.changing.bottom();
3626     break;
3627
3628   case BottomRight:
3629     dx = frame.rect.right() - frame.changing.right();
3630     dy = frame.rect.bottom() - frame.changing.bottom();
3631     break;
3632
3633   default:
3634     assert(false);  // unhandled corner
3635   }
3636   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3637 }
3638
3639
3640 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3641                             unsigned int max_length,
3642                             unsigned int modifier) const {
3643   size_t text_len = text.size();
3644   unsigned int length;
3645
3646   do {
3647     length = font->measureString(string(text, 0, text_len)) + modifier;
3648   } while (length > max_length && text_len-- > 0);
3649
3650   switch (justify) {
3651   case RightJustify:
3652     start_pos += max_length - length;
3653     break;
3654
3655   case CenterJustify:
3656     start_pos += (max_length - length) / 2;
3657     break;
3658
3659   case LeftJustify:
3660   default:
3661     break;
3662   }
3663 }
3664
3665
3666 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3667   : blackbox(b), group(_group) {
3668   XWindowAttributes wattrib;
3669   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3670     // group window doesn't seem to exist anymore
3671     delete this;
3672     return;
3673   }
3674
3675   /*
3676     watch for destroy notify on the group window (in addition to
3677     any other events we are looking for)
3678
3679     since some managed windows can also be window group controllers,
3680     we need to make sure that we don't clobber the event mask for the
3681     managed window
3682   */
3683   XSelectInput(blackbox->getXDisplay(), group,
3684                wattrib.your_event_mask | StructureNotifyMask);
3685
3686   blackbox->saveGroupSearch(group, this);
3687 }
3688
3689
3690 BWindowGroup::~BWindowGroup(void) {
3691   blackbox->removeGroupSearch(group);
3692 }
3693
3694
3695 BlackboxWindow *
3696 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3697   BlackboxWindow *ret = blackbox->getFocusedWindow();
3698
3699   // does the focus window match (or any transient_fors)?
3700   while (ret) {
3701     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3702       if (ret->isTransient() && allow_transients) break;
3703       else if (! ret->isTransient()) break;
3704     }
3705
3706     ret = ret->getTransientFor();
3707   }
3708
3709   if (ret) return ret;
3710
3711   // the focus window didn't match, look in the group's window list
3712   BlackboxWindowList::const_iterator it, end = windowList.end();
3713   for (it = windowList.begin(); it != end; ++it) {
3714     ret = *it;
3715     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3716       if (ret->isTransient() && allow_transients) break;
3717       else if (! ret->isTransient()) break;
3718     }
3719   }
3720
3721   return ret;
3722 }