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