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