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