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