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