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