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