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