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