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