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