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