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