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