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