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