]> Dogcows Code - chaz/openbox/blob - src/Window.cc
add the functionality for window-window snapping
[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 "GCCache.hh"
48 #include "Iconmenu.hh"
49 #include "Image.hh"
50 #include "Screen.hh"
51 #include "Toolbar.hh"
52 #include "Util.hh"
53 #include "Window.hh"
54 #include "Windowmenu.hh"
55 #include "Workspace.hh"
56 #include "Slit.hh"
57
58 using std::string;
59
60 /*
61 * Initializes the class with default values/the window's set initial values.
62 */
63 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
64 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
65 // sizeof(BlackboxWindow));
66
67 #ifdef DEBUG
68 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
69 #endif // DEBUG
70
71 /*
72 set timer to zero... it is initialized properly later, so we check
73 if timer is zero in the destructor, and assume that the window is not
74 fully constructed if timer is zero...
75 */
76 timer = 0;
77 blackbox = b;
78 client.window = w;
79 screen = s;
80 xatom = blackbox->getXAtom();
81
82 if (! validateClient()) {
83 delete this;
84 return;
85 }
86
87 // set the eventmask early in the game so that we make sure we get
88 // all the events we are interested in
89 XSetWindowAttributes attrib_set;
90 attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
91 StructureNotifyMask;
92 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
93 ButtonMotionMask;
94 XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
95 CWEventMask|CWDontPropagate, &attrib_set);
96
97 // fetch client size and placement
98 XWindowAttributes wattrib;
99 if ((! XGetWindowAttributes(blackbox->getXDisplay(),
100 client.window, &wattrib)) ||
101 (! wattrib.screen) || wattrib.override_redirect) {
102 #ifdef DEBUG
103 fprintf(stderr,
104 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
105 #endif // DEBUG
106
107 delete this;
108 return;
109 }
110
111 flags.moving = flags.resizing = flags.shaded = flags.visible =
112 flags.iconic = flags.focused = flags.stuck = flags.modal =
113 flags.send_focus_message = flags.shaped = flags.skip_taskbar =
114 flags.skip_pager = flags.fullscreen = False;
115 flags.maximized = 0;
116
117 blackbox_attrib.workspace = window_number = BSENTINEL;
118
119 blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack
120 = blackbox_attrib.decoration = 0l;
121 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
122 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
123
124 frame.border_w = 1;
125 frame.window = frame.plate = frame.title = frame.handle = None;
126 frame.close_button = frame.iconify_button = frame.maximize_button = None;
127 frame.right_grip = frame.left_grip = None;
128
129 frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
130 frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
131 frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
132 frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
133 frame.fgrip_pixel = 0;
134 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
135 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
136 frame.pbutton = frame.ugrip = frame.fgrip = decorations;
137
138 decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
139 Decor_Iconify | Decor_Maximize;
140 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
141
142 client.wm_hint_flags = client.normal_hint_flags = 0;
143 client.transient_for = 0;
144
145 /*
146 get the initial size and location of client window (relative to the
147 _root window_). This position is the reference point used with the
148 window's gravity to find the window's initial position.
149 */
150 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
151 client.old_bw = wattrib.border_width;
152
153 windowmenu = 0;
154 lastButtonPressTime = 0;
155
156 timer = new BTimer(blackbox, this);
157 timer->setTimeout(blackbox->getAutoRaiseDelay());
158
159 if (! getBlackboxHints()) {
160 getMWMHints();
161 getNetWMHints();
162 }
163
164 // get size, aspect, minimum/maximum size and other hints set by the
165 // client
166 getWMProtocols();
167 getWMHints();
168 getWMNormalHints();
169
170 if (client.initial_state == WithdrawnState) {
171 screen->getSlit()->addClient(client.window);
172 delete this;
173 return;
174 }
175
176 if (isKDESystrayWindow()) {
177 screen->addSystrayWindow(client.window);
178 delete this;
179 return;
180 }
181
182 frame.window = createToplevelWindow();
183 frame.plate = createChildWindow(frame.window);
184 associateClientWindow();
185
186 blackbox->saveWindowSearch(frame.window, this);
187 blackbox->saveWindowSearch(frame.plate, this);
188 blackbox->saveWindowSearch(client.window, this);
189
190 screen->addStrut(&client.strut);
191 updateStrut();
192
193 // determine if this is a transient window
194 getTransientInfo();
195
196 // determine the window's type, so we can decide its decorations and
197 // functionality, or if we should not manage it at all
198 getWindowType();
199
200 // adjust the window decorations/behavior based on the window type
201 switch (window_type) {
202 case Type_Desktop:
203 // desktop windows are not managed by us, we just make sure they stay on the
204 // bottom.
205 return;
206
207 case Type_Dock:
208 // docks (such as kicker) cannot be moved, and appear on all workspaces
209 functions &= ~(Func_Move);
210 flags.stuck = True;
211 case Type_Toolbar:
212 case Type_Menu:
213 case Type_Utility:
214 // these windows have minimal decorations, only a titlebar, and cannot
215 // be resized or iconified
216 decorations &= ~(Decor_Maximize | Decor_Handle | Decor_Border |
217 Decor_Iconify);
218 functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
219 break;
220
221 case Type_Splash:
222 // splash screens have no functionality or decorations, they are left up
223 // to the application which created them
224 decorations = 0;
225 functions = 0;
226 break;
227
228 case Type_Dialog:
229 // dialogs cannot be maximized, and don't display a handle
230 decorations &= ~(Decor_Maximize | Decor_Handle);
231 functions &= ~Func_Maximize;
232 break;
233
234 case Type_Normal:
235 // normal windows retain all of the possible decorations and functionality
236 break;
237 }
238
239 // further adjeust the window's decorations/behavior based on window sizes
240 if ((client.normal_hint_flags & PMinSize) &&
241 (client.normal_hint_flags & PMaxSize) &&
242 client.max_width <= client.min_width &&
243 client.max_height <= client.min_height) {
244 decorations &= ~(Decor_Maximize | Decor_Handle);
245 functions &= ~(Func_Resize | Func_Maximize);
246 }
247 upsize();
248
249 setAllowedActions();
250
251 bool place_window = True;
252 if (blackbox->isStartup() || isTransient() ||
253 client.normal_hint_flags & (PPosition|USPosition)) {
254 setGravityOffsets();
255
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 setFocusFlag(False);
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 XClearWindow(blackbox->getXDisplay(), frame.window);
848 setFocusFlag(flags.focused);
849
850 configure(frame.rect.x(), frame.rect.y(),
851 frame.rect.width(), frame.rect.height());
852
853 if (windowmenu) {
854 windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
855 windowmenu->reconfigure();
856 }
857 }
858
859
860 void BlackboxWindow::updateFocusModel(void) {
861 if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
862 // grab button 1 for changing focus/raising
863 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
864 GrabModeSync, GrabModeSync, None, None);
865 } else {
866 blackbox->ungrabButton(Button1, 0, frame.plate);
867 }
868 }
869
870
871 void BlackboxWindow::positionWindows(void) {
872 XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
873 frame.rect.x(), frame.rect.y(), frame.inside_w,
874 (flags.shaded) ? frame.title_h : frame.inside_h);
875 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window, 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 num < PropMwmHintsElements)
1231 return;
1232
1233 if (mwm_hint->flags & MwmHintsDecorations) {
1234 if (mwm_hint->decorations & MwmDecorAll) {
1235 decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1236 Decor_Iconify | Decor_Maximize | Decor_Close;
1237 } else {
1238 decorations = 0;
1239
1240 if (mwm_hint->decorations & MwmDecorBorder)
1241 decorations |= Decor_Border;
1242 if (mwm_hint->decorations & MwmDecorHandle)
1243 decorations |= Decor_Handle;
1244 if (mwm_hint->decorations & MwmDecorTitle)
1245 decorations |= Decor_Titlebar;
1246 if (mwm_hint->decorations & MwmDecorIconify)
1247 decorations |= Decor_Iconify;
1248 if (mwm_hint->decorations & MwmDecorMaximize)
1249 decorations |= Decor_Maximize;
1250 }
1251 }
1252
1253 if (mwm_hint->flags & MwmHintsFunctions) {
1254 if (mwm_hint->functions & MwmFuncAll) {
1255 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1256 Func_Close;
1257 } else {
1258 functions = 0;
1259
1260 if (mwm_hint->functions & MwmFuncResize)
1261 functions |= Func_Resize;
1262 if (mwm_hint->functions & MwmFuncMove)
1263 functions |= Func_Move;
1264 if (mwm_hint->functions & MwmFuncIconify)
1265 functions |= Func_Iconify;
1266 if (mwm_hint->functions & MwmFuncMaximize)
1267 functions |= Func_Maximize;
1268 if (mwm_hint->functions & MwmFuncClose)
1269 functions |= Func_Close;
1270 }
1271 }
1272 delete mwm_hint;
1273 }
1274
1275
1276 /*
1277 * Gets the blackbox hints from the class' contained window.
1278 * This is used while initializing the window to its first state, and not
1279 * thereafter.
1280 * Returns: true if the hints are successfully retreived and applied; false if
1281 * they are not.
1282 */
1283 bool BlackboxWindow::getBlackboxHints(void) {
1284 unsigned long num;
1285 BlackboxHints *blackbox_hint;
1286
1287 num = PropBlackboxHintsElements;
1288 if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1289 XAtom::blackbox_hints, num,
1290 (unsigned long **)&blackbox_hint) ||
1291 num < PropBlackboxHintsElements)
1292 return False;
1293
1294 if (blackbox_hint->flags & AttribShaded)
1295 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1296
1297 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1298 (blackbox_hint->flags & AttribMaxVert))
1299 flags.maximized = (blackbox_hint->attrib &
1300 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1301 else if (blackbox_hint->flags & AttribMaxVert)
1302 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1303 else if (blackbox_hint->flags & AttribMaxHoriz)
1304 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1305
1306 if (blackbox_hint->flags & AttribOmnipresent)
1307 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1308
1309 if (blackbox_hint->flags & AttribWorkspace)
1310 blackbox_attrib.workspace = blackbox_hint->workspace;
1311
1312 // if (blackbox_hint->flags & AttribStack)
1313 // don't yet have always on top/bottom for blackbox yet... working
1314 // on that
1315
1316 if (blackbox_hint->flags & AttribDecoration) {
1317 switch (blackbox_hint->decoration) {
1318 case DecorNone:
1319 // clear all decorations except close
1320 decorations &= Decor_Close;
1321 // clear all functions except close
1322 functions &= Func_Close;
1323
1324 break;
1325
1326 case DecorTiny:
1327 decorations |= Decor_Titlebar | Decor_Iconify;
1328 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1329 functions |= Func_Move | Func_Iconify;
1330 functions &= ~(Func_Resize | Func_Maximize);
1331
1332 break;
1333
1334 case DecorTool:
1335 decorations |= Decor_Titlebar;
1336 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1337 functions |= Func_Move;
1338 functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1339
1340 break;
1341
1342 case DecorNormal:
1343 default:
1344 decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1345 Decor_Iconify | Decor_Maximize;
1346 functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
1347
1348 break;
1349 }
1350
1351 reconfigure();
1352 }
1353
1354 delete blackbox_hint;
1355
1356 return True;
1357 }
1358
1359
1360 void BlackboxWindow::getTransientInfo(void) {
1361 if (client.transient_for &&
1362 client.transient_for != (BlackboxWindow *) ~0ul) {
1363 // the transient for hint was removed, so we need to tell our
1364 // previous transient_for that we are going away
1365 client.transient_for->client.transientList.remove(this);
1366 }
1367
1368 // we have no transient_for until we find a new one
1369 client.transient_for = 0;
1370
1371 Window trans_for;
1372 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1373 &trans_for)) {
1374 // transient_for hint not set
1375 return;
1376 }
1377
1378 if (trans_for == client.window) {
1379 // wierd client... treat this window as a normal window
1380 return;
1381 }
1382
1383 if (trans_for == None || trans_for == screen->getRootWindow()) {
1384 // this is an undocumented interpretation of the ICCCM. a transient
1385 // associated with None/Root/itself is assumed to be a modal root
1386 // transient. we don't support the concept of a global transient,
1387 // so we just associate this transient with nothing, and perhaps
1388 // we will add support later for global modality.
1389 client.transient_for = (BlackboxWindow *) ~0ul;
1390 flags.modal = True;
1391 return;
1392 }
1393
1394 client.transient_for = blackbox->searchWindow(trans_for);
1395 if (! client.transient_for &&
1396 client.window_group && trans_for == client.window_group) {
1397 // no direct transient_for, perhaps this is a group transient?
1398 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1399 if (group) client.transient_for = group->find(screen);
1400 }
1401
1402 if (! client.transient_for || client.transient_for == this) {
1403 // no transient_for found, or we have a wierd client that wants to be
1404 // a transient for itself, so we treat this window as a normal window
1405 client.transient_for = (BlackboxWindow*) 0;
1406 return;
1407 }
1408
1409 // register ourselves with our new transient_for
1410 client.transient_for->client.transientList.push_back(this);
1411 flags.stuck = client.transient_for->flags.stuck;
1412 }
1413
1414
1415 bool BlackboxWindow::isKDESystrayWindow(void) {
1416 Window systray;
1417 if (xatom->getValue(client.window, XAtom::kde_net_wm_system_tray_window_for,
1418 XAtom::window, systray) && systray)
1419 return True;
1420 return False;
1421 }
1422
1423
1424 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1425 if (client.transient_for &&
1426 client.transient_for != (BlackboxWindow*) ~0ul)
1427 return client.transient_for;
1428 return 0;
1429 }
1430
1431
1432 void BlackboxWindow::configure(int dx, int dy,
1433 unsigned int dw, unsigned int dh) {
1434 bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
1435
1436 if ((dw != frame.rect.width()) || (dh != frame.rect.height())) {
1437 frame.rect.setRect(dx, dy, dw, dh);
1438 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1439 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1440
1441 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1442 frame.rect.setPos(0, 0);
1443
1444 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1445 frame.rect.top() + frame.margin.top,
1446 frame.rect.right() - frame.margin.right,
1447 frame.rect.bottom() - frame.margin.bottom);
1448
1449 #ifdef SHAPE
1450 if (blackbox->hasShapeExtensions() && flags.shaped) {
1451 configureShape();
1452 }
1453 #endif // SHAPE
1454
1455 positionWindows();
1456 decorate();
1457 setFocusFlag(flags.focused);
1458 redrawAllButtons();
1459 } else {
1460 frame.rect.setPos(dx, dy);
1461
1462 XMoveWindow(blackbox->getXDisplay(), frame.window,
1463 frame.rect.x(), frame.rect.y());
1464
1465 if (! flags.moving) send_event = True;
1466 }
1467
1468 if (send_event && ! flags.moving) {
1469 client.rect.setPos(frame.rect.left() + frame.margin.left,
1470 frame.rect.top() + frame.margin.top);
1471
1472 XEvent event;
1473 event.type = ConfigureNotify;
1474
1475 event.xconfigure.display = blackbox->getXDisplay();
1476 event.xconfigure.event = client.window;
1477 event.xconfigure.window = client.window;
1478 event.xconfigure.x = client.rect.x();
1479 event.xconfigure.y = client.rect.y();
1480 event.xconfigure.width = client.rect.width();
1481 event.xconfigure.height = client.rect.height();
1482 event.xconfigure.border_width = client.old_bw;
1483 event.xconfigure.above = frame.window;
1484 event.xconfigure.override_redirect = False;
1485
1486 XSendEvent(blackbox->getXDisplay(), client.window, True,
1487 NoEventMask, &event);
1488
1489 screen->updateNetizenConfigNotify(&event);
1490 }
1491 }
1492
1493
1494 #ifdef SHAPE
1495 void BlackboxWindow::configureShape(void) {
1496 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1497 frame.margin.left - frame.border_w,
1498 frame.margin.top - frame.border_w,
1499 client.window, ShapeBounding, ShapeSet);
1500
1501 int num = 0;
1502 XRectangle xrect[2];
1503
1504 if (decorations & Decor_Titlebar) {
1505 xrect[0].x = xrect[0].y = -frame.border_w;
1506 xrect[0].width = frame.rect.width();
1507 xrect[0].height = frame.title_h + (frame.border_w * 2);
1508 ++num;
1509 }
1510
1511 if (decorations & Decor_Handle) {
1512 xrect[1].x = -frame.border_w;
1513 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1514 frame.mwm_border_w - frame.border_w;
1515 xrect[1].width = frame.rect.width();
1516 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1517 ++num;
1518 }
1519
1520 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1521 ShapeBounding, 0, 0, xrect, num,
1522 ShapeUnion, Unsorted);
1523 }
1524 #endif // SHAPE
1525
1526
1527 bool BlackboxWindow::setInputFocus(void) {
1528 if (flags.focused) return True;
1529
1530 assert(! flags.iconic);
1531
1532 // if the window is not visible, mark the window as wanting focus rather
1533 // than give it focus.
1534 if (! flags.visible) {
1535 Workspace *wkspc = screen->getWorkspace(blackbox_attrib.workspace);
1536 wkspc->setLastFocusedWindow(this);
1537 return True;
1538 }
1539
1540 if (! client.rect.intersects(screen->getRect())) {
1541 // client is outside the screen, move it to the center
1542 configure((screen->getWidth() - frame.rect.width()) / 2,
1543 (screen->getHeight() - frame.rect.height()) / 2,
1544 frame.rect.width(), frame.rect.height());
1545 }
1546
1547 if (client.transientList.size() > 0) {
1548 // transfer focus to any modal transients
1549 BlackboxWindowList::iterator it, end = client.transientList.end();
1550 for (it = client.transientList.begin(); it != end; ++it) {
1551 if ((*it)->flags.modal) return (*it)->setInputFocus();
1552 }
1553 }
1554
1555 bool ret = True;
1556 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1557 XSetInputFocus(blackbox->getXDisplay(), client.window,
1558 RevertToPointerRoot, CurrentTime);
1559
1560 blackbox->setFocusedWindow(this);
1561 } else {
1562 /* we could set the focus to none, since the window doesn't accept focus,
1563 * but we shouldn't set focus to nothing since this would surely make
1564 * someone angry
1565 */
1566 ret = False;
1567 }
1568
1569 if (flags.send_focus_message) {
1570 XEvent ce;
1571 ce.xclient.type = ClientMessage;
1572 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1573 ce.xclient.display = blackbox->getXDisplay();
1574 ce.xclient.window = client.window;
1575 ce.xclient.format = 32;
1576 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1577 ce.xclient.data.l[1] = blackbox->getLastTime();
1578 ce.xclient.data.l[2] = 0l;
1579 ce.xclient.data.l[3] = 0l;
1580 ce.xclient.data.l[4] = 0l;
1581 XSendEvent(blackbox->getXDisplay(), client.window, False,
1582 NoEventMask, &ce);
1583 }
1584
1585 return ret;
1586 }
1587
1588
1589 void BlackboxWindow::iconify(void) {
1590 if (flags.iconic) return;
1591
1592 // We don't need to worry about resizing because resizing always grabs the X
1593 // server. This should only ever happen if using opaque moving.
1594 if (flags.moving)
1595 endMove();
1596
1597 if (windowmenu) windowmenu->hide();
1598
1599 /*
1600 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1601 * we need to clear the event mask on client.window for a split second.
1602 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1603 * split second, leaving us with a ghost window... so, we need to do this
1604 * while the X server is grabbed
1605 */
1606 XGrabServer(blackbox->getXDisplay());
1607 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1608 XUnmapWindow(blackbox->getXDisplay(), client.window);
1609 XSelectInput(blackbox->getXDisplay(), client.window,
1610 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1611 XUngrabServer(blackbox->getXDisplay());
1612
1613 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1614 flags.visible = False;
1615 flags.iconic = True;
1616
1617 setState(IconicState);
1618
1619 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1620
1621 if (isTransient()) {
1622 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1623 ! client.transient_for->flags.iconic) {
1624 // iconify our transient_for
1625 client.transient_for->iconify();
1626 }
1627 }
1628
1629 screen->addIcon(this);
1630
1631 if (client.transientList.size() > 0) {
1632 // iconify all transients
1633 BlackboxWindowList::iterator it, end = client.transientList.end();
1634 for (it = client.transientList.begin(); it != end; ++it) {
1635 if (! (*it)->flags.iconic) (*it)->iconify();
1636 }
1637 }
1638 }
1639
1640
1641 void BlackboxWindow::show(void) {
1642 flags.visible = True;
1643 flags.iconic = False;
1644
1645 current_state = (flags.shaded) ? IconicState : NormalState;
1646 setState(current_state);
1647
1648 XMapWindow(blackbox->getXDisplay(), client.window);
1649 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1650 XMapWindow(blackbox->getXDisplay(), frame.window);
1651 }
1652
1653
1654 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1655 if (flags.iconic || reassoc)
1656 screen->reassociateWindow(this, BSENTINEL, False);
1657 else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID())
1658 return;
1659
1660 show();
1661
1662 // reassociate and deiconify all transients
1663 if (reassoc && client.transientList.size() > 0) {
1664 BlackboxWindowList::iterator it, end = client.transientList.end();
1665 for (it = client.transientList.begin(); it != end; ++it) {
1666 (*it)->deiconify(True, False);
1667 }
1668 }
1669
1670 if (raise)
1671 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1672 }
1673
1674
1675 void BlackboxWindow::close(void) {
1676 XEvent ce;
1677 ce.xclient.type = ClientMessage;
1678 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1679 ce.xclient.display = blackbox->getXDisplay();
1680 ce.xclient.window = client.window;
1681 ce.xclient.format = 32;
1682 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1683 ce.xclient.data.l[1] = CurrentTime;
1684 ce.xclient.data.l[2] = 0l;
1685 ce.xclient.data.l[3] = 0l;
1686 ce.xclient.data.l[4] = 0l;
1687 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1688 }
1689
1690
1691 void BlackboxWindow::withdraw(void) {
1692 // We don't need to worry about resizing because resizing always grabs the X
1693 // server. This should only ever happen if using opaque moving.
1694 if (flags.moving)
1695 endMove();
1696
1697 flags.visible = False;
1698 flags.iconic = False;
1699
1700 setState(current_state);
1701
1702 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1703
1704 XGrabServer(blackbox->getXDisplay());
1705
1706 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1707 XUnmapWindow(blackbox->getXDisplay(), client.window);
1708 XSelectInput(blackbox->getXDisplay(), client.window,
1709 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1710
1711 XUngrabServer(blackbox->getXDisplay());
1712
1713 if (windowmenu) windowmenu->hide();
1714 }
1715
1716
1717 void BlackboxWindow::maximize(unsigned int button) {
1718 // We don't need to worry about resizing because resizing always grabs the X
1719 // server. This should only ever happen if using opaque moving.
1720 if (flags.moving)
1721 endMove();
1722
1723 // handle case where menu is open then the max button is used instead
1724 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1725
1726 if (flags.maximized) {
1727 flags.maximized = 0;
1728
1729 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1730 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1731
1732 /*
1733 when a resize finishes, maximize(0) is called to clear any maximization
1734 flags currently set. Otherwise it still thinks it is maximized.
1735 so we do not need to call configure() because resizing will handle it
1736 */
1737 if (! flags.resizing)
1738 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1739 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1740
1741 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1742 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1743
1744 redrawAllButtons();
1745 setState(current_state);
1746 return;
1747 }
1748
1749 blackbox_attrib.premax_x = frame.rect.x();
1750 blackbox_attrib.premax_y = frame.rect.y();
1751 blackbox_attrib.premax_w = frame.rect.width();
1752 // use client.rect so that clients can be restored even if shaded
1753 blackbox_attrib.premax_h =
1754 client.rect.height() + frame.margin.top + frame.margin.bottom;
1755
1756 const Rect &screen_area = screen->availableArea();
1757 frame.changing = screen_area;
1758 constrain(TopLeft);
1759
1760 switch(button) {
1761 case 1:
1762 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1763 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1764 break;
1765
1766 case 2:
1767 blackbox_attrib.flags |= AttribMaxVert;
1768 blackbox_attrib.attrib |= AttribMaxVert;
1769
1770 frame.changing.setX(frame.rect.x());
1771 frame.changing.setWidth(frame.rect.width());
1772 break;
1773
1774 case 3:
1775 blackbox_attrib.flags |= AttribMaxHoriz;
1776 blackbox_attrib.attrib |= AttribMaxHoriz;
1777
1778 frame.changing.setY(frame.rect.y());
1779 frame.changing.setHeight(frame.rect.height());
1780 break;
1781 }
1782
1783 if (flags.shaded) {
1784 blackbox_attrib.flags ^= AttribShaded;
1785 blackbox_attrib.attrib ^= AttribShaded;
1786 flags.shaded = False;
1787 }
1788
1789 flags.maximized = button;
1790
1791 configure(frame.changing.x(), frame.changing.y(),
1792 frame.changing.width(), frame.changing.height());
1793 if (flags.focused)
1794 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1795 redrawAllButtons();
1796 setState(current_state);
1797 }
1798
1799
1800 // re-maximizes the window to take into account availableArea changes
1801 void BlackboxWindow::remaximize(void) {
1802 // save the original dimensions because maximize will wipe them out
1803 int premax_x = blackbox_attrib.premax_x,
1804 premax_y = blackbox_attrib.premax_y,
1805 premax_w = blackbox_attrib.premax_w,
1806 premax_h = blackbox_attrib.premax_h;
1807
1808 unsigned int button = flags.maximized;
1809 flags.maximized = 0; // trick maximize() into working
1810 maximize(button);
1811
1812 // restore saved values
1813 blackbox_attrib.premax_x = premax_x;
1814 blackbox_attrib.premax_y = premax_y;
1815 blackbox_attrib.premax_w = premax_w;
1816 blackbox_attrib.premax_h = premax_h;
1817 }
1818
1819
1820 void BlackboxWindow::setWorkspace(unsigned int n) {
1821 blackbox_attrib.flags |= AttribWorkspace;
1822 blackbox_attrib.workspace = n;
1823 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1824 }
1825
1826
1827 void BlackboxWindow::shade(void) {
1828 if (flags.shaded) {
1829 XResizeWindow(blackbox->getXDisplay(), frame.window,
1830 frame.inside_w, frame.inside_h);
1831 flags.shaded = False;
1832 blackbox_attrib.flags ^= AttribShaded;
1833 blackbox_attrib.attrib ^= AttribShaded;
1834
1835 setState(NormalState);
1836
1837 // set the frame rect to the normal size
1838 frame.rect.setHeight(client.rect.height() + frame.margin.top +
1839 frame.margin.bottom);
1840 } else {
1841 if (! (decorations & Decor_Titlebar))
1842 return;
1843
1844 XResizeWindow(blackbox->getXDisplay(), frame.window,
1845 frame.inside_w, frame.title_h);
1846 flags.shaded = True;
1847 blackbox_attrib.flags |= AttribShaded;
1848 blackbox_attrib.attrib |= AttribShaded;
1849
1850 setState(IconicState);
1851
1852 // set the frame rect to the shaded size
1853 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1854 }
1855 }
1856
1857
1858 /*
1859 * (Un)Sticks a window and its relatives.
1860 */
1861 void BlackboxWindow::stick(void) {
1862 if (flags.stuck) {
1863 blackbox_attrib.flags ^= AttribOmnipresent;
1864 blackbox_attrib.attrib ^= AttribOmnipresent;
1865
1866 flags.stuck = False;
1867
1868 if (! flags.iconic)
1869 screen->reassociateWindow(this, BSENTINEL, True);
1870 else
1871 // temporary fix since sticky windows suck. set the hint to what we
1872 // actually hold in our data.
1873 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1874 blackbox_attrib.workspace);
1875
1876 setState(current_state);
1877 } else {
1878 flags.stuck = True;
1879
1880 blackbox_attrib.flags |= AttribOmnipresent;
1881 blackbox_attrib.attrib |= AttribOmnipresent;
1882
1883 // temporary fix since sticky windows suck. set the hint to a different
1884 // value than that contained in the class' data.
1885 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1886 0xffffffff);
1887
1888 setState(current_state);
1889 }
1890 // go up the chain
1891 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1892 client.transient_for->isStuck() != flags.stuck)
1893 client.transient_for->stick();
1894 // go down the chain
1895 BlackboxWindowList::iterator it;
1896 const BlackboxWindowList::iterator end = client.transientList.end();
1897 for (it = client.transientList.begin(); it != end; ++it)
1898 if ((*it)->isStuck() != flags.stuck)
1899 (*it)->stick();
1900 }
1901
1902
1903 void BlackboxWindow::setFocusFlag(bool focus) {
1904 // only focus a window if it is visible
1905 if (focus && !flags.visible)
1906 return;
1907
1908 flags.focused = focus;
1909
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 if (screen->isSloppyFocus() && screen->doAutoRaise()) {
1987 if (isFocused()) timer->start();
1988 else timer->stop();
1989 }
1990
1991 if (isFocused())
1992 blackbox->setFocusedWindow(this);
1993 }
1994
1995
1996 void BlackboxWindow::installColormap(bool install) {
1997 int i = 0, ncmap = 0;
1998 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
1999 client.window, &ncmap);
2000 XWindowAttributes wattrib;
2001 if (cmaps) {
2002 if (XGetWindowAttributes(blackbox->getXDisplay(),
2003 client.window, &wattrib)) {
2004 if (install) {
2005 // install the window's colormap
2006 for (i = 0; i < ncmap; i++) {
2007 if (*(cmaps + i) == wattrib.colormap)
2008 // this window is using an installed color map... do not install
2009 install = False;
2010 }
2011 // otherwise, install the window's colormap
2012 if (install)
2013 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2014 } else {
2015 // uninstall the window's colormap
2016 for (i = 0; i < ncmap; i++) {
2017 if (*(cmaps + i) == wattrib.colormap)
2018 // we found the colormap to uninstall
2019 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2020 }
2021 }
2022 }
2023
2024 XFree(cmaps);
2025 }
2026 }
2027
2028
2029 void BlackboxWindow::setAllowedActions(void) {
2030 Atom actions[7];
2031 int num = 0;
2032
2033 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2034 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2035 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2036
2037 if (functions & Func_Move)
2038 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2039 if (functions & Func_Resize)
2040 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2041 if (functions & Func_Maximize) {
2042 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2043 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2044 }
2045
2046 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2047 actions, num);
2048 }
2049
2050
2051 void BlackboxWindow::setState(unsigned long new_state) {
2052 current_state = new_state;
2053
2054 unsigned long state[2];
2055 state[0] = current_state;
2056 state[1] = None;
2057 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2058
2059 xatom->setValue(client.window, XAtom::blackbox_attributes,
2060 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2061 PropBlackboxAttributesElements);
2062
2063 Atom netstate[8];
2064 int num = 0;
2065 if (flags.modal)
2066 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2067 if (flags.shaded)
2068 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2069 if (flags.iconic)
2070 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2071 if (flags.skip_taskbar)
2072 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2073 if (flags.skip_pager)
2074 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2075 if (flags.fullscreen)
2076 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2077 if (flags.maximized == 1 || flags.maximized == 2)
2078 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2079 if (flags.maximized == 1 || flags.maximized == 3)
2080 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2081 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2082 netstate, num);
2083 }
2084
2085
2086 bool BlackboxWindow::getState(void) {
2087 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2088 current_state);
2089 if (! ret) current_state = 0;
2090 return ret;
2091 }
2092
2093
2094 void BlackboxWindow::restoreAttributes(void) {
2095 unsigned long num = PropBlackboxAttributesElements;
2096 BlackboxAttributes *net;
2097 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2098 XAtom::blackbox_attributes, num,
2099 (unsigned long **)&net))
2100 return;
2101 if (num < PropBlackboxAttributesElements) {
2102 delete net;
2103 return;
2104 }
2105
2106 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2107 flags.shaded = False;
2108 shade();
2109
2110 /*
2111 Because the iconic'ness of shaded windows is lost, we need to set the
2112 state to NormalState so that shaded windows on other workspaces will not
2113 get shown on the first workspace.
2114 At this point in the life of a window, current_state should only be set
2115 to IconicState if the window was an *icon*, not if it was shaded.
2116 */
2117 current_state = NormalState;
2118 }
2119
2120 if ((net->workspace != screen->getCurrentWorkspaceID()) &&
2121 (net->workspace < screen->getWorkspaceCount()))
2122 screen->reassociateWindow(this, net->workspace, True);
2123
2124 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2125 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2126 // set to WithdrawnState so it will be mapped on the new workspace
2127 if (current_state == NormalState) current_state = WithdrawnState;
2128 } else if (current_state == WithdrawnState) {
2129 // the window is on this workspace and is Withdrawn, so it is waiting to
2130 // be mapped
2131 current_state = NormalState;
2132 }
2133
2134 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2135 flags.stuck = False;
2136 stick();
2137
2138 // if the window was on another workspace, it was going to be hidden. this
2139 // specifies that the window should be mapped since it is sticky.
2140 if (current_state == WithdrawnState) current_state = NormalState;
2141 }
2142
2143 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2144 int x = net->premax_x, y = net->premax_y;
2145 unsigned int w = net->premax_w, h = net->premax_h;
2146 flags.maximized = 0;
2147
2148 unsigned int m = 0;
2149 if ((net->flags & AttribMaxHoriz) &&
2150 (net->flags & AttribMaxVert))
2151 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2152 else if (net->flags & AttribMaxVert)
2153 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2154 else if (net->flags & AttribMaxHoriz)
2155 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2156
2157 if (m) maximize(m);
2158
2159 blackbox_attrib.premax_x = x;
2160 blackbox_attrib.premax_y = y;
2161 blackbox_attrib.premax_w = w;
2162 blackbox_attrib.premax_h = h;
2163 }
2164
2165 // with the state set it will then be the map events job to read the window's
2166 // state and behave accordingly
2167
2168 delete net;
2169 }
2170
2171
2172 /*
2173 * Positions the frame according the the client window position and window
2174 * gravity.
2175 */
2176 void BlackboxWindow::setGravityOffsets(void) {
2177 // x coordinates for each gravity type
2178 const int x_west = client.rect.x();
2179 const int x_east = client.rect.right() - frame.inside_w + 1;
2180 const int x_center = client.rect.left() +
2181 ((client.rect.width() - frame.rect.width()) / 2);
2182 // y coordinates for each gravity type
2183 const int y_north = client.rect.y();
2184 const int y_south = client.rect.bottom() - frame.inside_h + 1;
2185 const int y_center = client.rect.top() +
2186 ((client.rect.height() - frame.rect.height()) / 2);
2187
2188 switch (client.win_gravity) {
2189 default:
2190 case NorthWestGravity: frame.rect.setPos(x_west, y_north); break;
2191 case NorthGravity: frame.rect.setPos(x_center, y_north); break;
2192 case NorthEastGravity: frame.rect.setPos(x_east, y_north); break;
2193 case SouthWestGravity: frame.rect.setPos(x_west, y_south); break;
2194 case SouthGravity: frame.rect.setPos(x_center, y_south); break;
2195 case SouthEastGravity: frame.rect.setPos(x_east, y_south); break;
2196 case WestGravity: frame.rect.setPos(x_west, y_center); break;
2197 case CenterGravity: frame.rect.setPos(x_center, y_center); break;
2198 case EastGravity: frame.rect.setPos(x_east, y_center); break;
2199
2200 case ForgetGravity:
2201 case StaticGravity:
2202 frame.rect.setPos(client.rect.x() - frame.margin.left,
2203 client.rect.y() - frame.margin.top);
2204 break;
2205 }
2206 }
2207
2208
2209 /*
2210 * The reverse of the setGravityOffsets function. Uses the frame window's
2211 * position to find the window's reference point.
2212 */
2213 void BlackboxWindow::restoreGravity(void) {
2214 // x coordinates for each gravity type
2215 const int x_west = frame.rect.x();
2216 const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
2217 const int x_center = frame.rect.x() -
2218 ((client.rect.width() - frame.rect.width()) / 2);
2219 // y coordinates for each gravity type
2220 const int y_north = frame.rect.y();
2221 const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
2222 const int y_center = frame.rect.y() -
2223 ((client.rect.height() - frame.rect.height()) / 2);
2224
2225 switch(client.win_gravity) {
2226 default:
2227 case NorthWestGravity: client.rect.setPos(x_west, y_north); break;
2228 case NorthGravity: client.rect.setPos(x_center, y_north); break;
2229 case NorthEastGravity: client.rect.setPos(x_east, y_north); break;
2230 case SouthWestGravity: client.rect.setPos(x_west, y_south); break;
2231 case SouthGravity: client.rect.setPos(x_center, y_south); break;
2232 case SouthEastGravity: client.rect.setPos(x_east, y_south); break;
2233 case WestGravity: client.rect.setPos(x_west, y_center); break;
2234 case CenterGravity: client.rect.setPos(x_center, y_center); break;
2235 case EastGravity: client.rect.setPos(x_east, y_center); break;
2236
2237 case ForgetGravity:
2238 case StaticGravity:
2239 client.rect.setPos(frame.rect.left() + frame.margin.left,
2240 frame.rect.top() + frame.margin.top);
2241 break;
2242 }
2243 }
2244
2245
2246 void BlackboxWindow::redrawLabel(void) {
2247 if (flags.focused) {
2248 if (frame.flabel)
2249 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2250 frame.label, frame.flabel);
2251 else
2252 XSetWindowBackground(blackbox->getXDisplay(),
2253 frame.label, frame.flabel_pixel);
2254 } else {
2255 if (frame.ulabel)
2256 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2257 frame.label, frame.ulabel);
2258 else
2259 XSetWindowBackground(blackbox->getXDisplay(),
2260 frame.label, frame.ulabel_pixel);
2261 }
2262 XClearWindow(blackbox->getXDisplay(), frame.label);
2263
2264 WindowStyle *style = screen->getWindowStyle();
2265
2266 int pos = frame.bevel_w * 2,
2267 dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
2268 frame.bevel_w * 4, i18n.multibyte());
2269
2270 BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
2271 style->font);
2272 if (i18n.multibyte())
2273 XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
2274 pen.gc(), pos,
2275 (1 - style->fontset_extents->max_ink_extent.y),
2276 client.title.c_str(), dlen);
2277 else
2278 XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
2279 (style->font->ascent + 1), client.title.c_str(), dlen);
2280 }
2281
2282
2283 void BlackboxWindow::redrawAllButtons(void) {
2284 if (frame.iconify_button) redrawIconifyButton(False);
2285 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2286 if (frame.close_button) redrawCloseButton(False);
2287 }
2288
2289
2290 void BlackboxWindow::redrawIconifyButton(bool pressed) {
2291 if (! pressed) {
2292 if (flags.focused) {
2293 if (frame.fbutton)
2294 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2295 frame.iconify_button, frame.fbutton);
2296 else
2297 XSetWindowBackground(blackbox->getXDisplay(),
2298 frame.iconify_button, frame.fbutton_pixel);
2299 } else {
2300 if (frame.ubutton)
2301 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2302 frame.iconify_button, frame.ubutton);
2303 else
2304 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2305 frame.ubutton_pixel);
2306 }
2307 } else {
2308 if (frame.pbutton)
2309 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2310 frame.iconify_button, frame.pbutton);
2311 else
2312 XSetWindowBackground(blackbox->getXDisplay(),
2313 frame.iconify_button, frame.pbutton_pixel);
2314 }
2315 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2316
2317 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2318 screen->getWindowStyle()->b_pic_unfocus);
2319 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2320 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2321 }
2322
2323
2324 void BlackboxWindow::redrawMaximizeButton(bool pressed) {
2325 if (! pressed) {
2326 if (flags.focused) {
2327 if (frame.fbutton)
2328 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2329 frame.maximize_button, frame.fbutton);
2330 else
2331 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2332 frame.fbutton_pixel);
2333 } else {
2334 if (frame.ubutton)
2335 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2336 frame.maximize_button, frame.ubutton);
2337 else
2338 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2339 frame.ubutton_pixel);
2340 }
2341 } else {
2342 if (frame.pbutton)
2343 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2344 frame.maximize_button, frame.pbutton);
2345 else
2346 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2347 frame.pbutton_pixel);
2348 }
2349 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2350
2351 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2352 screen->getWindowStyle()->b_pic_unfocus);
2353 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2354 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2355 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2356 2, 3, (frame.button_w - 3), 3);
2357 }
2358
2359
2360 void BlackboxWindow::redrawCloseButton(bool pressed) {
2361 if (! pressed) {
2362 if (flags.focused) {
2363 if (frame.fbutton)
2364 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2365 frame.fbutton);
2366 else
2367 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2368 frame.fbutton_pixel);
2369 } else {
2370 if (frame.ubutton)
2371 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2372 frame.ubutton);
2373 else
2374 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2375 frame.ubutton_pixel);
2376 }
2377 } else {
2378 if (frame.pbutton)
2379 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2380 frame.close_button, frame.pbutton);
2381 else
2382 XSetWindowBackground(blackbox->getXDisplay(),
2383 frame.close_button, frame.pbutton_pixel);
2384 }
2385 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2386
2387 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2388 screen->getWindowStyle()->b_pic_unfocus);
2389 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2390 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2391 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2392 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2393 }
2394
2395
2396 void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
2397 if (re->window != client.window)
2398 return;
2399
2400 #ifdef DEBUG
2401 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2402 client.window);
2403 #endif // DEBUG
2404
2405 switch (current_state) {
2406 case IconicState:
2407 iconify();
2408 break;
2409
2410 case WithdrawnState:
2411 withdraw();
2412 break;
2413
2414 case NormalState:
2415 case InactiveState:
2416 case ZoomState:
2417 default:
2418 show();
2419 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2420 if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2421 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2422 setInputFocus();
2423 }
2424 break;
2425 }
2426 }
2427
2428
2429 void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
2430 if (ue->window != client.window)
2431 return;
2432
2433 #ifdef DEBUG
2434 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2435 client.window);
2436 #endif // DEBUG
2437
2438 screen->unmanageWindow(this, False);
2439 }
2440
2441
2442 void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
2443 if (de->window != client.window)
2444 return;
2445
2446 #ifdef DEBUG
2447 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2448 client.window);
2449 #endif // DEBUG
2450
2451 screen->unmanageWindow(this, False);
2452 }
2453
2454
2455 void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
2456 if (re->window != client.window || re->parent == frame.plate)
2457 return;
2458
2459 #ifdef DEBUG
2460 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2461 "0x%lx.\n", client.window, re->parent);
2462 #endif // DEBUG
2463
2464 XEvent ev;
2465 ev.xreparent = *re;
2466 XPutBackEvent(blackbox->getXDisplay(), &ev);
2467 screen->unmanageWindow(this, True);
2468 }
2469
2470
2471 void BlackboxWindow::propertyNotifyEvent(Atom atom) {
2472 switch(atom) {
2473 case XA_WM_CLASS:
2474 case XA_WM_CLIENT_MACHINE:
2475 case XA_WM_COMMAND:
2476 break;
2477
2478 case XA_WM_TRANSIENT_FOR: {
2479 // determine if this is a transient window
2480 getTransientInfo();
2481
2482 // adjust the window decorations based on transience
2483 if (isTransient()) {
2484 decorations &= ~(Decor_Maximize | Decor_Handle);
2485 functions &= ~Func_Maximize;
2486 setAllowedActions();
2487 }
2488
2489 reconfigure();
2490 }
2491 break;
2492
2493 case XA_WM_HINTS:
2494 getWMHints();
2495 break;
2496
2497 case XA_WM_ICON_NAME:
2498 getWMIconName();
2499 if (flags.iconic) screen->propagateWindowName(this);
2500 break;
2501
2502 case XAtom::net_wm_name:
2503 case XA_WM_NAME:
2504 getWMName();
2505
2506 if (decorations & Decor_Titlebar)
2507 redrawLabel();
2508
2509 screen->propagateWindowName(this);
2510 break;
2511
2512 case XA_WM_NORMAL_HINTS: {
2513 getWMNormalHints();
2514
2515 if ((client.normal_hint_flags & PMinSize) &&
2516 (client.normal_hint_flags & PMaxSize)) {
2517 if (client.max_width <= client.min_width &&
2518 client.max_height <= client.min_height) {
2519 decorations &= ~(Decor_Maximize | Decor_Handle);
2520 functions &= ~(Func_Resize | Func_Maximize);
2521 } else {
2522 decorations |= Decor_Maximize | Decor_Handle;
2523 functions |= Func_Resize | Func_Maximize;
2524 }
2525 setAllowedActions();
2526 }
2527
2528 Rect old_rect = frame.rect;
2529
2530 upsize();
2531
2532 if (old_rect != frame.rect)
2533 reconfigure();
2534
2535 break;
2536 }
2537
2538 default:
2539 if (atom == xatom->getAtom(XAtom::wm_protocols)) {
2540 getWMProtocols();
2541
2542 if ((decorations & Decor_Close) && (! frame.close_button)) {
2543 createCloseButton();
2544 if (decorations & Decor_Titlebar) {
2545 positionButtons(True);
2546 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2547 }
2548 if (windowmenu) windowmenu->reconfigure();
2549 }
2550 } else if (atom == xatom->getAtom(XAtom::net_wm_strut)) {
2551 updateStrut();
2552 }
2553
2554 break;
2555 }
2556 }
2557
2558
2559 void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
2560 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2561 redrawLabel();
2562 else if (frame.close_button == ee->window)
2563 redrawCloseButton(False);
2564 else if (frame.maximize_button == ee->window)
2565 redrawMaximizeButton(flags.maximized);
2566 else if (frame.iconify_button == ee->window)
2567 redrawIconifyButton(False);
2568 }
2569
2570
2571 void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
2572 if (cr->window != client.window || flags.iconic)
2573 return;
2574
2575 int cx = frame.rect.x(), cy = frame.rect.y();
2576 unsigned int cw = frame.rect.width(), ch = frame.rect.height();
2577
2578 if (cr->value_mask & CWBorderWidth)
2579 client.old_bw = cr->border_width;
2580
2581 if (cr->value_mask & CWX)
2582 cx = cr->x - frame.margin.left;
2583
2584 if (cr->value_mask & CWY)
2585 cy = cr->y - frame.margin.top;
2586
2587 if (cr->value_mask & CWWidth)
2588 cw = cr->width + frame.margin.left + frame.margin.right;
2589
2590 if (cr->value_mask & CWHeight)
2591 ch = cr->height + frame.margin.top + frame.margin.bottom;
2592
2593 if (frame.rect != Rect(cx, cy, cw, ch))
2594 configure(cx, cy, cw, ch);
2595
2596 if (cr->value_mask & CWStackMode) {
2597 switch (cr->detail) {
2598 case Below:
2599 case BottomIf:
2600 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2601 break;
2602
2603 case Above:
2604 case TopIf:
2605 default:
2606 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2607 break;
2608 }
2609 }
2610 }
2611
2612
2613 void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
2614 if (frame.maximize_button == be->window) {
2615 redrawMaximizeButton(True);
2616 } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2617 if (! flags.focused)
2618 setInputFocus();
2619
2620 if (frame.iconify_button == be->window) {
2621 redrawIconifyButton(True);
2622 } else if (frame.close_button == be->window) {
2623 redrawCloseButton(True);
2624 } else if (frame.plate == be->window) {
2625 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2626
2627 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2628
2629 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2630 } else {
2631 if (frame.title == be->window || frame.label == be->window) {
2632 if (((be->time - lastButtonPressTime) <=
2633 blackbox->getDoubleClickInterval()) ||
2634 (be->state & ControlMask)) {
2635 lastButtonPressTime = 0;
2636 shade();
2637 } else {
2638 lastButtonPressTime = be->time;
2639 }
2640 }
2641
2642 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2643
2644 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2645 }
2646 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2647 (be->window != frame.close_button)) {
2648 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2649 } else if (windowmenu && be->button == 3 &&
2650 (frame.title == be->window || frame.label == be->window ||
2651 frame.handle == be->window || frame.window == be->window)) {
2652 int mx = 0, my = 0;
2653
2654 if (frame.title == be->window || frame.label == be->window) {
2655 mx = be->x_root - (windowmenu->getWidth() / 2);
2656 my = frame.rect.y() + frame.title_h + frame.border_w;
2657 } else if (frame.handle == be->window) {
2658 mx = be->x_root - (windowmenu->getWidth() / 2);
2659 my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
2660 windowmenu->getHeight();
2661 } else {
2662 mx = be->x_root - (windowmenu->getWidth() / 2);
2663
2664 if (be->y <= static_cast<signed>(frame.bevel_w))
2665 my = frame.rect.y() + frame.title_h;
2666 else
2667 my = be->y_root - (windowmenu->getHeight() / 2);
2668 }
2669
2670 // snap the window menu into a corner if necessary - we check the
2671 // position of the menu with the coordinates of the client to
2672 // make the comparisions easier.
2673 // XXX: this needs some work!
2674 if (mx > client.rect.right() -
2675 static_cast<signed>(windowmenu->getWidth()))
2676 mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
2677 if (mx < client.rect.left())
2678 mx = frame.rect.x();
2679
2680 if (my > client.rect.bottom() -
2681 static_cast<signed>(windowmenu->getHeight()))
2682 my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
2683 if (my < client.rect.top())
2684 my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
2685 frame.title_h : 0);
2686
2687 if (windowmenu) {
2688 if (! windowmenu->isVisible()) {
2689 windowmenu->move(mx, my);
2690 windowmenu->show();
2691 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2692 XRaiseWindow(blackbox->getXDisplay(),
2693 windowmenu->getSendToMenu()->getWindowID());
2694 } else {
2695 windowmenu->hide();
2696 }
2697 }
2698 // mouse wheel up
2699 } else if (be->button == 4) {
2700 if ((be->window == frame.label ||
2701 be->window == frame.title) &&
2702 ! flags.shaded)
2703 shade();
2704 // mouse wheel down
2705 } else if (be->button == 5) {
2706 if ((be->window == frame.label ||
2707 be->window == frame.title) &&
2708 flags.shaded)
2709 shade();
2710 }
2711 }
2712
2713
2714 void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
2715 if (re->window == frame.maximize_button) {
2716 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2717 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2718 maximize(re->button);
2719 } else {
2720 redrawMaximizeButton(flags.maximized);
2721 }
2722 } else if (re->window == frame.iconify_button) {
2723 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2724 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2725 iconify();
2726 } else {
2727 redrawIconifyButton(False);
2728 }
2729 } else if (re->window == frame.close_button) {
2730 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2731 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2732 close();
2733 redrawCloseButton(False);
2734 } else if (flags.moving) {
2735 endMove();
2736 } else if (flags.resizing) {
2737 endResize();
2738 } else if (re->window == frame.window) {
2739 if (re->button == 2 && re->state == Mod1Mask)
2740 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2741 }
2742 }
2743
2744
2745 void BlackboxWindow::beginMove(int x_root, int y_root) {
2746 assert(! (flags.resizing || flags.moving));
2747
2748 /*
2749 Only one window can be moved/resized at a time. If another window is already
2750 being moved or resized, then stop it before whating to work with this one.
2751 */
2752 BlackboxWindow *changing = blackbox->getChangingWindow();
2753 if (changing && changing != this) {
2754 if (changing->flags.moving)
2755 changing->endMove();
2756 else // if (changing->flags.resizing)
2757 changing->endResize();
2758 }
2759
2760 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2761 PointerMotionMask | ButtonReleaseMask,
2762 GrabModeAsync, GrabModeAsync,
2763 None, blackbox->getMoveCursor(), CurrentTime);
2764
2765 if (windowmenu && windowmenu->isVisible())
2766 windowmenu->hide();
2767
2768 flags.moving = True;
2769 blackbox->setChangingWindow(this);
2770
2771 if (! screen->doOpaqueMove()) {
2772 XGrabServer(blackbox->getXDisplay());
2773
2774 frame.changing = frame.rect;
2775 screen->showPosition(frame.changing.x(), frame.changing.y());
2776
2777 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2778 screen->getOpGC(),
2779 frame.changing.x(),
2780 frame.changing.y(),
2781 frame.changing.width() - 1,
2782 frame.changing.height() - 1);
2783 }
2784
2785 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
2786 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
2787 }
2788
2789
2790 void BlackboxWindow::doMove(int x_root, int y_root) {
2791 assert(flags.moving);
2792 assert(blackbox->getChangingWindow() == this);
2793
2794 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
2795 dx -= frame.border_w;
2796 dy -= frame.border_w;
2797
2798 const int snap_distance = screen->getEdgeSnapThreshold();
2799
2800 if (snap_distance) {
2801 // window corners
2802 const int wleft = dx,
2803 wright = dx + frame.rect.width() - 1,
2804 wtop = dy,
2805 wbottom = dy + frame.rect.height() - 1;
2806
2807 if (screen->getWindowToWindowSnap()) {
2808 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2809 assert(w);
2810
2811 // try snap to another window
2812 for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2813 BlackboxWindow *snapwin = w->getWindow(i);
2814 if (snapwin == this)
2815 continue; // don't snap to self
2816
2817 bool snapped = False;
2818
2819 const Rect &winrect = snapwin->frameRect();
2820 int dleft = std::abs(wright - winrect.left()),
2821 dright = std::abs(wleft - winrect.right()),
2822 dtop = std::abs(wbottom - winrect.top()),
2823 dbottom = std::abs(wtop - winrect.bottom());
2824
2825 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2826 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2827
2828 // snap left of other window?
2829 if (dleft < snap_distance && dleft <= dright) {
2830 dx = winrect.left() - frame.rect.width();
2831 snapped = True;
2832 }
2833 // snap right of other window?
2834 else if (dright < snap_distance) {
2835 dx = winrect.right() + 1;
2836 snapped = True;
2837 }
2838
2839 if (snapped) {
2840 if (screen->getWindowCornerSnap()) {
2841 // try corner-snap to its other sides
2842 dtop = std::abs(wtop - winrect.top());
2843 dbottom = std::abs(wbottom - winrect.bottom());
2844 if (dtop < snap_distance && dtop <= dbottom)
2845 dy = winrect.top();
2846 else if (dbottom < snap_distance)
2847 dy = winrect.bottom() - frame.rect.height() + 1;
2848 }
2849
2850 continue;
2851 }
2852 }
2853
2854 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
2855 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
2856
2857 // snap top of other window?
2858 if (dtop < snap_distance && dtop <= dbottom) {
2859 dy = winrect.top() - frame.rect.height();
2860 snapped = True;
2861 }
2862 // snap bottom of other window?
2863 else if (dbottom < snap_distance) {
2864 dy = winrect.bottom() + 1;
2865 snapped = True;
2866 }
2867
2868 if (snapped) {
2869 if (screen->getWindowCornerSnap()) {
2870 // try corner-snap to its other sides
2871 dleft = std::abs(wleft - winrect.left());
2872 dright = std::abs(wright - winrect.right());
2873 if (dleft < snap_distance && dleft <= dright)
2874 dx = winrect.left();
2875 else if (dright < snap_distance)
2876 dx = winrect.right() - frame.rect.width() + 1;
2877 }
2878
2879 continue;
2880 }
2881 }
2882 }
2883 }
2884
2885 // try snap to the screen's available area
2886 Rect srect = screen->availableArea();
2887
2888 int dleft = std::abs(wleft - srect.left()),
2889 dright = std::abs(wright - srect.right()),
2890 dtop = std::abs(wtop - srect.top()),
2891 dbottom = std::abs(wbottom - srect.bottom());
2892
2893 // snap left?
2894 if (dleft < snap_distance && dleft < dright)
2895 dx = srect.left();
2896 // snap right?
2897 else if (dright < snap_distance && dright < dleft)
2898 dx = srect.right() - frame.rect.width() + 1;
2899
2900 // snap top?
2901 if (dtop < snap_distance && dtop < dbottom)
2902 dy = srect.top();
2903 // snap bottom?
2904 else if (dbottom < snap_distance && dbottom < dtop)
2905 dy = srect.bottom() - frame.rect.height() + 1;
2906
2907 srect = screen->getRect(); // now get the full screen
2908
2909 dleft = std::abs(wleft - srect.left()),
2910 dright = std::abs(wright - srect.right()),
2911 dtop = std::abs(wtop - srect.top()),
2912 dbottom = std::abs(wbottom - srect.bottom());
2913
2914 // snap left?
2915 if (dleft < snap_distance && dleft < dright)
2916 dx = srect.left();
2917 // snap right?
2918 else if (dright < snap_distance && dright < dleft)
2919 dx = srect.right() - frame.rect.width() + 1;
2920
2921 // snap top?
2922 if (dtop < snap_distance && dtop < dbottom)
2923 dy = srect.top();
2924 // snap bottom?
2925 else if (dbottom < snap_distance && dbottom < dtop)
2926 dy = srect.bottom() - frame.rect.height() + 1;
2927 }
2928
2929 if (screen->doOpaqueMove()) {
2930 configure(dx, dy, frame.rect.width(), frame.rect.height());
2931 } else {
2932 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2933 screen->getOpGC(),
2934 frame.changing.x(),
2935 frame.changing.y(),
2936 frame.changing.width() - 1,
2937 frame.changing.height() - 1);
2938
2939 frame.changing.setPos(dx, dy);
2940
2941 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2942 screen->getOpGC(),
2943 frame.changing.x(),
2944 frame.changing.y(),
2945 frame.changing.width() - 1,
2946 frame.changing.height() - 1);
2947 }
2948
2949 screen->showPosition(dx, dy);
2950 }
2951
2952
2953 void BlackboxWindow::endMove(void) {
2954 assert(flags.moving);
2955 assert(blackbox->getChangingWindow() == this);
2956
2957 flags.moving = False;
2958 blackbox->setChangingWindow(0);
2959
2960 if (! screen->doOpaqueMove()) {
2961 /* when drawing the rubber band, we need to make sure we only draw inside
2962 * the frame... frame.changing_* contain the new coords for the window,
2963 * so we need to subtract 1 from changing_w/changing_h every where we
2964 * draw the rubber band (for both moving and resizing)
2965 */
2966 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2967 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2968 frame.changing.width() - 1, frame.changing.height() - 1);
2969 XUngrabServer(blackbox->getXDisplay());
2970
2971 configure(frame.changing.x(), frame.changing.y(),
2972 frame.changing.width(), frame.changing.height());
2973 } else {
2974 configure(frame.rect.x(), frame.rect.y(),
2975 frame.rect.width(), frame.rect.height());
2976 }
2977 screen->hideGeometry();
2978
2979 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2980
2981 // if there are any left over motions from the move, drop them now
2982 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
2983 XEvent e;
2984 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
2985 MotionNotify, &e));
2986 }
2987
2988
2989 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
2990 assert(! (flags.resizing || flags.moving));
2991
2992 /*
2993 Only one window can be moved/resized at a time. If another window is already
2994 being moved or resized, then stop it before whating to work with this one.
2995 */
2996 BlackboxWindow *changing = blackbox->getChangingWindow();
2997 if (changing && changing != this) {
2998 if (changing->flags.moving)
2999 changing->endMove();
3000 else // if (changing->flags.resizing)
3001 changing->endResize();
3002 }
3003
3004 resize_dir = dir;
3005
3006 Cursor cursor;
3007 Corner anchor;
3008
3009 switch (resize_dir) {
3010 case BottomLeft:
3011 anchor = TopRight;
3012 cursor = blackbox->getLowerLeftAngleCursor();
3013 break;
3014
3015 case BottomRight:
3016 anchor = TopLeft;
3017 cursor = blackbox->getLowerRightAngleCursor();
3018 break;
3019
3020 case TopLeft:
3021 anchor = BottomRight;
3022 cursor = blackbox->getUpperLeftAngleCursor();
3023 break;
3024
3025 case TopRight:
3026 anchor = BottomLeft;
3027 cursor = blackbox->getUpperRightAngleCursor();
3028 break;
3029
3030 default:
3031 assert(false); // unhandled Corner
3032 }
3033
3034 XGrabServer(blackbox->getXDisplay());
3035 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3036 PointerMotionMask | ButtonReleaseMask,
3037 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3038
3039 flags.resizing = True;
3040 blackbox->setChangingWindow(this);
3041
3042 int gw, gh;
3043 frame.changing = frame.rect;
3044
3045 constrain(anchor, &gw, &gh);
3046
3047 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3048 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3049 frame.changing.width() - 1, frame.changing.height() - 1);
3050
3051 screen->showGeometry(gw, gh);
3052
3053 frame.grab_x = x_root;
3054 frame.grab_y = y_root;
3055 }
3056
3057
3058 void BlackboxWindow::doResize(int x_root, int y_root) {
3059 assert(flags.resizing);
3060 assert(blackbox->getChangingWindow() == this);
3061
3062 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3063 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3064 frame.changing.width() - 1, frame.changing.height() - 1);
3065
3066 int gw, gh;
3067 Corner anchor;
3068
3069 switch (resize_dir) {
3070 case BottomLeft:
3071 anchor = TopRight;
3072 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3073 frame.rect.height() + (y_root - frame.grab_y));
3074 break;
3075 case BottomRight:
3076 anchor = TopLeft;
3077 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3078 frame.rect.height() + (y_root - frame.grab_y));
3079 break;
3080 case TopLeft:
3081 anchor = BottomRight;
3082 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3083 frame.rect.height() - (y_root - frame.grab_y));
3084 break;
3085 case TopRight:
3086 anchor = BottomLeft;
3087 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3088 frame.rect.height() - (y_root - frame.grab_y));
3089 break;
3090
3091 default:
3092 assert(false); // unhandled Corner
3093 }
3094
3095 constrain(anchor, &gw, &gh);
3096
3097 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3098 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3099 frame.changing.width() - 1, frame.changing.height() - 1);
3100
3101 screen->showGeometry(gw, gh);
3102 }
3103
3104
3105 void BlackboxWindow::endResize(void) {
3106 assert(flags.resizing);
3107 assert(blackbox->getChangingWindow() == this);
3108
3109 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3110 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3111 frame.changing.width() - 1, frame.changing.height() - 1);
3112 XUngrabServer(blackbox->getXDisplay());
3113
3114 // unset maximized state after resized when fully maximized
3115 if (flags.maximized == 1)
3116 maximize(0);
3117
3118 flags.resizing = False;
3119 blackbox->setChangingWindow(0);
3120
3121 configure(frame.changing.x(), frame.changing.y(),
3122 frame.changing.width(), frame.changing.height());
3123 screen->hideGeometry();
3124
3125 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3126
3127 // if there are any left over motions from the resize, drop them now
3128 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3129 XEvent e;
3130 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3131 MotionNotify, &e));
3132 }
3133
3134
3135 void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
3136 if (flags.moving) {
3137 doMove(me->x_root, me->y_root);
3138 } else if (flags.resizing) {
3139 doResize(me->x_root, me->y_root);
3140 } else {
3141 if (! flags.resizing && (me->state & Button1Mask) &&
3142 (functions & Func_Move) &&
3143 (frame.title == me->window || frame.label == me->window ||
3144 frame.handle == me->window || frame.window == me->window)) {
3145 beginMove(me->x_root, me->y_root);
3146 } else if ((functions & Func_Resize) &&
3147 (((me->state & Button1Mask) &&
3148 (me->window == frame.right_grip ||
3149 me->window == frame.left_grip)) ||
3150 (me->state & (Mod1Mask | Button3Mask) &&
3151 me->window == frame.window))) {
3152 beginResize(me->x_root, me->y_root,
3153 (me->window == frame.left_grip) ? BottomLeft : BottomRight);
3154 }
3155 }
3156 }
3157
3158
3159 #ifdef SHAPE
3160 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3161 if (blackbox->hasShapeExtensions() && flags.shaped) {
3162 configureShape();
3163 }
3164 }
3165 #endif // SHAPE
3166
3167
3168 bool BlackboxWindow::validateClient(void) const {
3169 XSync(blackbox->getXDisplay(), False);
3170
3171 XEvent e;
3172 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3173 DestroyNotify, &e) ||
3174 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3175 UnmapNotify, &e)) {
3176 XPutBackEvent(blackbox->getXDisplay(), &e);
3177
3178 return False;
3179 }
3180
3181 return True;
3182 }
3183
3184
3185 void BlackboxWindow::restore(bool remap) {
3186 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3187 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3188 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3189
3190 restoreGravity();
3191
3192 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3193 XUnmapWindow(blackbox->getXDisplay(), client.window);
3194
3195 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3196
3197 XEvent ev;
3198 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3199 ReparentNotify, &ev)) {
3200 remap = True;
3201 } else {
3202 // according to the ICCCM - if the client doesn't reparent to
3203 // root, then we have to do it for them
3204 XReparentWindow(blackbox->getXDisplay(), client.window,
3205 screen->getRootWindow(),
3206 client.rect.x(), client.rect.y());
3207 }
3208
3209 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3210 }
3211
3212
3213 // timer for autoraise
3214 void BlackboxWindow::timeout(void) {
3215 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3216 }
3217
3218
3219 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3220 if ((net->flags & AttribShaded) &&
3221 ((blackbox_attrib.attrib & AttribShaded) !=
3222 (net->attrib & AttribShaded)))
3223 shade();
3224
3225 if (flags.visible && // watch out for requests when we can not be seen
3226 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3227 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3228 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3229 if (flags.maximized) {
3230 maximize(0);
3231 } else {
3232 int button = 0;
3233
3234 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3235 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3236 else if (net->flags & AttribMaxVert)
3237 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3238 else if (net->flags & AttribMaxHoriz)
3239 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3240
3241 maximize(button);
3242 }
3243 }
3244
3245 if ((net->flags & AttribOmnipresent) &&
3246 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3247 (net->attrib & AttribOmnipresent)))
3248 stick();
3249
3250 if ((net->flags & AttribWorkspace) &&
3251 (blackbox_attrib.workspace != net->workspace)) {
3252 screen->reassociateWindow(this, net->workspace, True);
3253
3254 if (screen->getCurrentWorkspaceID() != net->workspace) {
3255 withdraw();
3256 } else {
3257 show();
3258 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3259 }
3260 }
3261
3262 if (net->flags & AttribDecoration) {
3263 switch (net->decoration) {
3264 case DecorNone:
3265 // clear all decorations except close
3266 decorations &= Decor_Close;
3267
3268 break;
3269
3270 default:
3271 case DecorNormal:
3272 decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3273
3274 decorations = ((functions & Func_Resize) && !isTransient() ?
3275 decorations | Decor_Handle :
3276 decorations &= ~Decor_Handle);
3277 decorations = (functions & Func_Maximize ?
3278 decorations | Decor_Maximize :
3279 decorations &= ~Decor_Maximize);
3280
3281 break;
3282
3283 case DecorTiny:
3284 decorations |= Decor_Titlebar | Decor_Iconify;
3285 decorations &= ~(Decor_Border | Decor_Handle);
3286
3287 decorations = (functions & Func_Maximize ?
3288 decorations | Decor_Maximize :
3289 decorations &= ~Decor_Maximize);
3290
3291 break;
3292
3293 case DecorTool:
3294 decorations |= Decor_Titlebar;
3295 decorations &= ~(Decor_Iconify | Decor_Border);
3296
3297 decorations = ((functions & Func_Resize) && !isTransient() ?
3298 decorations | Decor_Handle :
3299 decorations &= ~Decor_Handle);
3300 decorations = (functions & Func_Maximize ?
3301 decorations | Decor_Maximize :
3302 decorations &= ~Decor_Maximize);
3303
3304 break;
3305 }
3306
3307 // we can not be shaded if we lack a titlebar
3308 if (flags.shaded && ! (decorations & Decor_Titlebar))
3309 shade();
3310
3311 if (frame.window) {
3312 XMapSubwindows(blackbox->getXDisplay(), frame.window);
3313 XMapWindow(blackbox->getXDisplay(), frame.window);
3314 }
3315
3316 reconfigure();
3317 setState(current_state);
3318 }
3319 }
3320
3321
3322 /*
3323 * Set the sizes of all components of the window frame
3324 * (the window decorations).
3325 * These values are based upon the current style settings and the client
3326 * window's dimensions.
3327 */
3328 void BlackboxWindow::upsize(void) {
3329 frame.bevel_w = screen->getBevelWidth();
3330
3331 if (decorations & Decor_Border) {
3332 frame.border_w = screen->getBorderWidth();
3333 if (! isTransient())
3334 frame.mwm_border_w = screen->getFrameWidth();
3335 else
3336 frame.mwm_border_w = 0;
3337 } else {
3338 frame.mwm_border_w = frame.border_w = 0;
3339 }
3340
3341 if (decorations & Decor_Titlebar) {
3342 // the height of the titlebar is based upon the height of the font being
3343 // used to display the window's title
3344 WindowStyle *style = screen->getWindowStyle();
3345 if (i18n.multibyte())
3346 frame.title_h = (style->fontset_extents->max_ink_extent.height +
3347 (frame.bevel_w * 2) + 2);
3348 else
3349 frame.title_h = (style->font->ascent + style->font->descent +
3350 (frame.bevel_w * 2) + 2);
3351
3352 frame.label_h = frame.title_h - (frame.bevel_w * 2);
3353 frame.button_w = (frame.label_h - 2);
3354
3355 // set the top frame margin
3356 frame.margin.top = frame.border_w + frame.title_h +
3357 frame.border_w + frame.mwm_border_w;
3358 } else {
3359 frame.title_h = 0;
3360 frame.label_h = 0;
3361 frame.button_w = 0;
3362
3363 // set the top frame margin
3364 frame.margin.top = frame.border_w + frame.mwm_border_w;
3365 }
3366
3367 // set the left/right frame margin
3368 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3369
3370 if (decorations & Decor_Handle) {
3371 frame.grip_w = frame.button_w * 2;
3372 frame.handle_h = screen->getHandleWidth();
3373
3374 // set the bottom frame margin
3375 frame.margin.bottom = frame.border_w + frame.handle_h +
3376 frame.border_w + frame.mwm_border_w;
3377 } else {
3378 frame.handle_h = 0;
3379 frame.grip_w = 0;
3380
3381 // set the bottom frame margin
3382 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3383 }
3384
3385 /*
3386 We first get the normal dimensions and use this to define the inside_w/h
3387 then we modify the height if shading is in effect.
3388 If the shade state is not considered then frame.rect gets reset to the
3389 normal window size on a reconfigure() call resulting in improper
3390 dimensions appearing in move/resize and other events.
3391 */
3392 unsigned int
3393 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3394 width = client.rect.width() + frame.margin.left + frame.margin.right;
3395
3396 frame.inside_w = width - (frame.border_w * 2);
3397 frame.inside_h = height - (frame.border_w * 2);
3398
3399 if (flags.shaded)
3400 height = frame.title_h + (frame.border_w * 2);
3401 frame.rect.setSize(width, height);
3402 }
3403
3404
3405 /*
3406 * Calculate the size of the client window and constrain it to the
3407 * size specified by the size hints of the client window.
3408 *
3409 * The logical width and height are placed into pw and ph, if they
3410 * are non-zero. Logical size refers to the users perception of
3411 * the window size (for example an xterm resizes in cells, not in pixels).
3412 *
3413 * The physical geometry is placed into frame.changing_{x,y,width,height}.
3414 * Physical geometry refers to the geometry of the window in pixels.
3415 */
3416 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3417 // frame.changing represents the requested frame size, we need to
3418 // strip the frame margin off and constrain the client size
3419 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3420 frame.changing.top() + frame.margin.top,
3421 frame.changing.right() - frame.margin.right,
3422 frame.changing.bottom() - frame.margin.bottom);
3423
3424 int dw = frame.changing.width(), dh = frame.changing.height(),
3425 base_width = (client.base_width) ? client.base_width : client.min_width,
3426 base_height = (client.base_height) ? client.base_height :
3427 client.min_height;
3428
3429 // constrain
3430 if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3431 if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3432 if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3433 if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3434
3435 dw -= base_width;
3436 dw /= client.width_inc;
3437 dh -= base_height;
3438 dh /= client.height_inc;
3439
3440 if (pw) *pw = dw;
3441 if (ph) *ph = dh;
3442
3443 dw *= client.width_inc;
3444 dw += base_width;
3445 dh *= client.height_inc;
3446 dh += base_height;
3447
3448 frame.changing.setSize(dw, dh);
3449
3450 // add the frame margin back onto frame.changing
3451 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3452 frame.changing.top() - frame.margin.top,
3453 frame.changing.right() + frame.margin.right,
3454 frame.changing.bottom() + frame.margin.bottom);
3455
3456 // move frame.changing to the specified anchor
3457 int dx = 0,
3458 dy = 0;
3459 switch (anchor) {
3460 case TopLeft:
3461 break;
3462
3463 case TopRight:
3464 dx = frame.rect.right() - frame.changing.right();
3465 break;
3466
3467 case BottomLeft:
3468 dy = frame.rect.bottom() - frame.changing.bottom();
3469 break;
3470
3471 case BottomRight:
3472 dx = frame.rect.right() - frame.changing.right();
3473 dy = frame.rect.bottom() - frame.changing.bottom();
3474 break;
3475
3476 default:
3477 assert(false); // unhandled corner
3478 }
3479 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3480 }
3481
3482
3483 int WindowStyle::doJustify(const char *text, int &start_pos,
3484 unsigned int max_length, unsigned int modifier,
3485 bool multibyte) const {
3486 size_t text_len = strlen(text);
3487 unsigned int length;
3488
3489 do {
3490 if (multibyte) {
3491 XRectangle ink, logical;
3492 XmbTextExtents(fontset, text, text_len, &ink, &logical);
3493 length = logical.width;
3494 } else {
3495 length = XTextWidth(font, text, text_len);
3496 }
3497 length += modifier;
3498 } while (length > max_length && text_len-- > 0);
3499
3500 switch (justify) {
3501 case RightJustify:
3502 start_pos += max_length - length;
3503 break;
3504
3505 case CenterJustify:
3506 start_pos += (max_length - length) / 2;
3507 break;
3508
3509 case LeftJustify:
3510 default:
3511 break;
3512 }
3513
3514 return text_len;
3515 }
3516
3517
3518 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3519 : blackbox(b), group(_group) {
3520 XWindowAttributes wattrib;
3521 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3522 // group window doesn't seem to exist anymore
3523 delete this;
3524 return;
3525 }
3526
3527 /*
3528 watch for destroy notify on the group window (in addition to
3529 any other events we are looking for)
3530
3531 since some managed windows can also be window group controllers,
3532 we need to make sure that we don't clobber the event mask for the
3533 managed window
3534 */
3535 XSelectInput(blackbox->getXDisplay(), group,
3536 wattrib.your_event_mask | StructureNotifyMask);
3537
3538 blackbox->saveGroupSearch(group, this);
3539 }
3540
3541
3542 BWindowGroup::~BWindowGroup(void) {
3543 blackbox->removeGroupSearch(group);
3544 }
3545
3546
3547 BlackboxWindow *
3548 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3549 BlackboxWindow *ret = blackbox->getFocusedWindow();
3550
3551 // does the focus window match (or any transient_fors)?
3552 while (ret) {
3553 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3554 if (ret->isTransient() && allow_transients) break;
3555 else if (! ret->isTransient()) break;
3556 }
3557
3558 ret = ret->getTransientFor();
3559 }
3560
3561 if (ret) return ret;
3562
3563 // the focus window didn't match, look in the group's window list
3564 BlackboxWindowList::const_iterator it, end = windowList.end();
3565 for (it = windowList.begin(); it != end; ++it) {
3566 ret = *it;
3567 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3568 if (ret->isTransient() && allow_transients) break;
3569 else if (! ret->isTransient()) break;
3570 }
3571 }
3572
3573 return ret;
3574 }
This page took 0.208827 seconds and 5 git commands to generate.