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