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