]> Dogcows Code - chaz/openbox/blob - src/Window.cc
08cad56db8c7ca10b87810353d07f203a9fdf51b
[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 // set no limit to how big a window can be by default
1222 client.max_width = (unsigned) -1;
1223 client.max_height = (unsigned) -1;
1224
1225 if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1226 &sizehint, &icccm_mask))
1227 return;
1228
1229 client.normal_hint_flags = sizehint.flags;
1230
1231 if (sizehint.flags & PMinSize) {
1232 if (sizehint.min_width >= 0)
1233 client.min_width = sizehint.min_width;
1234 if (sizehint.min_height >= 0)
1235 client.min_height = sizehint.min_height;
1236 }
1237
1238 if (sizehint.flags & PMaxSize) {
1239 if (sizehint.max_width > static_cast<signed>(client.min_width))
1240 client.max_width = sizehint.max_width;
1241 else
1242 client.max_width = client.min_width;
1243
1244 if (sizehint.max_height > static_cast<signed>(client.min_height))
1245 client.max_height = sizehint.max_height;
1246 else
1247 client.max_height = client.min_height;
1248 }
1249
1250 if (sizehint.flags & PResizeInc) {
1251 client.width_inc = sizehint.width_inc;
1252 client.height_inc = sizehint.height_inc;
1253 }
1254
1255 #if 0 // we do not support this at the moment
1256 if (sizehint.flags & PAspect) {
1257 client.min_aspect_x = sizehint.min_aspect.x;
1258 client.min_aspect_y = sizehint.min_aspect.y;
1259 client.max_aspect_x = sizehint.max_aspect.x;
1260 client.max_aspect_y = sizehint.max_aspect.y;
1261 }
1262 #endif
1263
1264 if (sizehint.flags & PBaseSize) {
1265 client.base_width = sizehint.base_width;
1266 client.base_height = sizehint.base_height;
1267 }
1268
1269 if (sizehint.flags & PWinGravity)
1270 client.win_gravity = sizehint.win_gravity;
1271 }
1272
1273
1274 /*
1275 * Gets the NETWM hints for the class' contained window.
1276 */
1277 void BlackboxWindow::getNetWMHints(void) {
1278 unsigned long workspace;
1279
1280 if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1281 workspace)) {
1282 if (workspace == 0xffffffff)
1283 flags.stuck = True;
1284 else
1285 blackbox_attrib.workspace = workspace;
1286 }
1287
1288 unsigned long *state;
1289 unsigned long num = (unsigned) -1;
1290 if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1291 num, &state)) {
1292 bool vert = False,
1293 horz = False;
1294 for (unsigned long i = 0; i < num; ++i) {
1295 if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1296 flags.modal = True;
1297 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1298 flags.shaded = True;
1299 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1300 flags.skip_taskbar = True;
1301 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1302 flags.skip_pager = True;
1303 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1304 flags.fullscreen = True;
1305 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1306 setState(IconicState);
1307 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1308 vert = True;
1309 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1310 horz = True;
1311 }
1312 if (vert && horz)
1313 flags.maximized = 1;
1314 else if (vert)
1315 flags.maximized = 2;
1316 else if (horz)
1317 flags.maximized = 3;
1318
1319 delete [] state;
1320 }
1321 }
1322
1323
1324 /*
1325 * Gets the MWM hints for the class' contained window.
1326 * This is used while initializing the window to its first state, and not
1327 * thereafter.
1328 * Returns: true if the MWM hints are successfully retreived and applied;
1329 * false if they are not.
1330 */
1331 void BlackboxWindow::getMWMHints(void) {
1332 unsigned long num;
1333 MwmHints *mwm_hint;
1334
1335 num = PropMwmHintsElements;
1336 if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1337 XAtom::motif_wm_hints, num,
1338 (unsigned long **)&mwm_hint))
1339 return;
1340 if (num < PropMwmHintsElements) {
1341 delete [] mwm_hint;
1342 return;
1343 }
1344
1345 if (mwm_hint->flags & MwmHintsDecorations) {
1346 if (mwm_hint->decorations & MwmDecorAll) {
1347 mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1348 Decor_Iconify | Decor_Maximize;
1349 } else {
1350 mwm_decorations = 0;
1351
1352 if (mwm_hint->decorations & MwmDecorBorder)
1353 mwm_decorations |= Decor_Border;
1354 if (mwm_hint->decorations & MwmDecorHandle)
1355 mwm_decorations |= Decor_Handle;
1356 if (mwm_hint->decorations & MwmDecorTitle)
1357 mwm_decorations |= Decor_Titlebar;
1358 if (mwm_hint->decorations & MwmDecorIconify)
1359 mwm_decorations |= Decor_Iconify;
1360 if (mwm_hint->decorations & MwmDecorMaximize)
1361 mwm_decorations |= Decor_Maximize;
1362 }
1363 }
1364
1365 if (mwm_hint->flags & MwmHintsFunctions) {
1366 if (mwm_hint->functions & MwmFuncAll) {
1367 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1368 Func_Close;
1369 } else {
1370 functions = 0;
1371
1372 if (mwm_hint->functions & MwmFuncResize)
1373 functions |= Func_Resize;
1374 if (mwm_hint->functions & MwmFuncMove)
1375 functions |= Func_Move;
1376 if (mwm_hint->functions & MwmFuncIconify)
1377 functions |= Func_Iconify;
1378 if (mwm_hint->functions & MwmFuncMaximize)
1379 functions |= Func_Maximize;
1380 if (mwm_hint->functions & MwmFuncClose)
1381 functions |= Func_Close;
1382 }
1383 }
1384 delete [] mwm_hint;
1385 }
1386
1387
1388 /*
1389 * Gets the blackbox hints from the class' contained window.
1390 * This is used while initializing the window to its first state, and not
1391 * thereafter.
1392 * Returns: true if the hints are successfully retreived and applied; false if
1393 * they are not.
1394 */
1395 bool BlackboxWindow::getBlackboxHints(void) {
1396 unsigned long num;
1397 BlackboxHints *blackbox_hint;
1398
1399 num = PropBlackboxHintsElements;
1400 if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1401 XAtom::blackbox_hints, num,
1402 (unsigned long **)&blackbox_hint))
1403 return False;
1404 if (num < PropBlackboxHintsElements) {
1405 delete [] blackbox_hint;
1406 return False;
1407 }
1408
1409 if (blackbox_hint->flags & AttribShaded)
1410 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1411
1412 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1413 (blackbox_hint->flags & AttribMaxVert))
1414 flags.maximized = (blackbox_hint->attrib &
1415 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1416 else if (blackbox_hint->flags & AttribMaxVert)
1417 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1418 else if (blackbox_hint->flags & AttribMaxHoriz)
1419 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1420
1421 if (blackbox_hint->flags & AttribOmnipresent)
1422 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1423
1424 if (blackbox_hint->flags & AttribWorkspace)
1425 blackbox_attrib.workspace = blackbox_hint->workspace;
1426
1427 // if (blackbox_hint->flags & AttribStack)
1428 // don't yet have always on top/bottom for blackbox yet... working
1429 // on that
1430
1431 if (blackbox_hint->flags & AttribDecoration) {
1432 switch (blackbox_hint->decoration) {
1433 case DecorNone:
1434 blackbox_attrib.decoration = DecorNone;
1435 break;
1436
1437 case DecorTiny:
1438 case DecorTool:
1439 case DecorNormal:
1440 default:
1441 // blackbox_attrib.decoration defaults to DecorNormal
1442 break;
1443 }
1444 }
1445
1446 delete [] blackbox_hint;
1447
1448 return True;
1449 }
1450
1451
1452 void BlackboxWindow::getTransientInfo(void) {
1453 if (client.transient_for &&
1454 client.transient_for != (BlackboxWindow *) ~0ul) {
1455 // reset transient_for in preparation of looking for a new owner
1456 client.transient_for->client.transientList.remove(this);
1457 }
1458
1459 // we have no transient_for until we find a new one
1460 client.transient_for = (BlackboxWindow *) 0;
1461
1462 Window trans_for;
1463 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1464 &trans_for)) {
1465 // transient_for hint not set
1466 return;
1467 }
1468
1469 if (trans_for == client.window) {
1470 // wierd client... treat this window as a normal window
1471 return;
1472 }
1473
1474 if (trans_for == None || trans_for == screen->getRootWindow()) {
1475 // this is an undocumented interpretation of the ICCCM. a transient
1476 // associated with None/Root/itself is assumed to be a modal root
1477 // transient. we don't support the concept of a global transient,
1478 // so we just associate this transient with nothing, and perhaps
1479 // we will add support later for global modality.
1480 client.transient_for = (BlackboxWindow *) ~0ul;
1481 flags.modal = True;
1482 return;
1483 }
1484
1485 client.transient_for = blackbox->searchWindow(trans_for);
1486 if (! client.transient_for &&
1487 client.window_group && trans_for == client.window_group) {
1488 // no direct transient_for, perhaps this is a group transient?
1489 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1490 if (group) client.transient_for = group->find(screen);
1491 }
1492
1493 if (! client.transient_for || client.transient_for == this) {
1494 // no transient_for found, or we have a wierd client that wants to be
1495 // a transient for itself, so we treat this window as a normal window
1496 client.transient_for = (BlackboxWindow*) 0;
1497 return;
1498 }
1499
1500 // Check for a circular transient state: this can lock up Blackbox
1501 // when it tries to find the non-transient window for a transient.
1502 BlackboxWindow *w = this;
1503 while(w->client.transient_for &&
1504 w->client.transient_for != (BlackboxWindow *) ~0ul) {
1505 if(w->client.transient_for == this) {
1506 client.transient_for = (BlackboxWindow*) 0;
1507 break;
1508 }
1509 w = w->client.transient_for;
1510 }
1511
1512 if (client.transient_for &&
1513 client.transient_for != (BlackboxWindow *) ~0ul) {
1514 // register ourselves with our new transient_for
1515 client.transient_for->client.transientList.push_back(this);
1516 flags.stuck = client.transient_for->flags.stuck;
1517 }
1518 }
1519
1520
1521 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1522 if (client.transient_for &&
1523 client.transient_for != (BlackboxWindow*) ~0ul)
1524 return client.transient_for;
1525 return 0;
1526 }
1527
1528
1529 /*
1530 * This function is responsible for updating both the client and the frame
1531 * rectangles.
1532 * According to the ICCCM a client message is not sent for a resize, only a
1533 * move.
1534 */
1535 void BlackboxWindow::configure(int dx, int dy,
1536 unsigned int dw, unsigned int dh) {
1537 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1538 ! flags.moving);
1539
1540 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1541 frame.rect.setRect(dx, dy, dw, dh);
1542 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1543 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1544
1545 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1546 frame.rect.setPos(0, 0);
1547
1548 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1549 frame.rect.top() + frame.margin.top,
1550 frame.rect.right() - frame.margin.right,
1551 frame.rect.bottom() - frame.margin.bottom);
1552
1553 #ifdef SHAPE
1554 if (blackbox->hasShapeExtensions() && flags.shaped) {
1555 configureShape();
1556 }
1557 #endif // SHAPE
1558
1559 positionWindows();
1560 decorate();
1561 redrawWindowFrame();
1562 } else {
1563 frame.rect.setPos(dx, dy);
1564
1565 XMoveWindow(blackbox->getXDisplay(), frame.window,
1566 frame.rect.x(), frame.rect.y());
1567 /*
1568 we may have been called just after an opaque window move, so even though
1569 the old coords match the new ones no ConfigureNotify has been sent yet.
1570 There are likely other times when this will be relevant as well.
1571 */
1572 if (! flags.moving) send_event = True;
1573 }
1574
1575 if (send_event) {
1576 // if moving, the update and event will occur when the move finishes
1577 client.rect.setPos(frame.rect.left() + frame.margin.left,
1578 frame.rect.top() + frame.margin.top);
1579
1580 XEvent event;
1581 event.type = ConfigureNotify;
1582
1583 event.xconfigure.display = blackbox->getXDisplay();
1584 event.xconfigure.event = client.window;
1585 event.xconfigure.window = client.window;
1586 event.xconfigure.x = client.rect.x();
1587 event.xconfigure.y = client.rect.y();
1588 event.xconfigure.width = client.rect.width();
1589 event.xconfigure.height = client.rect.height();
1590 event.xconfigure.border_width = client.old_bw;
1591 event.xconfigure.above = frame.window;
1592 event.xconfigure.override_redirect = False;
1593
1594 XSendEvent(blackbox->getXDisplay(), client.window, False,
1595 StructureNotifyMask, &event);
1596 screen->updateNetizenConfigNotify(&event);
1597 XFlush(blackbox->getXDisplay());
1598 }
1599 }
1600
1601
1602 #ifdef SHAPE
1603 void BlackboxWindow::configureShape(void) {
1604 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1605 frame.margin.left - frame.border_w,
1606 frame.margin.top - frame.border_w,
1607 client.window, ShapeBounding, ShapeSet);
1608
1609 int num = 0;
1610 XRectangle xrect[2];
1611
1612 if (decorations & Decor_Titlebar) {
1613 xrect[0].x = xrect[0].y = -frame.border_w;
1614 xrect[0].width = frame.rect.width();
1615 xrect[0].height = frame.title_h + (frame.border_w * 2);
1616 ++num;
1617 }
1618
1619 if (decorations & Decor_Handle) {
1620 xrect[1].x = -frame.border_w;
1621 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1622 frame.mwm_border_w - frame.border_w;
1623 xrect[1].width = frame.rect.width();
1624 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1625 ++num;
1626 }
1627
1628 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1629 ShapeBounding, 0, 0, xrect, num,
1630 ShapeUnion, Unsorted);
1631 }
1632 #endif // SHAPE
1633
1634
1635 bool BlackboxWindow::setInputFocus(void) {
1636 if (flags.focused) return True;
1637
1638 assert(flags.stuck || // window must be on the current workspace or sticky
1639 blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1640
1641 /*
1642 We only do this check for normal windows and dialogs because other windows
1643 do this on purpose, such as kde's kicker, and we don't want to go moving
1644 it.
1645 */
1646 if (window_type == Type_Normal || window_type == Type_Dialog)
1647 if (! frame.rect.intersects(screen->getRect())) {
1648 // client is outside the screen, move it to the center
1649 configure((screen->getWidth() - frame.rect.width()) / 2,
1650 (screen->getHeight() - frame.rect.height()) / 2,
1651 frame.rect.width(), frame.rect.height());
1652 }
1653
1654 if (client.transientList.size() > 0) {
1655 // transfer focus to any modal transients
1656 BlackboxWindowList::iterator it, end = client.transientList.end();
1657 for (it = client.transientList.begin(); it != end; ++it)
1658 if ((*it)->flags.modal) return (*it)->setInputFocus();
1659 }
1660
1661 bool ret = True;
1662 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1663 XSetInputFocus(blackbox->getXDisplay(), client.window,
1664 RevertToPointerRoot, CurrentTime);
1665 } else {
1666 /* we could set the focus to none, since the window doesn't accept focus,
1667 * but we shouldn't set focus to nothing since this would surely make
1668 * someone angry
1669 */
1670 ret = False;
1671 }
1672
1673 if (flags.send_focus_message) {
1674 XEvent ce;
1675 ce.xclient.type = ClientMessage;
1676 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1677 ce.xclient.display = blackbox->getXDisplay();
1678 ce.xclient.window = client.window;
1679 ce.xclient.format = 32;
1680 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1681 ce.xclient.data.l[1] = blackbox->getLastTime();
1682 ce.xclient.data.l[2] = 0l;
1683 ce.xclient.data.l[3] = 0l;
1684 ce.xclient.data.l[4] = 0l;
1685 XSendEvent(blackbox->getXDisplay(), client.window, False,
1686 NoEventMask, &ce);
1687 XFlush(blackbox->getXDisplay());
1688 }
1689
1690 return ret;
1691 }
1692
1693
1694 void BlackboxWindow::iconify(void) {
1695 if (flags.iconic || ! (functions & Func_Iconify)) return;
1696
1697 // We don't need to worry about resizing because resizing always grabs the X
1698 // server. This should only ever happen if using opaque moving.
1699 if (flags.moving)
1700 endMove();
1701
1702 if (windowmenu) windowmenu->hide();
1703
1704 /*
1705 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1706 * we need to clear the event mask on client.window for a split second.
1707 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1708 * split second, leaving us with a ghost window... so, we need to do this
1709 * while the X server is grabbed
1710 */
1711 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1712 StructureNotifyMask;
1713 XGrabServer(blackbox->getXDisplay());
1714 XSelectInput(blackbox->getXDisplay(), client.window,
1715 event_mask & ~StructureNotifyMask);
1716 XUnmapWindow(blackbox->getXDisplay(), client.window);
1717 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1718 XUngrabServer(blackbox->getXDisplay());
1719
1720 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1721 flags.visible = False;
1722 flags.iconic = True;
1723
1724 setState(IconicState);
1725
1726 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1727 if (flags.stuck) {
1728 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1729 if (i != blackbox_attrib.workspace)
1730 screen->getWorkspace(i)->removeWindow(this, True);
1731 }
1732
1733 if (isTransient()) {
1734 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1735 ! client.transient_for->flags.iconic) {
1736 // iconify our transient_for
1737 client.transient_for->iconify();
1738 }
1739 }
1740
1741 screen->addIcon(this);
1742
1743 if (client.transientList.size() > 0) {
1744 // iconify all transients
1745 BlackboxWindowList::iterator it, end = client.transientList.end();
1746 for (it = client.transientList.begin(); it != end; ++it) {
1747 if (! (*it)->flags.iconic) (*it)->iconify();
1748 }
1749 }
1750 screen->updateStackingList();
1751 }
1752
1753
1754 void BlackboxWindow::show(void) {
1755 flags.visible = True;
1756 flags.iconic = False;
1757
1758 current_state = (flags.shaded) ? IconicState : NormalState;
1759 setState(current_state);
1760
1761 XMapWindow(blackbox->getXDisplay(), client.window);
1762 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1763 XMapWindow(blackbox->getXDisplay(), frame.window);
1764
1765 #if 0
1766 int real_x, real_y;
1767 Window child;
1768 XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1769 screen->getRootWindow(),
1770 0, 0, &real_x, &real_y, &child);
1771 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1772 client.rect.left(), client.rect.top(), real_x, real_y);
1773 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1774 #endif
1775 }
1776
1777
1778 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1779 if (flags.iconic || reassoc)
1780 screen->reassociateWindow(this, BSENTINEL, False);
1781 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1782 return;
1783
1784 show();
1785
1786 // reassociate and deiconify all transients
1787 if (reassoc && client.transientList.size() > 0) {
1788 BlackboxWindowList::iterator it, end = client.transientList.end();
1789 for (it = client.transientList.begin(); it != end; ++it)
1790 (*it)->deiconify(True, False);
1791 }
1792
1793 if (raise)
1794 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1795 }
1796
1797
1798 void BlackboxWindow::close(void) {
1799 if (! (functions & Func_Close)) return;
1800
1801 XEvent ce;
1802 ce.xclient.type = ClientMessage;
1803 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1804 ce.xclient.display = blackbox->getXDisplay();
1805 ce.xclient.window = client.window;
1806 ce.xclient.format = 32;
1807 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1808 ce.xclient.data.l[1] = CurrentTime;
1809 ce.xclient.data.l[2] = 0l;
1810 ce.xclient.data.l[3] = 0l;
1811 ce.xclient.data.l[4] = 0l;
1812 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1813 XFlush(blackbox->getXDisplay());
1814 }
1815
1816
1817 void BlackboxWindow::withdraw(void) {
1818 // We don't need to worry about resizing because resizing always grabs the X
1819 // server. This should only ever happen if using opaque moving.
1820 if (flags.moving)
1821 endMove();
1822
1823 flags.visible = False;
1824 flags.iconic = False;
1825
1826 setState(current_state);
1827
1828 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1829
1830 XGrabServer(blackbox->getXDisplay());
1831
1832 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1833 StructureNotifyMask;
1834 XSelectInput(blackbox->getXDisplay(), client.window,
1835 event_mask & ~StructureNotifyMask);
1836 XUnmapWindow(blackbox->getXDisplay(), client.window);
1837 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1838
1839 XUngrabServer(blackbox->getXDisplay());
1840
1841 if (windowmenu) windowmenu->hide();
1842 }
1843
1844
1845 void BlackboxWindow::maximize(unsigned int button) {
1846 if (! (functions & Func_Maximize)) return;
1847
1848 // We don't need to worry about resizing because resizing always grabs the X
1849 // server. This should only ever happen if using opaque moving.
1850 if (flags.moving)
1851 endMove();
1852
1853 // handle case where menu is open then the max button is used instead
1854 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1855
1856 if (flags.maximized) {
1857 flags.maximized = 0;
1858
1859 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1860 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1861
1862 /*
1863 when a resize finishes, maximize(0) is called to clear any maximization
1864 flags currently set. Otherwise it still thinks it is maximized.
1865 so we do not need to call configure() because resizing will handle it
1866 */
1867 if (! flags.resizing)
1868 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1869 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1870
1871 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1872 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1873
1874 redrawAllButtons(); // in case it is not called in configure()
1875 setState(current_state);
1876 return;
1877 }
1878
1879 blackbox_attrib.premax_x = frame.rect.x();
1880 blackbox_attrib.premax_y = frame.rect.y();
1881 blackbox_attrib.premax_w = frame.rect.width();
1882 // use client.rect so that clients can be restored even if shaded
1883 blackbox_attrib.premax_h =
1884 client.rect.height() + frame.margin.top + frame.margin.bottom;
1885
1886 #ifdef XINERAMA
1887 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1888 // find the area to use
1889 RectList availableAreas = screen->allAvailableAreas();
1890 RectList::iterator it, end = availableAreas.end();
1891
1892 for (it = availableAreas.begin(); it != end; ++it)
1893 if (it->intersects(frame.rect)) break;
1894 if (it == end) // the window isn't inside an area
1895 it = availableAreas.begin(); // so just default to the first one
1896
1897 frame.changing = *it;
1898 } else
1899 #endif // XINERAMA
1900 frame.changing = screen->availableArea();
1901
1902 switch(button) {
1903 case 1:
1904 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1905 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1906 break;
1907
1908 case 2:
1909 blackbox_attrib.flags |= AttribMaxVert;
1910 blackbox_attrib.attrib |= AttribMaxVert;
1911
1912 frame.changing.setX(frame.rect.x());
1913 frame.changing.setWidth(frame.rect.width());
1914 break;
1915
1916 case 3:
1917 blackbox_attrib.flags |= AttribMaxHoriz;
1918 blackbox_attrib.attrib |= AttribMaxHoriz;
1919
1920 frame.changing.setY(frame.rect.y());
1921 frame.changing.setHeight(frame.rect.height());
1922 break;
1923 }
1924
1925 constrain(TopLeft);
1926
1927 if (flags.shaded) {
1928 blackbox_attrib.flags ^= AttribShaded;
1929 blackbox_attrib.attrib ^= AttribShaded;
1930 flags.shaded = False;
1931 }
1932
1933 flags.maximized = button;
1934
1935 configure(frame.changing.x(), frame.changing.y(),
1936 frame.changing.width(), frame.changing.height());
1937 if (flags.focused)
1938 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1939 redrawAllButtons(); // in case it is not called in configure()
1940 setState(current_state);
1941 }
1942
1943
1944 // re-maximizes the window to take into account availableArea changes
1945 void BlackboxWindow::remaximize(void) {
1946 if (flags.shaded) {
1947 // we only update the window's attributes otherwise we lose the shade bit
1948 switch(flags.maximized) {
1949 case 1:
1950 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1951 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1952 break;
1953
1954 case 2:
1955 blackbox_attrib.flags |= AttribMaxVert;
1956 blackbox_attrib.attrib |= AttribMaxVert;
1957 break;
1958
1959 case 3:
1960 blackbox_attrib.flags |= AttribMaxHoriz;
1961 blackbox_attrib.attrib |= AttribMaxHoriz;
1962 break;
1963 }
1964 return;
1965 }
1966
1967 // save the original dimensions because maximize will wipe them out
1968 int premax_x = blackbox_attrib.premax_x,
1969 premax_y = blackbox_attrib.premax_y,
1970 premax_w = blackbox_attrib.premax_w,
1971 premax_h = blackbox_attrib.premax_h;
1972
1973 unsigned int button = flags.maximized;
1974 flags.maximized = 0; // trick maximize() into working
1975 maximize(button);
1976
1977 // restore saved values
1978 blackbox_attrib.premax_x = premax_x;
1979 blackbox_attrib.premax_y = premax_y;
1980 blackbox_attrib.premax_w = premax_w;
1981 blackbox_attrib.premax_h = premax_h;
1982 }
1983
1984
1985 void BlackboxWindow::setWorkspace(unsigned int n) {
1986 blackbox_attrib.flags |= AttribWorkspace;
1987 blackbox_attrib.workspace = n;
1988 if (n == BSENTINEL) { // iconified window
1989 /*
1990 we set the workspace to 'all workspaces' so that taskbars will show the
1991 window. otherwise, it made uniconifying a window imposible without the
1992 blackbox workspace menu
1993 */
1994 n = 0xffffffff;
1995 }
1996 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1997 }
1998
1999
2000 void BlackboxWindow::shade(void) {
2001 if (flags.shaded) {
2002 XResizeWindow(blackbox->getXDisplay(), frame.window,
2003 frame.inside_w, frame.inside_h);
2004 flags.shaded = False;
2005 blackbox_attrib.flags ^= AttribShaded;
2006 blackbox_attrib.attrib ^= AttribShaded;
2007
2008 setState(NormalState);
2009
2010 // set the frame rect to the normal size
2011 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2012 frame.margin.bottom);
2013 } else {
2014 if (! (decorations & Decor_Titlebar))
2015 return; // can't shade it without a titlebar!
2016
2017 XResizeWindow(blackbox->getXDisplay(), frame.window,
2018 frame.inside_w, frame.title_h);
2019 flags.shaded = True;
2020 blackbox_attrib.flags |= AttribShaded;
2021 blackbox_attrib.attrib |= AttribShaded;
2022
2023 setState(IconicState);
2024
2025 // set the frame rect to the shaded size
2026 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
2027 }
2028 }
2029
2030
2031 /*
2032 * (Un)Sticks a window and its relatives.
2033 */
2034 void BlackboxWindow::stick(void) {
2035 if (flags.stuck) {
2036 blackbox_attrib.flags ^= AttribOmnipresent;
2037 blackbox_attrib.attrib ^= AttribOmnipresent;
2038
2039 flags.stuck = False;
2040
2041 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2042 if (i != blackbox_attrib.workspace)
2043 screen->getWorkspace(i)->removeWindow(this, True);
2044
2045 if (! flags.iconic)
2046 screen->reassociateWindow(this, BSENTINEL, True);
2047 // temporary fix since sticky windows suck. set the hint to what we
2048 // actually hold in our data.
2049 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2050 blackbox_attrib.workspace);
2051
2052 setState(current_state);
2053 } else {
2054 flags.stuck = True;
2055
2056 blackbox_attrib.flags |= AttribOmnipresent;
2057 blackbox_attrib.attrib |= AttribOmnipresent;
2058
2059 // temporary fix since sticky windows suck. set the hint to a different
2060 // value than that contained in the class' data.
2061 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2062 0xffffffff);
2063
2064 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2065 if (i != blackbox_attrib.workspace)
2066 screen->getWorkspace(i)->addWindow(this, False, True);
2067
2068 setState(current_state);
2069 }
2070 // go up the chain
2071 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2072 client.transient_for->isStuck() != flags.stuck)
2073 client.transient_for->stick();
2074 // go down the chain
2075 BlackboxWindowList::iterator it;
2076 const BlackboxWindowList::iterator end = client.transientList.end();
2077 for (it = client.transientList.begin(); it != end; ++it)
2078 if ((*it)->isStuck() != flags.stuck)
2079 (*it)->stick();
2080 }
2081
2082
2083 void BlackboxWindow::redrawWindowFrame(void) const {
2084 if (decorations & Decor_Titlebar) {
2085 if (flags.focused) {
2086 if (frame.ftitle)
2087 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2088 frame.title, frame.ftitle);
2089 else
2090 XSetWindowBackground(blackbox->getXDisplay(),
2091 frame.title, frame.ftitle_pixel);
2092 } else {
2093 if (frame.utitle)
2094 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2095 frame.title, frame.utitle);
2096 else
2097 XSetWindowBackground(blackbox->getXDisplay(),
2098 frame.title, frame.utitle_pixel);
2099 }
2100 XClearWindow(blackbox->getXDisplay(), frame.title);
2101
2102 redrawLabel();
2103 redrawAllButtons();
2104 }
2105
2106 if (decorations & Decor_Handle) {
2107 if (flags.focused) {
2108 if (frame.fhandle)
2109 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2110 frame.handle, frame.fhandle);
2111 else
2112 XSetWindowBackground(blackbox->getXDisplay(),
2113 frame.handle, frame.fhandle_pixel);
2114
2115 if (frame.fgrip) {
2116 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2117 frame.left_grip, frame.fgrip);
2118 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2119 frame.right_grip, frame.fgrip);
2120 } else {
2121 XSetWindowBackground(blackbox->getXDisplay(),
2122 frame.left_grip, frame.fgrip_pixel);
2123 XSetWindowBackground(blackbox->getXDisplay(),
2124 frame.right_grip, frame.fgrip_pixel);
2125 }
2126 } else {
2127 if (frame.uhandle)
2128 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2129 frame.handle, frame.uhandle);
2130 else
2131 XSetWindowBackground(blackbox->getXDisplay(),
2132 frame.handle, frame.uhandle_pixel);
2133
2134 if (frame.ugrip) {
2135 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2136 frame.left_grip, frame.ugrip);
2137 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2138 frame.right_grip, frame.ugrip);
2139 } else {
2140 XSetWindowBackground(blackbox->getXDisplay(),
2141 frame.left_grip, frame.ugrip_pixel);
2142 XSetWindowBackground(blackbox->getXDisplay(),
2143 frame.right_grip, frame.ugrip_pixel);
2144 }
2145 }
2146 XClearWindow(blackbox->getXDisplay(), frame.handle);
2147 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2148 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2149 }
2150
2151 if (decorations & Decor_Border) {
2152 if (flags.focused)
2153 XSetWindowBorder(blackbox->getXDisplay(),
2154 frame.plate, frame.fborder_pixel);
2155 else
2156 XSetWindowBorder(blackbox->getXDisplay(),
2157 frame.plate, frame.uborder_pixel);
2158 }
2159 }
2160
2161
2162 void BlackboxWindow::setFocusFlag(bool focus) {
2163 // only focus a window if it is visible
2164 if (focus && ! flags.visible)
2165 return;
2166
2167 flags.focused = focus;
2168
2169 redrawWindowFrame();
2170
2171 if (flags.focused)
2172 blackbox->setFocusedWindow(this);
2173
2174 if (! flags.iconic) {
2175 // iconic windows arent in a workspace menu!
2176 if (flags.stuck)
2177 screen->getCurrentWorkspace()->setFocused(this, isFocused());
2178 else
2179 screen->getWorkspace(blackbox_attrib.workspace)->
2180 setFocused(this, flags.focused);
2181 }
2182 }
2183
2184
2185 void BlackboxWindow::installColormap(bool install) {
2186 int i = 0, ncmap = 0;
2187 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2188 client.window, &ncmap);
2189 if (cmaps) {
2190 XWindowAttributes wattrib;
2191 if (XGetWindowAttributes(blackbox->getXDisplay(),
2192 client.window, &wattrib)) {
2193 if (install) {
2194 // install the window's colormap
2195 for (i = 0; i < ncmap; i++) {
2196 if (*(cmaps + i) == wattrib.colormap)
2197 // this window is using an installed color map... do not install
2198 install = False;
2199 }
2200 // otherwise, install the window's colormap
2201 if (install)
2202 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2203 } else {
2204 // uninstall the window's colormap
2205 for (i = 0; i < ncmap; i++) {
2206 if (*(cmaps + i) == wattrib.colormap)
2207 // we found the colormap to uninstall
2208 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2209 }
2210 }
2211 }
2212
2213 XFree(cmaps);
2214 }
2215 }
2216
2217
2218 void BlackboxWindow::setAllowedActions(void) {
2219 Atom actions[7];
2220 int num = 0;
2221
2222 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2223 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2224 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2225
2226 if (functions & Func_Move)
2227 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2228 if (functions & Func_Resize)
2229 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2230 if (functions & Func_Maximize) {
2231 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2232 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2233 }
2234
2235 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2236 actions, num);
2237 }
2238
2239
2240 void BlackboxWindow::setState(unsigned long new_state) {
2241 current_state = new_state;
2242
2243 unsigned long state[2];
2244 state[0] = current_state;
2245 state[1] = None;
2246 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2247
2248 xatom->setValue(client.window, XAtom::blackbox_attributes,
2249 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2250 PropBlackboxAttributesElements);
2251
2252 Atom netstate[8];
2253 int num = 0;
2254 if (flags.modal)
2255 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2256 if (flags.shaded)
2257 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2258 if (flags.iconic)
2259 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2260 if (flags.skip_taskbar)
2261 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2262 if (flags.skip_pager)
2263 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2264 if (flags.fullscreen)
2265 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2266 if (flags.maximized == 1 || flags.maximized == 2)
2267 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2268 if (flags.maximized == 1 || flags.maximized == 3)
2269 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2270 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2271 netstate, num);
2272 }
2273
2274
2275 bool BlackboxWindow::getState(void) {
2276 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2277 current_state);
2278 if (! ret) current_state = 0;
2279 return ret;
2280 }
2281
2282
2283 void BlackboxWindow::restoreAttributes(void) {
2284 unsigned long num = PropBlackboxAttributesElements;
2285 BlackboxAttributes *net;
2286 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2287 XAtom::blackbox_attributes, num,
2288 (unsigned long **)&net))
2289 return;
2290 if (num < PropBlackboxAttributesElements) {
2291 delete [] net;
2292 return;
2293 }
2294
2295 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2296 flags.shaded = False;
2297 unsigned long orig_state = current_state;
2298 shade();
2299
2300 /*
2301 At this point in the life of a window, current_state should only be set
2302 to IconicState if the window was an *icon*, not if it was shaded.
2303 */
2304 if (orig_state != IconicState)
2305 current_state = WithdrawnState;
2306 }
2307
2308 if (net->workspace != screen->getCurrentWorkspaceID() &&
2309 net->workspace < screen->getWorkspaceCount())
2310 screen->reassociateWindow(this, net->workspace, True);
2311
2312 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2313 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2314 // set to WithdrawnState so it will be mapped on the new workspace
2315 if (current_state == NormalState) current_state = WithdrawnState;
2316 } else if (current_state == WithdrawnState) {
2317 // the window is on this workspace and is Withdrawn, so it is waiting to
2318 // be mapped
2319 current_state = NormalState;
2320 }
2321
2322 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2323 ! flags.stuck) {
2324 stick();
2325
2326 // if the window was on another workspace, it was going to be hidden. this
2327 // specifies that the window should be mapped since it is sticky.
2328 if (current_state == WithdrawnState) current_state = NormalState;
2329 }
2330
2331 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2332 int x = net->premax_x, y = net->premax_y;
2333 unsigned int w = net->premax_w, h = net->premax_h;
2334 flags.maximized = 0;
2335
2336 unsigned int m = 0;
2337 if ((net->flags & AttribMaxHoriz) &&
2338 (net->flags & AttribMaxVert))
2339 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2340 else if (net->flags & AttribMaxVert)
2341 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2342 else if (net->flags & AttribMaxHoriz)
2343 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2344
2345 if (m) maximize(m);
2346
2347 blackbox_attrib.premax_x = x;
2348 blackbox_attrib.premax_y = y;
2349 blackbox_attrib.premax_w = w;
2350 blackbox_attrib.premax_h = h;
2351 }
2352
2353 if (net->flags & AttribDecoration) {
2354 switch (net->decoration) {
2355 case DecorNone:
2356 enableDecor(False);
2357 break;
2358
2359 /* since tools only let you toggle this anyways, we'll just make that all
2360 it supports for now.
2361 */
2362 default:
2363 case DecorNormal:
2364 case DecorTiny:
2365 case DecorTool:
2366 enableDecor(True);
2367 break;
2368 }
2369 }
2370
2371 // with the state set it will then be the map event's job to read the
2372 // window's state and behave accordingly
2373
2374 delete [] net;
2375 }
2376
2377
2378 /*
2379 * Positions the Rect r according the the client window position and
2380 * window gravity.
2381 */
2382 void BlackboxWindow::applyGravity(Rect &r) {
2383 // apply horizontal window gravity
2384 switch (client.win_gravity) {
2385 default:
2386 case NorthWestGravity:
2387 case SouthWestGravity:
2388 case WestGravity:
2389 r.setX(client.rect.x());
2390 break;
2391
2392 case NorthGravity:
2393 case SouthGravity:
2394 case CenterGravity:
2395 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2396 break;
2397
2398 case NorthEastGravity:
2399 case SouthEastGravity:
2400 case EastGravity:
2401 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2402 break;
2403
2404 case ForgetGravity:
2405 case StaticGravity:
2406 r.setX(client.rect.x() - frame.margin.left);
2407 break;
2408 }
2409
2410 // apply vertical window gravity
2411 switch (client.win_gravity) {
2412 default:
2413 case NorthWestGravity:
2414 case NorthEastGravity:
2415 case NorthGravity:
2416 r.setY(client.rect.y());
2417 break;
2418
2419 case CenterGravity:
2420 case EastGravity:
2421 case WestGravity:
2422 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2423 break;
2424
2425 case SouthWestGravity:
2426 case SouthEastGravity:
2427 case SouthGravity:
2428 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2429 break;
2430
2431 case ForgetGravity:
2432 case StaticGravity:
2433 r.setY(client.rect.y() - frame.margin.top);
2434 break;
2435 }
2436 }
2437
2438
2439 /*
2440 * The reverse of the applyGravity function.
2441 *
2442 * Positions the Rect r according to the frame window position and
2443 * window gravity.
2444 */
2445 void BlackboxWindow::restoreGravity(Rect &r) {
2446 // restore horizontal window gravity
2447 switch (client.win_gravity) {
2448 default:
2449 case NorthWestGravity:
2450 case SouthWestGravity:
2451 case WestGravity:
2452 r.setX(frame.rect.x());
2453 break;
2454
2455 case NorthGravity:
2456 case SouthGravity:
2457 case CenterGravity:
2458 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2459 break;
2460
2461 case NorthEastGravity:
2462 case SouthEastGravity:
2463 case EastGravity:
2464 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2465 break;
2466
2467 case ForgetGravity:
2468 case StaticGravity:
2469 r.setX(frame.rect.x() + frame.margin.left);
2470 break;
2471 }
2472
2473 // restore vertical window gravity
2474 switch (client.win_gravity) {
2475 default:
2476 case NorthWestGravity:
2477 case NorthEastGravity:
2478 case NorthGravity:
2479 r.setY(frame.rect.y());
2480 break;
2481
2482 case CenterGravity:
2483 case EastGravity:
2484 case WestGravity:
2485 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2486 break;
2487
2488 case SouthWestGravity:
2489 case SouthEastGravity:
2490 case SouthGravity:
2491 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2492 break;
2493
2494 case ForgetGravity:
2495 case StaticGravity:
2496 r.setY(frame.rect.y() + frame.margin.top);
2497 break;
2498 }
2499 }
2500
2501
2502 void BlackboxWindow::redrawLabel(void) const {
2503 if (flags.focused) {
2504 if (frame.flabel)
2505 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2506 frame.label, frame.flabel);
2507 else
2508 XSetWindowBackground(blackbox->getXDisplay(),
2509 frame.label, frame.flabel_pixel);
2510 } else {
2511 if (frame.ulabel)
2512 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2513 frame.label, frame.ulabel);
2514 else
2515 XSetWindowBackground(blackbox->getXDisplay(),
2516 frame.label, frame.ulabel_pixel);
2517 }
2518 XClearWindow(blackbox->getXDisplay(), frame.label);
2519
2520 WindowStyle *style = screen->getWindowStyle();
2521
2522 int pos = frame.bevel_w * 2;
2523 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2524 style->font->drawString(frame.label, pos, 1,
2525 (flags.focused ? style->l_text_focus :
2526 style->l_text_unfocus),
2527 client.title);
2528 }
2529
2530
2531 void BlackboxWindow::redrawAllButtons(void) const {
2532 if (frame.iconify_button) redrawIconifyButton(False);
2533 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2534 if (frame.close_button) redrawCloseButton(False);
2535 }
2536
2537
2538 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2539 if (! pressed) {
2540 if (flags.focused) {
2541 if (frame.fbutton)
2542 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2543 frame.iconify_button, frame.fbutton);
2544 else
2545 XSetWindowBackground(blackbox->getXDisplay(),
2546 frame.iconify_button, frame.fbutton_pixel);
2547 } else {
2548 if (frame.ubutton)
2549 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2550 frame.iconify_button, frame.ubutton);
2551 else
2552 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2553 frame.ubutton_pixel);
2554 }
2555 } else {
2556 if (frame.pbutton)
2557 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2558 frame.iconify_button, frame.pbutton);
2559 else
2560 XSetWindowBackground(blackbox->getXDisplay(),
2561 frame.iconify_button, frame.pbutton_pixel);
2562 }
2563 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2564
2565 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2566 screen->getWindowStyle()->b_pic_unfocus);
2567 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2568 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2569 }
2570
2571
2572 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2573 if (! pressed) {
2574 if (flags.focused) {
2575 if (frame.fbutton)
2576 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2577 frame.maximize_button, frame.fbutton);
2578 else
2579 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2580 frame.fbutton_pixel);
2581 } else {
2582 if (frame.ubutton)
2583 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2584 frame.maximize_button, frame.ubutton);
2585 else
2586 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2587 frame.ubutton_pixel);
2588 }
2589 } else {
2590 if (frame.pbutton)
2591 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2592 frame.maximize_button, frame.pbutton);
2593 else
2594 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2595 frame.pbutton_pixel);
2596 }
2597 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2598
2599 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2600 screen->getWindowStyle()->b_pic_unfocus);
2601 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2602 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2603 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2604 2, 3, (frame.button_w - 3), 3);
2605 }
2606
2607
2608 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2609 if (! pressed) {
2610 if (flags.focused) {
2611 if (frame.fbutton)
2612 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2613 frame.fbutton);
2614 else
2615 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2616 frame.fbutton_pixel);
2617 } else {
2618 if (frame.ubutton)
2619 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2620 frame.ubutton);
2621 else
2622 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2623 frame.ubutton_pixel);
2624 }
2625 } else {
2626 if (frame.pbutton)
2627 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2628 frame.close_button, frame.pbutton);
2629 else
2630 XSetWindowBackground(blackbox->getXDisplay(),
2631 frame.close_button, frame.pbutton_pixel);
2632 }
2633 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2634
2635 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2636 screen->getWindowStyle()->b_pic_unfocus);
2637 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2638 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2639 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2640 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2641 }
2642
2643
2644 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2645 if (re->window != client.window)
2646 return;
2647
2648 #ifdef DEBUG
2649 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2650 client.window);
2651 #endif // DEBUG
2652
2653 /*
2654 Even though the window wants to be shown, if it is not on the current
2655 workspace, then it isn't going to be shown right now.
2656 */
2657 if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2658 blackbox_attrib.workspace < screen->getWorkspaceCount())
2659 if (current_state == NormalState) current_state = WithdrawnState;
2660
2661 switch (current_state) {
2662 case IconicState:
2663 iconify();
2664 break;
2665
2666 case WithdrawnState:
2667 withdraw();
2668 break;
2669
2670 case NormalState:
2671 case InactiveState:
2672 case ZoomState:
2673 default:
2674 show();
2675 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2676 if (isNormal()) {
2677 if (! blackbox->isStartup()) {
2678 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2679 if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2680 getTransientFor()->isFocused())) {
2681 setInputFocus();
2682 }
2683 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2684 int x, y, rx, ry;
2685 Window c, r;
2686 unsigned int m;
2687 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2688 &r, &c, &rx, &ry, &x, &y, &m);
2689 beginMove(rx, ry);
2690 }
2691 }
2692 }
2693 break;
2694 }
2695 }
2696
2697
2698 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2699 if (ue->window != client.window)
2700 return;
2701
2702 #ifdef DEBUG
2703 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2704 client.window);
2705 #endif // DEBUG
2706
2707 screen->unmanageWindow(this, False);
2708 }
2709
2710
2711 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2712 if (de->window != client.window)
2713 return;
2714
2715 #ifdef DEBUG
2716 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2717 client.window);
2718 #endif // DEBUG
2719
2720 screen->unmanageWindow(this, False);
2721 }
2722
2723
2724 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2725 if (re->window != client.window || re->parent == frame.plate)
2726 return;
2727
2728 #ifdef DEBUG
2729 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2730 "0x%lx.\n", client.window, re->parent);
2731 #endif // DEBUG
2732
2733 XEvent ev;
2734 ev.xreparent = *re;
2735 XPutBackEvent(blackbox->getXDisplay(), &ev);
2736 screen->unmanageWindow(this, True);
2737 }
2738
2739
2740 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2741 if (pe->state == PropertyDelete || ! validateClient())
2742 return;
2743
2744 #if 0
2745 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2746 client.window);
2747 #endif
2748
2749 switch(pe->atom) {
2750 case XA_WM_CLASS:
2751 case XA_WM_CLIENT_MACHINE:
2752 case XA_WM_COMMAND:
2753 break;
2754
2755 case XA_WM_TRANSIENT_FOR: {
2756 bool s = flags.stuck;
2757
2758 // determine if this is a transient window
2759 getTransientInfo();
2760
2761 if (flags.stuck != s) stick();
2762
2763 // adjust the window decorations based on transience
2764 if (isTransient()) {
2765 functions &= ~Func_Maximize;
2766 setAllowedActions();
2767 setupDecor();
2768 }
2769
2770 reconfigure();
2771 }
2772 break;
2773
2774 case XA_WM_HINTS:
2775 getWMHints();
2776 break;
2777
2778 case XA_WM_ICON_NAME:
2779 getWMIconName();
2780 if (flags.iconic) screen->propagateWindowName(this);
2781 break;
2782
2783 case XAtom::net_wm_name:
2784 case XA_WM_NAME:
2785 getWMName();
2786
2787 if (decorations & Decor_Titlebar)
2788 redrawLabel();
2789
2790 screen->propagateWindowName(this);
2791 break;
2792
2793 case XA_WM_NORMAL_HINTS: {
2794 getWMNormalHints();
2795
2796 if ((client.normal_hint_flags & PMinSize) &&
2797 (client.normal_hint_flags & PMaxSize)) {
2798 // the window now can/can't resize itself, so the buttons need to be
2799 // regrabbed.
2800 ungrabButtons();
2801 if (client.max_width <= client.min_width &&
2802 client.max_height <= client.min_height) {
2803 functions &= ~(Func_Resize | Func_Maximize);
2804 } else {
2805 if (! isTransient())
2806 functions |= Func_Maximize;
2807 functions |= Func_Resize;
2808 }
2809 grabButtons();
2810 setAllowedActions();
2811 setupDecor();
2812 }
2813
2814 Rect old_rect = frame.rect;
2815
2816 upsize();
2817
2818 if (old_rect != frame.rect)
2819 reconfigure();
2820
2821 break;
2822 }
2823
2824 default:
2825 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2826 getWMProtocols();
2827
2828 if ((decorations & Decor_Close) && (! frame.close_button)) {
2829 createCloseButton();
2830 if (decorations & Decor_Titlebar) {
2831 positionButtons(True);
2832 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2833 }
2834 if (windowmenu) windowmenu->reconfigure();
2835 }
2836 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2837 updateStrut();
2838 }
2839
2840 break;
2841 }
2842 }
2843
2844
2845 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2846 #if 0
2847 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2848 #endif
2849
2850 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2851 redrawLabel();
2852 else if (frame.close_button == ee->window)
2853 redrawCloseButton(False);
2854 else if (frame.maximize_button == ee->window)
2855 redrawMaximizeButton(flags.maximized);
2856 else if (frame.iconify_button == ee->window)
2857 redrawIconifyButton(False);
2858 }
2859
2860
2861 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2862 if (cr->window != client.window || flags.iconic)
2863 return;
2864
2865 if (cr->value_mask & CWBorderWidth)
2866 client.old_bw = cr->border_width;
2867
2868 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2869 frame.changing = frame.rect;
2870
2871 if (cr->value_mask & (CWX | CWY)) {
2872 if (cr->value_mask & CWX)
2873 client.rect.setX(cr->x);
2874 if (cr->value_mask & CWY)
2875 client.rect.setY(cr->y);
2876
2877 applyGravity(frame.changing);
2878 }
2879
2880 if (cr->value_mask & (CWWidth | CWHeight)) {
2881 if (cr->value_mask & CWWidth)
2882 frame.changing.setWidth(cr->width +
2883 frame.margin.left + frame.margin.right);
2884
2885 if (cr->value_mask & CWHeight)
2886 frame.changing.setHeight(cr->height +
2887 frame.margin.top + frame.margin.bottom);
2888
2889 /*
2890 if a position change ha been specified, then that position will be used
2891 instead of determining a position based on the window's gravity.
2892 */
2893 if (cr->value_mask & (CWX | CWY)) {
2894 Corner corner;
2895 switch (client.win_gravity) {
2896 case NorthEastGravity:
2897 case EastGravity:
2898 corner = TopRight;
2899 break;
2900 case SouthWestGravity:
2901 case SouthGravity:
2902 corner = BottomLeft;
2903 break;
2904 case SouthEastGravity:
2905 corner = BottomRight;
2906 break;
2907 default: // NorthWest, Static, etc
2908 corner = TopLeft;
2909 }
2910 constrain(corner);
2911 }
2912 }
2913
2914 configure(frame.changing.x(), frame.changing.y(),
2915 frame.changing.width(), frame.changing.height());
2916 }
2917
2918 if (cr->value_mask & CWStackMode && !isDesktop()) {
2919 switch (cr->detail) {
2920 case Below:
2921 case BottomIf:
2922 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2923 break;
2924
2925 case Above:
2926 case TopIf:
2927 default:
2928 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2929 break;
2930 }
2931 }
2932 }
2933
2934
2935 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2936 #ifdef DEBUG
2937 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2938 client.window);
2939 #endif
2940
2941 if (frame.maximize_button == be->window && be->button <= 3) {
2942 redrawMaximizeButton(True);
2943 } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
2944 if (! flags.focused)
2945 setInputFocus();
2946
2947 if (frame.iconify_button == be->window) {
2948 redrawIconifyButton(True);
2949 } else if (frame.close_button == be->window) {
2950 redrawCloseButton(True);
2951 } else if (frame.plate == be->window) {
2952 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2953
2954 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2955
2956 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2957 } else {
2958 if (frame.title == be->window || frame.label == be->window) {
2959 if (((be->time - lastButtonPressTime) <=
2960 blackbox->getDoubleClickInterval()) ||
2961 (be->state == ControlMask)) {
2962 lastButtonPressTime = 0;
2963 shade();
2964 } else {
2965 lastButtonPressTime = be->time;
2966 }
2967 }
2968
2969 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2970
2971 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2972 }
2973 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2974 (be->window != frame.close_button)) {
2975 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2976 } else if (windowmenu && be->button == 3 &&
2977 (frame.title == be->window || frame.label == be->window ||
2978 frame.handle == be->window || frame.window == be->window)) {
2979 if (windowmenu->isVisible()) {
2980 windowmenu->hide();
2981 } else {
2982 int mx = be->x_root - windowmenu->getWidth() / 2,
2983 my = be->y_root - windowmenu->getHeight() / 2;
2984
2985 // snap the window menu into a corner/side if necessary
2986 int left_edge, right_edge, top_edge, bottom_edge;
2987
2988 /*
2989 the " + (frame.border_w * 2) - 1" bits are to get the proper width
2990 and height of the menu, as the sizes returned by it do not include
2991 the borders.
2992 */
2993 left_edge = frame.rect.x();
2994 right_edge = frame.rect.right() -
2995 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2996 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2997 bottom_edge = client.rect.bottom() -
2998 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2999 (frame.border_w + frame.mwm_border_w);
3000
3001 if (mx < left_edge)
3002 mx = left_edge;
3003 if (mx > right_edge)
3004 mx = right_edge;
3005 if (my < top_edge)
3006 my = top_edge;
3007 if (my > bottom_edge)
3008 my = bottom_edge;
3009
3010 windowmenu->move(mx, my);
3011 windowmenu->show();
3012 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
3013 XRaiseWindow(blackbox->getXDisplay(),
3014 windowmenu->getSendToMenu()->getWindowID());
3015 }
3016 // mouse wheel up
3017 } else if (be->button == 4) {
3018 if ((be->window == frame.label ||
3019 be->window == frame.title ||
3020 be->window == frame.maximize_button ||
3021 be->window == frame.iconify_button ||
3022 be->window == frame.close_button) &&
3023 ! flags.shaded)
3024 shade();
3025 // mouse wheel down
3026 } else if (be->button == 5) {
3027 if ((be->window == frame.label ||
3028 be->window == frame.title ||
3029 be->window == frame.maximize_button ||
3030 be->window == frame.iconify_button ||
3031 be->window == frame.close_button) &&
3032 flags.shaded)
3033 shade();
3034 }
3035 }
3036
3037
3038 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
3039 #ifdef DEBUG
3040 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3041 client.window);
3042 #endif
3043
3044 if (re->window == frame.maximize_button &&
3045 re->button >= 1 && re->button <= 3) {
3046 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3047 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3048 maximize(re->button);
3049 } else {
3050 redrawMaximizeButton(flags.maximized);
3051 }
3052 } else if (re->window == frame.iconify_button && re->button == 1) {
3053 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3054 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3055 iconify();
3056 } else {
3057 redrawIconifyButton(False);
3058 }
3059 } else if (re->window == frame.close_button & re->button == 1) {
3060 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3061 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3062 close();
3063 redrawCloseButton(False);
3064 } else if (flags.moving) {
3065 endMove();
3066 } else if (flags.resizing) {
3067 endResize();
3068 } else if (re->window == frame.window) {
3069 if (re->button == 2 && re->state == mod_mask)
3070 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3071 }
3072 }
3073
3074
3075
3076 void BlackboxWindow::beginMove(int x_root, int y_root) {
3077 if (! (functions & Func_Move)) return;
3078
3079 assert(! (flags.resizing || flags.moving));
3080
3081 /*
3082 Only one window can be moved/resized at a time. If another window is already
3083 being moved or resized, then stop it before whating to work with this one.
3084 */
3085 BlackboxWindow *changing = blackbox->getChangingWindow();
3086 if (changing && changing != this) {
3087 if (changing->flags.moving)
3088 changing->endMove();
3089 else // if (changing->flags.resizing)
3090 changing->endResize();
3091 }
3092
3093 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3094 PointerMotionMask | ButtonReleaseMask,
3095 GrabModeAsync, GrabModeAsync,
3096 None, blackbox->getMoveCursor(), CurrentTime);
3097
3098 if (windowmenu && windowmenu->isVisible())
3099 windowmenu->hide();
3100
3101 flags.moving = True;
3102 blackbox->setChangingWindow(this);
3103
3104 if (! screen->doOpaqueMove()) {
3105 XGrabServer(blackbox->getXDisplay());
3106
3107 frame.changing = frame.rect;
3108 screen->showPosition(frame.changing.x(), frame.changing.y());
3109
3110 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3111 screen->getOpGC(),
3112 frame.changing.x(),
3113 frame.changing.y(),
3114 frame.changing.width() - 1,
3115 frame.changing.height() - 1);
3116 }
3117
3118 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3119 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3120 }
3121
3122
3123 void BlackboxWindow::doMove(int x_root, int y_root) {
3124 assert(flags.moving);
3125 assert(blackbox->getChangingWindow() == this);
3126
3127 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3128 dx -= frame.border_w;
3129 dy -= frame.border_w;
3130
3131 doWindowSnapping(dx, dy);
3132
3133 if (screen->doOpaqueMove()) {
3134 if (screen->doWorkspaceWarping())
3135 doWorkspaceWarping(x_root, y_root, dx);
3136
3137 configure(dx, dy, frame.rect.width(), frame.rect.height());
3138 } else {
3139 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3140 screen->getOpGC(),
3141 frame.changing.x(),
3142 frame.changing.y(),
3143 frame.changing.width() - 1,
3144 frame.changing.height() - 1);
3145
3146 if (screen->doWorkspaceWarping())
3147 doWorkspaceWarping(x_root, y_root, dx);
3148
3149 frame.changing.setPos(dx, dy);
3150
3151 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3152 screen->getOpGC(),
3153 frame.changing.x(),
3154 frame.changing.y(),
3155 frame.changing.width() - 1,
3156 frame.changing.height() - 1);
3157 }
3158
3159 screen->showPosition(dx, dy);
3160 }
3161
3162
3163 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
3164 // workspace warping
3165 bool warp = False;
3166 unsigned int dest = screen->getCurrentWorkspaceID();
3167 if (x_root <= 0) {
3168 warp = True;
3169
3170 if (dest > 0) dest--;
3171 else dest = screen->getNumberOfWorkspaces() - 1;
3172
3173 } else if (x_root >= screen->getRect().right()) {
3174 warp = True;
3175
3176 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3177 else dest = 0;
3178 }
3179 if (! warp)
3180 return;
3181
3182 bool focus = flags.focused; // had focus while moving?
3183
3184 int dest_x = x_root;
3185 if (x_root <= 0) {
3186 dest_x += screen->getRect().width() - 1;
3187 dx += screen->getRect().width() - 1;
3188 } else {
3189 dest_x -= screen->getRect().width() - 1;
3190 dx -= screen->getRect().width() - 1;
3191 }
3192
3193 if (! flags.stuck)
3194 screen->reassociateWindow(this, dest, False);
3195 screen->changeWorkspaceID(dest);
3196
3197 if (screen->doOpaqueMove())
3198 XGrabServer(blackbox->getXDisplay());
3199
3200 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3201 XWarpPointer(blackbox->getXDisplay(), None,
3202 screen->getRootWindow(), 0, 0, 0, 0,
3203 dest_x, y_root);
3204 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3205 PointerMotionMask | ButtonReleaseMask,
3206 GrabModeAsync, GrabModeAsync,
3207 None, blackbox->getMoveCursor(), CurrentTime);
3208
3209 if (screen->doOpaqueMove())
3210 XUngrabServer(blackbox->getXDisplay());
3211
3212 if (focus)
3213 setInputFocus();
3214
3215 }
3216
3217
3218 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3219 // how much resistance to edges to provide
3220 const int resistance_size = screen->getResistanceSize();
3221
3222 // how far away to snap
3223 const int snap_distance = screen->getSnapThreshold();
3224
3225 // how to snap windows
3226 const int snap_to_windows = screen->getWindowToWindowSnap();
3227 const int snap_to_edges = screen->getWindowToEdgeSnap();
3228 // the amount of space away from the edge to provide resistance/snap
3229 const int snap_offset = screen->getSnapOffset();
3230
3231 // find the geomeetery where the moving window currently is
3232 const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3233
3234 // window corners
3235 const int wleft = dx,
3236 wright = dx + frame.rect.width() - 1,
3237 wtop = dy,
3238 wbottom = dy + frame.rect.height() - 1;
3239
3240 if (snap_to_windows) {
3241 RectList rectlist;
3242
3243 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3244 assert(w);
3245
3246 // add windows on the workspace to the rect list
3247 const BlackboxWindowList& stack_list = w->getStackingList();
3248 BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3249 for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3250 if (*st_it != this) // don't snap to ourself
3251 rectlist.push_back( (*st_it)->frameRect() );
3252
3253 // add the toolbar and the slit to the rect list.
3254 // (only if they are not hidden)
3255 Toolbar *tbar = screen->getToolbar();
3256 Slit *slit = screen->getSlit();
3257 Rect tbar_rect, slit_rect;
3258 unsigned int bwidth = screen->getBorderWidth() * 2;
3259
3260 if (! (screen->doHideToolbar() || tbar->isHidden())) {
3261 tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3262 tbar->getHeight() + bwidth);
3263 rectlist.push_back(tbar_rect);
3264 }
3265
3266 if (! slit->isHidden()) {
3267 slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3268 slit->getHeight() + bwidth);
3269 rectlist.push_back(slit_rect);
3270 }
3271
3272 RectList::const_iterator it, end = rectlist.end();
3273 for (it = rectlist.begin(); it != end; ++it) {
3274 bool snapped = False;
3275 const Rect &winrect = *it;
3276 Rect offsetrect;
3277 offsetrect.setCoords(winrect.left() - snap_offset,
3278 winrect.top() - snap_offset,
3279 winrect.right() + snap_offset,
3280 winrect.bottom() + snap_offset);
3281
3282 if (snap_to_windows == BScreen::WindowResistance)
3283 // if the window is already over top of this snap target, then
3284 // resistance is futile, so just ignore it
3285 if (winrect.intersects(moving))
3286 continue;
3287
3288 int dleft, dright, dtop, dbottom;
3289
3290 // if the windows are in the same plane vertically
3291 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3292 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3293
3294 if (snap_to_windows == BScreen::WindowResistance) {
3295 dleft = wright - offsetrect.left();
3296 dright = offsetrect.right() - wleft;
3297
3298 // snap left of other window?
3299 if (dleft >= 0 && dleft < resistance_size &&
3300 dleft < (wright - wleft)) {
3301 dx = offsetrect.left() - frame.rect.width();
3302 snapped = True;
3303 }
3304 // snap right of other window?
3305 else if (dright >= 0 && dright < resistance_size &&
3306 dright < (wright - wleft)) {
3307 dx = offsetrect.right() + 1;
3308 snapped = True;
3309 }
3310 } else { // BScreen::WindowSnap
3311 dleft = abs(wright - offsetrect.left());
3312 dright = abs(wleft - offsetrect.right());
3313
3314 // snap left of other window?
3315 if (dleft < snap_distance && dleft <= dright) {
3316 dx = offsetrect.left() - frame.rect.width();
3317 snapped = True;
3318 }
3319 // snap right of other window?
3320 else if (dright < snap_distance) {
3321 dx = offsetrect.right() + 1;
3322 snapped = True;
3323 }
3324 }
3325
3326 if (snapped) {
3327 if (screen->getWindowCornerSnap()) {
3328 // try corner-snap to its other sides
3329 if (snap_to_windows == BScreen::WindowResistance) {
3330 dtop = winrect.top() - wtop;
3331 dbottom = wbottom - winrect.bottom();
3332 if (dtop > 0 && dtop < resistance_size) {
3333 // if we're already past the top edge, then don't provide
3334 // resistance
3335 if (moving.top() >= winrect.top())
3336 dy = winrect.top();
3337 } else if (dbottom > 0 && dbottom < resistance_size) {
3338 // if we're already past the bottom edge, then don't provide
3339 // resistance
3340 if (moving.bottom() <= winrect.bottom())
3341 dy = winrect.bottom() - frame.rect.height() + 1;
3342 }
3343 } else { // BScreen::WindowSnap
3344 dtop = abs(wtop - winrect.top());
3345 dbottom = abs(wbottom - winrect.bottom());
3346 if (dtop < snap_distance && dtop <= dbottom)
3347 dy = winrect.top();
3348 else if (dbottom < snap_distance)
3349 dy = winrect.bottom() - frame.rect.height() + 1;
3350 }
3351 }
3352
3353 continue;
3354 }
3355 }
3356
3357 // if the windows are on the same plane horizontally
3358 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3359 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3360
3361 if (snap_to_windows == BScreen::WindowResistance) {
3362 dtop = wbottom - offsetrect.top();
3363 dbottom = offsetrect.bottom() - wtop;
3364
3365 // snap top of other window?
3366 if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3367 dy = offsetrect.top() - frame.rect.height();
3368 snapped = True;
3369 }
3370 // snap bottom of other window?
3371 else if (dbottom >= 0 && dbottom < resistance_size &&
3372 dbottom < (wbottom - wtop)) {
3373 dy = offsetrect.bottom() + 1;
3374 snapped = True;
3375 }
3376 } else { // BScreen::WindowSnap
3377 dtop = abs(wbottom - offsetrect.top());
3378 dbottom = abs(wtop - offsetrect.bottom());
3379
3380 // snap top of other window?
3381 if (dtop < snap_distance && dtop <= dbottom) {
3382 dy = offsetrect.top() - frame.rect.height();
3383 snapped = True;
3384 }
3385 // snap bottom of other window?
3386 else if (dbottom < snap_distance) {
3387 dy = offsetrect.bottom() + 1;
3388 snapped = True;
3389 }
3390
3391 }
3392
3393 if (snapped) {
3394 if (screen->getWindowCornerSnap()) {
3395 // try corner-snap to its other sides
3396 if (snap_to_windows == BScreen::WindowResistance) {
3397 dleft = winrect.left() - wleft;
3398 dright = wright - winrect.right();
3399 if (dleft > 0 && dleft < resistance_size) {
3400 // if we're already past the left edge, then don't provide
3401 // resistance
3402 if (moving.left() >= winrect.left())
3403 dx = winrect.left();
3404 } else if (dright > 0 && dright < resistance_size) {
3405 // if we're already past the right edge, then don't provide
3406 // resistance
3407 if (moving.right() <= winrect.right())
3408 dx = winrect.right() - frame.rect.width() + 1;
3409 }
3410 } else { // BScreen::WindowSnap
3411 dleft = abs(wleft - winrect.left());
3412 dright = abs(wright - winrect.right());
3413 if (dleft < snap_distance && dleft <= dright)
3414 dx = winrect.left();
3415 else if (dright < snap_distance)
3416 dx = winrect.right() - frame.rect.width() + 1;
3417 }
3418 }
3419
3420 continue;
3421 }
3422 }
3423 }
3424 }
3425
3426 if (snap_to_edges) {
3427 RectList rectlist;
3428
3429 // snap to the screen edges (and screen boundaries for xinerama)
3430 #ifdef XINERAMA
3431 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3432 rectlist.insert(rectlist.begin(),
3433 screen->getXineramaAreas().begin(),
3434 screen->getXineramaAreas().end());
3435 } else
3436 #endif // XINERAMA
3437 rectlist.push_back(screen->getRect());
3438
3439 RectList::const_iterator it, end = rectlist.end();
3440 for (it = rectlist.begin(); it != end; ++it) {
3441 const Rect &srect = *it;
3442 Rect offsetrect;
3443 offsetrect.setCoords(srect.left() + snap_offset,
3444 srect.top() + snap_offset,
3445 srect.right() - snap_offset,
3446 srect.bottom() - snap_offset);
3447
3448 if (snap_to_edges == BScreen::WindowResistance) {
3449 // if we're not in the rectangle then don't snap to it.
3450 if (! srect.contains(moving))
3451 continue;
3452 } else { // BScreen::WindowSnap
3453 // if we're not in the rectangle then don't snap to it.
3454 if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3455 frame.rect.height())))
3456 continue;
3457 }
3458
3459 if (snap_to_edges == BScreen::WindowResistance) {
3460 int dleft = offsetrect.left() - wleft,
3461 dright = wright - offsetrect.right(),
3462 dtop = offsetrect.top() - wtop,
3463 dbottom = wbottom - offsetrect.bottom();
3464
3465 // snap left?
3466 if (dleft > 0 && dleft < resistance_size)
3467 dx = offsetrect.left();
3468 // snap right?
3469 else if (dright > 0 && dright < resistance_size)
3470 dx = offsetrect.right() - frame.rect.width() + 1;
3471
3472 // snap top?
3473 if (dtop > 0 && dtop < resistance_size)
3474 dy = offsetrect.top();
3475 // snap bottom?
3476 else if (dbottom > 0 && dbottom < resistance_size)
3477 dy = offsetrect.bottom() - frame.rect.height() + 1;
3478 } else { // BScreen::WindowSnap
3479 int dleft = abs(wleft - offsetrect.left()),
3480 dright = abs(wright - offsetrect.right()),
3481 dtop = abs(wtop - offsetrect.top()),
3482 dbottom = abs(wbottom - offsetrect.bottom());
3483
3484 // snap left?
3485 if (dleft < snap_distance && dleft <= dright)
3486 dx = offsetrect.left();
3487 // snap right?
3488 else if (dright < snap_distance)
3489 dx = offsetrect.right() - frame.rect.width() + 1;
3490
3491 // snap top?
3492 if (dtop < snap_distance && dtop <= dbottom)
3493 dy = offsetrect.top();
3494 // snap bottom?
3495 else if (dbottom < snap_distance)
3496 dy = offsetrect.bottom() - frame.rect.height() + 1;
3497 }
3498 }
3499 }
3500 }
3501
3502
3503 void BlackboxWindow::endMove(void) {
3504 assert(flags.moving);
3505 assert(blackbox->getChangingWindow() == this);
3506
3507 flags.moving = False;
3508 blackbox->setChangingWindow(0);
3509
3510 if (! screen->doOpaqueMove()) {
3511 /* when drawing the rubber band, we need to make sure we only draw inside
3512 * the frame... frame.changing_* contain the new coords for the window,
3513 * so we need to subtract 1 from changing_w/changing_h every where we
3514 * draw the rubber band (for both moving and resizing)
3515 */
3516 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3517 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3518 frame.changing.width() - 1, frame.changing.height() - 1);
3519 XUngrabServer(blackbox->getXDisplay());
3520
3521 configure(frame.changing.x(), frame.changing.y(),
3522 frame.changing.width(), frame.changing.height());
3523 } else {
3524 configure(frame.rect.x(), frame.rect.y(),
3525 frame.rect.width(), frame.rect.height());
3526 }
3527 screen->hideGeometry();
3528
3529 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3530
3531 // if there are any left over motions from the move, drop them now
3532 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3533 XEvent e;
3534 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3535 MotionNotify, &e));
3536 }
3537
3538
3539 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3540 if (! (functions & Func_Resize)) return;
3541
3542 assert(! (flags.resizing || flags.moving));
3543
3544 /*
3545 Only one window can be moved/resized at a time. If another window is
3546 already being moved or resized, then stop it before whating to work with
3547 this one.
3548 */
3549 BlackboxWindow *changing = blackbox->getChangingWindow();
3550 if (changing && changing != this) {
3551 if (changing->flags.moving)
3552 changing->endMove();
3553 else // if (changing->flags.resizing)
3554 changing->endResize();
3555 }
3556
3557 resize_dir = dir;
3558
3559 Cursor cursor;
3560 Corner anchor;
3561
3562 switch (resize_dir) {
3563 case BottomLeft:
3564 anchor = TopRight;
3565 cursor = blackbox->getLowerLeftAngleCursor();
3566 break;
3567
3568 case BottomRight:
3569 anchor = TopLeft;
3570 cursor = blackbox->getLowerRightAngleCursor();
3571 break;
3572
3573 case TopLeft:
3574 anchor = BottomRight;
3575 cursor = blackbox->getUpperLeftAngleCursor();
3576 break;
3577
3578 case TopRight:
3579 anchor = BottomLeft;
3580 cursor = blackbox->getUpperRightAngleCursor();
3581 break;
3582
3583 default:
3584 assert(false); // unhandled Corner
3585 return; // unreachable, for the compiler
3586 }
3587
3588 XGrabServer(blackbox->getXDisplay());
3589 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3590 PointerMotionMask | ButtonReleaseMask,
3591 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3592
3593 flags.resizing = True;
3594 blackbox->setChangingWindow(this);
3595
3596 unsigned int gw, gh;
3597 frame.changing = frame.rect;
3598
3599 constrain(anchor, &gw, &gh);
3600
3601 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3602 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3603 frame.changing.width() - 1, frame.changing.height() - 1);
3604
3605 screen->showGeometry(gw, gh);
3606
3607 frame.grab_x = x_root;
3608 frame.grab_y = y_root;
3609 }
3610
3611
3612 void BlackboxWindow::doResize(int x_root, int y_root) {
3613 assert(flags.resizing);
3614 assert(blackbox->getChangingWindow() == this);
3615
3616 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3617 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3618 frame.changing.width() - 1, frame.changing.height() - 1);
3619
3620 unsigned int gw, gh;
3621 Corner anchor;
3622
3623 switch (resize_dir) {
3624 case BottomLeft:
3625 anchor = TopRight;
3626 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3627 frame.rect.height() + (y_root - frame.grab_y));
3628 break;
3629 case BottomRight:
3630 anchor = TopLeft;
3631 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3632 frame.rect.height() + (y_root - frame.grab_y));
3633 break;
3634 case TopLeft:
3635 anchor = BottomRight;
3636 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3637 frame.rect.height() - (y_root - frame.grab_y));
3638 break;
3639 case TopRight:
3640 anchor = BottomLeft;
3641 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3642 frame.rect.height() - (y_root - frame.grab_y));
3643 break;
3644
3645 default:
3646 assert(false); // unhandled Corner
3647 return; // unreachable, for the compiler
3648 }
3649
3650 constrain(anchor, &gw, &gh);
3651
3652 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3653 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3654 frame.changing.width() - 1, frame.changing.height() - 1);
3655
3656 screen->showGeometry(gw, gh);
3657 }
3658
3659
3660 void BlackboxWindow::endResize(void) {
3661 assert(flags.resizing);
3662 assert(blackbox->getChangingWindow() == this);
3663
3664 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3665 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3666 frame.changing.width() - 1, frame.changing.height() - 1);
3667 XUngrabServer(blackbox->getXDisplay());
3668
3669 // unset maximized state after resized when fully maximized
3670 if (flags.maximized == 1)
3671 maximize(0);
3672
3673 flags.resizing = False;
3674 blackbox->setChangingWindow(0);
3675
3676 configure(frame.changing.x(), frame.changing.y(),
3677 frame.changing.width(), frame.changing.height());
3678 screen->hideGeometry();
3679
3680 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3681
3682 // if there are any left over motions from the resize, drop them now
3683 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3684 XEvent e;
3685 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3686 MotionNotify, &e));
3687 }
3688
3689
3690 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3691 #if 0
3692 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3693 client.window);
3694 #endif
3695
3696 if (flags.moving) {
3697 doMove(me->x_root, me->y_root);
3698 } else if (flags.resizing) {
3699 doResize(me->x_root, me->y_root);
3700 } else {
3701 if ((functions & Func_Move) &&
3702 (me->state & Button1Mask) &&
3703 (frame.title == me->window || frame.label == me->window ||
3704 frame.handle == me->window || frame.window == me->window)) {
3705 beginMove(me->x_root, me->y_root);
3706 } else if ((functions & Func_Resize) &&
3707 ((me->state & Button1Mask) && (me->window == frame.right_grip ||
3708 me->window == frame.left_grip)) ||
3709 ((me->state & Button3Mask) && (me->state & mod_mask) &&
3710 (frame.title == me->window || frame.label == me->window ||
3711 frame.handle == me->window || frame.window == me->window))) {
3712 unsigned int zones = screen->getResizeZones();
3713 Corner corner;
3714
3715 if (me->window == frame.left_grip) {
3716 corner = BottomLeft;
3717 } else if (me->window == frame.right_grip || zones == 1) {
3718 corner = BottomRight;
3719 } else {
3720 bool top;
3721 bool left = (me->x_root - frame.rect.x() <=
3722 static_cast<signed>(frame.rect.width() / 2));
3723 if (zones == 2)
3724 top = False;
3725 else // (zones == 4)
3726 top = (me->y_root - frame.rect.y() <=
3727 static_cast<signed>(frame.rect.height() / 2));
3728 corner = (top ? (left ? TopLeft : TopRight) :
3729 (left ? BottomLeft : BottomRight));
3730 }
3731
3732 beginResize(me->x_root, me->y_root, corner);
3733 }
3734 }
3735 }
3736
3737
3738 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3739 if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3740 return;
3741
3742 XEvent e;
3743 bool leave = False, inferior = False;
3744
3745 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3746 LeaveNotify, &e)) {
3747 if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3748 leave = True;
3749 inferior = (e.xcrossing.detail == NotifyInferior);
3750 }
3751 }
3752
3753 if (! leave || inferior) {
3754 if (! isFocused()) {
3755 bool success = setInputFocus();
3756 if (success) // if focus succeeded install the colormap
3757 installColormap(True); // XXX: shouldnt we honour no install?
3758 }
3759
3760 if (screen->doAutoRaise())
3761 timer->start();
3762 }
3763 }
3764
3765
3766 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3767 if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3768 return;
3769
3770 installColormap(False);
3771
3772 if (timer->isTiming())
3773 timer->stop();
3774 }
3775
3776
3777 #ifdef SHAPE
3778 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3779 if (blackbox->hasShapeExtensions() && flags.shaped) {
3780 configureShape();
3781 }
3782 }
3783 #endif // SHAPE
3784
3785
3786 bool BlackboxWindow::validateClient(void) const {
3787 XSync(blackbox->getXDisplay(), False);
3788
3789 XEvent e;
3790 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3791 DestroyNotify, &e) ||
3792 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3793 UnmapNotify, &e)) {
3794 XPutBackEvent(blackbox->getXDisplay(), &e);
3795
3796 return False;
3797 }
3798
3799 return True;
3800 }
3801
3802
3803 void BlackboxWindow::restore(bool remap) {
3804 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3805 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3806 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3807
3808 // do not leave a shaded window as an icon unless it was an icon
3809 if (flags.shaded && ! flags.iconic)
3810 setState(NormalState);
3811
3812 // erase the netwm stuff that we read when a window maps, so that it
3813 // doesn't persist between mappings.
3814 // (these are the ones read in getNetWMFlags().)
3815 xatom->eraseValue(client.window, XAtom::net_wm_desktop);
3816 xatom->eraseValue(client.window, XAtom::net_wm_state);
3817
3818 restoreGravity(client.rect);
3819
3820 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3821 XUnmapWindow(blackbox->getXDisplay(), client.window);
3822
3823 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3824
3825 XEvent ev;
3826 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3827 ReparentNotify, &ev)) {
3828 remap = True;
3829 } else {
3830 // according to the ICCCM - if the client doesn't reparent to
3831 // root, then we have to do it for them
3832 XReparentWindow(blackbox->getXDisplay(), client.window,
3833 screen->getRootWindow(),
3834 client.rect.x(), client.rect.y());
3835 }
3836
3837 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3838 }
3839
3840
3841 // timer for autoraise
3842 void BlackboxWindow::timeout(void) {
3843 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3844 }
3845
3846
3847 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3848 if ((net->flags & AttribShaded) &&
3849 ((blackbox_attrib.attrib & AttribShaded) !=
3850 (net->attrib & AttribShaded)))
3851 shade();
3852
3853 if (flags.visible && // watch out for requests when we can not be seen
3854 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3855 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3856 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3857 if (flags.maximized) {
3858 maximize(0);
3859 } else {
3860 int button = 0;
3861
3862 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3863 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3864 else if (net->flags & AttribMaxVert)
3865 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3866 else if (net->flags & AttribMaxHoriz)
3867 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3868
3869 maximize(button);
3870 }
3871 }
3872
3873 if ((net->flags & AttribOmnipresent) &&
3874 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3875 (net->attrib & AttribOmnipresent)))
3876 stick();
3877
3878 if ((net->flags & AttribWorkspace) &&
3879 (blackbox_attrib.workspace != net->workspace)) {
3880 screen->reassociateWindow(this, net->workspace, True);
3881
3882 if (screen->getCurrentWorkspaceID() != net->workspace) {
3883 withdraw();
3884 } else {
3885 show();
3886 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3887 }
3888 }
3889
3890 if (net->flags & AttribDecoration) {
3891 switch (net->decoration) {
3892 case DecorNone:
3893 enableDecor(False);
3894 break;
3895
3896 default:
3897 case DecorNormal:
3898 case DecorTiny:
3899 case DecorTool:
3900 enableDecor(True);
3901 break;
3902 }
3903 }
3904 }
3905
3906
3907 /*
3908 * Set the sizes of all components of the window frame
3909 * (the window decorations).
3910 * These values are based upon the current style settings and the client
3911 * window's dimensions.
3912 */
3913 void BlackboxWindow::upsize(void) {
3914 frame.bevel_w = screen->getBevelWidth();
3915
3916 if (decorations & Decor_Border) {
3917 frame.border_w = screen->getBorderWidth();
3918 if (! isTransient())
3919 frame.mwm_border_w = screen->getFrameWidth();
3920 else
3921 frame.mwm_border_w = 0;
3922 } else {
3923 frame.mwm_border_w = frame.border_w = 0;
3924 }
3925
3926 if (decorations & Decor_Titlebar) {
3927 // the height of the titlebar is based upon the height of the font being
3928 // used to display the window's title
3929 WindowStyle *style = screen->getWindowStyle();
3930 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3931
3932 frame.label_h = frame.title_h - (frame.bevel_w * 2);
3933 frame.button_w = (frame.label_h - 2);
3934
3935 // set the top frame margin
3936 frame.margin.top = frame.border_w + frame.title_h +
3937 frame.border_w + frame.mwm_border_w;
3938 } else {
3939 frame.title_h = 0;
3940 frame.label_h = 0;
3941 frame.button_w = 0;
3942
3943 // set the top frame margin
3944 frame.margin.top = frame.border_w + frame.mwm_border_w;
3945 }
3946
3947 // set the left/right frame margin
3948 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3949
3950 if (decorations & Decor_Handle) {
3951 frame.grip_w = frame.button_w * 2;
3952 frame.handle_h = screen->getHandleWidth();
3953
3954 // set the bottom frame margin
3955 frame.margin.bottom = frame.border_w + frame.handle_h +
3956 frame.border_w + frame.mwm_border_w;
3957 } else {
3958 frame.handle_h = 0;
3959 frame.grip_w = 0;
3960
3961 // set the bottom frame margin
3962 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3963 }
3964
3965 /*
3966 We first get the normal dimensions and use this to define the inside_w/h
3967 then we modify the height if shading is in effect.
3968 If the shade state is not considered then frame.rect gets reset to the
3969 normal window size on a reconfigure() call resulting in improper
3970 dimensions appearing in move/resize and other events.
3971 */
3972 unsigned int
3973 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3974 width = client.rect.width() + frame.margin.left + frame.margin.right;
3975
3976 frame.inside_w = width - (frame.border_w * 2);
3977 frame.inside_h = height - (frame.border_w * 2);
3978
3979 if (flags.shaded)
3980 height = frame.title_h + (frame.border_w * 2);
3981 frame.rect.setSize(width, height);
3982 }
3983
3984
3985 /*
3986 * Calculate the size of the client window and constrain it to the
3987 * size specified by the size hints of the client window.
3988 *
3989 * The logical width and height are placed into pw and ph, if they
3990 * are non-zero. Logical size refers to the users perception of
3991 * the window size (for example an xterm resizes in cells, not in pixels).
3992 * pw and ph are then used to display the geometry during window moves, resize,
3993 * etc.
3994 *
3995 * The physical geometry is placed into frame.changing_{x,y,width,height}.
3996 * Physical geometry refers to the geometry of the window in pixels.
3997 */
3998 void BlackboxWindow::constrain(Corner anchor,
3999 unsigned int *pw, unsigned int *ph) {
4000 // frame.changing represents the requested frame size, we need to
4001 // strip the frame margin off and constrain the client size
4002 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
4003 frame.changing.top() + frame.margin.top,
4004 frame.changing.right() - frame.margin.right,
4005 frame.changing.bottom() - frame.margin.bottom);
4006
4007 unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
4008 base_width = (client.base_width) ? client.base_width : client.min_width,
4009 base_height = (client.base_height) ? client.base_height :
4010 client.min_height;
4011
4012 // constrain
4013 if (dw < client.min_width) dw = client.min_width;
4014 if (dh < client.min_height) dh = client.min_height;
4015 if (dw > client.max_width) dw = client.max_width;
4016 if (dh > client.max_height) dh = client.max_height;
4017
4018 assert(dw >= base_width && dh >= base_height);
4019
4020 if (client.width_inc > 1) {
4021 dw -= base_width;
4022 dw /= client.width_inc;
4023 }
4024 if (client.height_inc > 1) {
4025 dh -= base_height;
4026 dh /= client.height_inc;
4027 }
4028
4029 if (pw)
4030 *pw = dw;
4031
4032 if (ph)
4033 *ph = dh;
4034
4035 if (client.width_inc > 1) {
4036 dw *= client.width_inc;
4037 dw += base_width;
4038 }
4039 if (client.height_inc > 1) {
4040 dh *= client.height_inc;
4041 dh += base_height;
4042 }
4043
4044 frame.changing.setSize(dw, dh);
4045
4046 // add the frame margin back onto frame.changing
4047 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4048 frame.changing.top() - frame.margin.top,
4049 frame.changing.right() + frame.margin.right,
4050 frame.changing.bottom() + frame.margin.bottom);
4051
4052 // move frame.changing to the specified anchor
4053 int dx = 0,
4054 dy = 0;
4055 switch (anchor) {
4056 case TopLeft:
4057 break;
4058
4059 case TopRight:
4060 dx = frame.rect.right() - frame.changing.right();
4061 break;
4062
4063 case BottomLeft:
4064 dy = frame.rect.bottom() - frame.changing.bottom();
4065 break;
4066
4067 case BottomRight:
4068 dx = frame.rect.right() - frame.changing.right();
4069 dy = frame.rect.bottom() - frame.changing.bottom();
4070 break;
4071
4072 default:
4073 assert(false); // unhandled corner
4074 }
4075 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4076 }
4077
4078
4079 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4080 unsigned int max_length,
4081 unsigned int modifier) const {
4082 size_t text_len = text.size();
4083 unsigned int length;
4084
4085 do {
4086 length = font->measureString(string(text, 0, text_len)) + modifier;
4087 } while (length > max_length && text_len-- > 0);
4088
4089 switch (justify) {
4090 case RightJustify:
4091 start_pos += max_length - length;
4092 break;
4093
4094 case CenterJustify:
4095 start_pos += (max_length - length) / 2;
4096 break;
4097
4098 case LeftJustify:
4099 default:
4100 break;
4101 }
4102 }
4103
4104
4105 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4106 : blackbox(b), group(_group) {
4107 XWindowAttributes wattrib;
4108 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4109 // group window doesn't seem to exist anymore
4110 delete this;
4111 return;
4112 }
4113
4114 XSelectInput(blackbox->getXDisplay(), group,
4115 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4116
4117 blackbox->saveGroupSearch(group, this);
4118 }
4119
4120
4121 BWindowGroup::~BWindowGroup(void) {
4122 blackbox->removeGroupSearch(group);
4123 }
4124
4125
4126 BlackboxWindow *
4127 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4128 BlackboxWindow *ret = blackbox->getFocusedWindow();
4129
4130 // does the focus window match (or any transient_fors)?
4131 for (; ret; ret = ret->getTransientFor()) {
4132 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4133 (! ret->isTransient() || allow_transients))
4134 break;
4135 }
4136
4137 if (ret) return ret;
4138
4139 // the focus window didn't match, look in the group's window list
4140 BlackboxWindowList::const_iterator it, end = windowList.end();
4141 for (it = windowList.begin(); it != end; ++it) {
4142 ret = *it;
4143 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4144 (! ret->isTransient() || allow_transients))
4145 break;
4146 }
4147
4148 return ret;
4149 }
This page took 0.207165 seconds and 3 git commands to generate.