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