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