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