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