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