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