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