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