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