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