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