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