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