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