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