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