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