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