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