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