]> icculus.org git repositories - dana/openbox.git/blob - src/Window.cc
fix the mozilla crash from the default premax being 0
[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       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3127
3128       XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
3129     } else {
3130       if (frame.title == be->window || frame.label == be->window) {
3131         if (((be->time - lastButtonPressTime) <=
3132              blackbox->getDoubleClickInterval()) ||
3133             (be->state == ControlMask)) {
3134           lastButtonPressTime = 0;
3135           shade();
3136         } else {
3137           lastButtonPressTime = be->time;
3138         }
3139       }
3140
3141       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3142
3143       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3144     }
3145   } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3146              (be->window != frame.close_button) &&
3147              (be->window != frame.stick_button)) {
3148     screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3149   } else if (windowmenu && be->button == 3 &&
3150              (frame.title == be->window || frame.label == be->window ||
3151               frame.handle == be->window || frame.window == be->window)) {
3152     if (windowmenu->isVisible()) {
3153       windowmenu->hide();
3154     } else {
3155       int mx = be->x_root - windowmenu->getWidth() / 2,
3156           my = be->y_root - windowmenu->getHeight() / 2;
3157
3158       // snap the window menu into a corner/side if necessary
3159       int left_edge, right_edge, top_edge, bottom_edge;
3160
3161       /*
3162          the " + (frame.border_w * 2) - 1" bits are to get the proper width
3163          and height of the menu, as the sizes returned by it do not include
3164          the borders.
3165        */
3166       left_edge = frame.rect.x();
3167       right_edge = frame.rect.right() -
3168         (windowmenu->getWidth() + (frame.border_w * 2) - 1);
3169       top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
3170       bottom_edge = client.rect.bottom() -
3171         (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
3172         (frame.border_w + frame.mwm_border_w);
3173
3174       if (mx < left_edge)
3175         mx = left_edge;
3176       else if (mx > right_edge)
3177         mx = right_edge;
3178       if (my < top_edge)
3179         my = top_edge;
3180       else if (my > bottom_edge)
3181         my = bottom_edge;
3182
3183       if (my + windowmenu->getHeight() > screen->getHeight())
3184         my = screen->getHeight() - windowmenu->getHeight() -
3185           (screen->getBorderWidth() * 2);
3186       
3187       windowmenu->move(mx, my);
3188       windowmenu->show();
3189       XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
3190       XRaiseWindow(blackbox->getXDisplay(),
3191                    windowmenu->getSendToMenu()->getWindowID());
3192     }
3193   // mouse wheel up
3194   } else if (be->button == 4) {
3195     if ((be->window == frame.label ||
3196          be->window == frame.title ||
3197          be->window == frame.maximize_button ||
3198          be->window == frame.iconify_button ||
3199          be->window == frame.close_button ||
3200          be->window == frame.stick_button) &&
3201         ! flags.shaded)
3202       shade();
3203   // mouse wheel down
3204   } else if (be->button == 5) {
3205     if ((be->window == frame.label ||
3206          be->window == frame.title ||
3207          be->window == frame.maximize_button ||
3208          be->window == frame.iconify_button ||
3209          be->window == frame.close_button ||
3210          be->window == frame.stick_button) &&
3211         flags.shaded)
3212       shade();
3213   }
3214 }
3215
3216
3217 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
3218 #ifdef DEBUG
3219   fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3220           client.window);
3221 #endif
3222
3223   if (re->window == frame.maximize_button &&
3224       re->button >= 1 && re->button <= 3) {
3225     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3226         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3227       maximize(re->button);
3228     } else {
3229       redrawMaximizeButton(flags.maximized);
3230     }
3231   } else if (re->window == frame.iconify_button && re->button == 1) {
3232     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3233         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3234       iconify();
3235     } else {
3236       redrawIconifyButton(False);
3237     }
3238   } else if (re->window == frame.stick_button && re->button == 1) {
3239     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3240         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3241       stick();
3242     } else {
3243       redrawStickyButton(False);
3244     }
3245   } else if (re->window == frame.close_button & re->button == 1) {
3246     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3247         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3248       close();
3249     redrawCloseButton(False);
3250   } else if (flags.moving) {
3251     endMove();
3252   } else if (flags.resizing) {
3253     endResize();
3254   } else if (re->window == frame.window) {
3255     if (re->button == 2 && re->state == mod_mask)
3256       XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3257   }
3258 }
3259
3260
3261
3262 void BlackboxWindow::beginMove(int x_root, int y_root) {
3263   if (! (functions & Func_Move)) return;
3264
3265   assert(! (flags.resizing || flags.moving));
3266
3267   /*
3268     Only one window can be moved/resized at a time. If another window is already
3269     being moved or resized, then stop it before whating to work with this one.
3270   */
3271   BlackboxWindow *changing = blackbox->getChangingWindow();
3272   if (changing && changing != this) {
3273     if (changing->flags.moving)
3274       changing->endMove();
3275     else // if (changing->flags.resizing)
3276       changing->endResize();
3277   }
3278   
3279   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3280                PointerMotionMask | ButtonReleaseMask,
3281                GrabModeAsync, GrabModeAsync,
3282                None, blackbox->getMoveCursor(), CurrentTime);
3283
3284   if (windowmenu && windowmenu->isVisible())
3285     windowmenu->hide();
3286
3287   flags.moving = True;
3288   blackbox->setChangingWindow(this);
3289
3290   if (! screen->doOpaqueMove()) {
3291     XGrabServer(blackbox->getXDisplay());
3292
3293     frame.changing = frame.rect;
3294     screen->showPosition(frame.changing.x(), frame.changing.y());
3295
3296     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3297                    screen->getOpGC(),
3298                    frame.changing.x(),
3299                    frame.changing.y(),
3300                    frame.changing.width() - 1,
3301                    frame.changing.height() - 1);
3302   }
3303
3304   frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3305   frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3306 }
3307
3308
3309 void BlackboxWindow::doMove(int x_root, int y_root) {
3310   assert(flags.moving);
3311   assert(blackbox->getChangingWindow() == this);
3312
3313   int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3314   dx -= frame.border_w;
3315   dy -= frame.border_w;
3316
3317   doWindowSnapping(dx, dy);
3318
3319   if (screen->doOpaqueMove()) {
3320     if (screen->doWorkspaceWarping())
3321       doWorkspaceWarping(x_root, y_root, dx);
3322
3323     configure(dx, dy, frame.rect.width(), frame.rect.height());
3324   } else {
3325     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3326                    screen->getOpGC(),
3327                    frame.changing.x(),
3328                    frame.changing.y(),
3329                    frame.changing.width() - 1,
3330                    frame.changing.height() - 1);
3331
3332     if (screen->doWorkspaceWarping())
3333       doWorkspaceWarping(x_root, y_root, dx);
3334
3335     frame.changing.setPos(dx, dy);
3336
3337     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3338                    screen->getOpGC(),
3339                    frame.changing.x(),
3340                    frame.changing.y(),
3341                    frame.changing.width() - 1,
3342                    frame.changing.height() - 1);
3343   }
3344
3345   screen->showPosition(dx, dy);
3346 }
3347
3348
3349 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
3350   // workspace warping
3351   bool warp = False;
3352   unsigned int dest = screen->getCurrentWorkspaceID();
3353   if (x_root <= 0) {
3354     warp = True;
3355
3356     if (dest > 0) dest--;
3357     else dest = screen->getNumberOfWorkspaces() - 1;
3358
3359   } else if (x_root >= screen->getRect().right()) {
3360     warp = True;
3361
3362     if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3363     else dest = 0;
3364   }
3365   if (! warp)
3366     return;
3367
3368   bool focus = flags.focused; // had focus while moving?
3369
3370   int dest_x = x_root;
3371   if (x_root <= 0) {
3372     dest_x += screen->getRect().width() - 1;
3373     dx += screen->getRect().width() - 1;
3374   } else {
3375     dest_x -= screen->getRect().width() - 1;
3376     dx -= screen->getRect().width() - 1;
3377   }
3378
3379   if (! flags.stuck)
3380     screen->reassociateWindow(this, dest, False);
3381   screen->changeWorkspaceID(dest);
3382
3383   if (screen->doOpaqueMove())
3384     XGrabServer(blackbox->getXDisplay());
3385
3386   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3387   XWarpPointer(blackbox->getXDisplay(), None, 
3388                screen->getRootWindow(), 0, 0, 0, 0,
3389                dest_x, y_root);
3390   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3391                PointerMotionMask | ButtonReleaseMask,
3392                GrabModeAsync, GrabModeAsync,
3393                None, blackbox->getMoveCursor(), CurrentTime);
3394
3395   if (screen->doOpaqueMove())
3396     XUngrabServer(blackbox->getXDisplay());
3397
3398   if (focus)
3399     setInputFocus();
3400
3401 }
3402
3403
3404 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3405   // how much resistance to edges to provide
3406   const int resistance_size = screen->getResistanceSize();
3407
3408   // how far away to snap
3409   const int snap_distance = screen->getSnapThreshold();
3410
3411   // how to snap windows
3412   const int snap_to_windows = screen->getWindowToWindowSnap();
3413   const int snap_to_edges = screen->getWindowToEdgeSnap();
3414   // the amount of space away from the edge to provide resistance/snap
3415   const int snap_offset = screen->getSnapOffset();
3416
3417   // find the geomeetery where the moving window currently is
3418   const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3419
3420   // window corners
3421   const int wleft = dx,
3422            wright = dx + frame.rect.width() - 1,
3423              wtop = dy,
3424           wbottom = dy + frame.rect.height() - 1;
3425
3426   if (snap_to_windows) {
3427     RectList rectlist;
3428
3429     Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3430     assert(w);
3431
3432     // add windows on the workspace to the rect list
3433     const BlackboxWindowList& stack_list = w->getStackingList();
3434     BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3435     for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3436       if (*st_it != this) // don't snap to ourself
3437         rectlist.push_back( (*st_it)->frameRect() );
3438
3439     // add the toolbar and the slit to the rect list.
3440     // (only if they are not hidden)
3441     Toolbar *tbar = screen->getToolbar();
3442     Slit *slit = screen->getSlit();
3443     Rect tbar_rect, slit_rect;
3444     unsigned int bwidth = screen->getBorderWidth() * 2;
3445
3446     if (! (screen->doHideToolbar() || tbar->isHidden())) {
3447       tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3448                         tbar->getHeight() + bwidth);
3449       rectlist.push_back(tbar_rect);
3450     }
3451
3452     if (! slit->isHidden()) {
3453       slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3454                         slit->getHeight() + bwidth);
3455       rectlist.push_back(slit_rect);
3456     }
3457
3458     RectList::const_iterator it, end = rectlist.end();
3459     for (it = rectlist.begin(); it != end; ++it) {
3460       bool snapped = False;
3461       const Rect &winrect = *it;
3462       Rect offsetrect;
3463       offsetrect.setCoords(winrect.left() - snap_offset,
3464                            winrect.top() - snap_offset,
3465                            winrect.right() + snap_offset,
3466                            winrect.bottom() + snap_offset);
3467
3468       if (snap_to_windows == BScreen::WindowResistance)
3469         // if the window is already over top of this snap target, then
3470         // resistance is futile, so just ignore it
3471         if (winrect.intersects(moving))
3472           continue;
3473
3474       int dleft, dright, dtop, dbottom;
3475
3476       // if the windows are in the same plane vertically
3477       if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3478           wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3479
3480         if (snap_to_windows == BScreen::WindowResistance) {
3481           dleft = wright - offsetrect.left();
3482           dright = offsetrect.right() - wleft;
3483
3484           // snap left of other window?
3485           if (dleft >= 0 && dleft < resistance_size &&
3486               dleft < (wright - wleft)) {
3487             dx = offsetrect.left() - frame.rect.width();
3488             snapped = True;
3489           }
3490           // snap right of other window?
3491           else if (dright >= 0 && dright < resistance_size &&
3492                    dright < (wright - wleft)) {
3493             dx = offsetrect.right() + 1;
3494             snapped = True;
3495           }
3496         } else { // BScreen::WindowSnap
3497           dleft = abs(wright - offsetrect.left());
3498           dright = abs(wleft - offsetrect.right());
3499
3500           // snap left of other window?
3501           if (dleft < snap_distance && dleft <= dright) {
3502             dx = offsetrect.left() - frame.rect.width();
3503             snapped = True;
3504           }
3505           // snap right of other window?
3506           else if (dright < snap_distance) {
3507             dx = offsetrect.right() + 1;
3508             snapped = True;
3509           }            
3510         }
3511
3512         if (snapped) {
3513           if (screen->getWindowCornerSnap()) {
3514             // try corner-snap to its other sides
3515             if (snap_to_windows == BScreen::WindowResistance) {
3516               dtop = winrect.top() - wtop;
3517               dbottom = wbottom - winrect.bottom();
3518               if (dtop > 0 && dtop < resistance_size) {
3519                 // if we're already past the top edge, then don't provide
3520                 // resistance
3521                 if (moving.top() >= winrect.top())
3522                   dy = winrect.top();
3523               } else if (dbottom > 0 && dbottom < resistance_size) {
3524                 // if we're already past the bottom edge, then don't provide
3525                 // resistance
3526                 if (moving.bottom() <= winrect.bottom())
3527                   dy = winrect.bottom() - frame.rect.height() + 1;
3528               }
3529             } else { // BScreen::WindowSnap
3530               dtop = abs(wtop - winrect.top());
3531               dbottom = abs(wbottom - winrect.bottom());
3532               if (dtop < snap_distance && dtop <= dbottom)
3533                 dy = winrect.top();
3534               else if (dbottom < snap_distance)
3535                 dy = winrect.bottom() - frame.rect.height() + 1;
3536             }
3537           }
3538
3539           continue;
3540         }
3541       }
3542
3543       // if the windows are on the same plane horizontally
3544       if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3545           wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3546
3547         if (snap_to_windows == BScreen::WindowResistance) {
3548           dtop = wbottom - offsetrect.top();
3549           dbottom = offsetrect.bottom() - wtop;
3550
3551           // snap top of other window?
3552           if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3553             dy = offsetrect.top() - frame.rect.height();
3554             snapped = True;
3555           }
3556           // snap bottom of other window?
3557           else if (dbottom >= 0 && dbottom < resistance_size &&
3558                    dbottom < (wbottom - wtop)) {
3559             dy = offsetrect.bottom() + 1;
3560             snapped = True;
3561           }
3562         } else { // BScreen::WindowSnap
3563           dtop = abs(wbottom - offsetrect.top());
3564           dbottom = abs(wtop - offsetrect.bottom());
3565
3566           // snap top of other window?
3567           if (dtop < snap_distance && dtop <= dbottom) {
3568             dy = offsetrect.top() - frame.rect.height();
3569             snapped = True;
3570           }
3571           // snap bottom of other window?
3572           else if (dbottom < snap_distance) {
3573             dy = offsetrect.bottom() + 1;
3574             snapped = True;
3575           }
3576
3577         }
3578
3579         if (snapped) {
3580           if (screen->getWindowCornerSnap()) {
3581             // try corner-snap to its other sides
3582             if (snap_to_windows == BScreen::WindowResistance) {
3583               dleft = winrect.left() - wleft;
3584               dright = wright - winrect.right();
3585               if (dleft > 0 && dleft < resistance_size) {
3586                 // if we're already past the left edge, then don't provide
3587                 // resistance
3588                 if (moving.left() >= winrect.left())
3589                   dx = winrect.left();
3590               } else if (dright > 0 && dright < resistance_size) {
3591                 // if we're already past the right edge, then don't provide
3592                 // resistance
3593                 if (moving.right() <= winrect.right())
3594                   dx = winrect.right() - frame.rect.width() + 1;
3595               }
3596             } else { // BScreen::WindowSnap
3597               dleft = abs(wleft - winrect.left());
3598               dright = abs(wright - winrect.right());
3599               if (dleft < snap_distance && dleft <= dright)
3600                 dx = winrect.left();
3601               else if (dright < snap_distance)
3602                 dx = winrect.right() - frame.rect.width() + 1;
3603             }
3604           }
3605
3606           continue;
3607         }
3608       }
3609     }
3610   }
3611
3612   if (snap_to_edges) {
3613     RectList rectlist;
3614
3615     // snap to the screen edges (and screen boundaries for xinerama)
3616 #ifdef    XINERAMA
3617     if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3618       rectlist.insert(rectlist.begin(),
3619                       screen->getXineramaAreas().begin(),
3620                       screen->getXineramaAreas().end());
3621     } else
3622 #endif // XINERAMA
3623       rectlist.push_back(screen->getRect());
3624
3625     RectList::const_iterator it, end = rectlist.end();
3626     for (it = rectlist.begin(); it != end; ++it) {
3627       const Rect &srect = *it;
3628       Rect offsetrect;
3629       offsetrect.setCoords(srect.left() + snap_offset,
3630                            srect.top() + snap_offset,
3631                            srect.right() - snap_offset,
3632                            srect.bottom() - snap_offset);
3633
3634       if (snap_to_edges == BScreen::WindowResistance) {
3635         // if we're not in the rectangle then don't snap to it.
3636         if (! srect.contains(moving))
3637           continue;
3638       } else { // BScreen::WindowSnap
3639         // if we're not in the rectangle then don't snap to it.
3640         if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3641                                     frame.rect.height())))
3642           continue;
3643       }
3644
3645       if (snap_to_edges == BScreen::WindowResistance) {
3646       int dleft = offsetrect.left() - wleft,
3647          dright = wright - offsetrect.right(),
3648            dtop = offsetrect.top() - wtop,
3649         dbottom = wbottom - offsetrect.bottom();
3650
3651         // snap left?
3652         if (dleft > 0 && dleft < resistance_size)
3653           dx = offsetrect.left();
3654         // snap right?
3655         else if (dright > 0 && dright < resistance_size)
3656           dx = offsetrect.right() - frame.rect.width() + 1;
3657
3658         // snap top?
3659         if (dtop > 0 && dtop < resistance_size)
3660           dy = offsetrect.top();
3661         // snap bottom?
3662         else if (dbottom > 0 && dbottom < resistance_size)
3663           dy = offsetrect.bottom() - frame.rect.height() + 1;
3664       } else { // BScreen::WindowSnap
3665         int dleft = abs(wleft - offsetrect.left()),
3666            dright = abs(wright - offsetrect.right()),
3667              dtop = abs(wtop - offsetrect.top()),
3668           dbottom = abs(wbottom - offsetrect.bottom());
3669
3670         // snap left?
3671         if (dleft < snap_distance && dleft <= dright)
3672           dx = offsetrect.left();
3673         // snap right?
3674         else if (dright < snap_distance)
3675           dx = offsetrect.right() - frame.rect.width() + 1;
3676
3677         // snap top?
3678         if (dtop < snap_distance && dtop <= dbottom)
3679           dy = offsetrect.top();
3680         // snap bottom?
3681         else if (dbottom < snap_distance)
3682           dy = offsetrect.bottom() - frame.rect.height() + 1;
3683       }
3684     }
3685   }
3686 }
3687
3688
3689 void BlackboxWindow::endMove(void) {
3690   assert(flags.moving);
3691   assert(blackbox->getChangingWindow() == this);
3692
3693   flags.moving = False;
3694   blackbox->setChangingWindow(0);
3695
3696   if (! screen->doOpaqueMove()) {
3697     /* when drawing the rubber band, we need to make sure we only draw inside
3698      * the frame... frame.changing_* contain the new coords for the window,
3699      * so we need to subtract 1 from changing_w/changing_h every where we
3700      * draw the rubber band (for both moving and resizing)
3701      */
3702     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3703                    screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3704                    frame.changing.width() - 1, frame.changing.height() - 1);
3705       XUngrabServer(blackbox->getXDisplay());
3706   
3707       configure(frame.changing.x(), frame.changing.y(),
3708                 frame.changing.width(), frame.changing.height());
3709   } else {
3710     configure(frame.rect.x(), frame.rect.y(),
3711               frame.rect.width(), frame.rect.height());
3712   }
3713   screen->hideGeometry();
3714
3715   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3716
3717   // if there are any left over motions from the move, drop them now
3718   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3719   XEvent e;
3720   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3721                                 MotionNotify, &e));
3722 }
3723
3724
3725 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3726   if (! (functions & Func_Resize)) return;
3727
3728   assert(! (flags.resizing || flags.moving));
3729
3730   /*
3731     Only one window can be moved/resized at a time. If another window is
3732     already being moved or resized, then stop it before whating to work with
3733     this one.
3734   */
3735   BlackboxWindow *changing = blackbox->getChangingWindow();
3736   if (changing && changing != this) {
3737     if (changing->flags.moving)
3738       changing->endMove();
3739     else // if (changing->flags.resizing)
3740       changing->endResize();
3741   }
3742
3743   resize_dir = dir;
3744
3745   Cursor cursor;
3746   Corner anchor;
3747   
3748   switch (resize_dir) {
3749   case BottomLeft:
3750     anchor = TopRight;
3751     cursor = blackbox->getLowerLeftAngleCursor();
3752     break;
3753
3754   case BottomRight:
3755     anchor = TopLeft;
3756     cursor = blackbox->getLowerRightAngleCursor();
3757     break;
3758
3759   case TopLeft:
3760     anchor = BottomRight;
3761     cursor = blackbox->getUpperLeftAngleCursor();
3762     break;
3763
3764   case TopRight:
3765     anchor = BottomLeft;
3766     cursor = blackbox->getUpperRightAngleCursor();
3767     break;
3768
3769   default:
3770     assert(false); // unhandled Corner
3771     return;        // unreachable, for the compiler
3772   }
3773   
3774   XGrabServer(blackbox->getXDisplay());
3775   XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3776                PointerMotionMask | ButtonReleaseMask,
3777                GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3778
3779   flags.resizing = True;
3780   blackbox->setChangingWindow(this);
3781
3782   unsigned int gw, gh;
3783   frame.changing = frame.rect;
3784
3785   constrain(anchor,  &gw, &gh);
3786
3787   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3788                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3789                  frame.changing.width() - 1, frame.changing.height() - 1);
3790
3791   screen->showGeometry(gw, gh);
3792   
3793   frame.grab_x = x_root;
3794   frame.grab_y = y_root;
3795 }
3796
3797
3798 void BlackboxWindow::doResize(int x_root, int y_root) {
3799   assert(flags.resizing);
3800   assert(blackbox->getChangingWindow() == this);
3801
3802   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3803                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3804                  frame.changing.width() - 1, frame.changing.height() - 1);
3805
3806   unsigned int gw, gh;
3807   Corner anchor;
3808   int dx, dy; // the amount of change in the size of the window
3809
3810   switch (resize_dir) {
3811   case BottomLeft:
3812     anchor = TopRight;
3813     dx = - (x_root - frame.grab_x);
3814     dy = + (y_root - frame.grab_y);
3815     break;
3816   case BottomRight:
3817     anchor = TopLeft;
3818     dx = + (x_root - frame.grab_x);
3819     dy = + (y_root - frame.grab_y);
3820     break;
3821   case TopLeft:
3822     anchor = BottomRight;
3823     dx = - (x_root - frame.grab_x);
3824     dy = - (y_root - frame.grab_y);
3825     break;
3826   case TopRight:
3827     anchor = BottomLeft;
3828     dx = + (x_root - frame.grab_x);
3829     dy = - (y_root - frame.grab_y);
3830     break;
3831
3832   default:
3833     assert(false); // unhandled Corner
3834     return;        // unreachable, for the compiler
3835   }
3836
3837   // make sure the user cant resize the window smaller than 0, which makes it
3838   // wrap around and become huge
3839   if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width();
3840   if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height();
3841
3842   frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy);
3843
3844   constrain(anchor, &gw, &gh);
3845
3846   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3847                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3848                  frame.changing.width() - 1, frame.changing.height() - 1);
3849
3850   screen->showGeometry(gw, gh);
3851 }
3852
3853
3854 void BlackboxWindow::endResize(void) {
3855   assert(flags.resizing);
3856   assert(blackbox->getChangingWindow() == this);
3857
3858   XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3859                  screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3860                  frame.changing.width() - 1, frame.changing.height() - 1);
3861   XUngrabServer(blackbox->getXDisplay());
3862
3863   // unset maximized state after resized when fully maximized
3864   if (flags.maximized == 1)
3865     maximize(0);
3866   
3867   flags.resizing = False;
3868   blackbox->setChangingWindow(0);
3869
3870   configure(frame.changing.x(), frame.changing.y(),
3871             frame.changing.width(), frame.changing.height());
3872   screen->hideGeometry();
3873
3874   XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3875   
3876   // if there are any left over motions from the resize, drop them now
3877   XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3878   XEvent e;
3879   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3880                                 MotionNotify, &e));
3881 }
3882
3883
3884 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3885 #if 0
3886   fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3887           client.window);
3888 #endif
3889
3890   if (flags.moving) {
3891     doMove(me->x_root, me->y_root);
3892   } else if (flags.resizing) {
3893     doResize(me->x_root, me->y_root);
3894   } else {
3895     if ((functions & Func_Move) &&
3896        (me->state & Button1Mask) &&
3897         (frame.title == me->window || frame.label == me->window ||
3898          frame.handle == me->window || frame.window == me->window)) {
3899       beginMove(me->x_root, me->y_root);
3900     } else if ((functions & Func_Resize) &&
3901                ((me->state & Button1Mask) &&
3902                 (me->window == frame.right_grip ||
3903                  me->window == frame.left_grip)) ||
3904                ((me->state & Button3Mask) && (me->state & mod_mask) &&
3905                 (frame.title == me->window || frame.label == me->window ||
3906                  frame.handle == me->window || frame.window == me->window ||
3907                  frame.right_grip == me->window ||
3908                  frame.left_grip == me->window))) {
3909       unsigned int zones = screen->getResizeZones();
3910       Corner corner;
3911       
3912       if (me->window == frame.left_grip) {
3913         corner = BottomLeft;
3914       } else if (me->window == frame.right_grip || zones == 1) {
3915         corner = BottomRight;
3916       } else {
3917         bool top;
3918         bool left = (me->x_root - frame.rect.x() <=
3919                      static_cast<signed>(frame.rect.width() / 2));
3920         if (zones == 2)
3921           top = False;
3922         else // (zones == 4)
3923           top = (me->y_root - frame.rect.y() <=
3924                  static_cast<signed>(frame.rect.height() / 2));
3925         corner = (top ? (left ? TopLeft : TopRight) :
3926                         (left ? BottomLeft : BottomRight));
3927       }
3928
3929       beginResize(me->x_root, me->y_root, corner);
3930     }
3931   }
3932 }
3933
3934
3935 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3936   if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3937     return;
3938
3939   XEvent e;
3940   bool leave = False, inferior = False;
3941
3942   while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3943                                 LeaveNotify, &e)) {
3944     if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3945       leave = True;
3946       inferior = (e.xcrossing.detail == NotifyInferior);
3947     }
3948   }
3949
3950   if (! leave || inferior) {
3951     if (! isFocused()) {
3952       bool success = setInputFocus();
3953       if (success)    // if focus succeeded install the colormap
3954         installColormap(True); // XXX: shouldnt we honour no install?
3955
3956       /*
3957         We only auto-raise when the window wasn't focused because otherwise
3958         we run into problems with gtk+ drop-down lists. The window ends up
3959         raising over the list.
3960       */
3961       if (screen->doAutoRaise())
3962         timer->start();
3963     }
3964   }
3965 }
3966
3967
3968 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3969   if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3970     return;
3971
3972   installColormap(False);
3973
3974   if (timer->isTiming())
3975     timer->stop();
3976 }
3977
3978
3979 #ifdef    SHAPE
3980 void BlackboxWindow::shapeEvent(XShapeEvent *e) {
3981   if (blackbox->hasShapeExtensions()) {
3982     if (! e->shaped && flags.shaped) {
3983       clearShape();
3984       flags.shaped = False;
3985     } else if (e->shaped) {
3986       configureShape();
3987       flags.shaped = True;
3988     }
3989   }
3990 }
3991 #endif // SHAPE
3992
3993
3994 bool BlackboxWindow::validateClient(void) const {
3995   XSync(blackbox->getXDisplay(), False);
3996
3997   XEvent e;
3998   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3999                              DestroyNotify, &e) ||
4000       XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
4001                              UnmapNotify, &e)) {
4002     XPutBackEvent(blackbox->getXDisplay(), &e);
4003
4004     return False;
4005   }
4006
4007   return True;
4008 }
4009
4010
4011 void BlackboxWindow::restore(bool remap) {
4012   XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
4013   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
4014   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
4015
4016   // do not leave a shaded window as an icon unless it was an icon
4017   if (flags.shaded && ! flags.iconic)
4018     setState(NormalState);
4019
4020   // erase the netwm stuff that we read when a window maps, so that it
4021   // doesn't persist between mappings.
4022   // (these are the ones read in getNetWMFlags().)
4023   xatom->eraseValue(client.window, XAtom::net_wm_desktop);
4024   xatom->eraseValue(client.window, XAtom::net_wm_state);
4025
4026   restoreGravity(client.rect);
4027
4028   XUnmapWindow(blackbox->getXDisplay(), frame.window);
4029   XUnmapWindow(blackbox->getXDisplay(), client.window);
4030
4031   XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
4032
4033   XEvent ev;
4034   if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
4035                              ReparentNotify, &ev)) {
4036     remap = True;
4037   } else {
4038     // according to the ICCCM - if the client doesn't reparent to
4039     // root, then we have to do it for them
4040     XReparentWindow(blackbox->getXDisplay(), client.window,
4041                     screen->getRootWindow(),
4042                     client.rect.x(), client.rect.y());
4043   }
4044
4045   if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
4046 }
4047
4048
4049 // timer for autoraise
4050 void BlackboxWindow::timeout(void) {
4051   screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
4052 }
4053
4054
4055 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
4056   if ((net->flags & AttribShaded) &&
4057       ((blackbox_attrib.attrib & AttribShaded) !=
4058        (net->attrib & AttribShaded)))
4059     shade();
4060
4061   if (flags.visible && // watch out for requests when we can not be seen
4062       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
4063       ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
4064        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
4065     if (flags.maximized) {
4066       maximize(0);
4067     } else {
4068       int button = 0;
4069
4070       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
4071         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
4072       else if (net->flags & AttribMaxVert)
4073         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
4074       else if (net->flags & AttribMaxHoriz)
4075         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
4076
4077       maximize(button);
4078     }
4079   }
4080
4081   if ((net->flags & AttribOmnipresent) &&
4082       ((blackbox_attrib.attrib & AttribOmnipresent) !=
4083        (net->attrib & AttribOmnipresent)))
4084     stick();
4085
4086   if ((net->flags & AttribWorkspace) &&
4087       (blackbox_attrib.workspace != net->workspace)) {
4088     screen->reassociateWindow(this, net->workspace, True);
4089
4090     if (screen->getCurrentWorkspaceID() != net->workspace) {
4091       withdraw();
4092     } else {
4093       show();
4094       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
4095     }
4096   }
4097
4098   if (net->flags & AttribDecoration) {
4099     switch (net->decoration) {
4100     case DecorNone:
4101       enableDecor(False);
4102       break;
4103
4104     default:
4105     case DecorNormal:
4106     case DecorTiny:
4107     case DecorTool:
4108       enableDecor(True);
4109       break;
4110     }
4111   }
4112 }
4113
4114
4115 /*
4116  * Set the sizes of all components of the window frame
4117  * (the window decorations).
4118  * These values are based upon the current style settings and the client
4119  * window's dimensions.
4120  */
4121 void BlackboxWindow::upsize(void) {
4122   frame.bevel_w = screen->getBevelWidth();
4123
4124   if (decorations & Decor_Border) {
4125     frame.border_w = screen->getBorderWidth();
4126     if (! isTransient())
4127       frame.mwm_border_w = screen->getFrameWidth();
4128     else
4129       frame.mwm_border_w = 0;
4130   } else {
4131     frame.mwm_border_w = frame.border_w = 0;
4132   }
4133
4134   if (decorations & Decor_Titlebar) {
4135     // the height of the titlebar is based upon the height of the font being
4136     // used to display the window's title
4137     WindowStyle *style = screen->getWindowStyle();
4138     frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
4139
4140     frame.label_h = frame.title_h - (frame.bevel_w * 2);
4141     frame.button_w = (frame.label_h - 2);
4142
4143     // set the top frame margin
4144     frame.margin.top = frame.border_w + frame.title_h +
4145                        frame.border_w + frame.mwm_border_w;
4146   } else {
4147     frame.title_h = 0;
4148     frame.label_h = 0;
4149     frame.button_w = 0;
4150
4151     // set the top frame margin
4152     frame.margin.top = frame.border_w + frame.mwm_border_w;
4153   }
4154
4155   // set the left/right frame margin
4156   frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
4157
4158   if (decorations & Decor_Handle) {
4159     frame.grip_w = frame.button_w * 2;
4160     frame.handle_h = screen->getHandleWidth();
4161
4162     // set the bottom frame margin
4163     frame.margin.bottom = frame.border_w + frame.handle_h +
4164                           frame.border_w + frame.mwm_border_w;
4165   } else {
4166     frame.handle_h = 0;
4167     frame.grip_w = 0;
4168
4169     // set the bottom frame margin
4170     frame.margin.bottom = frame.border_w + frame.mwm_border_w;
4171   }
4172
4173   /*
4174     We first get the normal dimensions and use this to define the inside_w/h
4175     then we modify the height if shading is in effect.
4176     If the shade state is not considered then frame.rect gets reset to the
4177     normal window size on a reconfigure() call resulting in improper
4178     dimensions appearing in move/resize and other events.
4179   */
4180   unsigned int
4181     height = client.rect.height() + frame.margin.top + frame.margin.bottom,
4182     width = client.rect.width() + frame.margin.left + frame.margin.right;
4183
4184   frame.inside_w = width - (frame.border_w * 2);
4185   frame.inside_h = height - (frame.border_w * 2);
4186
4187   if (flags.shaded)
4188     height = frame.title_h + (frame.border_w * 2);
4189   frame.rect.setSize(width, height);
4190 }
4191
4192
4193 /*
4194  * Calculate the size of the client window and constrain it to the
4195  * size specified by the size hints of the client window.
4196  *
4197  * The logical width and height are placed into pw and ph, if they
4198  * are non-zero.  Logical size refers to the users perception of
4199  * the window size (for example an xterm resizes in cells, not in pixels).
4200  * pw and ph are then used to display the geometry during window moves, resize,
4201  * etc.
4202  *
4203  * The physical geometry is placed into frame.changing_{x,y,width,height}.
4204  * Physical geometry refers to the geometry of the window in pixels.
4205  */
4206 void BlackboxWindow::constrain(Corner anchor,
4207                                unsigned int *pw, unsigned int *ph) {
4208   // frame.changing represents the requested frame size, we need to
4209   // strip the frame margin off and constrain the client size
4210   frame.changing.setCoords(frame.changing.left() + frame.margin.left,
4211                            frame.changing.top() + frame.margin.top,
4212                            frame.changing.right() - frame.margin.right,
4213                            frame.changing.bottom() - frame.margin.bottom);
4214
4215   unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
4216     base_width = (client.base_width) ? client.base_width : client.min_width,
4217     base_height = (client.base_height) ? client.base_height :
4218                                          client.min_height;
4219
4220   // constrain, but only if the min/max are being used. if they aren't, then
4221   // this resize is going to be from a ConfigureRequest because the window
4222   // isn't allowed to be resized by the user. And in that case, we don't want
4223   // to limit what the app can do
4224   if (client.max_width > client.min_width ||
4225       client.max_height > client.min_height) {
4226     if (dw < client.min_width) dw = client.min_width;
4227     if (dh < client.min_height) dh = client.min_height;
4228     if (dw > client.max_width) dw = client.max_width;
4229     if (dh > client.max_height) dh = client.max_height;
4230   }
4231
4232   if (client.width_inc > 1) {
4233     dw -= base_width;
4234     dw /= client.width_inc;
4235   }
4236   if (client.height_inc > 1) {
4237     dh -= base_height;
4238     dh /= client.height_inc;
4239   }
4240
4241   if (pw)
4242     *pw = dw;
4243
4244   if (ph)
4245     *ph = dh;
4246
4247   if (client.width_inc > 1) {
4248     dw *= client.width_inc;
4249     dw += base_width;
4250   }
4251   if (client.height_inc > 1) {
4252     dh *= client.height_inc;
4253     dh += base_height;
4254   }
4255
4256   frame.changing.setSize(dw, dh);
4257
4258   // add the frame margin back onto frame.changing
4259   frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4260                            frame.changing.top() - frame.margin.top,
4261                            frame.changing.right() + frame.margin.right,
4262                            frame.changing.bottom() + frame.margin.bottom);
4263
4264   // move frame.changing to the specified anchor
4265   int dx = 0,
4266       dy = 0;
4267   switch (anchor) {
4268   case TopLeft:
4269     break;
4270
4271   case TopRight:
4272     dx = frame.rect.right() - frame.changing.right();
4273     break;
4274
4275   case BottomLeft:
4276     dy = frame.rect.bottom() - frame.changing.bottom();
4277     break;
4278
4279   case BottomRight:
4280     dx = frame.rect.right() - frame.changing.right();
4281     dy = frame.rect.bottom() - frame.changing.bottom();
4282     break;
4283
4284   default:
4285     assert(false);  // unhandled corner
4286   }
4287   frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4288 }
4289
4290
4291 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4292                             unsigned int max_length,
4293                             unsigned int modifier) const {
4294   size_t text_len = text.size();
4295   unsigned int length;
4296
4297   do {
4298     length = font->measureString(string(text, 0, text_len)) + modifier;
4299   } while (length > max_length && text_len-- > 0);
4300
4301   switch (justify) {
4302   case RightJustify:
4303     start_pos += max_length - length;
4304     break;
4305
4306   case CenterJustify:
4307     start_pos += (max_length - length) / 2;
4308     break;
4309
4310   case LeftJustify:
4311   default:
4312     break;
4313   }
4314 }
4315
4316
4317 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4318   : blackbox(b), group(_group) {
4319   XWindowAttributes wattrib;
4320   if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4321     // group window doesn't seem to exist anymore
4322     delete this;
4323     return;
4324   }
4325
4326   XSelectInput(blackbox->getXDisplay(), group,
4327                PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4328
4329   blackbox->saveGroupSearch(group, this);
4330 }
4331
4332
4333 BWindowGroup::~BWindowGroup(void) {
4334   blackbox->removeGroupSearch(group);
4335 }
4336
4337
4338 BlackboxWindow *
4339 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4340   BlackboxWindow *ret = blackbox->getFocusedWindow();
4341
4342   // does the focus window match (or any transient_fors)?
4343   for (; ret; ret = ret->getTransientFor()) {
4344     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4345         (! ret->isTransient() || allow_transients))
4346       break;
4347   }
4348
4349   if (ret) return ret;
4350
4351   // the focus window didn't match, look in the group's window list
4352   BlackboxWindowList::const_iterator it, end = windowList.end();
4353   for (it = windowList.begin(); it != end; ++it) {
4354     ret = *it;
4355     if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4356         (! ret->isTransient() || allow_transients))
4357       break;
4358   }
4359
4360   return ret;
4361 }