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