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