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