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