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