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