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