]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
better sticky windows.
[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
61 using std::string;
62 using std::abs;
63
64 // change this to change what modifier keys openbox uses for mouse bindings
65 // for example: Mod1Mask | ControlMask
66 //          or: ControlMask| ShiftMask
67 const unsigned int ModMask = Mod1Mask;
68
69 /*
70  * Initializes the class with default values/the window's set initial values.
71  */
72 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
73   // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
74   // sizeof(BlackboxWindow));
75
76 #ifdef    DEBUG
77   fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
78 #endif // DEBUG
79
80   /*
81     set timer to zero... it is initialized properly later, so we check
82     if timer is zero in the destructor, and assume that the window is not
83     fully constructed if timer is zero...
84   */
85   timer = 0;
86   blackbox = b;
87   client.window = w;
88   screen = s;
89   xatom = blackbox->getXAtom();
90
91   if (! validateClient()) {
92     delete this;
93     return;
94   }
95
96   // fetch client size and placement
97   XWindowAttributes wattrib;
98   if (! XGetWindowAttributes(blackbox->getXDisplay(),
99                              client.window, &wattrib) ||
100       ! wattrib.screen || wattrib.override_redirect) {
101 #ifdef    DEBUG
102     fprintf(stderr,
103             "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
104 #endif // DEBUG
105
106     delete this;
107     return;
108   }
109
110   // set the eventmask early in the game so that we make sure we get
111   // all the events we are interested in
112   XSetWindowAttributes attrib_set;
113   attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
114                           StructureNotifyMask;
115   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
116                                      ButtonMotionMask;
117   XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
118                           CWEventMask|CWDontPropagate, &attrib_set);
119
120   flags.moving = flags.resizing = flags.shaded = flags.visible =
121     flags.iconic = flags.focused = flags.stuck = flags.modal =
122     flags.send_focus_message = flags.shaped = flags.skip_taskbar =
123     flags.skip_pager = flags.fullscreen = False;
124   flags.maximized = 0;
125
126   blackbox_attrib.workspace = window_number = BSENTINEL;
127
128   blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack
129     = blackbox_attrib.decoration = 0l;
130   blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
131   blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
132
133   frame.border_w = 1;
134   frame.window = frame.plate = frame.title = frame.handle = None;
135   frame.close_button = frame.iconify_button = frame.maximize_button = None;
136   frame.right_grip = frame.left_grip = None;
137
138   frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
139   frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
140     frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
141     frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
142     frame.fgrip_pixel = 0;
143   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
144   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
145   frame.pbutton = frame.ugrip = frame.fgrip = None;
146
147   decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
148                 Decor_Iconify | Decor_Maximize;
149   functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
150
151   client.normal_hint_flags = 0;
152   client.window_group = None;
153   client.transient_for = 0;
154
155   current_state = NormalState;
156
157   /*
158     get the initial size and location of client window (relative to the
159     _root window_). This position is the reference point used with the
160     window's gravity to find the window's initial position.
161   */
162   client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
163   client.old_bw = wattrib.border_width;
164
165   lastButtonPressTime = 0;
166
167   timer = new BTimer(blackbox, this);
168   timer->setTimeout(blackbox->getAutoRaiseDelay());
169
170   windowmenu = new Windowmenu(this);
171
172   // get size, aspect, minimum/maximum size and other hints set by the
173   // client
174
175   if (! getBlackboxHints()) {
176     getMWMHints();
177     getNetWMHints();
178   }
179
180   getWMProtocols();
181   getWMHints();
182   getWMNormalHints();
183
184   frame.window = createToplevelWindow();
185
186   blackbox->saveWindowSearch(frame.window, this);
187   
188   frame.plate = createChildWindow(frame.window);
189   blackbox->saveWindowSearch(frame.plate, this);
190
191   // determine if this is a transient window
192   getTransientInfo();
193
194   // determine the window's type, so we can decide its decorations and
195   // functionality, or if we should not manage it at all
196   getWindowType();
197
198   // adjust the window decorations/behavior based on the window type
199
200   switch (window_type) {
201   case Type_Desktop:
202   case Type_Dock:
203   case Type_Menu:
204   case Type_Toolbar:
205   case Type_Utility:
206   case Type_Splash:
207     // none of these windows are decorated or manipulated by the window manager
208     decorations = 0;
209     functions = 0;
210     blackbox_attrib.workspace = 0;  // we do need to belong to a workspace
211     flags.stuck = True;             // we show up on all workspaces
212     break;
213
214   case Type_Dialog:
215     // dialogs cannot be maximized, and don't display a handle
216     decorations &= ~(Decor_Maximize | Decor_Handle);
217     functions &= ~Func_Maximize;
218     break;
219
220   case Type_Normal:
221     // normal windows retain all of the possible decorations and functionality
222     break;
223   }
224   
225   setAllowedActions();
226
227   // further adjeust the window's decorations/behavior based on window sizes
228   if ((client.normal_hint_flags & PMinSize) &&
229       (client.normal_hint_flags & PMaxSize) &&
230       client.max_width <= client.min_width &&
231       client.max_height <= client.min_height) {
232     decorations &= ~(Decor_Maximize | Decor_Handle);
233     functions &= ~(Func_Resize | Func_Maximize);
234   }
235   
236   if (decorations & Decor_Titlebar)
237     createTitlebar();
238
239   if (decorations & Decor_Handle)
240     createHandle();
241
242   // apply the size and gravity hint to the frame
243
244   upsize();
245
246   bool place_window = True;
247   if (blackbox->isStartup() || isTransient() ||
248       client.normal_hint_flags & (PPosition|USPosition)) {
249     applyGravity(frame.rect);
250
251     if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
252       place_window = False;
253   }
254
255   // add the window's strut. note this is done *after* placing the window.
256   screen->addStrut(&client.strut);
257   updateStrut();
258   
259 #ifdef    SHAPE
260   if (blackbox->hasShapeExtensions() && flags.shaped)
261     configureShape();
262 #endif // SHAPE
263   
264   // get the window's title before adding it to the workspace
265   getWMName();
266   getWMIconName();
267
268   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
269     screen->getCurrentWorkspace()->addWindow(this, place_window);
270   else
271     screen->getWorkspace(blackbox_attrib.workspace)->
272       addWindow(this, place_window);
273
274   /*
275     the server needs to be grabbed here to prevent client's from sending
276     events while we are in the process of configuring their window.
277     We hold the grab until after we are done moving the window around.
278   */
279
280   XGrabServer(blackbox->getXDisplay());
281
282   associateClientWindow();
283
284   blackbox->saveWindowSearch(client.window, this);
285
286   if (! place_window) {
287     // don't need to call configure if we are letting the workspace
288     // place the window
289     configure(frame.rect.x(), frame.rect.y(),
290               frame.rect.width(), frame.rect.height());
291
292   }
293
294   positionWindows();
295
296   XUngrabServer(blackbox->getXDisplay());
297
298   // now that we know where to put the window and what it should look like
299   // we apply the decorations
300   decorate();
301
302   grabButtons();
303
304   XMapSubwindows(blackbox->getXDisplay(), frame.window);
305
306   // this ensures the title, buttons, and other decor are properly displayed
307   redrawWindowFrame();
308
309   // preserve the window's initial state on first map, and its current state
310   // across a restart
311   unsigned long initial_state = current_state;
312   if (! getState())
313     current_state = initial_state;
314
315   // get sticky state from our parent window if we've got one
316   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
317       client.transient_for->isStuck() != flags.stuck)
318     stick();
319
320   // the following flags are set by blackbox native apps only
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     if (! flags.iconic)
1945       screen->reassociateWindow(this, BSENTINEL, True);
1946     // temporary fix since sticky windows suck. set the hint to what we
1947     // actually hold in our data.
1948     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1949                     blackbox_attrib.workspace);
1950
1951     setState(current_state);
1952   } else {
1953     flags.stuck = True;
1954
1955     blackbox_attrib.flags |= AttribOmnipresent;
1956     blackbox_attrib.attrib |= AttribOmnipresent;
1957
1958     // temporary fix since sticky windows suck. set the hint to a different
1959     // value than that contained in the class' data.
1960     xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1961                     0xffffffff);
1962     
1963     for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1964       if (i != blackbox_attrib.workspace)
1965         screen->getWorkspace(i)->addWindow(this, False, True);
1966
1967     setState(current_state);
1968   }
1969   // go up the chain
1970   if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1971       client.transient_for->isStuck() != flags.stuck)
1972     client.transient_for->stick();
1973   // go down the chain
1974   BlackboxWindowList::iterator it;
1975   const BlackboxWindowList::iterator end = client.transientList.end();
1976   for (it = client.transientList.begin(); it != end; ++it)
1977     if ((*it)->isStuck() != flags.stuck)
1978       (*it)->stick();
1979 }
1980
1981
1982 void BlackboxWindow::redrawWindowFrame(void) const {
1983   if (decorations & Decor_Titlebar) {
1984     if (flags.focused) {
1985       if (frame.ftitle)
1986         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1987                                    frame.title, frame.ftitle);
1988       else
1989         XSetWindowBackground(blackbox->getXDisplay(),
1990                              frame.title, frame.ftitle_pixel);
1991     } else {
1992       if (frame.utitle)
1993         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1994                                    frame.title, frame.utitle);
1995       else
1996         XSetWindowBackground(blackbox->getXDisplay(),
1997                              frame.title, frame.utitle_pixel);
1998     }
1999     XClearWindow(blackbox->getXDisplay(), frame.title);
2000
2001     redrawLabel();
2002     redrawAllButtons();
2003   }
2004
2005   if (decorations & Decor_Handle) {
2006     if (flags.focused) {
2007       if (frame.fhandle)
2008         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2009                                    frame.handle, frame.fhandle);
2010       else
2011         XSetWindowBackground(blackbox->getXDisplay(),
2012                              frame.handle, frame.fhandle_pixel);
2013
2014       if (frame.fgrip) {
2015         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2016                                    frame.left_grip, frame.fgrip);
2017         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2018                                    frame.right_grip, frame.fgrip);
2019       } else {
2020         XSetWindowBackground(blackbox->getXDisplay(),
2021                              frame.left_grip, frame.fgrip_pixel);
2022         XSetWindowBackground(blackbox->getXDisplay(),
2023                              frame.right_grip, frame.fgrip_pixel);
2024       }
2025     } else {
2026       if (frame.uhandle)
2027         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2028                                    frame.handle, frame.uhandle);
2029       else
2030         XSetWindowBackground(blackbox->getXDisplay(),
2031                              frame.handle, frame.uhandle_pixel);
2032
2033       if (frame.ugrip) {
2034         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2035                                    frame.left_grip, frame.ugrip);
2036         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2037                                    frame.right_grip, frame.ugrip);
2038       } else {
2039         XSetWindowBackground(blackbox->getXDisplay(),
2040                              frame.left_grip, frame.ugrip_pixel);
2041         XSetWindowBackground(blackbox->getXDisplay(),
2042                              frame.right_grip, frame.ugrip_pixel);
2043       }
2044     }
2045     XClearWindow(blackbox->getXDisplay(), frame.handle);
2046     XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2047     XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2048   }
2049
2050   if (decorations & Decor_Border) {
2051     if (flags.focused)
2052       XSetWindowBorder(blackbox->getXDisplay(),
2053                        frame.plate, frame.fborder_pixel);
2054     else
2055       XSetWindowBorder(blackbox->getXDisplay(),
2056                        frame.plate, frame.uborder_pixel);
2057   }
2058 }
2059
2060
2061 void BlackboxWindow::setFocusFlag(bool focus) {
2062   // only focus a window if it is visible
2063   if (focus && !flags.visible)
2064     return;
2065
2066   flags.focused = focus;
2067
2068   redrawWindowFrame();
2069
2070   if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2071     if (isFocused()) timer->start();
2072     else timer->stop();
2073   }
2074
2075   if (flags.focused)
2076     blackbox->setFocusedWindow(this);
2077  
2078   if (! flags.iconic) {
2079     // iconic windows arent in a workspace menu!
2080     if (flags.stuck)
2081       screen->getCurrentWorkspace()->setFocused(this, isFocused());
2082     else
2083       screen->getWorkspace(blackbox_attrib.workspace)->
2084         setFocused(this, flags.focused);
2085   }
2086 }
2087
2088
2089 void BlackboxWindow::installColormap(bool install) {
2090   int i = 0, ncmap = 0;
2091   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2092                                             client.window, &ncmap);
2093   if (cmaps) {
2094     XWindowAttributes wattrib;
2095     if (XGetWindowAttributes(blackbox->getXDisplay(),
2096                              client.window, &wattrib)) {
2097       if (install) {
2098         // install the window's colormap
2099         for (i = 0; i < ncmap; i++) {
2100           if (*(cmaps + i) == wattrib.colormap)
2101             // this window is using an installed color map... do not install
2102             install = False;
2103         }
2104         // otherwise, install the window's colormap
2105         if (install)
2106           XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2107       } else {
2108         // uninstall the window's colormap
2109         for (i = 0; i < ncmap; i++) {
2110           if (*(cmaps + i) == wattrib.colormap)
2111             // we found the colormap to uninstall
2112             XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2113         }
2114       }
2115     }
2116
2117     XFree(cmaps);
2118   }
2119 }
2120
2121
2122 void BlackboxWindow::setAllowedActions(void) {
2123   Atom actions[7];
2124   int num = 0;
2125   
2126   actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2127   actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2128   actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2129
2130   if (functions & Func_Move)
2131     actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2132   if (functions & Func_Resize)
2133     actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2134   if (functions & Func_Maximize) {
2135     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2136     actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2137   }
2138
2139   xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2140                   actions, num);
2141 }
2142
2143
2144 void BlackboxWindow::setState(unsigned long new_state) {
2145   current_state = new_state;
2146
2147   unsigned long state[2];
2148   state[0] = current_state;
2149   state[1] = None;
2150   xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2151  
2152   xatom->setValue(client.window, XAtom::blackbox_attributes,
2153                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2154                   PropBlackboxAttributesElements);
2155
2156   Atom netstate[8];
2157   int num = 0;
2158   if (flags.modal)
2159     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2160   if (flags.shaded)
2161     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2162   if (flags.iconic)
2163     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2164   if (flags.skip_taskbar)
2165     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2166   if (flags.skip_pager)
2167     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2168   if (flags.fullscreen)
2169     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2170   if (flags.maximized == 1 || flags.maximized == 2)
2171     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2172   if (flags.maximized == 1 || flags.maximized == 3)
2173     netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2174   xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2175                   netstate, num);
2176 }
2177
2178
2179 bool BlackboxWindow::getState(void) {
2180   bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2181                              current_state);
2182   if (! ret) current_state = 0;
2183   return ret;
2184 }
2185
2186
2187 void BlackboxWindow::restoreAttributes(void) {
2188   unsigned long num = PropBlackboxAttributesElements;
2189   BlackboxAttributes *net;
2190   if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2191                         XAtom::blackbox_attributes, num,
2192                         (unsigned long **)&net))
2193     return;
2194   if (num < PropBlackboxAttributesElements) {
2195     delete [] net;
2196     return;
2197   }
2198
2199   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2200     flags.shaded = False;
2201     unsigned long orig_state = current_state;
2202     shade();
2203
2204     /*
2205       At this point in the life of a window, current_state should only be set
2206       to IconicState if the window was an *icon*, not if it was shaded.
2207     */
2208     if (orig_state != IconicState)
2209       current_state = WithdrawnState;
2210  }
2211
2212   if (net->workspace != screen->getCurrentWorkspaceID() &&
2213       net->workspace < screen->getWorkspaceCount())
2214     screen->reassociateWindow(this, net->workspace, True);
2215
2216   if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2217       (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2218     // set to WithdrawnState so it will be mapped on the new workspace
2219     if (current_state == NormalState) current_state = WithdrawnState;
2220   } else if (current_state == WithdrawnState) {
2221     // the window is on this workspace and is Withdrawn, so it is waiting to
2222     // be mapped
2223     current_state = NormalState;
2224   }
2225
2226   if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2227     flags.stuck = False;
2228     stick();
2229
2230     // if the window was on another workspace, it was going to be hidden. this
2231     // specifies that the window should be mapped since it is sticky.
2232     if (current_state == WithdrawnState) current_state = NormalState;
2233   }
2234
2235   if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2236     int x = net->premax_x, y = net->premax_y;
2237     unsigned int w = net->premax_w, h = net->premax_h;
2238     flags.maximized = 0;
2239
2240     unsigned int m = 0;
2241     if ((net->flags & AttribMaxHoriz) &&
2242         (net->flags & AttribMaxVert))
2243       m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2244     else if (net->flags & AttribMaxVert)
2245       m = (net->attrib & AttribMaxVert) ? 2 : 0;
2246     else if (net->flags & AttribMaxHoriz)
2247       m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2248
2249     if (m) maximize(m);
2250
2251     blackbox_attrib.premax_x = x;
2252     blackbox_attrib.premax_y = y;
2253     blackbox_attrib.premax_w = w;
2254     blackbox_attrib.premax_h = h;
2255   }
2256
2257   if (net->flags & AttribDecoration) {
2258     switch (net->decoration) {
2259     case DecorNone:
2260       decorations = 0;
2261
2262       break;
2263
2264     default:
2265     case DecorNormal:
2266       decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2267         Decor_Iconify | Decor_Maximize;
2268
2269       break;
2270
2271     case DecorTiny:
2272       decorations |= Decor_Titlebar | Decor_Iconify;
2273       decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2274
2275       break;
2276
2277     case DecorTool:
2278       decorations |= Decor_Titlebar;
2279       decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2280
2281       break;
2282     }
2283
2284     // sanity check the new decor
2285     if (! (functions & Func_Resize) || isTransient())
2286       decorations &= ~(Decor_Maximize | Decor_Handle);
2287     if (! (functions & Func_Maximize))
2288       decorations &= ~Decor_Maximize;
2289
2290     if (decorations & Decor_Titlebar) {
2291       if (functions & Func_Close)   // close button is controlled by function
2292         decorations |= Decor_Close; // not decor type
2293     } else { 
2294       if (flags.shaded) // we can not be shaded if we lack a titlebar
2295         shade();
2296     }
2297
2298     if (flags.visible && frame.window) {
2299       XMapSubwindows(blackbox->getXDisplay(), frame.window);
2300       XMapWindow(blackbox->getXDisplay(), frame.window);
2301     }
2302
2303     reconfigure();
2304     setState(current_state);
2305   }
2306
2307   // with the state set it will then be the map event's job to read the
2308   // window's state and behave accordingly
2309
2310   delete [] net;
2311 }
2312
2313
2314 /*
2315  * Positions the Rect r according the the client window position and
2316  * window gravity.
2317  */
2318 void BlackboxWindow::applyGravity(Rect &r) {
2319   // apply horizontal window gravity
2320   switch (client.win_gravity) {
2321   default:
2322   case NorthWestGravity:
2323   case SouthWestGravity:
2324   case WestGravity:
2325     r.setX(client.rect.x());
2326     break;
2327
2328   case NorthGravity:
2329   case SouthGravity:
2330   case CenterGravity:
2331     r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2332     break;
2333
2334   case NorthEastGravity:
2335   case SouthEastGravity:
2336   case EastGravity:
2337     r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2338     break;
2339
2340   case ForgetGravity:
2341   case StaticGravity:
2342     r.setX(client.rect.x() - frame.margin.left);
2343     break;
2344   }
2345
2346   // apply vertical window gravity
2347   switch (client.win_gravity) {
2348   default:
2349   case NorthWestGravity:
2350   case NorthEastGravity:
2351   case NorthGravity:
2352     r.setY(client.rect.y());
2353     break;
2354
2355   case CenterGravity:
2356   case EastGravity:
2357   case WestGravity:
2358     r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2359     break;
2360
2361   case SouthWestGravity:
2362   case SouthEastGravity:
2363   case SouthGravity:
2364     r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2365     break;
2366
2367   case ForgetGravity:
2368   case StaticGravity:
2369     r.setY(client.rect.y() - frame.margin.top);
2370     break;
2371   }
2372 }
2373
2374
2375 /*
2376  * The reverse of the applyGravity function.
2377  *
2378  * Positions the Rect r according to the frame window position and
2379  * window gravity.
2380  */
2381 void BlackboxWindow::restoreGravity(Rect &r) {
2382   // restore horizontal window gravity
2383   switch (client.win_gravity) {
2384   default:
2385   case NorthWestGravity:
2386   case SouthWestGravity:
2387   case WestGravity:
2388     r.setX(frame.rect.x());
2389     break;
2390
2391   case NorthGravity:
2392   case SouthGravity:
2393   case CenterGravity:
2394     r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2395     break;
2396
2397   case NorthEastGravity:
2398   case SouthEastGravity:
2399   case EastGravity:
2400     r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2401     break;
2402
2403   case ForgetGravity:
2404   case StaticGravity:
2405     r.setX(frame.rect.x() + frame.margin.left);
2406     break;
2407   }
2408
2409   // restore vertical window gravity
2410   switch (client.win_gravity) {
2411   default:
2412   case NorthWestGravity:
2413   case NorthEastGravity:
2414   case NorthGravity:
2415     r.setY(frame.rect.y());
2416     break;
2417
2418   case CenterGravity:
2419   case EastGravity:
2420   case WestGravity:
2421     r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2422     break;
2423
2424   case SouthWestGravity:
2425   case SouthEastGravity:
2426   case SouthGravity:
2427     r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2428     break;
2429
2430   case ForgetGravity:
2431   case StaticGravity:
2432     r.setY(frame.rect.y() + frame.margin.top);
2433     break;
2434   }
2435 }
2436
2437
2438 void BlackboxWindow::redrawLabel(void) const {
2439   if (flags.focused) {
2440     if (frame.flabel)
2441       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2442                                  frame.label, frame.flabel);
2443     else
2444       XSetWindowBackground(blackbox->getXDisplay(),
2445                            frame.label, frame.flabel_pixel);
2446   } else {
2447     if (frame.ulabel)
2448       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2449                                  frame.label, frame.ulabel);
2450     else
2451       XSetWindowBackground(blackbox->getXDisplay(),
2452                            frame.label, frame.ulabel_pixel);
2453   }
2454   XClearWindow(blackbox->getXDisplay(), frame.label);
2455
2456   WindowStyle *style = screen->getWindowStyle();
2457
2458   int pos = frame.bevel_w * 2;
2459   style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2460   style->font->drawString(frame.label, pos, 1,
2461                           (flags.focused ? style->l_text_focus :
2462                            style->l_text_unfocus),
2463                           client.title);
2464 }
2465
2466
2467 void BlackboxWindow::redrawAllButtons(void) const {
2468   if (frame.iconify_button) redrawIconifyButton(False);
2469   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2470   if (frame.close_button) redrawCloseButton(False);
2471 }
2472
2473
2474 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2475   if (! pressed) {
2476     if (flags.focused) {
2477       if (frame.fbutton)
2478         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2479                                    frame.iconify_button, frame.fbutton);
2480       else
2481         XSetWindowBackground(blackbox->getXDisplay(),
2482                              frame.iconify_button, frame.fbutton_pixel);
2483     } else {
2484       if (frame.ubutton)
2485         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2486                                    frame.iconify_button, frame.ubutton);
2487       else
2488         XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2489                              frame.ubutton_pixel);
2490     }
2491   } else {
2492     if (frame.pbutton)
2493       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2494                                  frame.iconify_button, frame.pbutton);
2495     else
2496       XSetWindowBackground(blackbox->getXDisplay(),
2497                            frame.iconify_button, frame.pbutton_pixel);
2498   }
2499   XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2500
2501   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2502            screen->getWindowStyle()->b_pic_unfocus);
2503   XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2504                  2, (frame.button_w - 5), (frame.button_w - 5), 2);
2505 }
2506
2507
2508 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2509   if (! pressed) {
2510     if (flags.focused) {
2511       if (frame.fbutton)
2512         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2513                                    frame.maximize_button, frame.fbutton);
2514       else
2515         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2516                              frame.fbutton_pixel);
2517     } else {
2518       if (frame.ubutton)
2519         XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2520                                    frame.maximize_button, frame.ubutton);
2521       else
2522         XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2523                              frame.ubutton_pixel);
2524     }
2525   } else {
2526     if (frame.pbutton)
2527       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2528                                  frame.maximize_button, frame.pbutton);
2529     else
2530       XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2531                            frame.pbutton_pixel);
2532   }
2533   XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2534
2535   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2536            screen->getWindowStyle()->b_pic_unfocus);
2537   XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2538                  2, 2, (frame.button_w - 5), (frame.button_w - 5));
2539   XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2540             2, 3, (frame.button_w - 3), 3);
2541 }
2542
2543
2544 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2545   if (! pressed) {
2546     if (flags.focused) {
2547       if (frame.fbutton)
2548         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2549                                    frame.fbutton);
2550       else
2551         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2552                              frame.fbutton_pixel);
2553     } else {
2554       if (frame.ubutton)
2555         XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2556                                    frame.ubutton);
2557       else
2558         XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2559                              frame.ubutton_pixel);
2560     }
2561   } else {
2562     if (frame.pbutton)
2563       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2564                                  frame.close_button, frame.pbutton);
2565     else
2566       XSetWindowBackground(blackbox->getXDisplay(),
2567                            frame.close_button, frame.pbutton_pixel);
2568   }
2569   XClearWindow(blackbox->getXDisplay(), frame.close_button);
2570
2571   BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2572            screen->getWindowStyle()->b_pic_unfocus);
2573   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2574             2, 2, (frame.button_w - 3), (frame.button_w - 3));
2575   XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2576             2, (frame.button_w - 3), (frame.button_w - 3), 2);
2577 }
2578
2579
2580 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2581   if (re->window != client.window)
2582     return;
2583
2584 #ifdef    DEBUG
2585   fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2586           client.window);
2587 #endif // DEBUG
2588
2589   switch (current_state) {
2590   case IconicState:
2591     iconify();
2592     break;
2593
2594   case WithdrawnState:
2595     withdraw();
2596     break;
2597
2598   case NormalState:
2599   case InactiveState:
2600   case ZoomState:
2601   default:
2602     show();
2603     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2604     if (isNormal()) {
2605       if (! blackbox->isStartup()) {
2606         XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2607         if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2608                                     getTransientFor()->isFocused())) {
2609           setInputFocus();
2610         }
2611         if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2612           int x, y, rx, ry;
2613           Window c, r;
2614           unsigned int m;
2615           XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2616                         &r, &c, &rx, &ry, &x, &y, &m);
2617           beginMove(rx, ry);
2618         }
2619       }
2620     }
2621     break;
2622   }
2623 }
2624
2625
2626 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2627   if (ue->window != client.window)
2628     return;
2629
2630 #ifdef    DEBUG
2631   fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2632           client.window);
2633 #endif // DEBUG
2634
2635   screen->unmanageWindow(this, False);
2636 }
2637
2638
2639 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2640   if (de->window != client.window)
2641     return;
2642
2643 #ifdef    DEBUG
2644   fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2645           client.window);
2646 #endif // DEBUG
2647
2648   screen->unmanageWindow(this, False);
2649 }
2650
2651
2652 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2653   if (re->window != client.window || re->parent == frame.plate)
2654     return;
2655
2656 #ifdef    DEBUG
2657   fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2658           "0x%lx.\n", client.window, re->parent);
2659 #endif // DEBUG
2660
2661   XEvent ev;
2662   ev.xreparent = *re;
2663   XPutBackEvent(blackbox->getXDisplay(), &ev);
2664   screen->unmanageWindow(this, True);
2665 }
2666
2667
2668 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2669   if (pe->state == PropertyDelete)
2670     return;
2671
2672 #ifdef    DEBUG
2673   fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2674           client.window);
2675 #endif
2676
2677   switch(pe->atom) {
2678   case XA_WM_CLASS:
2679   case XA_WM_CLIENT_MACHINE:
2680   case XA_WM_COMMAND:
2681     break;
2682
2683   case XA_WM_TRANSIENT_FOR: {
2684     // determine if this is a transient window
2685     getTransientInfo();
2686
2687     // adjust the window decorations based on transience
2688     if (isTransient()) {
2689       decorations &= ~(Decor_Maximize | Decor_Handle);
2690       functions &= ~Func_Maximize;
2691       setAllowedActions();
2692     }
2693
2694     reconfigure();
2695   }
2696     break;
2697
2698   case XA_WM_HINTS:
2699     getWMHints();
2700     break;
2701
2702   case XA_WM_ICON_NAME:
2703     getWMIconName();
2704     if (flags.iconic) screen->propagateWindowName(this);
2705     break;
2706
2707   case XAtom::net_wm_name:
2708   case XA_WM_NAME:
2709     getWMName();
2710
2711     if (decorations & Decor_Titlebar)
2712       redrawLabel();
2713
2714     screen->propagateWindowName(this);
2715     break;
2716
2717   case XA_WM_NORMAL_HINTS: {
2718     getWMNormalHints();
2719
2720     if ((client.normal_hint_flags & PMinSize) &&
2721         (client.normal_hint_flags & PMaxSize)) {
2722       // the window now can/can't resize itself, so the buttons need to be
2723       // regrabbed.
2724       ungrabButtons();
2725       if (client.max_width <= client.min_width &&
2726           client.max_height <= client.min_height) {
2727         decorations &= ~(Decor_Maximize | Decor_Handle);
2728         functions &= ~(Func_Resize | Func_Maximize);
2729       } else {
2730         if (! isTransient()) {
2731           decorations |= Decor_Maximize | Decor_Handle;
2732           functions |= Func_Maximize;
2733         }
2734         functions |= Func_Resize;
2735       }
2736       grabButtons();
2737       setAllowedActions();
2738     }
2739
2740     Rect old_rect = frame.rect;
2741
2742     upsize();
2743
2744     if (old_rect != frame.rect)
2745       reconfigure();
2746
2747     break;
2748   }
2749
2750   default:
2751     if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2752       getWMProtocols();
2753
2754       if ((decorations & Decor_Close) && (! frame.close_button)) {
2755         createCloseButton();
2756         if (decorations & Decor_Titlebar) {
2757           positionButtons(True);
2758           XMapSubwindows(blackbox->getXDisplay(), frame.title);
2759         }
2760         if (windowmenu) windowmenu->reconfigure();
2761       }
2762     } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2763       updateStrut();
2764     }
2765
2766     break;
2767   }
2768 }
2769
2770
2771 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2772 #ifdef DEBUG
2773   fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2774 #endif
2775
2776   if (frame.label == ee->window && (decorations & Decor_Titlebar))
2777     redrawLabel();
2778   else if (frame.close_button == ee->window)
2779     redrawCloseButton(False);
2780   else if (frame.maximize_button == ee->window)
2781     redrawMaximizeButton(flags.maximized);
2782   else if (frame.iconify_button == ee->window)
2783     redrawIconifyButton(False);
2784 }
2785
2786
2787 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2788   if (cr->window != client.window || flags.iconic)
2789     return;
2790
2791   if (cr->value_mask & CWBorderWidth)
2792     client.old_bw = cr->border_width;
2793
2794   if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2795     Rect req = frame.rect;
2796
2797     if (cr->value_mask & (CWX | CWY)) {
2798       if (cr->value_mask & CWX)
2799         client.rect.setX(cr->x);
2800       if (cr->value_mask & CWY)
2801         client.rect.setY(cr->y);
2802
2803       applyGravity(req);
2804     }
2805
2806     if (cr->value_mask & CWWidth)
2807       req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2808
2809     if (cr->value_mask & CWHeight)
2810       req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2811
2812     configure(req.x(), req.y(), req.width(), req.height());
2813   }
2814
2815   if (cr->value_mask & CWStackMode && !isDesktop()) {
2816     switch (cr->detail) {
2817     case Below:
2818     case BottomIf:
2819       screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2820       break;
2821
2822     case Above:
2823     case TopIf:
2824     default:
2825       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2826       break;
2827     }
2828   }
2829 }
2830
2831
2832 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2833 #ifdef DEBUG
2834   fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2835           client.window);
2836 #endif
2837
2838   if (frame.maximize_button == be->window && be->button <= 3) {
2839     redrawMaximizeButton(True);
2840   } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2841     if (! flags.focused)
2842       setInputFocus();
2843
2844     if (frame.iconify_button == be->window) {
2845       redrawIconifyButton(True);
2846     } else if (frame.close_button == be->window) {
2847       redrawCloseButton(True);
2848     } else if (frame.plate == be->window) {
2849       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2850
2851       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2852
2853       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2854     } else {
2855       if (frame.title == be->window || frame.label == be->window) {
2856         if (((be->time - lastButtonPressTime) <=
2857              blackbox->getDoubleClickInterval()) ||
2858             (be->state == ControlMask)) {
2859           lastButtonPressTime = 0;
2860           shade();
2861         } else {
2862           lastButtonPressTime = be->time;
2863         }
2864       }
2865
2866       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2867
2868       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2869     }
2870   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2871              (be->window != frame.close_button)) {
2872     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2873   } else if (windowmenu && be->button == 3 &&
2874              (frame.title == be->window || frame.label == be->window ||
2875               frame.handle == be->window || frame.window == be->window)) {
2876     if (windowmenu->isVisible()) {
2877       windowmenu->hide();
2878     } else {
2879       int mx = be->x_root - windowmenu->getWidth() / 2,
2880           my = be->y_root - windowmenu->getHeight() / 2;
2881
2882       // snap the window menu into a corner/side if necessary
2883       int left_edge, right_edge, top_edge, bottom_edge;
2884
2885       /*
2886          the " + (frame.border_w * 2) - 1" bits are to get the proper width
2887          and height of the menu, as the sizes returned by it do not include
2888          the borders.
2889        */
2890       left_edge = frame.rect.x();
2891       right_edge = frame.rect.right() -
2892         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2893       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2894       bottom_edge = client.rect.bottom() -
2895         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2896         (frame.border_w + frame.mwm_border_w);
2897
2898       if (mx < left_edge)
2899         mx = left_edge;
2900       if (mx > right_edge)
2901         mx = right_edge;
2902       if (my < top_edge)
2903         my = top_edge;
2904       if (my > bottom_edge)
2905         my = bottom_edge;
2906
2907       windowmenu->move(mx, my);
2908       windowmenu->show();
2909       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2910       XRaiseWindow(blackbox->getXDisplay(),
2911                    windowmenu->getSendToMenu()->getWindowID());
2912     }
2913   // mouse wheel up
2914   } else if (be->button == 4) {
2915     if ((be->window == frame.label ||
2916          be->window == frame.title ||
2917          be->window == frame.maximize_button ||
2918          be->window == frame.iconify_button ||
2919          be->window == frame.close_button) &&
2920         ! flags.shaded)
2921       shade();
2922   // mouse wheel down
2923   } else if (be->button == 5) {
2924     if ((be->window == frame.label ||
2925          be->window == frame.title ||
2926          be->window == frame.maximize_button ||
2927          be->window == frame.iconify_button ||
2928          be->window == frame.close_button) &&
2929         flags.shaded)
2930       shade();
2931   }
2932 }
2933
2934
2935 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2936 #ifdef DEBUG
2937   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2938           client.window);
2939 #endif
2940
2941   if (re->window == frame.maximize_button &&
2942       re->button >= 1 && re->button <= 3) {
2943     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2944         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2945       maximize(re->button);
2946     } else {
2947       redrawMaximizeButton(flags.maximized);
2948     }
2949   } else if (re->window == frame.iconify_button && re->button == 1) {
2950     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2951         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2952       iconify();
2953     } else {
2954       redrawIconifyButton(False);
2955     }
2956   } else if (re->window == frame.close_button & re->button == 1) {
2957     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2958         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2959       close();
2960     redrawCloseButton(False);
2961   } else if (flags.moving) {
2962     endMove();
2963   } else if (flags.resizing) {
2964     endResize();
2965   } else if (re->window == frame.window) {
2966     if (re->button == 2 && re->state == ModMask)
2967       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2968   }
2969 }
2970
2971
2972
2973 void BlackboxWindow::beginMove(int x_root, int y_root) {
2974   assert(! (flags.resizing || flags.moving));
2975
2976   /*
2977     Only one window can be moved/resized at a time. If another window is already
2978     being moved or resized, then stop it before whating to work with this one.
2979   */
2980   BlackboxWindow *changing = blackbox->getChangingWindow();
2981   if (changing && changing != this) {
2982     if (changing->flags.moving)
2983       changing->endMove();
2984     else // if (changing->flags.resizing)
2985       changing->endResize();
2986   }
2987   
2988   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2989                PointerMotionMask | ButtonReleaseMask,
2990                GrabModeAsync, GrabModeAsync,
2991                None, blackbox->getMoveCursor(), CurrentTime);
2992
2993   if (windowmenu && windowmenu->isVisible())
2994     windowmenu->hide();
2995
2996   flags.moving = True;
2997   blackbox->setChangingWindow(this);
2998
2999   if (! screen->doOpaqueMove()) {
3000     XGrabServer(blackbox->getXDisplay());
3001
3002     frame.changing = frame.rect;
3003     screen->showPosition(frame.changing.x(), frame.changing.y());
3004
3005     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3006                    screen->getOpGC(),
3007                    frame.changing.x(),
3008                    frame.changing.y(),
3009                    frame.changing.width() - 1,
3010                    frame.changing.height() - 1);
3011   }
3012
3013   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3014   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3015 }
3016
3017
3018 void BlackboxWindow::doMove(int x_root, int y_root) {
3019   assert(flags.moving);
3020   assert(blackbox->getChangingWindow() == this);
3021
3022   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3023   dx -= frame.border_w;
3024   dy -= frame.border_w;
3025
3026   if (screen->doWorkspaceWarping()) {
3027     // workspace warping
3028     bool warp = False;
3029     unsigned int dest = screen->getCurrentWorkspaceID();
3030     if (x_root <= 0) {
3031       warp = True;
3032
3033       if (dest > 0) dest--;
3034       else dest = screen->getNumberOfWorkspaces() - 1;
3035
3036     } else if (x_root >= screen->getRect().right()) {
3037       warp = True;
3038
3039       if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3040       else dest = 0;
3041     }
3042     if (warp) {
3043       endMove();
3044       bool focus = flags.focused; // had focus while moving?
3045       if (! flags.stuck)
3046         screen->reassociateWindow(this, dest, False);
3047       screen->changeWorkspaceID(dest);
3048       if (focus)
3049         setInputFocus();
3050
3051       /*
3052          If the XWarpPointer is done after the configure, we can end up
3053          grabbing another window, so made sure you do it first.
3054       */
3055       int dest_x;
3056       if (x_root <= 0) {
3057         dest_x = screen->getRect().right() - 1;
3058         XWarpPointer(blackbox->getXDisplay(), None, 
3059                      screen->getRootWindow(), 0, 0, 0, 0,
3060                      dest_x, y_root);
3061
3062         configure(dx + (screen->getRect().width() - 1), dy,
3063                   frame.rect.width(), frame.rect.height());
3064       } else {
3065         dest_x = 0;
3066         XWarpPointer(blackbox->getXDisplay(), None, 
3067                      screen->getRootWindow(), 0, 0, 0, 0,
3068                      dest_x, y_root);
3069
3070         configure(dx - (screen->getRect().width() - 1), dy,
3071                   frame.rect.width(), frame.rect.height());
3072       }
3073
3074       beginMove(dest_x, y_root);
3075       return;
3076     }
3077   }
3078
3079   const int snap_distance = screen->getEdgeSnapThreshold();
3080
3081   if (snap_distance) {
3082     // window corners
3083     const int wleft = dx,
3084               wright = dx + frame.rect.width() - 1,
3085               wtop = dy,
3086               wbottom = dy + frame.rect.height() - 1;
3087
3088     if (screen->getWindowToWindowSnap()) {
3089       Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3090       assert(w);
3091
3092       // try snap to another window
3093       for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
3094         BlackboxWindow *snapwin = w->getWindow(i);
3095         if (snapwin == this)
3096           continue;   // don't snap to self
3097
3098         bool snapped = False;
3099         
3100         const Rect &winrect = snapwin->frameRect();
3101         int dleft = abs(wright - winrect.left()),
3102            dright = abs(wleft - winrect.right()),
3103              dtop = abs(wbottom - winrect.top()),
3104           dbottom = abs(wtop - winrect.bottom());
3105
3106         if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3107             wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3108
3109           // snap left of other window?
3110           if (dleft < snap_distance && dleft <= dright) {
3111             dx = winrect.left() - frame.rect.width();
3112             snapped = True;
3113           }
3114           // snap right of other window?
3115           else if (dright < snap_distance) {
3116             dx = winrect.right() + 1;
3117             snapped = True;
3118           }
3119
3120           if (snapped) {
3121             if (screen->getWindowCornerSnap()) {
3122               // try corner-snap to its other sides
3123               dtop = abs(wtop - winrect.top());
3124               dbottom = abs(wbottom - winrect.bottom());
3125               if (dtop < snap_distance && dtop <= dbottom)
3126                 dy = winrect.top();
3127               else if (dbottom < snap_distance)
3128                 dy = winrect.bottom() - frame.rect.height() + 1;
3129             }
3130
3131             continue;
3132           }
3133         }
3134
3135         if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3136             wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3137
3138           // snap top of other window?
3139           if (dtop < snap_distance && dtop <= dbottom) {
3140             dy = winrect.top() - frame.rect.height();
3141             snapped = True;
3142           }
3143           // snap bottom of other window?
3144           else if (dbottom < snap_distance) {
3145             dy = winrect.bottom() + 1;
3146             snapped = True;
3147           }
3148
3149           if (snapped) {
3150             if (screen->getWindowCornerSnap()) {
3151               // try corner-snap to its other sides
3152               dleft = abs(wleft - winrect.left());
3153               dright = abs(wright - winrect.right());
3154               if (dleft < snap_distance && dleft <= dright)
3155                 dx = winrect.left();
3156               else if (dright < snap_distance)
3157                 dx = winrect.right() - frame.rect.width() + 1;
3158             }
3159
3160             continue;
3161           }
3162         }
3163       }
3164     }
3165
3166     RectList snaplist; // the list of rects we will try to snap to
3167
3168     // snap to the strut (and screen boundaries for xinerama)
3169 #ifdef    XINERAMA
3170     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3171       if (! screen->doFullMax())
3172         snaplist.insert(snaplist.begin(),
3173                         screen->allAvailableAreas().begin(),
3174                         screen->allAvailableAreas().end());
3175
3176       // always snap to the screen edges
3177       snaplist.insert(snaplist.begin(),
3178                       screen->getXineramaAreas().begin(),
3179                       screen->getXineramaAreas().end());
3180     } else
3181 #endif // XINERAMA
3182     {
3183       if (! screen->doFullMax())
3184         snaplist.push_back(screen->availableArea());
3185
3186       // always snap to the screen edges
3187       snaplist.push_back(screen->getRect());
3188     }
3189
3190     RectList::const_iterator it, end = snaplist.end();
3191     for (it = snaplist.begin(); it != end; ++it) {
3192       const Rect &srect = *it;
3193
3194       // if we're not in the rectangle then don't snap to it.
3195       if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3196                                   frame.rect.height())))
3197         continue;
3198
3199       int dleft = abs(wleft - srect.left()),
3200          dright = abs(wright - srect.right()),
3201            dtop = abs(wtop - srect.top()),
3202         dbottom = abs(wbottom - srect.bottom());
3203
3204         // snap left?
3205         if (dleft < snap_distance && dleft <= dright)
3206           dx = srect.left();
3207         // snap right?
3208         else if (dright < snap_distance)
3209           dx = srect.right() - frame.rect.width() + 1;
3210
3211         // snap top?
3212         if (dtop < snap_distance && dtop <= dbottom)
3213           dy = srect.top();
3214         // snap bottom?
3215         else if (dbottom < snap_distance)
3216           dy = srect.bottom() - frame.rect.height() + 1;
3217     }
3218   }
3219
3220   if (screen->doOpaqueMove()) {
3221     configure(dx, dy, frame.rect.width(), frame.rect.height());
3222   } else {
3223     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3224                    screen->getOpGC(),
3225                    frame.changing.x(),
3226                    frame.changing.y(),
3227                    frame.changing.width() - 1,
3228                    frame.changing.height() - 1);
3229
3230     frame.changing.setPos(dx, dy);
3231
3232     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3233                    screen->getOpGC(),
3234                    frame.changing.x(),
3235                    frame.changing.y(),
3236                    frame.changing.width() - 1,
3237                    frame.changing.height() - 1);
3238   }
3239
3240   screen->showPosition(dx, dy);
3241 }
3242
3243
3244 void BlackboxWindow::endMove(void) {
3245   assert(flags.moving);
3246   assert(blackbox->getChangingWindow() == this);
3247
3248   flags.moving = False;
3249   blackbox->setChangingWindow(0);
3250
3251   if (! screen->doOpaqueMove()) {
3252     /* when drawing the rubber band, we need to make sure we only draw inside
3253      * the frame... frame.changing_* contain the new coords for the window,
3254      * so we need to subtract 1 from changing_w/changing_h every where we
3255      * draw the rubber band (for both moving and resizing)
3256      */
3257     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3258                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3259                    frame.changing.width() - 1, frame.changing.height() - 1);
3260       XUngrabServer(blackbox->getXDisplay());
3261   
3262       configure(frame.changing.x(), frame.changing.y(),
3263                 frame.changing.width(), frame.changing.height());
3264   } else {
3265     configure(frame.rect.x(), frame.rect.y(),
3266               frame.rect.width(), frame.rect.height());
3267   }
3268   screen->hideGeometry();
3269
3270   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3271
3272   // if there are any left over motions from the move, drop them now
3273   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3274   XEvent e;
3275   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3276                                 MotionNotify, &e));
3277 }
3278
3279
3280 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3281   assert(! (flags.resizing || flags.moving));
3282
3283   /*
3284     Only one window can be moved/resized at a time. If another window is already
3285     being moved or resized, then stop it before whating to work with this one.
3286   */
3287   BlackboxWindow *changing = blackbox->getChangingWindow();
3288   if (changing && changing != this) {
3289     if (changing->flags.moving)
3290       changing->endMove();
3291     else // if (changing->flags.resizing)
3292       changing->endResize();
3293   }
3294
3295   resize_dir = dir;
3296
3297   Cursor cursor;
3298   Corner anchor;
3299   
3300   switch (resize_dir) {
3301   case BottomLeft:
3302     anchor = TopRight;
3303     cursor = blackbox->getLowerLeftAngleCursor();
3304     break;
3305
3306   case BottomRight:
3307     anchor = TopLeft;
3308     cursor = blackbox->getLowerRightAngleCursor();
3309     break;
3310
3311   case TopLeft:
3312     anchor = BottomRight;
3313     cursor = blackbox->getUpperLeftAngleCursor();
3314     break;
3315
3316   case TopRight:
3317     anchor = BottomLeft;
3318     cursor = blackbox->getUpperRightAngleCursor();
3319     break;
3320
3321   default:
3322     assert(false); // unhandled Corner
3323     return;        // unreachable, for the compiler
3324   }
3325   
3326   XGrabServer(blackbox->getXDisplay());
3327   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3328                PointerMotionMask | ButtonReleaseMask,
3329                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3330
3331   flags.resizing = True;
3332   blackbox->setChangingWindow(this);
3333
3334   int gw, gh;
3335   frame.changing = frame.rect;
3336
3337   constrain(anchor,  &gw, &gh);
3338
3339   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3340                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3341                  frame.changing.width() - 1, frame.changing.height() - 1);
3342
3343   screen->showGeometry(gw, gh);
3344   
3345   frame.grab_x = x_root;
3346   frame.grab_y = y_root;
3347 }
3348
3349
3350 void BlackboxWindow::doResize(int x_root, int y_root) {
3351   assert(flags.resizing);
3352   assert(blackbox->getChangingWindow() == this);
3353
3354   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3355                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3356                  frame.changing.width() - 1, frame.changing.height() - 1);
3357
3358   int gw, gh;
3359   Corner anchor;
3360
3361   switch (resize_dir) {
3362   case BottomLeft:
3363     anchor = TopRight;
3364     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3365                            frame.rect.height() + (y_root - frame.grab_y));
3366     break;
3367   case BottomRight:
3368     anchor = TopLeft;
3369     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3370                            frame.rect.height() + (y_root - frame.grab_y));
3371     break;
3372   case TopLeft:
3373     anchor = BottomRight;
3374     frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3375                            frame.rect.height() - (y_root - frame.grab_y));
3376     break;
3377   case TopRight:
3378     anchor = BottomLeft;
3379     frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3380                            frame.rect.height() - (y_root - frame.grab_y));
3381     break;
3382
3383   default:
3384     assert(false); // unhandled Corner
3385     return;        // unreachable, for the compiler
3386   }
3387   
3388   constrain(anchor, &gw, &gh);
3389
3390   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3391                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3392                  frame.changing.width() - 1, frame.changing.height() - 1);
3393
3394   screen->showGeometry(gw, gh);
3395 }
3396
3397
3398 void BlackboxWindow::endResize(void) {
3399   assert(flags.resizing);
3400   assert(blackbox->getChangingWindow() == this);
3401
3402   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3403                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3404                  frame.changing.width() - 1, frame.changing.height() - 1);
3405   XUngrabServer(blackbox->getXDisplay());
3406
3407   // unset maximized state after resized when fully maximized
3408   if (flags.maximized == 1)
3409     maximize(0);
3410   
3411   flags.resizing = False;
3412   blackbox->setChangingWindow(0);
3413
3414   configure(frame.changing.x(), frame.changing.y(),
3415             frame.changing.width(), frame.changing.height());
3416   screen->hideGeometry();
3417
3418   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3419   
3420   // if there are any left over motions from the resize, drop them now
3421   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3422   XEvent e;
3423   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3424                                 MotionNotify, &e));
3425 }
3426
3427
3428 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3429 #ifdef DEBUG
3430   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3431           client.window);
3432 #endif
3433
3434   if (flags.moving) {
3435     doMove(me->x_root, me->y_root);
3436   } else if (flags.resizing) {
3437     doResize(me->x_root, me->y_root);
3438   } else {
3439     if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3440         (frame.title == me->window || frame.label == me->window ||
3441          frame.handle == me->window || frame.window == me->window)) {
3442       beginMove(me->x_root, me->y_root);
3443     } else if ((functions & Func_Resize) &&
3444                (me->state & Button1Mask && (me->window == frame.right_grip ||
3445                                             me->window == frame.left_grip)) ||
3446                (me->state & Button3Mask && me->state & ModMask &&
3447                 me->window == frame.window)) {
3448       unsigned int zones = screen->getResizeZones();
3449       Corner corner;
3450       
3451       if (me->window == frame.left_grip) {
3452         corner = BottomLeft;
3453       } else if (me->window == frame.right_grip || zones == 1) {
3454         corner = BottomRight;
3455       } else {
3456         bool top;
3457         bool left = (me->x_root - frame.rect.x() <=
3458                      static_cast<signed>(frame.rect.width() / 2));
3459         if (zones == 2)
3460           top = False;
3461         else // (zones == 4)
3462           top = (me->y_root - frame.rect.y() <=
3463                  static_cast<signed>(frame.rect.height() / 2));
3464         corner = (top ? (left ? TopLeft : TopRight) :
3465                         (left ? BottomLeft : BottomRight));
3466       }
3467
3468       beginResize(me->x_root, me->y_root, corner);
3469     }
3470   }
3471 }
3472
3473
3474 #ifdef    SHAPE
3475 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3476   if (blackbox->hasShapeExtensions() && flags.shaped) {
3477     configureShape();
3478   }
3479 }
3480 #endif // SHAPE
3481
3482
3483 bool BlackboxWindow::validateClient(void) const {
3484   XSync(blackbox->getXDisplay(), False);
3485
3486   XEvent e;
3487   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3488                              DestroyNotify, &e) ||
3489       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3490                              UnmapNotify, &e)) {
3491     XPutBackEvent(blackbox->getXDisplay(), &e);
3492
3493     return False;
3494   }
3495
3496   return True;
3497 }
3498
3499
3500 void BlackboxWindow::restore(bool remap) {
3501   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3502   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3503   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3504
3505   // do not leave a shaded window as an icon unless it was an icon
3506   if (flags.shaded && ! flags.iconic)
3507     setState(NormalState);
3508
3509   restoreGravity(client.rect);
3510
3511   XUnmapWindow(blackbox->getXDisplay(), frame.window);
3512   XUnmapWindow(blackbox->getXDisplay(), client.window);
3513
3514   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3515
3516   XEvent ev;
3517   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3518                              ReparentNotify, &ev)) {
3519     remap = True;
3520   } else {
3521     // according to the ICCCM - if the client doesn't reparent to
3522     // root, then we have to do it for them
3523     XReparentWindow(blackbox->getXDisplay(), client.window,
3524                     screen->getRootWindow(),
3525                     client.rect.x(), client.rect.y());
3526   }
3527
3528   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3529 }
3530
3531
3532 // timer for autoraise
3533 void BlackboxWindow::timeout(void) {
3534   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3535 }
3536
3537
3538 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3539   if ((net->flags & AttribShaded) &&
3540       ((blackbox_attrib.attrib & AttribShaded) !=
3541        (net->attrib & AttribShaded)))
3542     shade();
3543
3544   if (flags.visible && // watch out for requests when we can not be seen
3545       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3546       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3547        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3548     if (flags.maximized) {
3549       maximize(0);
3550     } else {
3551       int button = 0;
3552
3553       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3554         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3555       else if (net->flags & AttribMaxVert)
3556         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3557       else if (net->flags & AttribMaxHoriz)
3558         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3559
3560       maximize(button);
3561     }
3562   }
3563
3564   if ((net->flags & AttribOmnipresent) &&
3565       ((blackbox_attrib.attrib & AttribOmnipresent) !=
3566        (net->attrib & AttribOmnipresent)))
3567     stick();
3568
3569   if ((net->flags & AttribWorkspace) &&
3570       (blackbox_attrib.workspace != net->workspace)) {
3571     screen->reassociateWindow(this, net->workspace, True);
3572
3573     if (screen->getCurrentWorkspaceID() != net->workspace) {
3574       withdraw();
3575     } else {
3576       show();
3577       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3578     }
3579   }
3580
3581   if (net->flags & AttribDecoration) {
3582     switch (net->decoration) {
3583     case DecorNone:
3584       decorations = 0;
3585
3586       break;
3587
3588     default:
3589     case DecorNormal:
3590       decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3591   
3592       decorations = ((functions & Func_Resize) && !isTransient() ?
3593                      decorations | Decor_Handle :
3594                      decorations &= ~Decor_Handle);
3595       decorations = (functions & Func_Maximize ?
3596                      decorations | Decor_Maximize :
3597                      decorations &= ~Decor_Maximize);
3598
3599       break;
3600
3601     case DecorTiny:
3602       decorations |= Decor_Titlebar | Decor_Iconify;
3603       decorations &= ~(Decor_Border | Decor_Handle);
3604       
3605       decorations = (functions & Func_Maximize ?
3606                      decorations | Decor_Maximize :
3607                      decorations &= ~Decor_Maximize);
3608
3609       break;
3610
3611     case DecorTool:
3612       decorations |= Decor_Titlebar;
3613       decorations &= ~(Decor_Iconify | Decor_Border);
3614
3615       decorations = ((functions & Func_Resize) && !isTransient() ?
3616                      decorations | Decor_Handle :
3617                      decorations &= ~Decor_Handle);
3618       decorations = (functions & Func_Maximize ?
3619                      decorations | Decor_Maximize :
3620                      decorations &= ~Decor_Maximize);
3621
3622       break;
3623     }
3624
3625     // we can not be shaded if we lack a titlebar
3626     if (flags.shaded && ! (decorations & Decor_Titlebar))
3627       shade();
3628
3629     if (flags.visible && frame.window) {
3630       XMapSubwindows(blackbox->getXDisplay(), frame.window);
3631       XMapWindow(blackbox->getXDisplay(), frame.window);
3632     }
3633
3634     reconfigure();
3635     setState(current_state);
3636   }
3637 }
3638
3639
3640 /*
3641  * Set the sizes of all components of the window frame
3642  * (the window decorations).
3643  * These values are based upon the current style settings and the client
3644  * window's dimensions.
3645  */
3646 void BlackboxWindow::upsize(void) {
3647   frame.bevel_w = screen->getBevelWidth();
3648
3649   if (decorations & Decor_Border) {
3650     frame.border_w = screen->getBorderWidth();
3651     if (! isTransient())
3652       frame.mwm_border_w = screen->getFrameWidth();
3653     else
3654       frame.mwm_border_w = 0;
3655   } else {
3656     frame.mwm_border_w = frame.border_w = 0;
3657   }
3658
3659   if (decorations & Decor_Titlebar) {
3660     // the height of the titlebar is based upon the height of the font being
3661     // used to display the window's title
3662     WindowStyle *style = screen->getWindowStyle();
3663     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3664
3665     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3666     frame.button_w = (frame.label_h - 2);
3667
3668     // set the top frame margin
3669     frame.margin.top = frame.border_w + frame.title_h +
3670                        frame.border_w + frame.mwm_border_w;
3671   } else {
3672     frame.title_h = 0;
3673     frame.label_h = 0;
3674     frame.button_w = 0;
3675
3676     // set the top frame margin
3677     frame.margin.top = frame.border_w + frame.mwm_border_w;
3678   }
3679
3680   // set the left/right frame margin
3681   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3682
3683   if (decorations & Decor_Handle) {
3684     frame.grip_w = frame.button_w * 2;
3685     frame.handle_h = screen->getHandleWidth();
3686
3687     // set the bottom frame margin
3688     frame.margin.bottom = frame.border_w + frame.handle_h +
3689                           frame.border_w + frame.mwm_border_w;
3690   } else {
3691     frame.handle_h = 0;
3692     frame.grip_w = 0;
3693
3694     // set the bottom frame margin
3695     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3696   }
3697
3698   /*
3699     We first get the normal dimensions and use this to define the inside_w/h
3700     then we modify the height if shading is in effect.
3701     If the shade state is not considered then frame.rect gets reset to the
3702     normal window size on a reconfigure() call resulting in improper
3703     dimensions appearing in move/resize and other events.
3704   */
3705   unsigned int
3706     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3707     width = client.rect.width() + frame.margin.left + frame.margin.right;
3708
3709   frame.inside_w = width - (frame.border_w * 2);
3710   frame.inside_h = height - (frame.border_w * 2);
3711
3712   if (flags.shaded)
3713     height = frame.title_h + (frame.border_w * 2);
3714   frame.rect.setSize(width, height);
3715 }
3716
3717
3718 /*
3719  * Calculate the size of the client window and constrain it to the
3720  * size specified by the size hints of the client window.
3721  *
3722  * The logical width and height are placed into pw and ph, if they
3723  * are non-zero.  Logical size refers to the users perception of
3724  * the window size (for example an xterm resizes in cells, not in pixels).
3725  *
3726  * The physical geometry is placed into frame.changing_{x,y,width,height}.
3727  * Physical geometry refers to the geometry of the window in pixels.
3728  */
3729 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3730   // frame.changing represents the requested frame size, we need to
3731   // strip the frame margin off and constrain the client size
3732   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3733                            frame.changing.top() + frame.margin.top,
3734                            frame.changing.right() - frame.margin.right,
3735                            frame.changing.bottom() - frame.margin.bottom);
3736
3737   int dw = frame.changing.width(), dh = frame.changing.height(),
3738     base_width = (client.base_width) ? client.base_width : client.min_width,
3739     base_height = (client.base_height) ? client.base_height :
3740                                          client.min_height;
3741
3742   // constrain
3743   if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3744   if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3745   if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3746   if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3747
3748   dw -= base_width;
3749   dw /= client.width_inc;
3750   dh -= base_height;
3751   dh /= client.height_inc;
3752
3753   if (pw) {
3754     if (client.width_inc == 1)
3755       *pw = dw + base_width;
3756     else
3757       *pw = dw;
3758   }
3759   if (ph) {
3760     if (client.height_inc == 1)
3761       *ph = dh + base_height;
3762     else
3763       *ph = dh;
3764   }
3765
3766   dw *= client.width_inc;
3767   dw += base_width;
3768   dh *= client.height_inc;
3769   dh += base_height;
3770
3771   frame.changing.setSize(dw, dh);
3772
3773   // add the frame margin back onto frame.changing
3774   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3775                            frame.changing.top() - frame.margin.top,
3776                            frame.changing.right() + frame.margin.right,
3777                            frame.changing.bottom() + frame.margin.bottom);
3778
3779   // move frame.changing to the specified anchor
3780   int dx = 0,
3781       dy = 0;
3782   switch (anchor) {
3783   case TopLeft:
3784     break;
3785
3786   case TopRight:
3787     dx = frame.rect.right() - frame.changing.right();
3788     break;
3789
3790   case BottomLeft:
3791     dy = frame.rect.bottom() - frame.changing.bottom();
3792     break;
3793
3794   case BottomRight:
3795     dx = frame.rect.right() - frame.changing.right();
3796     dy = frame.rect.bottom() - frame.changing.bottom();
3797     break;
3798
3799   default:
3800     assert(false);  // unhandled corner
3801   }
3802   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3803 }
3804
3805
3806 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3807                             unsigned int max_length,
3808                             unsigned int modifier) const {
3809   size_t text_len = text.size();
3810   unsigned int length;
3811
3812   do {
3813     length = font->measureString(string(text, 0, text_len)) + modifier;
3814   } while (length > max_length && text_len-- > 0);
3815
3816   switch (justify) {
3817   case RightJustify:
3818     start_pos += max_length - length;
3819     break;
3820
3821   case CenterJustify:
3822     start_pos += (max_length - length) / 2;
3823     break;
3824
3825   case LeftJustify:
3826   default:
3827     break;
3828   }
3829 }
3830
3831
3832 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3833   : blackbox(b), group(_group) {
3834   XWindowAttributes wattrib;
3835   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3836     // group window doesn't seem to exist anymore
3837     delete this;
3838     return;
3839   }
3840
3841   XSelectInput(blackbox->getXDisplay(), group,
3842                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3843
3844   blackbox->saveGroupSearch(group, this);
3845 }
3846
3847
3848 BWindowGroup::~BWindowGroup(void) {
3849   blackbox->removeGroupSearch(group);
3850 }
3851
3852
3853 BlackboxWindow *
3854 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3855   BlackboxWindow *ret = blackbox->getFocusedWindow();
3856
3857   // does the focus window match (or any transient_fors)?
3858   while (ret) {
3859     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3860       if (ret->isTransient() && allow_transients) break;
3861       else if (! ret->isTransient()) break;
3862     }
3863
3864     ret = ret->getTransientFor();
3865   }
3866
3867   if (ret) return ret;
3868
3869   // the focus window didn't match, look in the group's window list
3870   BlackboxWindowList::const_iterator it, end = windowList.end();
3871   for (it = windowList.begin(); it != end; ++it) {
3872     ret = *it;
3873     if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3874       if (ret->isTransient() && allow_transients) break;
3875       else if (! ret->isTransient()) break;
3876     }
3877   }
3878
3879   return ret;
3880 }