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