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