]> Dogcows Code - chaz/openbox/blob - src/Window.cc
make the wheel mouse shade anywhere on the titlebar, and not press buttons. thanks...
[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 && be->button <= 3) {
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 be->window == frame.maximize_button ||
2822 be->window == frame.iconify_button ||
2823 be->window == frame.close_button) &&
2824 ! flags.shaded)
2825 shade();
2826 // mouse wheel down
2827 } else if (be->button == 5) {
2828 if ((be->window == frame.label ||
2829 be->window == frame.title ||
2830 be->window == frame.maximize_button ||
2831 be->window == frame.iconify_button ||
2832 be->window == frame.close_button) &&
2833 flags.shaded)
2834 shade();
2835 }
2836 }
2837
2838
2839 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2840 #ifdef DEBUG
2841 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2842 client.window);
2843 #endif
2844
2845 if (re->window == frame.maximize_button &&
2846 re->button >= 1 && re->button <= 3) {
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 maximize(re->button);
2850 } else {
2851 redrawMaximizeButton(flags.maximized);
2852 }
2853 } else if (re->window == frame.iconify_button && re->button == 1) {
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 iconify();
2857 } else {
2858 redrawIconifyButton(False);
2859 }
2860 } else if (re->window == frame.close_button & re->button == 1) {
2861 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2862 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2863 close();
2864 redrawCloseButton(False);
2865 } else if (flags.moving) {
2866 endMove();
2867 } else if (flags.resizing) {
2868 endResize();
2869 } else if (re->window == frame.window) {
2870 if (re->button == 2 && re->state == Mod1Mask)
2871 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2872 }
2873 }
2874
2875
2876
2877 void BlackboxWindow::beginMove(int x_root, int y_root) {
2878 assert(! (flags.resizing || flags.moving));
2879
2880 /*
2881 Only one window can be moved/resized at a time. If another window is already
2882 being moved or resized, then stop it before whating to work with this one.
2883 */
2884 BlackboxWindow *changing = blackbox->getChangingWindow();
2885 if (changing && changing != this) {
2886 if (changing->flags.moving)
2887 changing->endMove();
2888 else // if (changing->flags.resizing)
2889 changing->endResize();
2890 }
2891
2892 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
2893 PointerMotionMask | ButtonReleaseMask,
2894 GrabModeAsync, GrabModeAsync,
2895 None, blackbox->getMoveCursor(), CurrentTime);
2896
2897 if (windowmenu && windowmenu->isVisible())
2898 windowmenu->hide();
2899
2900 flags.moving = True;
2901 blackbox->setChangingWindow(this);
2902
2903 if (! screen->doOpaqueMove()) {
2904 XGrabServer(blackbox->getXDisplay());
2905
2906 frame.changing = frame.rect;
2907 screen->showPosition(frame.changing.x(), frame.changing.y());
2908
2909 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2910 screen->getOpGC(),
2911 frame.changing.x(),
2912 frame.changing.y(),
2913 frame.changing.width() - 1,
2914 frame.changing.height() - 1);
2915 }
2916
2917 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
2918 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
2919 }
2920
2921
2922 void BlackboxWindow::doMove(int x_root, int y_root) {
2923 assert(flags.moving);
2924 assert(blackbox->getChangingWindow() == this);
2925
2926 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
2927 dx -= frame.border_w;
2928 dy -= frame.border_w;
2929
2930 const int snap_distance = screen->getEdgeSnapThreshold();
2931
2932 if (snap_distance) {
2933 // window corners
2934 const int wleft = dx,
2935 wright = dx + frame.rect.width() - 1,
2936 wtop = dy,
2937 wbottom = dy + frame.rect.height() - 1;
2938
2939 if (screen->getWindowToWindowSnap()) {
2940 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
2941 assert(w);
2942
2943 // try snap to another window
2944 for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
2945 BlackboxWindow *snapwin = w->getWindow(i);
2946 if (snapwin == this)
2947 continue; // don't snap to self
2948
2949 bool snapped = False;
2950
2951 const Rect &winrect = snapwin->frameRect();
2952 int dleft = std::abs(wright - winrect.left()),
2953 dright = std::abs(wleft - winrect.right()),
2954 dtop = std::abs(wbottom - winrect.top()),
2955 dbottom = std::abs(wtop - winrect.bottom());
2956
2957 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
2958 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
2959
2960 // snap left of other window?
2961 if (dleft < snap_distance && dleft <= dright) {
2962 dx = winrect.left() - frame.rect.width();
2963 snapped = True;
2964 }
2965 // snap right of other window?
2966 else if (dright < snap_distance) {
2967 dx = winrect.right() + 1;
2968 snapped = True;
2969 }
2970
2971 if (snapped) {
2972 if (screen->getWindowCornerSnap()) {
2973 // try corner-snap to its other sides
2974 dtop = std::abs(wtop - winrect.top());
2975 dbottom = std::abs(wbottom - winrect.bottom());
2976 if (dtop < snap_distance && dtop <= dbottom)
2977 dy = winrect.top();
2978 else if (dbottom < snap_distance)
2979 dy = winrect.bottom() - frame.rect.height() + 1;
2980 }
2981
2982 continue;
2983 }
2984 }
2985
2986 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
2987 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
2988
2989 // snap top of other window?
2990 if (dtop < snap_distance && dtop <= dbottom) {
2991 dy = winrect.top() - frame.rect.height();
2992 snapped = True;
2993 }
2994 // snap bottom of other window?
2995 else if (dbottom < snap_distance) {
2996 dy = winrect.bottom() + 1;
2997 snapped = True;
2998 }
2999
3000 if (snapped) {
3001 if (screen->getWindowCornerSnap()) {
3002 // try corner-snap to its other sides
3003 dleft = std::abs(wleft - winrect.left());
3004 dright = std::abs(wright - winrect.right());
3005 if (dleft < snap_distance && dleft <= dright)
3006 dx = winrect.left();
3007 else if (dright < snap_distance)
3008 dx = winrect.right() - frame.rect.width() + 1;
3009 }
3010
3011 continue;
3012 }
3013 }
3014 }
3015 }
3016
3017 // try snap to the screen's available area
3018 Rect srect = screen->availableArea();
3019
3020 int dleft = std::abs(wleft - srect.left()),
3021 dright = std::abs(wright - srect.right()),
3022 dtop = std::abs(wtop - srect.top()),
3023 dbottom = std::abs(wbottom - srect.bottom());
3024
3025 // snap left?
3026 if (dleft < snap_distance && dleft <= dright)
3027 dx = srect.left();
3028 // snap right?
3029 else if (dright < snap_distance)
3030 dx = srect.right() - frame.rect.width() + 1;
3031
3032 // snap top?
3033 if (dtop < snap_distance && dtop <= dbottom)
3034 dy = srect.top();
3035 // snap bottom?
3036 else if (dbottom < snap_distance)
3037 dy = srect.bottom() - frame.rect.height() + 1;
3038
3039 srect = screen->getRect(); // now get the full screen
3040
3041 dleft = std::abs(wleft - srect.left()),
3042 dright = std::abs(wright - srect.right()),
3043 dtop = std::abs(wtop - srect.top()),
3044 dbottom = std::abs(wbottom - srect.bottom());
3045
3046 // snap left?
3047 if (dleft < snap_distance && dleft <= dright)
3048 dx = srect.left();
3049 // snap right?
3050 else if (dright < snap_distance)
3051 dx = srect.right() - frame.rect.width() + 1;
3052
3053 // snap top?
3054 if (dtop < snap_distance && dtop <= dbottom)
3055 dy = srect.top();
3056 // snap bottom?
3057 else if (dbottom < snap_distance)
3058 dy = srect.bottom() - frame.rect.height() + 1;
3059 }
3060
3061 if (screen->doOpaqueMove()) {
3062 configure(dx, dy, frame.rect.width(), frame.rect.height());
3063 } else {
3064 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3065 screen->getOpGC(),
3066 frame.changing.x(),
3067 frame.changing.y(),
3068 frame.changing.width() - 1,
3069 frame.changing.height() - 1);
3070
3071 frame.changing.setPos(dx, dy);
3072
3073 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3074 screen->getOpGC(),
3075 frame.changing.x(),
3076 frame.changing.y(),
3077 frame.changing.width() - 1,
3078 frame.changing.height() - 1);
3079 }
3080
3081 screen->showPosition(dx, dy);
3082 }
3083
3084
3085 void BlackboxWindow::endMove(void) {
3086 assert(flags.moving);
3087 assert(blackbox->getChangingWindow() == this);
3088
3089 flags.moving = False;
3090 blackbox->setChangingWindow(0);
3091
3092 if (! screen->doOpaqueMove()) {
3093 /* when drawing the rubber band, we need to make sure we only draw inside
3094 * the frame... frame.changing_* contain the new coords for the window,
3095 * so we need to subtract 1 from changing_w/changing_h every where we
3096 * draw the rubber band (for both moving and resizing)
3097 */
3098 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3099 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3100 frame.changing.width() - 1, frame.changing.height() - 1);
3101 XUngrabServer(blackbox->getXDisplay());
3102
3103 configure(frame.changing.x(), frame.changing.y(),
3104 frame.changing.width(), frame.changing.height());
3105 } else {
3106 configure(frame.rect.x(), frame.rect.y(),
3107 frame.rect.width(), frame.rect.height());
3108 }
3109 screen->hideGeometry();
3110
3111 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3112
3113 // if there are any left over motions from the move, drop them now
3114 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3115 XEvent e;
3116 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3117 MotionNotify, &e));
3118 }
3119
3120
3121 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3122 assert(! (flags.resizing || flags.moving));
3123
3124 /*
3125 Only one window can be moved/resized at a time. If another window is already
3126 being moved or resized, then stop it before whating to work with this one.
3127 */
3128 BlackboxWindow *changing = blackbox->getChangingWindow();
3129 if (changing && changing != this) {
3130 if (changing->flags.moving)
3131 changing->endMove();
3132 else // if (changing->flags.resizing)
3133 changing->endResize();
3134 }
3135
3136 resize_dir = dir;
3137
3138 Cursor cursor;
3139 Corner anchor;
3140
3141 switch (resize_dir) {
3142 case BottomLeft:
3143 anchor = TopRight;
3144 cursor = blackbox->getLowerLeftAngleCursor();
3145 break;
3146
3147 case BottomRight:
3148 anchor = TopLeft;
3149 cursor = blackbox->getLowerRightAngleCursor();
3150 break;
3151
3152 case TopLeft:
3153 anchor = BottomRight;
3154 cursor = blackbox->getUpperLeftAngleCursor();
3155 break;
3156
3157 case TopRight:
3158 anchor = BottomLeft;
3159 cursor = blackbox->getUpperRightAngleCursor();
3160 break;
3161
3162 default:
3163 assert(false); // unhandled Corner
3164 return; // unreachable, for the compiler
3165 }
3166
3167 XGrabServer(blackbox->getXDisplay());
3168 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3169 PointerMotionMask | ButtonReleaseMask,
3170 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3171
3172 flags.resizing = True;
3173 blackbox->setChangingWindow(this);
3174
3175 int gw, gh;
3176 frame.changing = frame.rect;
3177
3178 constrain(anchor, &gw, &gh);
3179
3180 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3181 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3182 frame.changing.width() - 1, frame.changing.height() - 1);
3183
3184 screen->showGeometry(gw, gh);
3185
3186 frame.grab_x = x_root;
3187 frame.grab_y = y_root;
3188 }
3189
3190
3191 void BlackboxWindow::doResize(int x_root, int y_root) {
3192 assert(flags.resizing);
3193 assert(blackbox->getChangingWindow() == this);
3194
3195 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3196 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3197 frame.changing.width() - 1, frame.changing.height() - 1);
3198
3199 int gw, gh;
3200 Corner anchor;
3201
3202 switch (resize_dir) {
3203 case BottomLeft:
3204 anchor = TopRight;
3205 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3206 frame.rect.height() + (y_root - frame.grab_y));
3207 break;
3208 case BottomRight:
3209 anchor = TopLeft;
3210 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3211 frame.rect.height() + (y_root - frame.grab_y));
3212 break;
3213 case TopLeft:
3214 anchor = BottomRight;
3215 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3216 frame.rect.height() - (y_root - frame.grab_y));
3217 break;
3218 case TopRight:
3219 anchor = BottomLeft;
3220 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3221 frame.rect.height() - (y_root - frame.grab_y));
3222 break;
3223
3224 default:
3225 assert(false); // unhandled Corner
3226 return; // unreachable, for the compiler
3227 }
3228
3229 constrain(anchor, &gw, &gh);
3230
3231 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3232 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3233 frame.changing.width() - 1, frame.changing.height() - 1);
3234
3235 screen->showGeometry(gw, gh);
3236 }
3237
3238
3239 void BlackboxWindow::endResize(void) {
3240 assert(flags.resizing);
3241 assert(blackbox->getChangingWindow() == this);
3242
3243 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3244 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3245 frame.changing.width() - 1, frame.changing.height() - 1);
3246 XUngrabServer(blackbox->getXDisplay());
3247
3248 // unset maximized state after resized when fully maximized
3249 if (flags.maximized == 1)
3250 maximize(0);
3251
3252 flags.resizing = False;
3253 blackbox->setChangingWindow(0);
3254
3255 configure(frame.changing.x(), frame.changing.y(),
3256 frame.changing.width(), frame.changing.height());
3257 screen->hideGeometry();
3258
3259 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3260
3261 // if there are any left over motions from the resize, drop them now
3262 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3263 XEvent e;
3264 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3265 MotionNotify, &e));
3266 }
3267
3268
3269 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3270 #ifdef DEBUG
3271 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3272 client.window);
3273 #endif
3274
3275 if (flags.moving) {
3276 doMove(me->x_root, me->y_root);
3277 } else if (flags.resizing) {
3278 doResize(me->x_root, me->y_root);
3279 } else {
3280 if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3281 (frame.title == me->window || frame.label == me->window ||
3282 frame.handle == me->window || frame.window == me->window)) {
3283 beginMove(me->x_root, me->y_root);
3284 } else if ((functions & Func_Resize) &&
3285 (me->state & Button1Mask && (me->window == frame.right_grip ||
3286 me->window == frame.left_grip)) ||
3287 (me->state & Button3Mask && me->state & Mod1Mask &&
3288 me->window == frame.window)) {
3289 unsigned int zones = screen->getResizeZones();
3290 Corner corner;
3291
3292 if (me->window == frame.left_grip) {
3293 corner = BottomLeft;
3294 } else if (me->window == frame.right_grip || zones == 1) {
3295 corner = BottomRight;
3296 } else {
3297 bool top;
3298 bool left = (me->x_root - frame.rect.x() <=
3299 static_cast<signed>(frame.rect.width() / 2));
3300 if (zones == 2)
3301 top = False;
3302 else // (zones == 4)
3303 top = (me->y_root - frame.rect.y() <=
3304 static_cast<signed>(frame.rect.height() / 2));
3305 corner = (top ? (left ? TopLeft : TopRight) :
3306 (left ? BottomLeft : BottomRight));
3307 }
3308
3309 beginResize(me->x_root, me->y_root, corner);
3310 }
3311 }
3312 }
3313
3314
3315 #ifdef SHAPE
3316 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3317 if (blackbox->hasShapeExtensions() && flags.shaped) {
3318 configureShape();
3319 }
3320 }
3321 #endif // SHAPE
3322
3323
3324 bool BlackboxWindow::validateClient(void) const {
3325 XSync(blackbox->getXDisplay(), False);
3326
3327 XEvent e;
3328 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3329 DestroyNotify, &e) ||
3330 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3331 UnmapNotify, &e)) {
3332 XPutBackEvent(blackbox->getXDisplay(), &e);
3333
3334 return False;
3335 }
3336
3337 return True;
3338 }
3339
3340
3341 void BlackboxWindow::restore(bool remap) {
3342 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3343 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3344 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3345
3346 // do not leave a shaded window as an icon unless it was an icon
3347 if (flags.shaded && ! flags.iconic) setState(NormalState);
3348
3349 restoreGravity(client.rect);
3350
3351 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3352 XUnmapWindow(blackbox->getXDisplay(), client.window);
3353
3354 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3355
3356 XEvent ev;
3357 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3358 ReparentNotify, &ev)) {
3359 remap = True;
3360 } else {
3361 // according to the ICCCM - if the client doesn't reparent to
3362 // root, then we have to do it for them
3363 XReparentWindow(blackbox->getXDisplay(), client.window,
3364 screen->getRootWindow(),
3365 client.rect.x(), client.rect.y());
3366 }
3367
3368 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3369 }
3370
3371
3372 // timer for autoraise
3373 void BlackboxWindow::timeout(void) {
3374 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3375 }
3376
3377
3378 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
3379 if ((net->flags & AttribShaded) &&
3380 ((blackbox_attrib.attrib & AttribShaded) !=
3381 (net->attrib & AttribShaded)))
3382 shade();
3383
3384 if (flags.visible && // watch out for requests when we can not be seen
3385 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3386 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3387 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3388 if (flags.maximized) {
3389 maximize(0);
3390 } else {
3391 int button = 0;
3392
3393 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3394 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3395 else if (net->flags & AttribMaxVert)
3396 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3397 else if (net->flags & AttribMaxHoriz)
3398 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3399
3400 maximize(button);
3401 }
3402 }
3403
3404 if ((net->flags & AttribOmnipresent) &&
3405 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3406 (net->attrib & AttribOmnipresent)))
3407 stick();
3408
3409 if ((net->flags & AttribWorkspace) &&
3410 (blackbox_attrib.workspace != net->workspace)) {
3411 screen->reassociateWindow(this, net->workspace, True);
3412
3413 if (screen->getCurrentWorkspaceID() != net->workspace) {
3414 withdraw();
3415 } else {
3416 show();
3417 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3418 }
3419 }
3420
3421 if (net->flags & AttribDecoration) {
3422 switch (net->decoration) {
3423 case DecorNone:
3424 // clear all decorations except close
3425 decorations &= Decor_Close;
3426
3427 break;
3428
3429 default:
3430 case DecorNormal:
3431 decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3432
3433 decorations = ((functions & Func_Resize) && !isTransient() ?
3434 decorations | Decor_Handle :
3435 decorations &= ~Decor_Handle);
3436 decorations = (functions & Func_Maximize ?
3437 decorations | Decor_Maximize :
3438 decorations &= ~Decor_Maximize);
3439
3440 break;
3441
3442 case DecorTiny:
3443 decorations |= Decor_Titlebar | Decor_Iconify;
3444 decorations &= ~(Decor_Border | Decor_Handle);
3445
3446 decorations = (functions & Func_Maximize ?
3447 decorations | Decor_Maximize :
3448 decorations &= ~Decor_Maximize);
3449
3450 break;
3451
3452 case DecorTool:
3453 decorations |= Decor_Titlebar;
3454 decorations &= ~(Decor_Iconify | Decor_Border);
3455
3456 decorations = ((functions & Func_Resize) && !isTransient() ?
3457 decorations | Decor_Handle :
3458 decorations &= ~Decor_Handle);
3459 decorations = (functions & Func_Maximize ?
3460 decorations | Decor_Maximize :
3461 decorations &= ~Decor_Maximize);
3462
3463 break;
3464 }
3465
3466 // we can not be shaded if we lack a titlebar
3467 if (flags.shaded && ! (decorations & Decor_Titlebar))
3468 shade();
3469
3470 if (flags.visible && frame.window) {
3471 XMapSubwindows(blackbox->getXDisplay(), frame.window);
3472 XMapWindow(blackbox->getXDisplay(), frame.window);
3473 }
3474
3475 reconfigure();
3476 setState(current_state);
3477 }
3478 }
3479
3480
3481 /*
3482 * Set the sizes of all components of the window frame
3483 * (the window decorations).
3484 * These values are based upon the current style settings and the client
3485 * window's dimensions.
3486 */
3487 void BlackboxWindow::upsize(void) {
3488 frame.bevel_w = screen->getBevelWidth();
3489
3490 if (decorations & Decor_Border) {
3491 frame.border_w = screen->getBorderWidth();
3492 if (! isTransient())
3493 frame.mwm_border_w = screen->getFrameWidth();
3494 else
3495 frame.mwm_border_w = 0;
3496 } else {
3497 frame.mwm_border_w = frame.border_w = 0;
3498 }
3499
3500 if (decorations & Decor_Titlebar) {
3501 // the height of the titlebar is based upon the height of the font being
3502 // used to display the window's title
3503 WindowStyle *style = screen->getWindowStyle();
3504 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3505
3506 frame.label_h = frame.title_h - (frame.bevel_w * 2);
3507 frame.button_w = (frame.label_h - 2);
3508
3509 // set the top frame margin
3510 frame.margin.top = frame.border_w + frame.title_h +
3511 frame.border_w + frame.mwm_border_w;
3512 } else {
3513 frame.title_h = 0;
3514 frame.label_h = 0;
3515 frame.button_w = 0;
3516
3517 // set the top frame margin
3518 frame.margin.top = frame.border_w + frame.mwm_border_w;
3519 }
3520
3521 // set the left/right frame margin
3522 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3523
3524 if (decorations & Decor_Handle) {
3525 frame.grip_w = frame.button_w * 2;
3526 frame.handle_h = screen->getHandleWidth();
3527
3528 // set the bottom frame margin
3529 frame.margin.bottom = frame.border_w + frame.handle_h +
3530 frame.border_w + frame.mwm_border_w;
3531 } else {
3532 frame.handle_h = 0;
3533 frame.grip_w = 0;
3534
3535 // set the bottom frame margin
3536 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3537 }
3538
3539 /*
3540 We first get the normal dimensions and use this to define the inside_w/h
3541 then we modify the height if shading is in effect.
3542 If the shade state is not considered then frame.rect gets reset to the
3543 normal window size on a reconfigure() call resulting in improper
3544 dimensions appearing in move/resize and other events.
3545 */
3546 unsigned int
3547 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3548 width = client.rect.width() + frame.margin.left + frame.margin.right;
3549
3550 frame.inside_w = width - (frame.border_w * 2);
3551 frame.inside_h = height - (frame.border_w * 2);
3552
3553 if (flags.shaded)
3554 height = frame.title_h + (frame.border_w * 2);
3555 frame.rect.setSize(width, height);
3556 }
3557
3558
3559 /*
3560 * Calculate the size of the client window and constrain it to the
3561 * size specified by the size hints of the client window.
3562 *
3563 * The logical width and height are placed into pw and ph, if they
3564 * are non-zero. Logical size refers to the users perception of
3565 * the window size (for example an xterm resizes in cells, not in pixels).
3566 *
3567 * The physical geometry is placed into frame.changing_{x,y,width,height}.
3568 * Physical geometry refers to the geometry of the window in pixels.
3569 */
3570 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3571 // frame.changing represents the requested frame size, we need to
3572 // strip the frame margin off and constrain the client size
3573 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3574 frame.changing.top() + frame.margin.top,
3575 frame.changing.right() - frame.margin.right,
3576 frame.changing.bottom() - frame.margin.bottom);
3577
3578 int dw = frame.changing.width(), dh = frame.changing.height(),
3579 base_width = (client.base_width) ? client.base_width : client.min_width,
3580 base_height = (client.base_height) ? client.base_height :
3581 client.min_height;
3582
3583 // constrain
3584 if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3585 if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3586 if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3587 if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3588
3589 dw -= base_width;
3590 dw /= client.width_inc;
3591 dh -= base_height;
3592 dh /= client.height_inc;
3593
3594 if (pw) {
3595 if (client.width_inc == 1)
3596 *pw = dw + base_width;
3597 else
3598 *pw = dw;
3599 }
3600 if (ph) {
3601 if (client.height_inc == 1)
3602 *ph = dh + base_height;
3603 else
3604 *ph = dh;
3605 }
3606
3607 dw *= client.width_inc;
3608 dw += base_width;
3609 dh *= client.height_inc;
3610 dh += base_height;
3611
3612 frame.changing.setSize(dw, dh);
3613
3614 // add the frame margin back onto frame.changing
3615 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3616 frame.changing.top() - frame.margin.top,
3617 frame.changing.right() + frame.margin.right,
3618 frame.changing.bottom() + frame.margin.bottom);
3619
3620 // move frame.changing to the specified anchor
3621 int dx = 0,
3622 dy = 0;
3623 switch (anchor) {
3624 case TopLeft:
3625 break;
3626
3627 case TopRight:
3628 dx = frame.rect.right() - frame.changing.right();
3629 break;
3630
3631 case BottomLeft:
3632 dy = frame.rect.bottom() - frame.changing.bottom();
3633 break;
3634
3635 case BottomRight:
3636 dx = frame.rect.right() - frame.changing.right();
3637 dy = frame.rect.bottom() - frame.changing.bottom();
3638 break;
3639
3640 default:
3641 assert(false); // unhandled corner
3642 }
3643 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3644 }
3645
3646
3647 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3648 unsigned int max_length,
3649 unsigned int modifier) const {
3650 size_t text_len = text.size();
3651 unsigned int length;
3652
3653 do {
3654 length = font->measureString(string(text, 0, text_len)) + modifier;
3655 } while (length > max_length && text_len-- > 0);
3656
3657 switch (justify) {
3658 case RightJustify:
3659 start_pos += max_length - length;
3660 break;
3661
3662 case CenterJustify:
3663 start_pos += (max_length - length) / 2;
3664 break;
3665
3666 case LeftJustify:
3667 default:
3668 break;
3669 }
3670 }
3671
3672
3673 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3674 : blackbox(b), group(_group) {
3675 XWindowAttributes wattrib;
3676 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3677 // group window doesn't seem to exist anymore
3678 delete this;
3679 return;
3680 }
3681
3682 /*
3683 watch for destroy notify on the group window (in addition to
3684 any other events we are looking for)
3685
3686 since some managed windows can also be window group controllers,
3687 we need to make sure that we don't clobber the event mask for the
3688 managed window
3689 */
3690 XSelectInput(blackbox->getXDisplay(), group,
3691 wattrib.your_event_mask | StructureNotifyMask);
3692
3693 blackbox->saveGroupSearch(group, this);
3694 }
3695
3696
3697 BWindowGroup::~BWindowGroup(void) {
3698 blackbox->removeGroupSearch(group);
3699 }
3700
3701
3702 BlackboxWindow *
3703 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3704 BlackboxWindow *ret = blackbox->getFocusedWindow();
3705
3706 // does the focus window match (or any transient_fors)?
3707 while (ret) {
3708 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3709 if (ret->isTransient() && allow_transients) break;
3710 else if (! ret->isTransient()) break;
3711 }
3712
3713 ret = ret->getTransientFor();
3714 }
3715
3716 if (ret) return ret;
3717
3718 // the focus window didn't match, look in the group's window list
3719 BlackboxWindowList::const_iterator it, end = windowList.end();
3720 for (it = windowList.begin(); it != end; ++it) {
3721 ret = *it;
3722 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3723 if (ret->isTransient() && allow_transients) break;
3724 else if (! ret->isTransient()) break;
3725 }
3726 }
3727
3728 return ret;
3729 }
This page took 0.207609 seconds and 5 git commands to generate.