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