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