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