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