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