merge the sticky window fix from 1.2.
[mikachu/openbox.git] / src / Window.cc
1 // Window.cc for Openbox
2 // Copyright (c) 2002 - 2002 Ben Jansens (ben at orodu.net)
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry (shaleh at debian.org)
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes at tcac.net)
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 // stupid macros needed to access some functions in version 2 of the GNU C
25 // library
26 #ifndef   _GNU_SOURCE
27 #define   _GNU_SOURCE
28 #endif // _GNU_SOURCE
29
30 #ifdef    HAVE_CONFIG_H
31 #  include "../config.h"
32 #endif // HAVE_CONFIG_H
33
34 #include <X11/Xatom.h>
35 #include <X11/keysym.h>
36
37 #ifdef    HAVE_STRING_H
38 #  include <string.h>
39 #endif // HAVE_STRING_H
40
41 #ifdef    DEBUG
42 #  ifdef    HAVE_STDIO_H
43 #    include <stdio.h>
44 #  endif // HAVE_STDIO_H
45 #endif // DEBUG
46
47 #include "i18n.h"
48 #include "openbox.h"
49 #include "Iconmenu.h"
50 #include "Screen.h"
51 #include "Toolbar.h"
52 #include "Window.h"
53 #include "Windowmenu.h"
54 #include "Workspace.h"
55 #ifdef    SLIT
56 #  include "Slit.h"
57 #endif // SLIT
58 #include "Util.h"
59
60 /*
61  * Initializes the class with default values/the window's set initial values.
62  */
63 OpenboxWindow::OpenboxWindow(Openbox &o, Window w, BScreen *s) : openbox(o) {
64 #ifdef    DEBUG
65   fprintf(stderr, i18n(WindowSet, WindowCreating,
66                        "OpenboxWindow::OpenboxWindow(): creating 0x%lx\n"),
67           w);
68 #endif // DEBUG
69
70   client.window = w;
71   display = openbox.getXDisplay();
72
73   openbox.grab();
74   if (! validateClient()) return;
75
76   // fetch client size and placement
77   XWindowAttributes wattrib;
78   if ((! XGetWindowAttributes(display, client.window, &wattrib)) ||
79       (! wattrib.screen) || wattrib.override_redirect) {
80 #ifdef    DEBUG
81     fprintf(stderr,
82             i18n(WindowSet, WindowXGetWindowAttributesFail,
83                  "OpenboxWindow::OpenboxWindow(): XGetWindowAttributes "
84                  "failed\n"));
85 #endif // DEBUG
86
87     openbox.ungrab();
88     return;
89   }
90
91   if (s) {
92     screen = s;
93   } else {
94     screen = openbox.searchScreen(RootWindowOfScreen(wattrib.screen));
95     if (! screen) {
96 #ifdef    DEBUG
97       fprintf(stderr, i18n(WindowSet, WindowCannotFindScreen,
98                            "OpenboxWindow::OpenboxWindow(): can't find screen\n"
99                            "\tfor root window 0x%lx\n"),
100               RootWindowOfScreen(wattrib.screen));
101 #endif // DEBUG
102
103       openbox.ungrab();
104       return;
105     }
106   }
107
108   flags.moving = flags.resizing = flags.shaded = flags.visible =
109     flags.iconic = flags.transient = flags.focused =
110     flags.stuck = flags.modal =  flags.send_focus_message =
111     flags.shaped = flags.managed = False;
112   flags.maximized = 0;
113
114   openbox_attrib.workspace = workspace_number = window_number = -1;
115
116   openbox_attrib.flags = openbox_attrib.attrib = openbox_attrib.stack
117     = openbox_attrib.decoration = 0l;
118   openbox_attrib.premax_x = openbox_attrib.premax_y = 0;
119   openbox_attrib.premax_w = openbox_attrib.premax_h = 0;
120
121   frame.window = frame.plate = frame.title = frame.handle = None;
122   frame.close_button = frame.iconify_button = frame.maximize_button = None;
123   frame.right_grip = frame.left_grip = None;
124
125   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
126   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
127   frame.pbutton = frame.ugrip = frame.fgrip = None;
128
129   decorations.titlebar = decorations.border = decorations.handle = True;
130   decorations.iconify = decorations.maximize = decorations.menu = True;
131   functions.resize = functions.move = functions.iconify =
132     functions.maximize = True;
133   functions.close = decorations.close = False;
134
135   client.wm_hint_flags = client.normal_hint_flags = 0;
136   client.transient_for = client.transient = 0;
137   client.title = 0;
138   client.title_len = 0;
139   client.icon_title = 0;
140   client.mwm_hint = (MwmHints *) 0;
141   client.openbox_hint = (OpenboxHints *) 0;
142
143   // get the initial size and location of client window (relative to the
144   // _root window_). This position is the reference point used with the
145   // window's gravity to find the window's initial position.
146   client.x = wattrib.x;
147   client.y = wattrib.y;
148   client.width = wattrib.width;
149   client.height = wattrib.height;
150   client.old_bw = wattrib.border_width;
151
152   windowmenu = 0;
153   lastButtonPressTime = 0;
154   image_ctrl = screen->getImageControl();
155
156   timer = new BTimer(openbox, *this);
157   timer->setTimeout(openbox.getAutoRaiseDelay());
158   timer->fireOnce(True);
159
160   getOpenboxHints();
161   if (! client.openbox_hint)
162     getMWMHints();
163
164   // get size, aspect, minimum/maximum size and other hints set by the
165   // client
166   getWMProtocols();
167   getWMHints();
168   getWMNormalHints();
169
170 #ifdef    SLIT
171   if (client.initial_state == WithdrawnState) {
172     screen->getSlit()->addClient(client.window);
173     openbox.ungrab();
174     delete this;
175     return;
176   }
177 #endif // SLIT
178
179   flags.managed = True;
180   openbox.saveWindowSearch(client.window, this);
181
182   // determine if this is a transient window
183   Window win;
184   if (XGetTransientForHint(display, client.window, &win)) {
185     if (win && (win != client.window)) {
186       OpenboxWindow *tr;
187       if ((tr = openbox.searchWindow(win))) {
188         while (tr->client.transient) tr = tr->client.transient;
189         client.transient_for = tr;
190         tr->client.transient = this;
191         flags.stuck = client.transient_for->flags.stuck;
192         flags.transient = True;
193       } else if (win == client.window_group) {
194         if ((tr = openbox.searchGroup(win, this))) {
195           while (tr->client.transient) tr = tr->client.transient;
196           client.transient_for = tr;
197           tr->client.transient = this;
198           flags.stuck = client.transient_for->flags.stuck;
199           flags.transient = True;
200         }
201       }
202     }
203
204     if (win == screen->getRootWindow()) flags.modal = True;
205   }
206
207   // adjust the window decorations based on transience and window sizes
208   if (flags.transient)
209     decorations.maximize = decorations.handle = functions.maximize = False;
210
211   if ((client.normal_hint_flags & PMinSize) &&
212       (client.normal_hint_flags & PMaxSize) &&
213       client.max_width <= client.min_width &&
214       client.max_height <= client.min_height) {
215     decorations.maximize = decorations.handle =
216       functions.resize = functions.maximize = False;
217   }
218   upsize();
219
220   place_window = true;
221   if (openbox.isStartup() || flags.transient ||
222       client.normal_hint_flags & (PPosition|USPosition)) {
223     setGravityOffsets();
224
225     if ((openbox.isStartup()) ||
226         (frame.x >= 0 &&
227          (signed) (frame.y + frame.y_border) >= 0 &&
228          frame.x <= (signed) screen->size().w() &&
229          frame.y <= (signed) screen->size().h()))
230       place_window = false;
231   }
232
233   frame.window = createToplevelWindow(frame.x, frame.y, frame.width,
234                                       frame.height,
235                                       frame.border_w);
236   openbox.saveWindowSearch(frame.window, this);
237
238   frame.plate = createChildWindow(frame.window);
239   openbox.saveWindowSearch(frame.plate, this);
240
241   if (decorations.titlebar) {
242     frame.title = createChildWindow(frame.window);
243     frame.label = createChildWindow(frame.title);
244     openbox.saveWindowSearch(frame.title, this);
245     openbox.saveWindowSearch(frame.label, this);
246   }
247
248   if (decorations.handle) {
249     frame.handle = createChildWindow(frame.window);
250     openbox.saveWindowSearch(frame.handle, this);
251
252     frame.left_grip =
253       createChildWindow(frame.handle, openbox.getLowerLeftAngleCursor());
254     openbox.saveWindowSearch(frame.left_grip, this);
255
256     frame.right_grip =
257       createChildWindow(frame.handle, openbox.getLowerRightAngleCursor());
258     openbox.saveWindowSearch(frame.right_grip, this);
259   }
260
261   associateClientWindow();
262
263   if (! screen->sloppyFocus())
264     openbox.grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
265                        GrabModeSync, GrabModeSync, frame.plate, None);
266
267   openbox.grabButton(Button1, Mod1Mask, frame.window, True,
268                      ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
269                      GrabModeAsync, frame.window, openbox.getMoveCursor());
270   openbox.grabButton(Button2, Mod1Mask, frame.window, True,
271                      ButtonReleaseMask, GrabModeAsync, GrabModeAsync, frame.window, None);
272   openbox.grabButton(Button3, Mod1Mask, frame.window, True,
273                      ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
274                      GrabModeAsync, frame.window, None);
275
276   positionWindows();
277   XRaiseWindow(display, frame.plate);
278   XMapSubwindows(display, frame.plate);
279   if (decorations.titlebar) XMapSubwindows(display, frame.title);
280   XMapSubwindows(display, frame.window);
281
282   if (decorations.menu)
283     windowmenu = new Windowmenu(*this);
284
285   decorate();
286
287   if (workspace_number < 0 || workspace_number >= screen->getWorkspaceCount())
288     screen->getCurrentWorkspace()->addWindow(this, place_window);
289   else
290     screen->getWorkspace(workspace_number)->addWindow(this, place_window);
291
292   configure(frame.x, frame.y, frame.width, frame.height);
293
294   if (flags.shaded) {
295     flags.shaded = False;
296     shade();
297   }
298
299   if (flags.maximized && functions.maximize) {
300     unsigned int button = flags.maximized;
301     flags.maximized = 0;
302     maximize(button);
303   }
304
305   setFocusFlag(False);
306
307   openbox.ungrab();
308 }
309
310
311 OpenboxWindow::~OpenboxWindow(void) {
312   if (flags.moving || flags.resizing) {
313     screen->hideGeometry();
314     XUngrabPointer(display, CurrentTime);
315   }
316
317   if (workspace_number != -1 && window_number != -1) {
318     if (flags.stuck) {
319       // make sure no other workspaces think that we're focused
320       for (int i=0; i < screen->getWorkspaceCount(); i++)
321         screen->getWorkspace(i)->removeWindow(this);
322     } else
323       screen->getWorkspace(workspace_number)->removeWindow(this);
324   } else if (flags.iconic)
325     screen->removeIcon(this);
326
327   if (timer) {
328     if (timer->isTiming()) timer->stop();
329     delete timer;
330   }
331
332   if (windowmenu) delete windowmenu;
333
334   if (client.title)
335     delete [] client.title;
336
337   if (client.icon_title)
338     delete [] client.icon_title;
339
340   if (client.mwm_hint)
341     XFree(client.mwm_hint);
342
343   if (client.openbox_hint)
344     XFree(client.openbox_hint);
345
346   if (client.window_group)
347     openbox.removeGroupSearch(client.window_group);
348
349   if (flags.transient && client.transient_for)
350     client.transient_for->client.transient = client.transient;
351   if (client.transient)
352     client.transient->client.transient_for = client.transient_for;
353
354   if (frame.close_button) {
355     openbox.removeWindowSearch(frame.close_button);
356     XDestroyWindow(display, frame.close_button);
357   }
358
359   if (frame.iconify_button) {
360     openbox.removeWindowSearch(frame.iconify_button);
361     XDestroyWindow(display, frame.iconify_button);
362   }
363
364   if (frame.maximize_button) {
365     openbox.removeWindowSearch(frame.maximize_button);
366     XDestroyWindow(display, frame.maximize_button);
367   }
368
369   if (frame.title) {
370     if (frame.ftitle)
371       image_ctrl->removeImage(frame.ftitle);
372
373     if (frame.utitle)
374       image_ctrl->removeImage(frame.utitle);
375
376     if (frame.flabel)
377       image_ctrl->removeImage(frame.flabel);
378
379     if( frame.ulabel)
380       image_ctrl->removeImage(frame.ulabel);
381
382     openbox.removeWindowSearch(frame.label);
383     openbox.removeWindowSearch(frame.title);
384     XDestroyWindow(display, frame.label);
385     XDestroyWindow(display, frame.title);
386   }
387
388   if (frame.handle) {
389     if (frame.fhandle)
390       image_ctrl->removeImage(frame.fhandle);
391
392     if (frame.uhandle)
393       image_ctrl->removeImage(frame.uhandle);
394
395     if (frame.fgrip)
396       image_ctrl->removeImage(frame.fgrip);
397
398     if (frame.ugrip)
399       image_ctrl->removeImage(frame.ugrip);
400
401     openbox.removeWindowSearch(frame.handle);
402     openbox.removeWindowSearch(frame.right_grip);
403     openbox.removeWindowSearch(frame.left_grip);
404     XDestroyWindow(display, frame.right_grip);
405     XDestroyWindow(display, frame.left_grip);
406     XDestroyWindow(display, frame.handle);
407   }
408
409   if (frame.fbutton)
410     image_ctrl->removeImage(frame.fbutton);
411
412   if (frame.ubutton)
413     image_ctrl->removeImage(frame.ubutton);
414
415   if (frame.pbutton)
416     image_ctrl->removeImage(frame.pbutton);
417
418   if (frame.plate) {
419     openbox.removeWindowSearch(frame.plate);
420     XDestroyWindow(display, frame.plate);
421   }
422
423   if (frame.window) {
424     openbox.removeWindowSearch(frame.window);
425     XDestroyWindow(display, frame.window);
426   }
427
428   if (flags.managed) {
429     openbox.removeWindowSearch(client.window);
430     screen->removeNetizen(client.window);
431   }
432 }
433
434
435 /*
436  * Creates a new top level window, with a given location, size, and border
437  * width.
438  * Returns: the newly created window
439  */
440 Window OpenboxWindow::createToplevelWindow(int x, int y, unsigned int width,
441                                            unsigned int height,
442                                            unsigned int borderwidth)
443 {
444   XSetWindowAttributes attrib_create;
445   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
446     CWOverrideRedirect | CWEventMask;
447
448   attrib_create.background_pixmap = None;
449   attrib_create.colormap = screen->getColormap();
450   attrib_create.override_redirect = True;
451   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
452     ButtonMotionMask | EnterWindowMask;
453
454   return XCreateWindow(display, screen->getRootWindow(), x, y, width, height,
455                        borderwidth, screen->getDepth(), InputOutput,
456                        screen->getVisual(), create_mask,
457                        &attrib_create);
458 }
459
460
461 /*
462  * Creates a child window, and optionally associates a given cursor with
463  * the new window.
464  */
465 Window OpenboxWindow::createChildWindow(Window parent, Cursor cursor) {
466   XSetWindowAttributes attrib_create;
467   unsigned long create_mask = CWBackPixmap | CWBorderPixel |
468     CWEventMask;
469
470   attrib_create.background_pixmap = None;
471   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
472     ButtonMotionMask | ExposureMask |
473     EnterWindowMask | LeaveWindowMask;
474
475   if (cursor) {
476     create_mask |= CWCursor;
477     attrib_create.cursor = cursor;
478   }
479
480   return XCreateWindow(display, parent, 0, 0, 1, 1, 0, screen->getDepth(),
481                        InputOutput, screen->getVisual(), create_mask,
482                        &attrib_create);
483 }
484
485
486 void OpenboxWindow::associateClientWindow(void) {
487   XSetWindowBorderWidth(display, client.window, 0);
488   getWMName();
489   getWMIconName();
490
491   XChangeSaveSet(display, client.window, SetModeInsert);
492   XSetWindowAttributes attrib_set;
493
494   XSelectInput(display, frame.plate, NoEventMask);
495   XReparentWindow(display, client.window, frame.plate, 0, 0);
496   XSelectInput(display, frame.plate, SubstructureRedirectMask);
497
498   XFlush(display);
499
500   attrib_set.event_mask = PropertyChangeMask | StructureNotifyMask |
501     FocusChangeMask;
502   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
503     ButtonMotionMask;
504
505   XChangeWindowAttributes(display, client.window, CWEventMask|CWDontPropagate,
506                           &attrib_set);
507
508 #ifdef    SHAPE
509   if (openbox.hasShapeExtensions()) {
510     XShapeSelectInput(display, client.window, ShapeNotifyMask);
511
512     int foo;
513     unsigned int ufoo;
514
515     XShapeQueryExtents(display, client.window, &flags.shaped, &foo, &foo,
516                        &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
517
518     if (flags.shaped) {
519       XShapeCombineShape(display, frame.window, ShapeBounding,
520                          frame.mwm_border_w, frame.y_border +
521                          frame.mwm_border_w, client.window,
522                          ShapeBounding, ShapeSet);
523
524       int num = 1;
525       XRectangle xrect[2];
526       xrect[0].x = xrect[0].y = 0;
527       xrect[0].width = frame.width;
528       xrect[0].height = frame.y_border;
529
530       if (decorations.handle) {
531         xrect[1].x = 0;
532         xrect[1].y = frame.y_handle;
533         xrect[1].width = frame.width;
534         xrect[1].height = frame.handle_h + frame.border_w;
535         num++;
536       }
537
538       XShapeCombineRectangles(display, frame.window, ShapeBounding, 0, 0,
539                               xrect, num, ShapeUnion, Unsorted);
540     }
541   }
542 #endif // SHAPE
543
544   if (decorations.iconify) createIconifyButton();
545   if (decorations.maximize) createMaximizeButton();
546   if (decorations.close) createCloseButton();
547 }
548
549
550 void OpenboxWindow::decorate(void) {
551   Pixmap tmp = frame.fbutton;
552   BTexture *texture = &(screen->getWindowStyle()->b_focus);
553   if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
554     frame.fbutton = None;
555     frame.fbutton_pixel = texture->getColor()->getPixel();
556   } else {
557     frame.fbutton =
558       image_ctrl->renderImage(frame.button_w, frame.button_h, texture);
559   }
560   if (tmp) image_ctrl->removeImage(tmp);
561
562   tmp = frame.ubutton;
563   texture = &(screen->getWindowStyle()->b_unfocus);
564   if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
565     frame.ubutton = None;
566     frame.ubutton_pixel = texture->getColor()->getPixel();
567   } else {
568     frame.ubutton =
569       image_ctrl->renderImage(frame.button_w, frame.button_h, texture);
570   }
571   if (tmp) image_ctrl->removeImage(tmp);
572
573   tmp = frame.pbutton;
574   texture = &(screen->getWindowStyle()->b_pressed);
575   if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
576     frame.pbutton = None;
577     frame.pbutton_pixel = texture->getColor()->getPixel();
578   } else {
579     frame.pbutton =
580       image_ctrl->renderImage(frame.button_w, frame.button_h, texture);
581   }
582   if (tmp) image_ctrl->removeImage(tmp);
583
584   if (decorations.titlebar) {
585     tmp = frame.ftitle;
586     texture = &(screen->getWindowStyle()->t_focus);
587     if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
588       frame.ftitle = None;
589       frame.ftitle_pixel = texture->getColor()->getPixel();
590     } else {
591       frame.ftitle =
592         image_ctrl->renderImage(frame.width, frame.title_h, texture);
593     }
594     if (tmp) image_ctrl->removeImage(tmp);
595
596     tmp = frame.utitle;
597     texture = &(screen->getWindowStyle()->t_unfocus);
598     if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
599       frame.utitle = None;
600       frame.utitle_pixel = texture->getColor()->getPixel();
601     } else {
602       frame.utitle =
603         image_ctrl->renderImage(frame.width, frame.title_h, texture);
604     }
605     if (tmp) image_ctrl->removeImage(tmp);
606
607     XSetWindowBorder(display, frame.title,
608                      screen->getBorderColor()->getPixel());
609
610     decorateLabel();
611   }
612
613   if (decorations.border) {
614     frame.fborder_pixel = screen->getWindowStyle()->f_focus.getPixel();
615     frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.getPixel();
616     openbox_attrib.flags |= AttribDecoration;
617     openbox_attrib.decoration = DecorNormal;
618   } else {
619     openbox_attrib.flags |= AttribDecoration;
620     openbox_attrib.decoration = DecorNone;
621   }
622
623   if (decorations.handle) {
624     tmp = frame.fhandle;
625     texture = &(screen->getWindowStyle()->h_focus);
626     if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
627       frame.fhandle = None;
628       frame.fhandle_pixel = texture->getColor()->getPixel();
629     } else {
630       frame.fhandle =
631         image_ctrl->renderImage(frame.width, frame.handle_h, texture);
632     }
633     if (tmp) image_ctrl->removeImage(tmp);
634
635     tmp = frame.uhandle;
636     texture = &(screen->getWindowStyle()->h_unfocus);
637     if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
638       frame.uhandle = None;
639       frame.uhandle_pixel = texture->getColor()->getPixel();
640     } else {
641       frame.uhandle =
642         image_ctrl->renderImage(frame.width, frame.handle_h, texture);
643     }
644     if (tmp) image_ctrl->removeImage(tmp);
645
646     tmp = frame.fgrip;
647     texture = &(screen->getWindowStyle()->g_focus);
648     if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
649       frame.fgrip = None;
650       frame.fgrip_pixel = texture->getColor()->getPixel();
651     } else {
652       frame.fgrip =
653         image_ctrl->renderImage(frame.grip_w, frame.grip_h, texture);
654     }
655     if (tmp) image_ctrl->removeImage(tmp);
656
657     tmp = frame.ugrip;
658     texture = &(screen->getWindowStyle()->g_unfocus);
659     if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
660       frame.ugrip = None;
661       frame.ugrip_pixel = texture->getColor()->getPixel();
662     } else {
663       frame.ugrip =
664         image_ctrl->renderImage(frame.grip_w, frame.grip_h, texture);
665     }
666     if (tmp) image_ctrl->removeImage(tmp);
667
668     XSetWindowBorder(display, frame.handle,
669                      screen->getBorderColor()->getPixel());
670     XSetWindowBorder(display, frame.left_grip,
671                      screen->getBorderColor()->getPixel());
672     XSetWindowBorder(display, frame.right_grip,
673                      screen->getBorderColor()->getPixel());
674   }
675
676   XSetWindowBorder(display, frame.window,
677                    screen->getBorderColor()->getPixel());
678 }
679
680
681 void OpenboxWindow::decorateLabel(void) {
682   Pixmap tmp = frame.flabel;
683   BTexture *texture = &(screen->getWindowStyle()->l_focus);
684   if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
685     frame.flabel = None;
686     frame.flabel_pixel = texture->getColor()->getPixel();
687   } else {
688     frame.flabel =
689       image_ctrl->renderImage(frame.label_w, frame.label_h, texture);
690   }
691   if (tmp) image_ctrl->removeImage(tmp);
692
693   tmp = frame.ulabel;
694   texture = &(screen->getWindowStyle()->l_unfocus);
695   if (texture->getTexture() == (BImage_Flat | BImage_Solid)) {
696     frame.ulabel = None;
697     frame.ulabel_pixel = texture->getColor()->getPixel();
698   } else {
699     frame.ulabel =
700       image_ctrl->renderImage(frame.label_w, frame.label_h, texture);
701   }
702   if (tmp) image_ctrl->removeImage(tmp);
703 }
704
705
706 void OpenboxWindow::createCloseButton(void) {
707   if (decorations.close && frame.title != None) {
708     frame.close_button = createChildWindow(frame.title);
709     openbox.saveWindowSearch(frame.close_button, this);
710   }
711 }
712
713
714 void OpenboxWindow::createIconifyButton(void) {
715   if (decorations.iconify && frame.title != None) {
716     frame.iconify_button = createChildWindow(frame.title);
717     openbox.saveWindowSearch(frame.iconify_button, this);
718   }
719 }
720
721
722 void OpenboxWindow::createMaximizeButton(void) {
723   if (decorations.maximize && frame.title != None) {
724     frame.maximize_button = createChildWindow(frame.title);
725     openbox.saveWindowSearch(frame.maximize_button, this);
726   }
727 }
728
729
730 void OpenboxWindow::positionButtons() {
731   const char *format = openbox.getTitleBarLayout();
732   const unsigned int bw = frame.bevel_w + 1;
733   const unsigned int by = frame.bevel_w + 1;
734   unsigned int bx = frame.bevel_w + 1;
735   unsigned int bcount = strlen(format) - 1;
736
737   if (!decorations.close)
738     bcount--;
739   if (!decorations.maximize)
740     bcount--;
741   if (!decorations.iconify)
742     bcount--;
743   frame.label_w = frame.width - bx * 2 - (frame.button_w + bw) * bcount;
744
745   bool hasclose, hasiconify, hasmaximize;
746   hasclose = hasiconify = hasmaximize = false;
747
748   for (int i = 0; format[i] != '\0' && i < 4; i++) {
749     switch(format[i]) {
750     case 'C':
751       if (decorations.close) {
752         if (frame.close_button == None)
753           createCloseButton();
754         XMoveResizeWindow(display, frame.close_button, bx, by,
755                           frame.button_w, frame.button_h);
756         XMapWindow(display, frame.close_button);
757         XClearWindow(display, frame.close_button);
758         bx += frame.button_w + bw;
759         hasclose = true;
760       }
761       break;
762     case 'I':
763       if (decorations.iconify) {
764         if (frame.iconify_button == None)
765           createIconifyButton();
766         XMoveResizeWindow(display, frame.iconify_button, bx, by,
767                           frame.button_w, frame.button_h);
768         XMapWindow(display, frame.iconify_button);
769         XClearWindow(display, frame.iconify_button);
770         bx += frame.button_w + bw;
771         hasiconify = true;
772       }
773       break;
774     case 'M':
775       if (decorations.maximize) {
776         if (frame.maximize_button == None)
777           createMaximizeButton();
778         XMoveResizeWindow(display, frame.maximize_button, bx, by,
779                           frame.button_w, frame.button_h);
780         XMapWindow(display, frame.maximize_button);
781         XClearWindow(display, frame.maximize_button);
782         bx += frame.button_w + bw;
783         hasmaximize = true;
784       }
785       break;
786     case 'L':
787       XMoveResizeWindow(display, frame.label, bx, by - 1,
788                         frame.label_w, frame.label_h);
789       bx += frame.label_w + bw;
790       break;
791     }
792   }
793
794   if (!hasclose && frame.close_button) {
795     openbox.removeWindowSearch(frame.close_button);
796     XDestroyWindow(display, frame.close_button);
797     frame.close_button = None;
798   }
799   if (!hasiconify && frame.iconify_button) {
800     openbox.removeWindowSearch(frame.iconify_button);
801     XDestroyWindow(display, frame.iconify_button);
802     frame.iconify_button = None;
803   }
804   if (!hasmaximize && frame.iconify_button) {
805     openbox.removeWindowSearch(frame.maximize_button);
806     XDestroyWindow(display, frame.maximize_button);                 
807     frame.maximize_button = None;
808   }
809
810   redrawLabel();
811   redrawAllButtons();
812 }
813
814
815 void OpenboxWindow::reconfigure(void) {
816   upsize();
817
818   client.x = frame.x + frame.mwm_border_w + frame.border_w;
819   client.y = frame.y + frame.y_border + frame.mwm_border_w +
820     frame.border_w;
821
822   if (client.title) {
823     if (i18n.multibyte()) {
824       XRectangle ink, logical;
825       XmbTextExtents(screen->getWindowStyle()->fontset,
826                      client.title, client.title_len, &ink, &logical);
827       client.title_text_w = logical.width;
828     } else {
829       client.title_text_w = XTextWidth(screen->getWindowStyle()->font,
830                                        client.title, client.title_len);
831     }
832     client.title_text_w += (frame.bevel_w * 4);
833   }
834
835   positionWindows();
836   decorate();
837
838   XClearWindow(display, frame.window);
839   setFocusFlag(flags.focused);
840
841   configure(frame.x, frame.y, frame.width, frame.height);
842
843   if (! screen->sloppyFocus())
844     openbox.grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
845                        GrabModeSync, GrabModeSync, None, None);
846   else
847     openbox.ungrabButton(Button1, 0, frame.plate);
848
849   if (windowmenu) {
850     windowmenu->move(windowmenu->getX(), frame.y + frame.title_h);
851     windowmenu->reconfigure();
852   }
853
854   // re-get the timeout delay
855   timer->setTimeout(openbox.getAutoRaiseDelay());
856 }
857
858
859 void OpenboxWindow::positionWindows(void) {
860   XResizeWindow(display, frame.window, frame.width,
861                 ((flags.shaded) ? frame.title_h : frame.height));
862   XSetWindowBorderWidth(display, frame.window, frame.border_w);
863   XSetWindowBorderWidth(display, frame.plate, frame.mwm_border_w);
864   XMoveResizeWindow(display, frame.plate, 0, frame.y_border,
865                     client.width, client.height);
866   XMoveResizeWindow(display, client.window, 0, 0, client.width, client.height);
867
868   if (decorations.titlebar) {
869     XSetWindowBorderWidth(display, frame.title, frame.border_w);
870     XMoveResizeWindow(display, frame.title, -frame.border_w,
871                       -frame.border_w, frame.width, frame.title_h);
872
873     positionButtons();
874   } else if (frame.title) {
875     XUnmapWindow(display, frame.title);
876   }
877   if (decorations.handle) {
878     XSetWindowBorderWidth(display, frame.handle, frame.border_w);
879     XSetWindowBorderWidth(display, frame.left_grip, frame.border_w);
880     XSetWindowBorderWidth(display, frame.right_grip, frame.border_w);
881
882     XMoveResizeWindow(display, frame.handle, -frame.border_w,
883                       frame.y_handle - frame.border_w,
884                       frame.width, frame.handle_h);
885     XMoveResizeWindow(display, frame.left_grip, -frame.border_w,
886                       -frame.border_w, frame.grip_w, frame.grip_h);
887     XMoveResizeWindow(display, frame.right_grip,
888                       frame.width - frame.grip_w - frame.border_w,
889                       -frame.border_w, frame.grip_w, frame.grip_h);
890     XMapSubwindows(display, frame.handle);
891   } else if (frame.handle) {
892     XUnmapWindow(display, frame.handle);
893   }
894 }
895
896
897 void OpenboxWindow::getWMName(void) {
898   if (client.title) {
899     delete [] client.title;
900     client.title = (char *) 0;
901   }
902
903   XTextProperty text_prop;
904   char **list;
905   int num;
906
907   if (XGetWMName(display, client.window, &text_prop)) {
908     if (text_prop.value && text_prop.nitems > 0) {
909       if (text_prop.encoding != XA_STRING) {
910         text_prop.nitems = strlen((char *) text_prop.value);
911
912         if ((XmbTextPropertyToTextList(display, &text_prop,
913                                        &list, &num) == Success) &&
914             (num > 0) && *list) {
915           client.title = bstrdup(*list);
916           XFreeStringList(list);
917         } else {
918           client.title = bstrdup((char *) text_prop.value);
919         }
920       } else {
921         client.title = bstrdup((char *) text_prop.value);
922       }
923       XFree((char *) text_prop.value);
924     } else {
925       client.title = bstrdup(i18n(WindowSet, WindowUnnamed,
926                                   "Unnamed"));
927     }
928   } else {
929     client.title = bstrdup(i18n(WindowSet, WindowUnnamed,
930                                 "Unnamed"));
931   }
932   client.title_len = strlen(client.title);
933
934   if (i18n.multibyte()) {
935     XRectangle ink, logical;
936     XmbTextExtents(screen->getWindowStyle()->fontset,
937                    client.title, client.title_len, &ink, &logical);
938     client.title_text_w = logical.width;
939   } else {
940     client.title_len = strlen(client.title);
941     client.title_text_w = XTextWidth(screen->getWindowStyle()->font,
942                                      client.title, client.title_len);
943   }
944
945   client.title_text_w += (frame.bevel_w * 4);
946 }
947
948
949 void OpenboxWindow::getWMIconName(void) {
950   if (client.icon_title) {
951     delete [] client.icon_title;
952     client.icon_title = (char *) 0;
953   }
954
955   XTextProperty text_prop;
956   char **list;
957   int num;
958
959   if (XGetWMIconName(display, client.window, &text_prop)) {
960     if (text_prop.value && text_prop.nitems > 0) {
961       if (text_prop.encoding != XA_STRING) {
962         text_prop.nitems = strlen((char *) text_prop.value);
963
964         if ((XmbTextPropertyToTextList(display, &text_prop,
965                                        &list, &num) == Success) &&
966             (num > 0) && *list) {
967           client.icon_title = bstrdup(*list);
968           XFreeStringList(list);
969         } else {
970           client.icon_title = bstrdup((char *) text_prop.value);
971         }
972       } else {
973         client.icon_title = bstrdup((char *) text_prop.value);
974       }
975       XFree((char *) text_prop.value);
976     } else {
977       client.icon_title = bstrdup(client.title);
978     }
979   } else {
980     client.icon_title = bstrdup(client.title);
981   }
982 }
983
984
985 /*
986  * Retrieve which WM Protocols are supported by the client window.
987  * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
988  * window's decorations and allow the close behavior.
989  * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
990  * this.
991  */
992 void OpenboxWindow::getWMProtocols(void) {
993   Atom *proto;
994   int num_return = 0;
995
996   if (XGetWMProtocols(display, client.window, &proto, &num_return)) {
997     for (int i = 0; i < num_return; ++i) {
998       if (proto[i] == openbox.getWMDeleteAtom())
999         functions.close = decorations.close = True;
1000       else if (proto[i] == openbox.getWMTakeFocusAtom())
1001         flags.send_focus_message = True;
1002       else if (proto[i] == openbox.getOpenboxStructureMessagesAtom())
1003         screen->addNetizen(new Netizen(*screen, client.window));
1004     }
1005
1006     XFree(proto);
1007   }
1008 }
1009
1010
1011 /*
1012  * Gets the value of the WM_HINTS property.
1013  * If the property is not set, then use a set of default values.
1014  */
1015 void OpenboxWindow::getWMHints(void) {
1016   XWMHints *wmhint = XGetWMHints(display, client.window);
1017   if (! wmhint) {
1018     flags.visible = True;
1019     flags.iconic = False;
1020     focus_mode = F_Passive;
1021     client.window_group = None;
1022     client.initial_state = NormalState;
1023     return;
1024   }
1025   client.wm_hint_flags = wmhint->flags;
1026   if (wmhint->flags & InputHint) {
1027     if (wmhint->input == True) {
1028       if (flags.send_focus_message)
1029         focus_mode = F_LocallyActive;
1030       else
1031         focus_mode = F_Passive;
1032     } else {
1033       if (flags.send_focus_message)
1034         focus_mode = F_GloballyActive;
1035       else
1036         focus_mode = F_NoInput;
1037     }
1038   } else {
1039     focus_mode = F_Passive;
1040   }
1041
1042   if (wmhint->flags & StateHint)
1043     client.initial_state = wmhint->initial_state;
1044   else
1045     client.initial_state = NormalState;
1046
1047   if (wmhint->flags & WindowGroupHint) {
1048     if (! client.window_group) {
1049       client.window_group = wmhint->window_group;
1050       openbox.saveGroupSearch(client.window_group, this);
1051     }
1052   } else {
1053     client.window_group = None;
1054   }
1055   XFree(wmhint);
1056 }
1057
1058
1059 /*
1060  * Gets the value of the WM_NORMAL_HINTS property.
1061  * If the property is not set, then use a set of default values.
1062  */
1063 void OpenboxWindow::getWMNormalHints(void) {
1064   long icccm_mask;
1065   XSizeHints sizehint;
1066
1067   client.min_width = client.min_height =
1068     client.base_width = client.base_height =
1069     client.width_inc = client.height_inc = 1;
1070   client.max_width = screen->size().w();
1071   client.max_height = screen->size().h();
1072   client.min_aspect_x = client.min_aspect_y =
1073     client.max_aspect_x = client.max_aspect_y = 1;
1074   client.win_gravity = NorthWestGravity;
1075
1076   if (! XGetWMNormalHints(display, client.window, &sizehint, &icccm_mask))
1077     return;
1078
1079   client.normal_hint_flags = sizehint.flags;
1080
1081   if (sizehint.flags & PMinSize) {
1082     client.min_width = sizehint.min_width;
1083     client.min_height = sizehint.min_height;
1084   }
1085
1086   if (sizehint.flags & PMaxSize) {
1087     client.max_width = sizehint.max_width;
1088     client.max_height = sizehint.max_height;
1089   }
1090
1091   if (sizehint.flags & PResizeInc) {
1092     client.width_inc = sizehint.width_inc;
1093     client.height_inc = sizehint.height_inc;
1094   }
1095
1096   if (sizehint.flags & PAspect) {
1097     client.min_aspect_x = sizehint.min_aspect.x;
1098     client.min_aspect_y = sizehint.min_aspect.y;
1099     client.max_aspect_x = sizehint.max_aspect.x;
1100     client.max_aspect_y = sizehint.max_aspect.y;
1101   }
1102
1103   if (sizehint.flags & PBaseSize) {
1104     client.base_width = sizehint.base_width;
1105     client.base_height = sizehint.base_height;
1106   }
1107
1108   if (sizehint.flags & PWinGravity)
1109     client.win_gravity = sizehint.win_gravity;
1110 }
1111
1112
1113 /*
1114  * Gets the MWM hints for the class' contained window.
1115  * This is used while initializing the window to its first state, and not
1116  * thereafter.
1117  * Returns: true if the MWM hints are successfully retreived and applied; false
1118  * if they are not.
1119  */
1120 void OpenboxWindow::getMWMHints(void) {
1121   int format;
1122   Atom atom_return;
1123   unsigned long num, len;
1124
1125   int ret = XGetWindowProperty(display, client.window,
1126                                openbox.getMotifWMHintsAtom(), 0,
1127                                PropMwmHintsElements, False,
1128                                openbox.getMotifWMHintsAtom(), &atom_return,
1129                                &format, &num, &len,
1130                                (unsigned char **) &client.mwm_hint);
1131
1132   if (ret != Success || !client.mwm_hint || num != PropMwmHintsElements)
1133     return;
1134
1135   if (client.mwm_hint->flags & MwmHintsDecorations) {
1136     if (client.mwm_hint->decorations & MwmDecorAll) {
1137       decorations.titlebar = decorations.handle = decorations.border =
1138         decorations.iconify = decorations.maximize =
1139         decorations.close = decorations.menu = True;
1140     } else {
1141       decorations.titlebar = decorations.handle = decorations.border =
1142         decorations.iconify = decorations.maximize =
1143         decorations.close = decorations.menu = False;
1144
1145       if (client.mwm_hint->decorations & MwmDecorBorder)
1146         decorations.border = True;
1147       if (client.mwm_hint->decorations & MwmDecorHandle)
1148         decorations.handle = True;
1149       if (client.mwm_hint->decorations & MwmDecorTitle)
1150         decorations.titlebar = True;
1151       if (client.mwm_hint->decorations & MwmDecorMenu)
1152         decorations.menu = True;
1153       if (client.mwm_hint->decorations & MwmDecorIconify)
1154         decorations.iconify = True;
1155       if (client.mwm_hint->decorations & MwmDecorMaximize)
1156         decorations.maximize = True;
1157     }
1158   }
1159
1160   if (client.mwm_hint->flags & MwmHintsFunctions) {
1161     if (client.mwm_hint->functions & MwmFuncAll) {
1162       functions.resize = functions.move = functions.iconify =
1163         functions.maximize = functions.close = True;
1164     } else {
1165       functions.resize = functions.move = functions.iconify =
1166         functions.maximize = functions.close = False;
1167
1168       if (client.mwm_hint->functions & MwmFuncResize)
1169         functions.resize = True;
1170       if (client.mwm_hint->functions & MwmFuncMove)
1171         functions.move = True;
1172       if (client.mwm_hint->functions & MwmFuncIconify)
1173         functions.iconify = True;
1174       if (client.mwm_hint->functions & MwmFuncMaximize)
1175         functions.maximize = True;
1176       if (client.mwm_hint->functions & MwmFuncClose)
1177         functions.close = True;
1178     }
1179   }
1180 }
1181
1182
1183 /*
1184  * Gets the openbox hints from the class' contained window.
1185  * This is used while initializing the window to its first state, and not
1186  * thereafter.
1187  * Returns: true if the hints are successfully retreived and applied; false if
1188  * they are not.
1189  */
1190 void OpenboxWindow::getOpenboxHints(void) {
1191   int format;
1192   Atom atom_return;
1193   unsigned long num, len;
1194
1195   int ret = XGetWindowProperty(display, client.window,
1196                                openbox.getOpenboxHintsAtom(), 0,
1197                                PropOpenboxHintsElements, False,
1198                                openbox.getOpenboxHintsAtom(), &atom_return,
1199                                &format, &num, &len,
1200                                (unsigned char **) &client.openbox_hint);
1201   if (ret != Success || !client.openbox_hint ||
1202       num != PropOpenboxHintsElements)
1203     return;
1204
1205   if (client.openbox_hint->flags & AttribShaded)
1206     flags.shaded = (client.openbox_hint->attrib & AttribShaded);
1207
1208   if ((client.openbox_hint->flags & AttribMaxHoriz) &&
1209       (client.openbox_hint->flags & AttribMaxVert))
1210     flags.maximized = (client.openbox_hint->attrib &
1211                        (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1212   else if (client.openbox_hint->flags & AttribMaxVert)
1213     flags.maximized = (client.openbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1214   else if (client.openbox_hint->flags & AttribMaxHoriz)
1215     flags.maximized = (client.openbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1216
1217   if (client.openbox_hint->flags & AttribOmnipresent)
1218     flags.stuck = (client.openbox_hint->attrib & AttribOmnipresent);
1219
1220   if (client.openbox_hint->flags & AttribWorkspace)
1221     workspace_number = client.openbox_hint->workspace;
1222
1223   // if (client.openbox_hint->flags & AttribStack)
1224   //   don't yet have always on top/bottom for openbox yet... working
1225   //   on that
1226
1227   if (client.openbox_hint->flags & AttribDecoration) {
1228     switch (client.openbox_hint->decoration) {
1229     case DecorNone:
1230       decorations.titlebar = decorations.border = decorations.handle =
1231         decorations.iconify = decorations.maximize =
1232         decorations.menu = False;
1233       functions.resize = functions.move = functions.iconify =
1234         functions.maximize = False;
1235
1236       break;
1237
1238     case DecorTiny:
1239       decorations.titlebar = decorations.iconify = decorations.menu =
1240         functions.move = functions.iconify = True;
1241       decorations.border = decorations.handle = decorations.maximize =
1242         functions.resize = functions.maximize = False;
1243
1244       break;
1245
1246     case DecorTool:
1247       decorations.titlebar = decorations.menu = functions.move = True;
1248       decorations.iconify = decorations.border = decorations.handle =
1249         decorations.maximize = functions.resize = functions.maximize =
1250         functions.iconify = False;
1251
1252       break;
1253
1254     case DecorNormal:
1255     default:
1256       decorations.titlebar = decorations.border = decorations.handle =
1257         decorations.iconify = decorations.maximize =
1258         decorations.menu = True;
1259       functions.resize = functions.move = functions.iconify =
1260         functions.maximize = True;
1261
1262       break;
1263     }
1264
1265     reconfigure();
1266   }
1267 }
1268
1269
1270 void OpenboxWindow::configure(int dx, int dy,
1271                               unsigned int dw, unsigned int dh) {
1272   Bool send_event = (frame.x != dx || frame.y != dy);
1273
1274   if ((dw != frame.width) || (dh != frame.height)) {
1275     if ((((signed) frame.width) + dx) < 0) dx = 0;
1276     if ((((signed) frame.height) + dy) < 0) dy = 0;
1277
1278     frame.x = dx;
1279     frame.y = dy;
1280     frame.width = dw;
1281     frame.height = dh;
1282
1283     downsize();
1284
1285 #ifdef    SHAPE
1286     if (openbox.hasShapeExtensions() && flags.shaped) {
1287       XShapeCombineShape(display, frame.window, ShapeBounding,
1288                          frame.mwm_border_w, frame.y_border +
1289                          frame.mwm_border_w, client.window,
1290                          ShapeBounding, ShapeSet);
1291
1292       int num = 1;
1293       XRectangle xrect[2];
1294       xrect[0].x = xrect[0].y = 0;
1295       xrect[0].width = frame.width;
1296       xrect[0].height = frame.y_border;
1297
1298       if (decorations.handle) {
1299         xrect[1].x = 0;
1300         xrect[1].y = frame.y_handle;
1301         xrect[1].width = frame.width;
1302         xrect[1].height = frame.handle_h + frame.border_w;
1303         num++;
1304       }
1305
1306       XShapeCombineRectangles(display, frame.window, ShapeBounding, 0, 0,
1307                               xrect, num, ShapeUnion, Unsorted);
1308     }
1309 #endif // SHAPE
1310
1311     XMoveWindow(display, frame.window, frame.x, frame.y);
1312
1313     setFocusFlag(flags.focused);
1314     positionWindows();
1315     decorate();
1316   } else {
1317     frame.x = dx;
1318     frame.y = dy;
1319
1320     XMoveWindow(display, frame.window, frame.x, frame.y);
1321
1322     if (! flags.moving) send_event = True;
1323   }
1324
1325   if (send_event && ! flags.moving) {
1326     client.x = dx + frame.mwm_border_w + frame.border_w;
1327     client.y = dy + frame.y_border + frame.mwm_border_w +
1328       frame.border_w;
1329
1330     XEvent event;
1331     event.type = ConfigureNotify;
1332
1333     event.xconfigure.display = display;
1334     event.xconfigure.event = client.window;
1335     event.xconfigure.window = client.window;
1336     event.xconfigure.x = client.x;
1337     event.xconfigure.y = client.y;
1338     event.xconfigure.width = client.width;
1339     event.xconfigure.height = client.height;
1340     event.xconfigure.border_width = client.old_bw;
1341     event.xconfigure.above = frame.window;
1342     event.xconfigure.override_redirect = False;
1343
1344     XSendEvent(display, client.window, True, NoEventMask, &event);
1345
1346     screen->updateNetizenConfigNotify(&event);
1347   }
1348 }
1349
1350
1351 bool OpenboxWindow::setInputFocus(void) {
1352   if (((signed) (frame.x + frame.width)) < 0) {
1353     if (((signed) (frame.y + frame.y_border)) < 0)
1354       configure(frame.border_w, frame.border_w, frame.width, frame.height);
1355     else if (frame.y > (signed) screen->size().h())
1356       configure(frame.border_w, screen->size().h() - frame.height,
1357                 frame.width, frame.height);
1358     else
1359       configure(frame.border_w, frame.y + frame.border_w,
1360                 frame.width, frame.height);
1361   } else if (frame.x > (signed) screen->size().w()) {
1362     if (((signed) (frame.y + frame.y_border)) < 0)
1363       configure(screen->size().w() - frame.width, frame.border_w,
1364                 frame.width, frame.height);
1365     else if (frame.y > (signed) screen->size().h())
1366       configure(screen->size().w() - frame.width,
1367                 screen->size().h() - frame.height, frame.width, frame.height);
1368     else
1369       configure(screen->size().w() - frame.width,
1370                 frame.y + frame.border_w, frame.width, frame.height);
1371   }
1372
1373   openbox.grab();
1374   if (! validateClient()) return False;
1375
1376   bool ret = false;
1377
1378   if (client.transient && flags.modal) {
1379     ret = client.transient->setInputFocus();
1380   } else if (! flags.focused) {
1381     if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1382       XSetInputFocus(display, client.window,
1383                      RevertToPointerRoot, CurrentTime);
1384       openbox.focusWindow(this);
1385
1386       if (flags.send_focus_message) {
1387         XEvent ce;
1388         ce.xclient.type = ClientMessage;
1389         ce.xclient.message_type = openbox.getWMProtocolsAtom();
1390         ce.xclient.display = display;
1391         ce.xclient.window = client.window;
1392         ce.xclient.format = 32;
1393         ce.xclient.data.l[0] = openbox.getWMTakeFocusAtom();
1394         ce.xclient.data.l[1] = openbox.getLastTime();
1395         ce.xclient.data.l[2] = 0l;
1396         ce.xclient.data.l[3] = 0l;
1397         ce.xclient.data.l[4] = 0l;
1398         XSendEvent(display, client.window, False, NoEventMask, &ce);
1399       }
1400
1401       if (screen->sloppyFocus() && screen->autoRaise())
1402         timer->start();
1403
1404       ret = true;
1405     }
1406   }
1407
1408   openbox.ungrab();
1409
1410   return ret;
1411 }
1412
1413
1414 void OpenboxWindow::iconify(void) {
1415   if (flags.iconic) return;
1416
1417   if (flags.moving)
1418     endMove();
1419
1420   if (windowmenu) windowmenu->hide();
1421
1422   setState(IconicState);
1423
1424   XSelectInput(display, client.window, NoEventMask);
1425   XUnmapWindow(display, client.window);
1426   XSelectInput(display, client.window,
1427                PropertyChangeMask | StructureNotifyMask | FocusChangeMask);
1428
1429   XUnmapWindow(display, frame.window);
1430   flags.visible = False;
1431   flags.iconic = True;
1432
1433   screen->getWorkspace(workspace_number)->removeWindow(this);
1434
1435   if (flags.transient && client.transient_for &&
1436       !client.transient_for->flags.iconic) {
1437     client.transient_for->iconify();
1438   }
1439   screen->addIcon(this);
1440
1441   if (client.transient && !client.transient->flags.iconic) {
1442     client.transient->iconify();
1443   }
1444 }
1445
1446
1447 void OpenboxWindow::deiconify(bool reassoc, bool raise, bool initial) {
1448   if (flags.iconic || reassoc)
1449     screen->reassociateWindow(this, -1, False);
1450   else if (workspace_number != screen->getCurrentWorkspace()->getWorkspaceID())
1451     return;
1452
1453   setState(NormalState);
1454
1455   XSelectInput(display, client.window, NoEventMask);
1456   XMapWindow(display, client.window);
1457   XSelectInput(display, client.window,
1458                PropertyChangeMask | StructureNotifyMask | FocusChangeMask);
1459
1460   XMapSubwindows(display, frame.window);
1461   XMapWindow(display, frame.window);
1462
1463   // if we're using the click to place placement type, then immediately
1464   // after the window is mapped, we need to start interactively moving it
1465   if (initial && place_window &&
1466       screen->placementPolicy() == BScreen::ClickMousePlacement) {
1467     int x, y, rx, ry;
1468     Window c, r;
1469     unsigned int m;
1470     XQueryPointer(openbox.getXDisplay(), screen->getRootWindow(),
1471                   &r, &c, &rx, &ry, &x, &y, &m);
1472     startMove(rx, ry);
1473   }
1474
1475   if (flags.iconic && screen->focusNew()) setInputFocus();
1476
1477   flags.visible = True;
1478   flags.iconic = False;
1479
1480   if (reassoc && client.transient) client.transient->deiconify(True, False);
1481
1482   if (raise)
1483     screen->getWorkspace(workspace_number)->raiseWindow(this);
1484 }
1485
1486
1487 void OpenboxWindow::close(void) {
1488   XEvent ce;
1489   ce.xclient.type = ClientMessage;
1490   ce.xclient.message_type = openbox.getWMProtocolsAtom();
1491   ce.xclient.display = display;
1492   ce.xclient.window = client.window;
1493   ce.xclient.format = 32;
1494   ce.xclient.data.l[0] = openbox.getWMDeleteAtom();
1495   ce.xclient.data.l[1] = CurrentTime;
1496   ce.xclient.data.l[2] = 0l;
1497   ce.xclient.data.l[3] = 0l;
1498   ce.xclient.data.l[4] = 0l;
1499   XSendEvent(display, client.window, False, NoEventMask, &ce);
1500 }
1501
1502
1503 void OpenboxWindow::withdraw(void) {
1504   if (flags.moving)
1505     endMove();
1506
1507   flags.visible = False;
1508   flags.iconic = False;
1509
1510   XUnmapWindow(display, frame.window);
1511
1512   XSelectInput(display, client.window, NoEventMask);
1513   XUnmapWindow(display, client.window);
1514   XSelectInput(display, client.window,
1515                PropertyChangeMask | StructureNotifyMask | FocusChangeMask);
1516
1517   if (windowmenu) windowmenu->hide();
1518 }
1519
1520
1521 void OpenboxWindow::maximize(unsigned int button) {
1522   if (flags.moving)
1523     endMove();
1524
1525   // handle case where menu is open then the max button is used instead
1526   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1527
1528   if (flags.maximized) {
1529     flags.maximized = 0;
1530
1531     openbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1532     openbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1533
1534     // when a resize is begun, maximize(0) is called to clear any maximization
1535     // flags currently set.  Otherwise it still thinks it is maximized.
1536     // so we do not need to call configure() because resizing will handle it
1537     if (!flags.resizing)
1538       configure(openbox_attrib.premax_x, openbox_attrib.premax_y,
1539                 openbox_attrib.premax_w, openbox_attrib.premax_h);
1540
1541     openbox_attrib.premax_x = openbox_attrib.premax_y = 0;
1542     openbox_attrib.premax_w = openbox_attrib.premax_h = 0;
1543
1544     redrawMaximizeButton(flags.maximized);
1545     setState(current_state);
1546     return;
1547   }
1548
1549   openbox_attrib.premax_x = frame.x;
1550   openbox_attrib.premax_y = frame.y;
1551   openbox_attrib.premax_w = frame.width;
1552   openbox_attrib.premax_h = frame.height;
1553
1554   Rect space = screen->availableArea();
1555   unsigned int dw = space.w(),
1556   dh = space.h();
1557   dw -= frame.border_w * 2;
1558   dw -= frame.mwm_border_w * 2;
1559   dw -= client.base_width;
1560
1561   dh -= frame.border_w * 2;
1562   dh -= frame.mwm_border_w * 2;
1563   dh -= ((frame.handle_h + frame.border_w) * decorations.handle);
1564   dh -= client.base_height;
1565   dh -= frame.y_border;
1566
1567   if (dw < client.min_width) dw = client.min_width;
1568   if (dh < client.min_height) dh = client.min_height;
1569   if (dw > client.max_width) dw = client.max_width;
1570   if (dh > client.max_height) dh = client.max_height;
1571
1572   dw -= (dw % client.width_inc);
1573   dw += client.base_width;
1574   dw += frame.mwm_border_w * 2;
1575
1576   dh -= (dh % client.height_inc);
1577   dh += client.base_height;
1578   dh += frame.y_border;
1579   dh += ((frame.handle_h + frame.border_w) * decorations.handle);
1580   dh += frame.mwm_border_w * 2;
1581
1582   int dx = space.x() + ((space.w() - dw) / 2) - frame.border_w,
1583   dy = space.y() + ((space.h() - dh) / 2) - frame.border_w;
1584
1585   switch(button) {
1586   case 1:
1587     openbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1588     openbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1589     break;
1590
1591   case 2:
1592     openbox_attrib.flags |= AttribMaxVert;
1593     openbox_attrib.attrib |= AttribMaxVert;
1594
1595     dw = frame.width;
1596     dx = frame.x;
1597     break;
1598
1599   case 3:
1600     openbox_attrib.flags |= AttribMaxHoriz;
1601     openbox_attrib.attrib |= AttribMaxHoriz;
1602
1603     dh = frame.height;
1604     dy = frame.y;
1605     break;
1606   }
1607
1608   if (flags.shaded) {
1609     openbox_attrib.flags ^= AttribShaded;
1610     openbox_attrib.attrib ^= AttribShaded;
1611     flags.shaded = False;
1612   }
1613
1614   flags.maximized = button;
1615
1616   configure(dx, dy, dw, dh);
1617   screen->getWorkspace(workspace_number)->raiseWindow(this);
1618   redrawMaximizeButton(flags.maximized);
1619   setState(current_state);
1620 }
1621
1622
1623 void OpenboxWindow::setWorkspace(int n) {
1624   ASSERT(n < screen->getWorkspaceCount());
1625   workspace_number = n;
1626
1627   openbox_attrib.flags |= AttribWorkspace;
1628   openbox_attrib.workspace = workspace_number;
1629 }
1630
1631
1632 void OpenboxWindow::shade(void) {
1633   if (!decorations.titlebar)
1634     return;
1635
1636   if (flags.shaded) {
1637     XResizeWindow(display, frame.window, frame.width, frame.height);
1638     flags.shaded = False;
1639     openbox_attrib.flags ^= AttribShaded;
1640     openbox_attrib.attrib ^= AttribShaded;
1641
1642     setState(NormalState);
1643   } else {
1644     XResizeWindow(display, frame.window, frame.width, frame.title_h);
1645     flags.shaded = True;
1646     openbox_attrib.flags |= AttribShaded;
1647     openbox_attrib.attrib |= AttribShaded;
1648
1649     setState(IconicState);
1650   }
1651 }
1652
1653
1654 void OpenboxWindow::stick(void) {
1655   if (flags.stuck) {
1656     openbox_attrib.flags ^= AttribOmnipresent;
1657     openbox_attrib.attrib ^= AttribOmnipresent;
1658
1659     flags.stuck = False;
1660
1661     if (! flags.iconic)
1662       screen->reassociateWindow(this, -1, True);
1663
1664     setState(current_state);
1665   } else {
1666     flags.stuck = True;
1667
1668     openbox_attrib.flags |= AttribOmnipresent;
1669     openbox_attrib.attrib |= AttribOmnipresent;
1670
1671     setState(current_state);
1672   }
1673 }
1674
1675
1676 void OpenboxWindow::setFocusFlag(Bool focus) {
1677   flags.focused = focus;
1678
1679   if (decorations.titlebar) {
1680     if (flags.focused) {
1681       if (frame.ftitle)
1682         XSetWindowBackgroundPixmap(display, frame.title, frame.ftitle);
1683       else
1684         XSetWindowBackground(display, frame.title, frame.ftitle_pixel);
1685     } else {
1686       if (frame.utitle)
1687         XSetWindowBackgroundPixmap(display, frame.title, frame.utitle);
1688       else
1689         XSetWindowBackground(display, frame.title, frame.utitle_pixel);
1690     }
1691     XClearWindow(display, frame.title);
1692
1693     redrawLabel();
1694     redrawAllButtons();
1695   }
1696
1697   if (decorations.handle) {
1698     if (flags.focused) {
1699       if (frame.fhandle)
1700         XSetWindowBackgroundPixmap(display, frame.handle, frame.fhandle);
1701       else
1702         XSetWindowBackground(display, frame.handle, frame.fhandle_pixel);
1703
1704       if (frame.fgrip) {
1705         XSetWindowBackgroundPixmap(display, frame.right_grip, frame.fgrip);
1706         XSetWindowBackgroundPixmap(display, frame.left_grip, frame.fgrip);
1707       } else {
1708         XSetWindowBackground(display, frame.right_grip, frame.fgrip_pixel);
1709         XSetWindowBackground(display, frame.left_grip, frame.fgrip_pixel);
1710       }
1711     } else {
1712       if (frame.uhandle)
1713         XSetWindowBackgroundPixmap(display, frame.handle, frame.uhandle);
1714       else
1715         XSetWindowBackground(display, frame.handle, frame.uhandle_pixel);
1716
1717       if (frame.ugrip) {
1718         XSetWindowBackgroundPixmap(display, frame.right_grip, frame.ugrip);
1719         XSetWindowBackgroundPixmap(display, frame.left_grip, frame.ugrip);
1720       } else {
1721         XSetWindowBackground(display, frame.right_grip, frame.ugrip_pixel);
1722         XSetWindowBackground(display, frame.left_grip, frame.ugrip_pixel);
1723       }
1724     }
1725     XClearWindow(display, frame.handle);
1726     XClearWindow(display, frame.right_grip);
1727     XClearWindow(display, frame.left_grip);
1728   }
1729
1730   if (decorations.border) {
1731     if (flags.focused)
1732       XSetWindowBorder(display, frame.plate, frame.fborder_pixel);
1733     else
1734       XSetWindowBorder(display, frame.plate, frame.uborder_pixel);
1735   }
1736
1737   if (screen->sloppyFocus() && screen->autoRaise() && timer->isTiming())
1738     timer->stop();
1739
1740 }
1741
1742
1743 void OpenboxWindow::installColormap(Bool install) {
1744   openbox.grab();
1745   if (! validateClient()) return;
1746
1747   int i = 0, ncmap = 0;
1748   Colormap *cmaps = XListInstalledColormaps(display, client.window, &ncmap);
1749   XWindowAttributes wattrib;
1750   if (cmaps) {
1751     if (XGetWindowAttributes(display, client.window, &wattrib)) {
1752       if (install) {
1753         // install the window's colormap
1754         for (i = 0; i < ncmap; i++) {
1755           if (*(cmaps + i) == wattrib.colormap)
1756             // this window is using an installed color map... do not install
1757             install = False;
1758         }
1759         // otherwise, install the window's colormap
1760         if (install)
1761           XInstallColormap(display, wattrib.colormap);
1762       } else {
1763         // uninstall the window's colormap
1764         for (i = 0; i < ncmap; i++) {
1765           if (*(cmaps + i) == wattrib.colormap)
1766             // we found the colormap to uninstall
1767             XUninstallColormap(display, wattrib.colormap);
1768         }
1769       }
1770     }
1771
1772     XFree(cmaps);
1773   }
1774
1775   openbox.ungrab();
1776 }
1777
1778
1779 void OpenboxWindow::setState(unsigned long new_state) {
1780   current_state = new_state;
1781
1782   unsigned long state[2];
1783   state[0] = (unsigned long) current_state;
1784   state[1] = (unsigned long) None;
1785   XChangeProperty(display, client.window, openbox.getWMStateAtom(),
1786                   openbox.getWMStateAtom(), 32, PropModeReplace,
1787                   (unsigned char *) state, 2);
1788
1789   XChangeProperty(display, client.window,
1790                   openbox.getOpenboxAttributesAtom(),
1791                   openbox.getOpenboxAttributesAtom(), 32, PropModeReplace,
1792                   (unsigned char *) &openbox_attrib,
1793                   PropOpenboxAttributesElements);
1794 }
1795
1796
1797 Bool OpenboxWindow::getState(void) {
1798   current_state = 0;
1799
1800   Atom atom_return;
1801   Bool ret = False;
1802   int foo;
1803   unsigned long *state, ulfoo, nitems;
1804
1805   if ((XGetWindowProperty(display, client.window, openbox.getWMStateAtom(),
1806                           0l, 2l, False, openbox.getWMStateAtom(),
1807                           &atom_return, &foo, &nitems, &ulfoo,
1808                           (unsigned char **) &state) != Success) ||
1809       (! state)) {
1810     openbox.ungrab();
1811     return False;
1812   }
1813
1814   if (nitems >= 1) {
1815     current_state = (unsigned long) state[0];
1816
1817     ret = True;
1818   }
1819
1820   XFree((void *) state);
1821
1822   return ret;
1823 }
1824
1825
1826 void OpenboxWindow::setGravityOffsets(void) {
1827   // x coordinates for each gravity type
1828   const int x_west = client.x;
1829   const int x_east = client.x + client.width - frame.width;
1830   const int x_center = client.x + client.width - frame.width/2;
1831   // y coordinates for each gravity type
1832   const int y_north = client.y;
1833   const int y_south = client.y + client.height - frame.height;
1834   const int y_center = client.y + client.height - frame.height/2;
1835
1836   switch (client.win_gravity) {
1837   case NorthWestGravity:
1838   default:
1839     frame.x = x_west;
1840     frame.y = y_north;
1841     break;
1842   case NorthGravity:
1843     frame.x = x_center;
1844     frame.y = y_north;
1845     break;
1846   case NorthEastGravity:
1847     frame.x = x_east;
1848     frame.y = y_north;
1849     break;
1850   case SouthWestGravity:
1851     frame.x = x_west;
1852     frame.y = y_south;
1853     break;
1854   case SouthGravity:
1855     frame.x = x_center;
1856     frame.y = y_south;
1857     break;
1858   case SouthEastGravity:
1859     frame.x = x_east;
1860     frame.y = y_south;
1861     break;
1862   case WestGravity:
1863     frame.x = x_west;
1864     frame.y = y_center;
1865     break;
1866   case EastGravity:
1867     frame.x = x_east;
1868     frame.y = y_center;
1869     break;
1870   case CenterGravity:
1871     frame.x = x_center;
1872     frame.y = y_center;
1873     break;
1874   case ForgetGravity:
1875   case StaticGravity:
1876     frame.x = client.x - frame.mwm_border_w + frame.border_w;
1877     frame.y = client.y - frame.y_border - frame.mwm_border_w - frame.border_w;
1878     break;
1879   }
1880 }
1881
1882
1883 void OpenboxWindow::restoreAttributes(void) {
1884   if (! getState()) current_state = NormalState;
1885
1886   Atom atom_return;
1887   int foo;
1888   unsigned long ulfoo, nitems;
1889
1890   OpenboxAttributes *net;
1891   int ret = XGetWindowProperty(display, client.window,
1892                                openbox.getOpenboxAttributesAtom(), 0l,
1893                                PropOpenboxAttributesElements, False,
1894                                openbox.getOpenboxAttributesAtom(),
1895                                &atom_return, &foo, &nitems, &ulfoo,
1896                                (unsigned char **) &net);
1897   if (ret != Success || !net || nitems != PropOpenboxAttributesElements)
1898     return;
1899
1900   openbox_attrib.flags = net->flags;
1901   openbox_attrib.attrib = net->attrib;
1902   openbox_attrib.decoration = net->decoration;
1903   openbox_attrib.workspace = net->workspace;
1904   openbox_attrib.stack = net->stack;
1905   openbox_attrib.premax_x = net->premax_x;
1906   openbox_attrib.premax_y = net->premax_y;
1907   openbox_attrib.premax_w = net->premax_w;
1908   openbox_attrib.premax_h = net->premax_h;
1909
1910   XFree((void *) net);
1911
1912   if (openbox_attrib.flags & AttribShaded &&
1913       openbox_attrib.attrib & AttribShaded) {
1914     int save_state =
1915       ((current_state == IconicState) ? NormalState : current_state);
1916
1917     flags.shaded = False;
1918     shade();
1919
1920     current_state = save_state;
1921   }
1922
1923   if (((int) openbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
1924       ((int) openbox_attrib.workspace < screen->getWorkspaceCount())) {
1925     screen->reassociateWindow(this, openbox_attrib.workspace, True);
1926
1927     if (current_state == NormalState) current_state = WithdrawnState;
1928   } else if (current_state == WithdrawnState) {
1929     current_state = NormalState;
1930   }
1931
1932   if (openbox_attrib.flags & AttribOmnipresent &&
1933       openbox_attrib.attrib & AttribOmnipresent) {
1934     flags.stuck = False;
1935     stick();
1936
1937     current_state = NormalState;
1938   }
1939
1940   if ((openbox_attrib.flags & AttribMaxHoriz) ||
1941       (openbox_attrib.flags & AttribMaxVert)) {
1942     int x = openbox_attrib.premax_x, y = openbox_attrib.premax_y;
1943     unsigned int w = openbox_attrib.premax_w, h = openbox_attrib.premax_h;
1944     flags.maximized = 0;
1945
1946     unsigned int m = False;
1947     if ((openbox_attrib.flags & AttribMaxHoriz) &&
1948         (openbox_attrib.flags & AttribMaxVert))
1949       m = (openbox_attrib.attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1950     else if (openbox_attrib.flags & AttribMaxVert)
1951       m = (openbox_attrib.attrib & AttribMaxVert) ? 2 : 0;
1952     else if (openbox_attrib.flags & AttribMaxHoriz)
1953       m = (openbox_attrib.attrib & AttribMaxHoriz) ? 3 : 0;
1954
1955     if (m) maximize(m);
1956
1957     openbox_attrib.premax_x = x;
1958     openbox_attrib.premax_y = y;
1959     openbox_attrib.premax_w = w;
1960     openbox_attrib.premax_h = h;
1961   }
1962
1963   setState(current_state);
1964 }
1965
1966
1967 /*
1968  * The reverse of the setGravityOffsets function. Uses the frame window's
1969  * position to find the window's reference point.
1970  */
1971 void OpenboxWindow::restoreGravity(void) {
1972   // x coordinates for each gravity type
1973   const int x_west = frame.x;
1974   const int x_east = frame.x + frame.width - client.width;
1975   const int x_center = frame.x + (frame.width/2) - client.width;
1976   // y coordinates for each gravity type
1977   const int y_north = frame.y;
1978   const int y_south = frame.y + frame.height - client.height;
1979   const int y_center = frame.y + (frame.height/2) - client.height;
1980
1981   switch(client.win_gravity) {
1982   default:
1983   case NorthWestGravity:
1984     client.x = x_west;
1985     client.y = y_north;
1986     break;
1987   case NorthGravity:
1988     client.x = x_center;
1989     client.y = y_north;
1990     break;
1991   case NorthEastGravity:
1992     client.x = x_east;
1993     client.y = y_north;
1994     break;
1995   case SouthWestGravity:
1996     client.x = x_west;
1997     client.y = y_south;
1998     break;
1999   case SouthGravity:
2000     client.x = x_center;
2001     client.y = y_south;
2002     break;
2003   case SouthEastGravity:
2004     client.x = x_east;
2005     client.y = y_south;
2006     break;
2007   case WestGravity:
2008     client.x = x_west;
2009     client.y = y_center;
2010     break;
2011   case EastGravity:
2012     client.x = x_east;
2013     client.y = y_center;
2014     break;
2015   case CenterGravity:
2016     client.x = x_center;
2017     client.y = y_center;
2018     break;
2019   case ForgetGravity:
2020   case StaticGravity:
2021     client.x = frame.x + frame.mwm_border_w + frame.border_w;
2022     client.y = frame.y + frame.y_border + frame.mwm_border_w +
2023       frame.border_w;
2024     break;
2025   }
2026 }
2027
2028
2029 void OpenboxWindow::redrawLabel(void) {
2030   int dx = frame.bevel_w * 2, dlen = client.title_len;
2031   unsigned int l = client.title_text_w;
2032
2033   if (flags.focused) {
2034     if (frame.flabel)
2035       XSetWindowBackgroundPixmap(display, frame.label, frame.flabel);
2036     else
2037       XSetWindowBackground(display, frame.label, frame.flabel_pixel);
2038   } else {
2039     if (frame.ulabel)
2040       XSetWindowBackgroundPixmap(display, frame.label, frame.ulabel);
2041     else
2042       XSetWindowBackground(display, frame.label, frame.ulabel_pixel);
2043   }
2044   XClearWindow(display, frame.label);
2045
2046   if (client.title_text_w > frame.label_w) {
2047     for (; dlen >= 0; dlen--) {
2048       if (i18n.multibyte()) {
2049         XRectangle ink, logical;
2050         XmbTextExtents(screen->getWindowStyle()->fontset, client.title, dlen,
2051                        &ink, &logical);
2052         l = logical.width;
2053       } else {
2054         l = XTextWidth(screen->getWindowStyle()->font, client.title, dlen);
2055       }
2056       l += (frame.bevel_w * 4);
2057
2058       if (l < frame.label_w)
2059         break;
2060     }
2061   }
2062
2063   switch (screen->getWindowStyle()->justify) {
2064   case BScreen::RightJustify:
2065     dx += frame.label_w - l;
2066     break;
2067
2068   case BScreen::CenterJustify:
2069     dx += (frame.label_w - l) / 2;
2070     break;
2071   }
2072
2073   WindowStyle *style = screen->getWindowStyle();
2074   GC text_gc = (flags.focused) ? style->l_text_focus_gc :
2075     style->l_text_unfocus_gc;
2076   if (i18n.multibyte())
2077     XmbDrawString(display, frame.label, style->fontset, text_gc, dx,
2078                   (1 - style->fontset_extents->max_ink_extent.y),
2079                   client.title, dlen);
2080   else
2081     XDrawString(display, frame.label, text_gc, dx,
2082                 (style->font->ascent + 1), client.title, dlen);
2083 }
2084
2085
2086 void OpenboxWindow::redrawAllButtons(void) {
2087   if (frame.iconify_button) redrawIconifyButton(False);
2088   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2089   if (frame.close_button) redrawCloseButton(False);
2090 }
2091
2092
2093 void OpenboxWindow::redrawIconifyButton(Bool pressed) {
2094   if (! pressed) {
2095     if (flags.focused) {
2096       if (frame.fbutton)
2097         XSetWindowBackgroundPixmap(display, frame.iconify_button,
2098                                    frame.fbutton);
2099       else
2100         XSetWindowBackground(display, frame.iconify_button,
2101                              frame.fbutton_pixel);
2102     } else {
2103       if (frame.ubutton)
2104         XSetWindowBackgroundPixmap(display, frame.iconify_button,
2105                                    frame.ubutton);
2106       else
2107         XSetWindowBackground(display, frame.iconify_button,
2108                              frame.ubutton_pixel);
2109     }
2110   } else {
2111     if (frame.pbutton)
2112       XSetWindowBackgroundPixmap(display, frame.iconify_button, frame.pbutton);
2113     else
2114       XSetWindowBackground(display, frame.iconify_button, frame.pbutton_pixel);
2115   }
2116   XClearWindow(display, frame.iconify_button);
2117
2118   XDrawRectangle(display, frame.iconify_button,
2119                  ((flags.focused) ? screen->getWindowStyle()->b_pic_focus_gc :
2120                   screen->getWindowStyle()->b_pic_unfocus_gc),
2121                  2, (frame.button_h - 5), (frame.button_w - 5), 2);
2122 }
2123
2124
2125 void OpenboxWindow::redrawMaximizeButton(Bool pressed) {
2126   if (! pressed) {
2127     if (flags.focused) {
2128       if (frame.fbutton)
2129         XSetWindowBackgroundPixmap(display, frame.maximize_button,
2130                                    frame.fbutton);
2131       else
2132         XSetWindowBackground(display, frame.maximize_button,
2133                              frame.fbutton_pixel);
2134     } else {
2135       if (frame.ubutton)
2136         XSetWindowBackgroundPixmap(display, frame.maximize_button,
2137                                    frame.ubutton);
2138       else
2139         XSetWindowBackground(display, frame.maximize_button,
2140                              frame.ubutton_pixel);
2141     }
2142   } else {
2143     if (frame.pbutton)
2144       XSetWindowBackgroundPixmap(display, frame.maximize_button,
2145                                  frame.pbutton);
2146     else
2147       XSetWindowBackground(display, frame.maximize_button,
2148                            frame.pbutton_pixel);
2149   }
2150   XClearWindow(display, frame.maximize_button);
2151
2152   XDrawRectangle(display, frame.maximize_button,
2153                  ((flags.focused) ? screen->getWindowStyle()->b_pic_focus_gc :
2154                   screen->getWindowStyle()->b_pic_unfocus_gc),
2155                  2, 2, (frame.button_w - 5), (frame.button_h - 5));
2156   XDrawLine(display, frame.maximize_button,
2157             ((flags.focused) ? screen->getWindowStyle()->b_pic_focus_gc :
2158              screen->getWindowStyle()->b_pic_unfocus_gc),
2159             2, 3, (frame.button_w - 3), 3);
2160 }
2161
2162
2163 void OpenboxWindow::redrawCloseButton(Bool pressed) {
2164   if (! pressed) {
2165     if (flags.focused) {
2166       if (frame.fbutton)
2167         XSetWindowBackgroundPixmap(display, frame.close_button,
2168                                    frame.fbutton);
2169       else
2170         XSetWindowBackground(display, frame.close_button,
2171                              frame.fbutton_pixel);
2172     } else {
2173       if (frame.ubutton)
2174         XSetWindowBackgroundPixmap(display, frame.close_button,
2175                                    frame.ubutton);
2176       else
2177         XSetWindowBackground(display, frame.close_button,
2178                              frame.ubutton_pixel);
2179     }
2180   } else {
2181     if (frame.pbutton)
2182       XSetWindowBackgroundPixmap(display, frame.close_button, frame.pbutton);
2183     else
2184       XSetWindowBackground(display, frame.close_button, frame.pbutton_pixel);
2185   }
2186   XClearWindow(display, frame.close_button);
2187
2188   XDrawLine(display, frame.close_button,
2189             ((flags.focused) ? screen->getWindowStyle()->b_pic_focus_gc :
2190              screen->getWindowStyle()->b_pic_unfocus_gc), 2, 2,
2191             (frame.button_w - 3), (frame.button_h - 3));
2192   XDrawLine(display, frame.close_button,
2193             ((flags.focused) ? screen->getWindowStyle()->b_pic_focus_gc :
2194              screen->getWindowStyle()->b_pic_unfocus_gc), 2,
2195             (frame.button_h - 3),
2196             (frame.button_w - 3), 2);
2197 }
2198
2199
2200 void OpenboxWindow::mapRequestEvent(XMapRequestEvent *re) {
2201   if (re->window == client.window) {
2202 #ifdef    DEBUG
2203     fprintf(stderr, i18n(WindowSet, WindowMapRequest,
2204                          "OpenboxWindow::mapRequestEvent() for 0x%lx\n"),
2205             client.window);
2206 #endif // DEBUG
2207
2208     openbox.grab();
2209     if (! validateClient()) return;
2210
2211     Bool get_state_ret = getState();
2212     if (! (get_state_ret && openbox.isStartup())) {
2213       if ((client.wm_hint_flags & StateHint) &&
2214           (! (current_state == NormalState || current_state == IconicState)))
2215         current_state = client.initial_state;
2216       else
2217         current_state = NormalState;
2218     } else if (flags.iconic) {
2219       current_state = NormalState;
2220     }
2221
2222     switch (current_state) {
2223     case IconicState:
2224       iconify();
2225       break;
2226
2227     case WithdrawnState:
2228       withdraw();
2229       break;
2230
2231     case NormalState:
2232     case InactiveState:
2233     case ZoomState:
2234     default:
2235       deiconify(False, True, True);     // specify that we're initializing the
2236       // window
2237       break;
2238     }
2239
2240     openbox.ungrab();
2241   }
2242 }
2243
2244
2245 void OpenboxWindow::mapNotifyEvent(XMapEvent *ne) {
2246   if ((ne->window == client.window) && (! ne->override_redirect)
2247       && (flags.visible)) {
2248     openbox.grab();
2249     if (! validateClient()) return;
2250
2251     setState(NormalState);
2252
2253     if (flags.transient || screen->focusNew())
2254       setInputFocus();
2255     else
2256       setFocusFlag(False);
2257
2258     flags.visible = True;
2259     flags.iconic = False;
2260
2261     openbox.ungrab();
2262   }
2263 }
2264
2265
2266 void OpenboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
2267   if (ue->window == client.window) {
2268 #ifdef    DEBUG
2269     fprintf(stderr, i18n(WindowSet, WindowUnmapNotify,
2270                          "OpenboxWindow::unmapNotifyEvent() for 0x%lx\n"),
2271             client.window);
2272 #endif // DEBUG
2273
2274     openbox.grab();
2275     if (! validateClient()) return;
2276
2277     if (flags.moving)
2278       endMove();
2279
2280     XChangeSaveSet(display, client.window, SetModeDelete);
2281     XSelectInput(display, client.window, NoEventMask);
2282
2283     XDeleteProperty(display, client.window, openbox.getWMStateAtom());
2284     XDeleteProperty(display, client.window,
2285                     openbox.getOpenboxAttributesAtom());
2286
2287     XUnmapWindow(display, frame.window);
2288     XUnmapWindow(display, client.window);
2289
2290     XEvent dummy;
2291     if (! XCheckTypedWindowEvent(display, client.window, ReparentNotify,
2292                                  &dummy)) {
2293 #ifdef    DEBUG
2294       fprintf(stderr, i18n(WindowSet, WindowUnmapNotifyReparent,
2295                            "OpenboxWindow::unmapNotifyEvent(): reparent 0x%lx to "
2296                            "root.\n"), client.window);
2297 #endif // DEBUG
2298
2299       restoreGravity();
2300       XReparentWindow(display, client.window, screen->getRootWindow(),
2301                       client.x, client.y);
2302     }
2303
2304     XFlush(display);
2305
2306     openbox.ungrab();
2307
2308     delete this;
2309   }
2310 }
2311
2312
2313 void OpenboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
2314   if (de->window == client.window) {
2315     if (flags.moving)
2316       endMove();
2317     XUnmapWindow(display, frame.window);
2318
2319     delete this;
2320   }
2321 }
2322
2323
2324 void OpenboxWindow::propertyNotifyEvent(Atom atom) {
2325   openbox.grab();
2326   if (! validateClient()) return;
2327
2328   switch(atom) {
2329   case XA_WM_CLASS:
2330   case XA_WM_CLIENT_MACHINE:
2331   case XA_WM_COMMAND:
2332     break;
2333
2334   case XA_WM_TRANSIENT_FOR:
2335     // determine if this is a transient window
2336     Window win;
2337     if (XGetTransientForHint(display, client.window, &win)) {
2338       if (win && (win != client.window)) {
2339         if ((client.transient_for = openbox.searchWindow(win))) {
2340           client.transient_for->client.transient = this;
2341           flags.stuck = client.transient_for->flags.stuck;
2342           flags.transient = True;
2343         } else if (win == client.window_group) {
2344           //jr This doesn't look quite right...
2345           if ((client.transient_for = openbox.searchGroup(win, this))) {
2346             client.transient_for->client.transient = this;
2347             flags.stuck = client.transient_for->flags.stuck;
2348             flags.transient = True;
2349           }
2350         }
2351       }
2352
2353       if (win == screen->getRootWindow()) flags.modal = True;
2354     }
2355
2356     // adjust the window decorations based on transience
2357     if (flags.transient)
2358       decorations.maximize = decorations.handle = functions.maximize = False;
2359
2360     reconfigure();
2361
2362     break;
2363
2364   case XA_WM_HINTS:
2365     getWMHints();
2366     break;
2367
2368   case XA_WM_ICON_NAME:
2369     getWMIconName();
2370     if (flags.iconic) screen->iconUpdate();
2371     break;
2372
2373   case XA_WM_NAME:
2374     getWMName();
2375
2376     if (decorations.titlebar)
2377       redrawLabel();
2378
2379     if (! flags.iconic)
2380       screen->getWorkspace(workspace_number)->update();
2381
2382     break;
2383
2384   case XA_WM_NORMAL_HINTS: {
2385     getWMNormalHints();
2386
2387     if ((client.normal_hint_flags & PMinSize) &&
2388         (client.normal_hint_flags & PMaxSize)) {
2389       if (client.max_width <= client.min_width &&
2390           client.max_height <= client.min_height)
2391         decorations.maximize = decorations.handle =
2392           functions.resize = functions.maximize = False;
2393       else
2394         decorations.maximize = decorations.handle =
2395           functions.resize = functions.maximize = True;
2396     }
2397
2398     int x = frame.x, y = frame.y;
2399     unsigned int w = frame.width, h = frame.height;
2400
2401     upsize();
2402
2403     if ((x != frame.x) || (y != frame.y) ||
2404         (w != frame.width) || (h != frame.height))
2405       reconfigure();
2406
2407     break;
2408   }
2409
2410   default:
2411     if (atom == openbox.getWMProtocolsAtom()) {
2412       getWMProtocols();
2413
2414       if (decorations.close && (! frame.close_button)) {
2415         createCloseButton();
2416         if (decorations.titlebar) {
2417           positionButtons();
2418           decorateLabel();
2419         }
2420         if (windowmenu) windowmenu->reconfigure();
2421       }
2422     }
2423
2424     break;
2425   }
2426
2427   openbox.ungrab();
2428 }
2429
2430
2431 void OpenboxWindow::exposeEvent(XExposeEvent *ee) {
2432   if (frame.label == ee->window && decorations.titlebar)
2433     redrawLabel();
2434   else if (frame.close_button == ee->window)
2435     redrawCloseButton(False);
2436   else if (frame.maximize_button == ee->window)
2437     redrawMaximizeButton(flags.maximized);
2438   else if (frame.iconify_button == ee->window)
2439     redrawIconifyButton(False);
2440 }
2441
2442
2443 void OpenboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
2444   if (cr->window == client.window) {
2445     openbox.grab();
2446     if (! validateClient()) return;
2447
2448     int cx = frame.x, cy = frame.y;
2449     unsigned int cw = frame.width, ch = frame.height;
2450
2451     if (cr->value_mask & CWBorderWidth)
2452       client.old_bw = cr->border_width;
2453
2454     if (cr->value_mask & CWX)
2455       cx = cr->x - frame.mwm_border_w - frame.border_w;
2456
2457     if (cr->value_mask & CWY)
2458       cy = cr->y - frame.y_border - frame.mwm_border_w -
2459         frame.border_w;
2460
2461     if (cr->value_mask & CWWidth)
2462       cw = cr->width + (frame.mwm_border_w * 2);
2463
2464     if (cr->value_mask & CWHeight)
2465       ch = cr->height + frame.y_border + (frame.mwm_border_w * 2) +
2466         (frame.border_w * decorations.handle) + frame.handle_h;
2467
2468     if (frame.x != cx || frame.y != cy ||
2469         frame.width != cw || frame.height != ch)
2470       configure(cx, cy, cw, ch);
2471
2472     if (cr->value_mask & CWStackMode) {
2473       switch (cr->detail) {
2474       case Above:
2475       case TopIf:
2476       default:
2477         if (flags.iconic) deiconify();
2478         screen->getWorkspace(workspace_number)->raiseWindow(this);
2479         break;
2480
2481       case Below:
2482       case BottomIf:
2483         if (flags.iconic) deiconify();
2484         screen->getWorkspace(workspace_number)->lowerWindow(this);
2485         break;
2486       }
2487     }
2488
2489     openbox.ungrab();
2490   }
2491 }
2492
2493
2494 void OpenboxWindow::buttonPressEvent(XButtonEvent *be) {
2495   openbox.grab();
2496   if (! validateClient())
2497     return;
2498
2499   int stack_change = 1; // < 0 means to lower the window
2500   // > 0 means to raise the window
2501   // 0 means to leave it where it is
2502
2503   // alt + left/right click begins interactively moving/resizing the window
2504   // when the mouse is moved
2505   if (be->state == Mod1Mask && (be->button == 1 || be->button == 3)) {
2506     if (be->button == 3) {
2507       if (screen->getWindowZones() == 4 &&
2508           be->y < (signed) frame.height / 2) {
2509         resize_zone = ZoneTop;
2510       } else {
2511         resize_zone = ZoneBottom;
2512       }
2513       if (screen->getWindowZones() >= 2 &&
2514           be->x < (signed) frame.width / 2) {
2515         resize_zone |= ZoneLeft;
2516       } else {
2517         resize_zone |= ZoneRight;
2518       }
2519     }
2520     // control + left click on the titlebar shades the window
2521   } else if (be->state == ControlMask && be->button == 1) {
2522     if (be->window == frame.title ||
2523         be->window == frame.label)
2524       shade();
2525     // left click
2526   } else if (be->state == 0 && be->button == 1) {
2527     if (windowmenu && windowmenu->isVisible())
2528       windowmenu->hide();
2529
2530     if (be->window == frame.maximize_button) {
2531       redrawMaximizeButton(True);
2532     } else if (be->window == frame.iconify_button) {
2533       redrawIconifyButton(True);
2534     } else if (be->window == frame.close_button) {
2535       redrawCloseButton(True);
2536     } else if (be->window == frame.plate) {
2537       XAllowEvents(display, ReplayPointer, be->time);
2538     } else if (be->window == frame.title ||
2539                be->window == frame.label) {
2540       // shade the window when the titlebar is double clicked
2541       if ( (be->time - lastButtonPressTime) <=
2542           openbox.getDoubleClickInterval()) {
2543         lastButtonPressTime = 0;
2544         shade();
2545       } else {
2546         lastButtonPressTime = be->time;
2547       }
2548       // clicking and dragging on the titlebar moves the window, so on a click
2549       // we need to save the coords of the click in case the user drags
2550       frame.grab_x = be->x_root - frame.x - frame.border_w;
2551       frame.grab_y = be->y_root - frame.y - frame.border_w;
2552     } else if (be->window == frame.handle ||
2553                be->window == frame.left_grip ||
2554                be->window == frame.right_grip ||
2555                be->window == frame.window) {
2556       // clicking and dragging on the window's frame moves the window, so on a
2557       // click we need to save the coords of the click in case the user drags
2558       frame.grab_x = be->x_root - frame.x - frame.border_w;
2559       frame.grab_y = be->y_root - frame.y - frame.border_w;
2560       if (be->window == frame.left_grip)
2561         resize_zone = ZoneBottom | ZoneLeft;
2562       else if (be->window == frame.right_grip)
2563         resize_zone = ZoneBottom | ZoneRight;
2564     }
2565     // middle click
2566   } else if (be->state == 0 && be->button == 2) {
2567     if (be->window == frame.maximize_button) {
2568       redrawMaximizeButton(True);
2569       // a middle click anywhere on the window's frame except for on the buttons
2570       // will lower the window
2571     } else if (! (be->window == frame.iconify_button ||
2572                   be->window == frame.close_button) ) {
2573       stack_change = -1;
2574     }
2575     // right click
2576   } else if (be->state == 0 && be->button == 3) {
2577     if (be->window == frame.maximize_button) {
2578       redrawMaximizeButton(True);
2579       // a right click on the window's frame will show or hide the window's
2580       // windowmenu
2581     } else if (be->window == frame.title ||
2582                be->window == frame.label ||
2583                be->window == frame.handle ||
2584                be->window == frame.window) {
2585       int mx, my;
2586       if (windowmenu) {
2587         if (windowmenu->isVisible()) {
2588           windowmenu->hide();
2589         } else {
2590           // get the coords for the menu
2591           mx = be->x_root - windowmenu->getWidth() / 2;
2592           if (be->window == frame.title || be->window == frame.label) {
2593             my = frame.y + frame.title_h;
2594           } else if (be->window == frame.handle) {
2595             my = frame.y + frame.y_handle - windowmenu->getHeight();
2596           } else { // (be->window == frame.window)
2597             if (be->y <= (signed) frame.bevel_w) {
2598               my = frame.y + frame.y_border;
2599             } else {
2600               my = be->y_root - (windowmenu->getHeight() / 2);
2601             }
2602           }
2603
2604           if (mx > (signed) (frame.x + frame.width -
2605                              windowmenu->getWidth())) {
2606             mx = frame.x + frame.width - windowmenu->getWidth();
2607           } else if (mx < frame.x) {
2608             mx = frame.x;
2609           }
2610
2611           if (my > (signed) (frame.y + frame.y_handle -
2612                              windowmenu->getHeight())) {
2613             my = frame.y + frame.y_handle - windowmenu->getHeight();
2614           } else if (my < (signed) (frame.y +
2615                                     ((decorations.titlebar) ? frame.title_h : frame.y_border))) {
2616             my = frame.y +
2617               ((decorations.titlebar) ? frame.title_h : frame.y_border);
2618           }
2619
2620           windowmenu->move(mx, my);
2621           windowmenu->show();
2622           XRaiseWindow(display, windowmenu->getWindowID());
2623           XRaiseWindow(display, windowmenu->getSendToMenu()->getWindowID());
2624           stack_change = 0;  // dont raise the window overtop of the menu
2625         }
2626       }
2627     }
2628     // mouse wheel up
2629   } else if (be->state == 0 && be->button == 4) {
2630     if ((be->window == frame.label ||
2631          be->window == frame.title) &&
2632         !flags.shaded)
2633       shade();
2634     // mouse wheel down
2635   } else if (be->state == 0 && be->button == 5) {
2636     if ((be->window == frame.label ||
2637          be->window == frame.title) &&
2638         flags.shaded)
2639       shade();
2640   }
2641
2642   if (! (flags.focused || screen->sloppyFocus()) ) {
2643     setInputFocus();  // any click focus' the window in 'click to focus'
2644   }
2645   if (stack_change < 0) {
2646     screen->getWorkspace(workspace_number)->lowerWindow(this);
2647   } else if (stack_change > 0) {
2648     screen->getWorkspace(workspace_number)->raiseWindow(this);
2649   }
2650
2651   openbox.ungrab();
2652 }
2653
2654
2655 void OpenboxWindow::buttonReleaseEvent(XButtonEvent *re) {
2656   openbox.grab();
2657   if (! validateClient())
2658     return;
2659
2660   // alt + middle button released
2661   if (re->state == (Mod1Mask & Button2Mask) && re->button == 2) {
2662     if (re->window == frame.window) {
2663       XUngrabPointer(display, CurrentTime); // why? i dont know
2664     }
2665     // left button released
2666   } else if (re->button == 1) {
2667     if (re->window == frame.maximize_button) {
2668       if (re->state == Button1Mask && // only the left button was depressed
2669           (re->x >= 0) && ((unsigned) re->x <= frame.button_w) &&
2670           (re->y >= 0) && ((unsigned) re->y <= frame.button_h)) {
2671         maximize(re->button);
2672       } else {
2673         redrawMaximizeButton(False);
2674       }
2675     } else if (re->window == frame.iconify_button) {
2676       if (re->state == Button1Mask && // only the left button was depressed
2677           (re->x >= 0) && ((unsigned) re->x <= frame.button_w) &&
2678           (re->y >= 0) && ((unsigned) re->y <= frame.button_h)) {
2679         iconify();
2680       } else {
2681         redrawIconifyButton(False);
2682       }
2683     } else if (re->window == frame.close_button) {
2684       if (re->state == Button1Mask && // only the left button was depressed
2685           (re->x >= 0) && ((unsigned) re->x <= frame.button_w) &&
2686           (re->y >= 0) && ((unsigned) re->y <= frame.button_h)) {
2687         close();
2688       }
2689       //we should always redraw the close button. some applications
2690       //eg. acroread don't honour the close.
2691       redrawCloseButton(False);
2692     }
2693     // middle button released
2694   } else if (re->button == 2) {
2695     if (re->window == frame.maximize_button) {
2696       if (re->state == Button2Mask && // only the middle button was depressed
2697           (re->x >= 0) && ((unsigned) re->x <= frame.button_w) &&
2698           (re->y >= 0) && ((unsigned) re->y <= frame.button_h)) {
2699         maximize(re->button);
2700       } else {
2701         redrawMaximizeButton(False);
2702       }
2703     }
2704     // right button released
2705   } else if (re->button == 3) {
2706     if (re->window == frame.maximize_button) {
2707       if (re->state == Button3Mask && // only the right button was depressed
2708           (re->x >= 0) && ((unsigned) re->x <= frame.button_w) &&
2709           (re->y >= 0) && ((unsigned) re->y <= frame.button_h)) {
2710         maximize(re->button);
2711       } else {
2712         redrawMaximizeButton(False);
2713       }
2714     }
2715   }
2716
2717   // when the window is being interactively moved, a button release stops the
2718   // move where it is
2719   if (flags.moving) {
2720     endMove();
2721     // when the window is being interactively resized, a button release stops the
2722     // resizing
2723   } else if (flags.resizing) {
2724     flags.resizing = False;
2725     XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2726                    frame.resize_x, frame.resize_y,
2727                    frame.resize_w - 1, frame.resize_h - 1);
2728     screen->hideGeometry();
2729     if (resize_zone & ZoneLeft) {
2730       left_fixsize();
2731     } else {  // when resizing with "Alt+Button3", the resize is the same as if
2732       // done with the right grip (the right side of the window is what
2733       // moves)
2734       right_fixsize();
2735     }
2736     // unset maximized state when resized after fully maximized
2737     if (flags.maximized == 1) {
2738       maximize(0);
2739     }
2740     configure(frame.resize_x, frame.resize_y,
2741               frame.resize_w - (frame.border_w * 2),
2742               frame.resize_h - (frame.border_w * 2));
2743     openbox.ungrab();
2744     XUngrabPointer(display, CurrentTime);
2745     resize_zone = 0;
2746   }
2747
2748   openbox.ungrab();
2749 }
2750
2751
2752 void OpenboxWindow::startMove(int x, int y) {
2753   ASSERT(!flags.moving);
2754
2755   // make sure only one window is moving at a time
2756   OpenboxWindow *w = openbox.getMaskedWindow();
2757   if (w != (OpenboxWindow *) 0 && w->flags.moving)
2758     w->endMove();
2759
2760   XGrabPointer(display, frame.window, False, PointerMotionMask |
2761                ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
2762                None, openbox.getMoveCursor(), CurrentTime);
2763
2764   if (windowmenu && windowmenu->isVisible())
2765     windowmenu->hide();
2766
2767   flags.moving = True;
2768
2769   openbox.maskWindowEvents(client.window, this);
2770
2771   if (! screen->opaqueMove()) {
2772     openbox.grab();
2773
2774     frame.move_x = frame.x;
2775     frame.move_y = frame.y;
2776     frame.resize_w = frame.width + (frame.border_w * 2);
2777     frame.resize_h = ((flags.shaded) ? frame.title_h : frame.height) +
2778       (frame.border_w * 2);
2779
2780     screen->showPosition(frame.x, frame.y);
2781
2782     XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2783                    frame.move_x, frame.move_y,
2784                    frame.resize_w - 1, frame.resize_h - 1);
2785   }
2786   frame.grab_x = x - frame.x - frame.border_w;
2787   frame.grab_y = y - frame.y - frame.border_w;
2788 }
2789
2790
2791 void OpenboxWindow::doMove(int x, int y) {
2792   ASSERT(flags.moving);
2793
2794   int dx = x - frame.grab_x, dy = y - frame.grab_y;
2795
2796   dx -= frame.border_w;
2797   dy -= frame.border_w;
2798
2799   int snap_distance = screen->edgeSnapThreshold();
2800   // width/height of the snapping window
2801   unsigned int snap_w = frame.width + (frame.border_w * 2);
2802   unsigned int snap_h = area().h() + (frame.border_w * 2);
2803   if (snap_distance) {
2804     int drx = screen->size().w() - (dx + snap_w);
2805
2806     if (dx < drx && (dx > 0 && dx < snap_distance) ||
2807         (dx < 0 && dx > -snap_distance) )
2808       dx = 0;
2809     else if ( (drx > 0 && drx < snap_distance) ||
2810              (drx < 0 && drx > -snap_distance) )
2811       dx = screen->size().w() - snap_w;
2812
2813     int dtty, dbby, dty, dby;
2814     switch (screen->getToolbar()->placement()) {
2815     case Toolbar::TopLeft:
2816     case Toolbar::TopCenter:
2817     case Toolbar::TopRight:
2818       dtty = screen->getToolbar()->getExposedHeight() +
2819         frame.border_w;
2820       dbby = screen->size().h();
2821       break;
2822
2823     default:
2824       dtty = 0;
2825       dbby = screen->getToolbar()->area().y();
2826       break;
2827     }
2828
2829     dty = dy - dtty;
2830     dby = dbby - (dy + snap_h);
2831
2832     if ( (dy > 0 && dty < snap_distance) ||
2833         (dy < 0 && dty > -snap_distance) )
2834       dy = dtty;
2835     else if ( (dby > 0 && dby < snap_distance) ||
2836              (dby < 0 && dby > -snap_distance) )
2837       dy = dbby - snap_h;
2838   }
2839
2840   if (screen->opaqueMove()) {
2841     configure(dx, dy, frame.width, frame.height);
2842   } else {
2843     XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2844                    frame.move_x, frame.move_y, frame.resize_w - 1,
2845                    frame.resize_h - 1);
2846
2847     frame.move_x = dx;
2848     frame.move_y = dy;
2849
2850     XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2851                    frame.move_x, frame.move_y, frame.resize_w - 1,
2852                    frame.resize_h - 1);
2853   }
2854
2855   screen->showPosition(dx, dy);
2856 }
2857
2858
2859 void OpenboxWindow::endMove() {
2860   ASSERT(flags.moving);
2861
2862   flags.moving = False;
2863
2864   openbox.maskWindowEvents(0, (OpenboxWindow *) 0);
2865   if (!screen->opaqueMove()) {
2866     XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2867                    frame.move_x, frame.move_y, frame.resize_w - 1,
2868                    frame.resize_h - 1);
2869
2870     configure(frame.move_x, frame.move_y, frame.width, frame.height);
2871     openbox.ungrab();
2872   } else {
2873     configure(frame.x, frame.y, frame.width, frame.height);
2874   }
2875   screen->hideGeometry();
2876   XUngrabPointer(display, CurrentTime);
2877   // if there are any left over motions from the move, drop them now cuz they
2878   // cause problems
2879   XEvent e;
2880   while (XCheckTypedWindowEvent(display, frame.window, MotionNotify, &e));
2881 }
2882
2883
2884 void OpenboxWindow::motionNotifyEvent(XMotionEvent *me) {
2885   if (flags.moving)
2886     doMove(me->x_root, me->y_root);
2887   else if (!flags.resizing && (me->state & Button1Mask) && functions.move &&
2888            (frame.title == me->window || frame.label == me->window ||
2889             frame.handle == me->window || frame.window == me->window))
2890     startMove(me->x_root, me->y_root);
2891   else if (functions.resize &&
2892            (((me->state & Button1Mask) && (me->window == frame.right_grip ||
2893                                            me->window == frame.left_grip)) ||
2894             (me->state == (Mod1Mask | Button3Mask) &&
2895              me->window == frame.window))) {
2896     Bool left = resize_zone & ZoneLeft;
2897
2898     if (! flags.resizing) {
2899       Cursor cursor;
2900       if (resize_zone & ZoneTop)
2901         cursor = (resize_zone & ZoneLeft) ?
2902           openbox.getUpperLeftAngleCursor() :
2903           openbox.getUpperRightAngleCursor();
2904       else
2905         cursor = (resize_zone & ZoneLeft) ?
2906           openbox.getLowerLeftAngleCursor() :
2907           openbox.getLowerRightAngleCursor();
2908       XGrabPointer(display, me->window, False, ButtonMotionMask |
2909                    ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None,
2910                    cursor, CurrentTime);
2911
2912       flags.resizing = True;
2913
2914       openbox.grab();
2915
2916       int gx, gy;
2917       if (resize_zone & ZoneRight)
2918         frame.grab_x = me->x - screen->getBorderWidth();
2919       else
2920         frame.grab_x = me->x + screen->getBorderWidth();
2921       if (resize_zone & ZoneTop)
2922         frame.grab_y = me->y + screen->getBorderWidth() * 2;
2923       else
2924         frame.grab_y = me->y - screen->getBorderWidth() * 2;
2925       frame.resize_x = frame.x;
2926       frame.resize_y = frame.y;
2927       frame.resize_w = frame.width + (frame.border_w * 2);
2928       frame.resize_h = frame.height + (frame.border_w * 2);
2929
2930       if (left)
2931         left_fixsize(&gx, &gy);
2932       else
2933         right_fixsize(&gx, &gy);
2934
2935       screen->showGeometry(gx, gy);
2936
2937       XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2938                      frame.resize_x, frame.resize_y,
2939                      frame.resize_w - 1, frame.resize_h - 1);
2940     } else {
2941       XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2942                      frame.resize_x, frame.resize_y,
2943                      frame.resize_w - 1, frame.resize_h - 1);
2944
2945       int gx, gy;
2946
2947       if (resize_zone & ZoneTop)
2948         frame.resize_h = frame.height - (me->y - frame.grab_y);
2949       else
2950         frame.resize_h = frame.height + (me->y - frame.grab_y);
2951       if (frame.resize_h < 1) frame.resize_h = 1;
2952
2953       if (left) {
2954         frame.resize_x = me->x_root - frame.grab_x;
2955         if (frame.resize_x > (signed) (frame.x + frame.width))
2956           frame.resize_x = frame.resize_x + frame.width - 1;
2957
2958         left_fixsize(&gx, &gy);
2959       } else {
2960         frame.resize_w = frame.width + (me->x - frame.grab_x);
2961         if (frame.resize_w < 1) frame.resize_w = 1;
2962
2963         right_fixsize(&gx, &gy);
2964       }
2965
2966       XDrawRectangle(display, screen->getRootWindow(), screen->getOpGC(),
2967                      frame.resize_x, frame.resize_y,
2968                      frame.resize_w - 1, frame.resize_h - 1);
2969
2970       screen->showGeometry(gx, gy);
2971     }
2972   }
2973 }
2974
2975
2976 #ifdef    SHAPE
2977 void OpenboxWindow::shapeEvent(XShapeEvent *) {
2978   if (openbox.hasShapeExtensions()) {
2979     if (flags.shaped) {
2980       openbox.grab();
2981       if (! validateClient()) return;
2982       XShapeCombineShape(display, frame.window, ShapeBounding,
2983                          frame.mwm_border_w, frame.y_border +
2984                          frame.mwm_border_w, client.window,
2985                          ShapeBounding, ShapeSet);
2986
2987       int num = 1;
2988       XRectangle xrect[2];
2989       xrect[0].x = xrect[0].y = 0;
2990       xrect[0].width = frame.width;
2991       xrect[0].height = frame.y_border;
2992
2993       if (decorations.handle) {
2994         xrect[1].x = 0;
2995         xrect[1].y = frame.y_handle;
2996         xrect[1].width = frame.width;
2997         xrect[1].height = frame.handle_h + frame.border_w;
2998         num++;
2999       }
3000
3001       XShapeCombineRectangles(display, frame.window, ShapeBounding, 0, 0,
3002                               xrect, num, ShapeUnion, Unsorted);
3003       openbox.ungrab();
3004     }
3005   }
3006 }
3007 #endif // SHAPE
3008
3009
3010 bool OpenboxWindow::validateClient(void) {
3011   XSync(display, False);
3012
3013   XEvent e;
3014   if (XCheckTypedWindowEvent(display, client.window, DestroyNotify, &e) ||
3015       XCheckTypedWindowEvent(display, client.window, UnmapNotify, &e)) {
3016     XPutBackEvent(display, &e);
3017     openbox.ungrab();
3018
3019     return false;
3020   }
3021
3022   return true;
3023 }
3024
3025
3026 void OpenboxWindow::restore(void) {
3027   XChangeSaveSet(display, client.window, SetModeDelete);
3028   XSelectInput(display, client.window, NoEventMask);
3029
3030   restoreGravity();
3031
3032   XUnmapWindow(display, frame.window);
3033   XUnmapWindow(display, client.window);
3034
3035   XSetWindowBorderWidth(display, client.window, client.old_bw);
3036   XReparentWindow(display, client.window, screen->getRootWindow(),
3037                   client.x, client.y);
3038   XMapWindow(display, client.window);
3039
3040   XFlush(display);
3041
3042   delete this;
3043 }
3044
3045
3046 void OpenboxWindow::timeout(void) {
3047   screen->getWorkspace(workspace_number)->raiseWindow(this);
3048 }
3049
3050
3051 void OpenboxWindow::changeOpenboxHints(OpenboxHints *net) {
3052   if ((net->flags & AttribShaded) &&
3053       ((openbox_attrib.attrib & AttribShaded) !=
3054        (net->attrib & AttribShaded)))
3055     shade();
3056
3057   if (flags.visible && // watch out for requests when we can not be seen
3058       (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3059       ((openbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3060        (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3061     if (flags.maximized) {
3062       maximize(0);
3063     } else {
3064       int button = 0;
3065
3066       if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3067         button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ?  1 : 0);
3068       else if (net->flags & AttribMaxVert)
3069         button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3070       else if (net->flags & AttribMaxHoriz)
3071         button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3072
3073       maximize(button);
3074     }
3075   }
3076
3077   if ((net->flags & AttribOmnipresent) &&
3078       ((openbox_attrib.attrib & AttribOmnipresent) !=
3079        (net->attrib & AttribOmnipresent)))
3080     stick();
3081
3082   if ((net->flags & AttribWorkspace) &&
3083       (workspace_number != (signed) net->workspace)) {
3084     screen->reassociateWindow(this, net->workspace, True);
3085
3086     if (screen->getCurrentWorkspaceID() != (signed) net->workspace) withdraw();
3087     else deiconify();
3088   }
3089
3090   if (net->flags & AttribDecoration) {
3091     switch (net->decoration) {
3092     case DecorNone:
3093       decorations.titlebar = decorations.border = decorations.handle =
3094         decorations.iconify = decorations.maximize = decorations.menu = False;
3095
3096       break;
3097
3098     default:
3099     case DecorNormal:
3100       decorations.titlebar = decorations.iconify = decorations.menu =
3101         decorations.border = True;
3102       decorations.handle = (functions.resize && !flags.transient);
3103       decorations.maximize = functions.maximize;
3104
3105       break;
3106
3107     case DecorTiny:
3108       decorations.titlebar = decorations.iconify = decorations.menu = True;
3109       decorations.border = decorations.border = decorations.handle = False;
3110       decorations.maximize = functions.maximize;
3111
3112       break;
3113
3114     case DecorTool:
3115       decorations.titlebar = decorations.menu = True;
3116       decorations.iconify = decorations.border = False;
3117       decorations.handle = (functions.resize && !flags.transient);
3118       decorations.maximize = functions.maximize;
3119
3120       break;
3121     }
3122     if (frame.window) {
3123       XMapSubwindows(display, frame.window);
3124       XMapWindow(display, frame.window);
3125     }
3126
3127     reconfigure();
3128     setState(current_state);
3129   }
3130 }
3131
3132
3133 /*
3134  * Set the sizes of all components of the window frame
3135  * (the window decorations).
3136  * These values are based upon the current style settings and the client
3137  * window's dimentions.
3138  */
3139 void OpenboxWindow::upsize(void) {
3140   frame.bevel_w = screen->getBevelWidth();
3141
3142   if (decorations.border) {
3143     frame.border_w = screen->getBorderWidth();
3144     if (!flags.transient)
3145       frame.mwm_border_w = screen->getFrameWidth();
3146     else
3147       frame.mwm_border_w = 0;
3148   } else {
3149     frame.mwm_border_w = frame.border_w = 0;
3150   }
3151
3152   if (decorations.titlebar) {
3153     // the height of the titlebar is based upon the height of the font being
3154     // used to display the window's title
3155     WindowStyle *style = screen->getWindowStyle();
3156     if (i18n.multibyte())
3157       frame.title_h = (style->fontset_extents->max_ink_extent.height +
3158                        (frame.bevel_w * 2) + 2);
3159     else
3160       frame.title_h = (style->font->ascent + style->font->descent +
3161                        (frame.bevel_w * 2) + 2);
3162
3163     frame.label_h = frame.title_h - (frame.bevel_w * 2);
3164     frame.button_w = frame.button_h = (frame.label_h - 2);
3165     frame.y_border = frame.title_h + frame.border_w;
3166   } else {
3167     frame.title_h = 0;
3168     frame.label_h = 0;
3169     frame.button_w = frame.button_h = 0;
3170     frame.y_border = 0;
3171   }
3172
3173   frame.border_h = client.height + frame.mwm_border_w * 2;
3174
3175   if (decorations.handle) {
3176     frame.y_handle = frame.y_border + frame.border_h + frame.border_w;
3177     frame.grip_w = frame.button_w * 2;
3178     frame.grip_h = frame.handle_h = screen->getHandleWidth();
3179   } else {
3180     frame.y_handle = frame.y_border + frame.border_h;
3181     frame.handle_h = 0;
3182     frame.grip_w = frame.grip_h = 0;
3183   }
3184
3185   frame.width = client.width + (frame.mwm_border_w * 2);
3186   frame.height = frame.y_handle + frame.handle_h;
3187 }
3188
3189
3190 /*
3191  * Set the size and position of the client window.
3192  * These values are based upon the current style settings and the frame
3193  * window's dimensions.
3194  */
3195 void OpenboxWindow::downsize(void) {
3196   frame.y_handle = frame.height - frame.handle_h;
3197   frame.border_h = frame.y_handle - frame.y_border -
3198     (decorations.handle ? frame.border_w : 0);
3199
3200   client.x = frame.x + frame.mwm_border_w + frame.border_w;
3201   client.y = frame.y + frame.y_border + frame.mwm_border_w + frame.border_w;
3202
3203   client.width = frame.width - (frame.mwm_border_w * 2);
3204   client.height = frame.height - frame.y_border - (frame.mwm_border_w * 2)
3205     - frame.handle_h - (decorations.handle ? frame.border_w : 0);
3206
3207   frame.y_handle = frame.border_h + frame.y_border + frame.border_w;
3208 }
3209
3210
3211 void OpenboxWindow::right_fixsize(int *gx, int *gy) {
3212   // calculate the size of the client window and conform it to the
3213   // size specified by the size hints of the client window...
3214   int dx = 1 + frame.resize_w - client.base_width - (frame.mwm_border_w * 2) -
3215     (frame.border_w * 2) + (client.width_inc / 2);
3216   int dy = 1 + frame.resize_h - frame.y_border - client.base_height -
3217     frame.handle_h - (frame.border_w * 3) - (frame.mwm_border_w * 2)
3218     + (client.height_inc / 2);
3219
3220   if (dx < (signed) client.min_width) dx = client.min_width;
3221   if (dy < (signed) client.min_height) dy = client.min_height;
3222   if ((unsigned) dx > client.max_width) dx = client.max_width;
3223   if ((unsigned) dy > client.max_height) dy = client.max_height;
3224
3225   dx /= client.width_inc;
3226   dy /= client.height_inc;
3227
3228   if (gx) *gx = dx;
3229   if (gy) *gy = dy;
3230
3231   dx = (dx * client.width_inc) + client.base_width;
3232   dy = (dy * client.height_inc) + client.base_height;
3233
3234   frame.resize_w = dx + (frame.mwm_border_w * 2) + (frame.border_w * 2) - 1;
3235   frame.resize_h = dy + frame.y_border + frame.handle_h +
3236     (frame.mwm_border_w * 2) +  (frame.border_w * 3) - 1;
3237   if (resize_zone & ZoneTop)
3238     frame.resize_y = frame.y + frame.height - frame.resize_h +
3239       screen->getBorderWidth() * 2;
3240 }
3241
3242
3243 void OpenboxWindow::left_fixsize(int *gx, int *gy) {
3244   // calculate the size of the client window and conform it to the
3245   // size specified by the size hints of the client window...
3246   int dx = 1 + frame.x + frame.width - frame.resize_x - client.base_width -
3247     (frame.mwm_border_w * 2) + (client.width_inc / 2);
3248   int dy = 1 + frame.resize_h - frame.y_border - client.base_height -
3249     frame.handle_h - (frame.border_w * 3) - (frame.mwm_border_w * 2)
3250     + (client.height_inc / 2);
3251
3252   if (dx < (signed) client.min_width) dx = client.min_width;
3253   if (dy < (signed) client.min_height) dy = client.min_height;
3254   if ((unsigned) dx > client.max_width) dx = client.max_width;
3255   if ((unsigned) dy > client.max_height) dy = client.max_height;
3256
3257   dx /= client.width_inc;
3258   dy /= client.height_inc;
3259
3260   if (gx) *gx = dx;
3261   if (gy) *gy = dy;
3262
3263   dx = (dx * client.width_inc) + client.base_width;
3264   dy = (dy * client.height_inc) + client.base_height;
3265
3266   frame.resize_w = dx + (frame.mwm_border_w * 2) + (frame.border_w * 2) - 1;
3267   frame.resize_x = frame.x + frame.width - frame.resize_w +
3268     (frame.border_w * 2);
3269   frame.resize_h = dy + frame.y_border + frame.handle_h +
3270     (frame.mwm_border_w * 2) + (frame.border_w * 3) - 1;
3271   if (resize_zone & ZoneTop)
3272     frame.resize_y = frame.y + frame.height - frame.resize_h +
3273       screen->getBorderWidth() * 2;
3274
3275 }