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