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