]> Dogcows Code - chaz/openbox/blob - src/Window.cc
add Configuration class for generic configuration data load/save-ing.
[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 const Rect& screen_area = screen->availableArea();
910
911 client.min_width = client.min_height =
912 client.width_inc = client.height_inc = 1;
913 client.base_width = client.base_height = 0;
914 client.max_width = screen_area.width();
915 client.max_height = screen_area.height();
916 client.min_aspect_x = client.min_aspect_y =
917 client.max_aspect_x = client.max_aspect_y = 1;
918 client.win_gravity = NorthWestGravity;
919
920 if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
921 &sizehint, &icccm_mask))
922 return;
923
924 client.normal_hint_flags = sizehint.flags;
925
926 if (sizehint.flags & PMinSize) {
927 client.min_width = sizehint.min_width;
928 client.min_height = sizehint.min_height;
929 }
930
931 if (sizehint.flags & PMaxSize) {
932 client.max_width = sizehint.max_width;
933 client.max_height = sizehint.max_height;
934 }
935
936 if (sizehint.flags & PResizeInc) {
937 client.width_inc = sizehint.width_inc;
938 client.height_inc = sizehint.height_inc;
939 }
940
941 if (sizehint.flags & PAspect) {
942 client.min_aspect_x = sizehint.min_aspect.x;
943 client.min_aspect_y = sizehint.min_aspect.y;
944 client.max_aspect_x = sizehint.max_aspect.x;
945 client.max_aspect_y = sizehint.max_aspect.y;
946 }
947
948 if (sizehint.flags & PBaseSize) {
949 client.base_width = sizehint.base_width;
950 client.base_height = sizehint.base_height;
951 }
952
953 if (sizehint.flags & PWinGravity)
954 client.win_gravity = sizehint.win_gravity;
955 }
956
957
958 /*
959 * Gets the MWM hints for the class' contained window.
960 * This is used while initializing the window to its first state, and not
961 * thereafter.
962 * Returns: true if the MWM hints are successfully retreived and applied;
963 * false if they are not.
964 */
965 void BlackboxWindow::getMWMHints(void) {
966 int format;
967 Atom atom_return;
968 unsigned long num, len;
969 MwmHints *mwm_hint = 0;
970
971 int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
972 blackbox->getMotifWMHintsAtom(), 0,
973 PropMwmHintsElements, False,
974 blackbox->getMotifWMHintsAtom(), &atom_return,
975 &format, &num, &len,
976 (unsigned char **) &mwm_hint);
977
978 if (ret != Success || ! mwm_hint || num != PropMwmHintsElements)
979 return;
980
981 if (mwm_hint->flags & MwmHintsDecorations) {
982 if (mwm_hint->decorations & MwmDecorAll) {
983 decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
984 Decor_Iconify | Decor_Maximize | Decor_Close;
985 } else {
986 decorations = 0;
987
988 if (mwm_hint->decorations & MwmDecorBorder)
989 decorations |= Decor_Border;
990 if (mwm_hint->decorations & MwmDecorHandle)
991 decorations |= Decor_Handle;
992 if (mwm_hint->decorations & MwmDecorTitle)
993 decorations |= Decor_Titlebar;
994 if (mwm_hint->decorations & MwmDecorIconify)
995 decorations |= Decor_Iconify;
996 if (mwm_hint->decorations & MwmDecorMaximize)
997 decorations |= Decor_Maximize;
998 }
999 }
1000
1001 if (mwm_hint->flags & MwmHintsFunctions) {
1002 if (mwm_hint->functions & MwmFuncAll) {
1003 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1004 Func_Close;
1005 } else {
1006 functions = 0;
1007
1008 if (mwm_hint->functions & MwmFuncResize)
1009 functions |= Func_Resize;
1010 if (mwm_hint->functions & MwmFuncMove)
1011 functions |= Func_Move;
1012 if (mwm_hint->functions & MwmFuncIconify)
1013 functions |= Func_Iconify;
1014 if (mwm_hint->functions & MwmFuncMaximize)
1015 functions |= Func_Maximize;
1016 if (mwm_hint->functions & MwmFuncClose)
1017 functions |= Func_Close;
1018 }
1019 }
1020 XFree(mwm_hint);
1021 }
1022
1023
1024 /*
1025 * Gets the blackbox hints from the class' contained window.
1026 * This is used while initializing the window to its first state, and not
1027 * thereafter.
1028 * Returns: true if the hints are successfully retreived and applied; false if
1029 * they are not.
1030 */
1031 bool BlackboxWindow::getBlackboxHints(void) {
1032 int format;
1033 Atom atom_return;
1034 unsigned long num, len;
1035 BlackboxHints *blackbox_hint = 0;
1036
1037 int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1038 blackbox->getBlackboxHintsAtom(), 0,
1039 PropBlackboxHintsElements, False,
1040 blackbox->getBlackboxHintsAtom(), &atom_return,
1041 &format, &num, &len,
1042 (unsigned char **) &blackbox_hint);
1043 if (ret != Success || ! blackbox_hint || num != PropBlackboxHintsElements)
1044 return False;
1045
1046 if (blackbox_hint->flags & AttribShaded)
1047 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1048
1049 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1050 (blackbox_hint->flags & AttribMaxVert))
1051 flags.maximized = (blackbox_hint->attrib &
1052 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1053 else if (blackbox_hint->flags & AttribMaxVert)
1054 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1055 else if (blackbox_hint->flags & AttribMaxHoriz)
1056 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1057
1058 if (blackbox_hint->flags & AttribOmnipresent)
1059 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1060
1061 if (blackbox_hint->flags & AttribWorkspace)
1062 blackbox_attrib.workspace = blackbox_hint->workspace;
1063
1064 // if (blackbox_hint->flags & AttribStack)
1065 // don't yet have always on top/bottom for blackbox yet... working
1066 // on that
1067
1068 if (blackbox_hint->flags & AttribDecoration) {
1069 switch (blackbox_hint->decoration) {
1070 case DecorNone:
1071 // clear all decorations except close
1072 decorations &= Decor_Close;
1073 // clear all functions except close
1074 functions &= Func_Close;
1075
1076 break;
1077
1078 case DecorTiny:
1079 decorations |= Decor_Titlebar | Decor_Iconify;
1080 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1081 functions |= Func_Move | Func_Iconify;
1082 functions &= ~(Func_Resize | Func_Maximize);
1083
1084 break;
1085
1086 case DecorTool:
1087 decorations |= Decor_Titlebar;
1088 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1089 functions |= Func_Move;
1090 functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1091
1092 break;
1093
1094 case DecorNormal:
1095 default:
1096 decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1097 Decor_Iconify | Decor_Maximize;
1098 functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
1099
1100 break;
1101 }
1102
1103 reconfigure();
1104 }
1105 XFree(blackbox_hint);
1106 return True;
1107 }
1108
1109
1110 void BlackboxWindow::getTransientInfo(void) {
1111 if (client.transient_for &&
1112 client.transient_for != (BlackboxWindow *) ~0ul) {
1113 // the transient for hint was removed, so we need to tell our
1114 // previous transient_for that we are going away
1115 client.transient_for->client.transientList.remove(this);
1116 }
1117
1118 // we have no transient_for until we find a new one
1119 client.transient_for = 0;
1120
1121 Window trans_for;
1122 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1123 &trans_for)) {
1124 // transient_for hint not set
1125 return;
1126 }
1127
1128 if (trans_for == client.window) {
1129 // wierd client... treat this window as a normal window
1130 return;
1131 }
1132
1133 if (trans_for == None || trans_for == screen->getRootWindow()) {
1134 // this is an undocumented interpretation of the ICCCM. a transient
1135 // associated with None/Root/itself is assumed to be a modal root
1136 // transient. we don't support the concept of a global transient,
1137 // so we just associate this transient with nothing, and perhaps
1138 // we will add support later for global modality.
1139 client.transient_for = (BlackboxWindow *) ~0ul;
1140 flags.modal = True;
1141 return;
1142 }
1143
1144 client.transient_for = blackbox->searchWindow(trans_for);
1145 if (! client.transient_for &&
1146 client.window_group && trans_for == client.window_group) {
1147 // no direct transient_for, perhaps this is a group transient?
1148 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1149 if (group) client.transient_for = group->find(screen);
1150 }
1151
1152 if (! client.transient_for || client.transient_for == this) {
1153 // no transient_for found, or we have a wierd client that wants to be
1154 // a transient for itself, so we treat this window as a normal window
1155 client.transient_for = (BlackboxWindow*) 0;
1156 return;
1157 }
1158
1159 // register ourselves with our new transient_for
1160 client.transient_for->client.transientList.push_back(this);
1161 flags.stuck = client.transient_for->flags.stuck;
1162 }
1163
1164
1165 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1166 if (client.transient_for &&
1167 client.transient_for != (BlackboxWindow*) ~0ul)
1168 return client.transient_for;
1169 return 0;
1170 }
1171
1172
1173 void BlackboxWindow::configure(int dx, int dy,
1174 unsigned int dw, unsigned int dh) {
1175 bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
1176
1177 if ((dw != frame.rect.width()) || (dh != frame.rect.height())) {
1178 frame.rect.setRect(dx, dy, dw, dh);
1179 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1180 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1181
1182 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1183 frame.rect.setPos(0, 0);
1184
1185 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1186 frame.rect.top() + frame.margin.top,
1187 frame.rect.right() - frame.margin.right,
1188 frame.rect.bottom() - frame.margin.bottom);
1189
1190 #ifdef SHAPE
1191 if (blackbox->hasShapeExtensions() && flags.shaped) {
1192 configureShape();
1193 }
1194 #endif // SHAPE
1195
1196 positionWindows();
1197 decorate();
1198 setFocusFlag(flags.focused);
1199 redrawAllButtons();
1200 } else {
1201 frame.rect.setPos(dx, dy);
1202
1203 XMoveWindow(blackbox->getXDisplay(), frame.window,
1204 frame.rect.x(), frame.rect.y());
1205
1206 if (! flags.moving) send_event = True;
1207 }
1208
1209 if (send_event && ! flags.moving) {
1210 client.rect.setPos(frame.rect.left() + frame.margin.left,
1211 frame.rect.top() + frame.margin.top);
1212
1213 XEvent event;
1214 event.type = ConfigureNotify;
1215
1216 event.xconfigure.display = blackbox->getXDisplay();
1217 event.xconfigure.event = client.window;
1218 event.xconfigure.window = client.window;
1219 event.xconfigure.x = client.rect.x();
1220 event.xconfigure.y = client.rect.y();
1221 event.xconfigure.width = client.rect.width();
1222 event.xconfigure.height = client.rect.height();
1223 event.xconfigure.border_width = client.old_bw;
1224 event.xconfigure.above = frame.window;
1225 event.xconfigure.override_redirect = False;
1226
1227 XSendEvent(blackbox->getXDisplay(), client.window, True,
1228 NoEventMask, &event);
1229
1230 screen->updateNetizenConfigNotify(&event);
1231 }
1232 }
1233
1234
1235 #ifdef SHAPE
1236 void BlackboxWindow::configureShape(void) {
1237 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1238 frame.margin.left - frame.border_w,
1239 frame.margin.top - frame.border_w,
1240 client.window, ShapeBounding, ShapeSet);
1241
1242 int num = 0;
1243 XRectangle xrect[2];
1244
1245 if (decorations & Decor_Titlebar) {
1246 xrect[0].x = xrect[0].y = -frame.border_w;
1247 xrect[0].width = frame.rect.width();
1248 xrect[0].height = frame.title_h + (frame.border_w * 2);
1249 ++num;
1250 }
1251
1252 if (decorations & Decor_Handle) {
1253 xrect[1].x = -frame.border_w;
1254 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1255 frame.mwm_border_w - frame.border_w;
1256 xrect[1].width = frame.rect.width();
1257 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1258 ++num;
1259 }
1260
1261 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1262 ShapeBounding, 0, 0, xrect, num,
1263 ShapeUnion, Unsorted);
1264 }
1265 #endif // SHAPE
1266
1267
1268 bool BlackboxWindow::setInputFocus(void) {
1269 if (flags.focused) return True;
1270
1271 if (! client.rect.intersects(screen->getRect())) {
1272 // client is outside the screen, move it to the center
1273 configure((screen->getWidth() - frame.rect.width()) / 2,
1274 (screen->getHeight() - frame.rect.height()) / 2,
1275 frame.rect.width(), frame.rect.height());
1276 }
1277
1278 if (client.transientList.size() > 0) {
1279 // transfer focus to any modal transients
1280 BlackboxWindowList::iterator it, end = client.transientList.end();
1281 for (it = client.transientList.begin(); it != end; ++it) {
1282 if ((*it)->flags.modal) return (*it)->setInputFocus();
1283 }
1284 }
1285
1286 bool ret = True;
1287 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1288 XSetInputFocus(blackbox->getXDisplay(), client.window,
1289 RevertToPointerRoot, CurrentTime);
1290
1291 blackbox->setFocusedWindow(this);
1292 } else {
1293 /* we could set the focus to none, since the window doesn't accept focus,
1294 * but we shouldn't set focus to nothing since this would surely make
1295 * someone angry
1296 */
1297 ret = False;
1298 }
1299
1300 if (flags.send_focus_message) {
1301 XEvent ce;
1302 ce.xclient.type = ClientMessage;
1303 ce.xclient.message_type = blackbox->getWMProtocolsAtom();
1304 ce.xclient.display = blackbox->getXDisplay();
1305 ce.xclient.window = client.window;
1306 ce.xclient.format = 32;
1307 ce.xclient.data.l[0] = blackbox->getWMTakeFocusAtom();
1308 ce.xclient.data.l[1] = blackbox->getLastTime();
1309 ce.xclient.data.l[2] = 0l;
1310 ce.xclient.data.l[3] = 0l;
1311 ce.xclient.data.l[4] = 0l;
1312 XSendEvent(blackbox->getXDisplay(), client.window, False,
1313 NoEventMask, &ce);
1314 }
1315
1316 return ret;
1317 }
1318
1319
1320 void BlackboxWindow::iconify(void) {
1321 if (flags.iconic) return;
1322
1323 if (windowmenu) windowmenu->hide();
1324
1325 setState(IconicState);
1326
1327 /*
1328 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1329 * we need to clear the event mask on client.window for a split second.
1330 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1331 * split second, leaving us with a ghost window... so, we need to do this
1332 * while the X server is grabbed
1333 */
1334 XGrabServer(blackbox->getXDisplay());
1335 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1336 XUnmapWindow(blackbox->getXDisplay(), client.window);
1337 XSelectInput(blackbox->getXDisplay(), client.window,
1338 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1339 XUngrabServer(blackbox->getXDisplay());
1340
1341 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1342 flags.visible = False;
1343 flags.iconic = True;
1344
1345 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1346
1347 if (isTransient()) {
1348 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1349 ! client.transient_for->flags.iconic) {
1350 // iconify our transient_for
1351 client.transient_for->iconify();
1352 }
1353 }
1354
1355 screen->addIcon(this);
1356
1357 if (client.transientList.size() > 0) {
1358 // iconify all transients
1359 BlackboxWindowList::iterator it, end = client.transientList.end();
1360 for (it = client.transientList.begin(); it != end; ++it) {
1361 if (! (*it)->flags.iconic) (*it)->iconify();
1362 }
1363 }
1364 }
1365
1366
1367 void BlackboxWindow::show(void) {
1368 setState(NormalState);
1369
1370 XMapWindow(blackbox->getXDisplay(), client.window);
1371 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1372 XMapWindow(blackbox->getXDisplay(), frame.window);
1373
1374 flags.visible = True;
1375 flags.iconic = False;
1376 }
1377
1378
1379 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1380 if (flags.iconic || reassoc)
1381 screen->reassociateWindow(this, BSENTINEL, False);
1382 else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID())
1383 return;
1384
1385 show();
1386
1387 // reassociate and deiconify all transients
1388 if (reassoc && client.transientList.size() > 0) {
1389 BlackboxWindowList::iterator it, end = client.transientList.end();
1390 for (it = client.transientList.begin(); it != end; ++it) {
1391 (*it)->deiconify(True, False);
1392 }
1393 }
1394
1395 if (raise)
1396 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1397 }
1398
1399
1400 void BlackboxWindow::close(void) {
1401 XEvent ce;
1402 ce.xclient.type = ClientMessage;
1403 ce.xclient.message_type = blackbox->getWMProtocolsAtom();
1404 ce.xclient.display = blackbox->getXDisplay();
1405 ce.xclient.window = client.window;
1406 ce.xclient.format = 32;
1407 ce.xclient.data.l[0] = blackbox->getWMDeleteAtom();
1408 ce.xclient.data.l[1] = CurrentTime;
1409 ce.xclient.data.l[2] = 0l;
1410 ce.xclient.data.l[3] = 0l;
1411 ce.xclient.data.l[4] = 0l;
1412 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1413 }
1414
1415
1416 void BlackboxWindow::withdraw(void) {
1417 setState(current_state);
1418
1419 flags.visible = False;
1420 flags.iconic = False;
1421
1422 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1423
1424 XGrabServer(blackbox->getXDisplay());
1425 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
1426 XUnmapWindow(blackbox->getXDisplay(), client.window);
1427 XSelectInput(blackbox->getXDisplay(), client.window,
1428 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
1429 XUngrabServer(blackbox->getXDisplay());
1430
1431 if (windowmenu) windowmenu->hide();
1432 }
1433
1434
1435 void BlackboxWindow::maximize(unsigned int button) {
1436 // handle case where menu is open then the max button is used instead
1437 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1438
1439 if (flags.maximized) {
1440 flags.maximized = 0;
1441
1442 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1443 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1444
1445 /*
1446 when a resize is begun, maximize(0) is called to clear any maximization
1447 flags currently set. Otherwise it still thinks it is maximized.
1448 so we do not need to call configure() because resizing will handle it
1449 */
1450 if (! flags.resizing)
1451 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1452 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1453
1454 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1455 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1456
1457 redrawAllButtons();
1458 setState(current_state);
1459 return;
1460 }
1461
1462 blackbox_attrib.premax_x = frame.rect.x();
1463 blackbox_attrib.premax_y = frame.rect.y();
1464 blackbox_attrib.premax_w = frame.rect.width();
1465 blackbox_attrib.premax_h = frame.rect.height();
1466
1467 const Rect &screen_area = screen->availableArea();
1468 frame.changing = screen_area;
1469 constrain(TopLeft);
1470
1471 switch(button) {
1472 case 1:
1473 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1474 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1475 break;
1476
1477 case 2:
1478 blackbox_attrib.flags |= AttribMaxVert;
1479 blackbox_attrib.attrib |= AttribMaxVert;
1480
1481 frame.changing.setX(frame.rect.x());
1482 frame.changing.setWidth(frame.rect.width());
1483 break;
1484
1485 case 3:
1486 blackbox_attrib.flags |= AttribMaxHoriz;
1487 blackbox_attrib.attrib |= AttribMaxHoriz;
1488
1489 frame.changing.setY(frame.rect.y());
1490 frame.changing.setHeight(frame.rect.height());
1491 break;
1492 }
1493
1494 if (flags.shaded) {
1495 blackbox_attrib.flags ^= AttribShaded;
1496 blackbox_attrib.attrib ^= AttribShaded;
1497 flags.shaded = False;
1498 }
1499
1500 flags.maximized = button;
1501
1502 configure(frame.changing.x(), frame.changing.y(),
1503 frame.changing.width(), frame.changing.height());
1504 if (flags.focused)
1505 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1506 redrawAllButtons();
1507 setState(current_state);
1508 }
1509
1510
1511 // re-maximizes the window to take into account availableArea changes
1512 void BlackboxWindow::remaximize(void) {
1513 // save the original dimensions because maximize will wipe them out
1514 int premax_x = blackbox_attrib.premax_x,
1515 premax_y = blackbox_attrib.premax_y,
1516 premax_w = blackbox_attrib.premax_w,
1517 premax_h = blackbox_attrib.premax_h;
1518
1519 unsigned int button = flags.maximized;
1520 flags.maximized = 0; // trick maximize() into working
1521 maximize(button);
1522
1523 // restore saved values
1524 blackbox_attrib.premax_x = premax_x;
1525 blackbox_attrib.premax_y = premax_y;
1526 blackbox_attrib.premax_w = premax_w;
1527 blackbox_attrib.premax_h = premax_h;
1528 }
1529
1530
1531 void BlackboxWindow::setWorkspace(unsigned int n) {
1532 blackbox_attrib.flags |= AttribWorkspace;
1533 blackbox_attrib.workspace = n;
1534 }
1535
1536
1537 void BlackboxWindow::shade(void) {
1538 if (! (decorations & Decor_Titlebar))
1539 return;
1540
1541 if (flags.shaded) {
1542 XResizeWindow(blackbox->getXDisplay(), frame.window,
1543 frame.inside_w, frame.inside_h);
1544 flags.shaded = False;
1545 blackbox_attrib.flags ^= AttribShaded;
1546 blackbox_attrib.attrib ^= AttribShaded;
1547
1548 setState(NormalState);
1549
1550 // set the frame rect to the normal size
1551 frame.rect.setHeight(client.rect.height() + frame.margin.top +
1552 frame.margin.bottom);
1553 } else {
1554 XResizeWindow(blackbox->getXDisplay(), frame.window,
1555 frame.inside_w, frame.title_h);
1556 flags.shaded = True;
1557 blackbox_attrib.flags |= AttribShaded;
1558 blackbox_attrib.attrib |= AttribShaded;
1559
1560 setState(IconicState);
1561
1562 // set the frame rect to the shaded size
1563 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1564 }
1565 }
1566
1567
1568 void BlackboxWindow::stick(void) {
1569 if (flags.stuck) {
1570 blackbox_attrib.flags ^= AttribOmnipresent;
1571 blackbox_attrib.attrib ^= AttribOmnipresent;
1572
1573 flags.stuck = False;
1574
1575 if (! flags.iconic)
1576 screen->reassociateWindow(this, BSENTINEL, True);
1577
1578 setState(current_state);
1579 } else {
1580 flags.stuck = True;
1581
1582 blackbox_attrib.flags |= AttribOmnipresent;
1583 blackbox_attrib.attrib |= AttribOmnipresent;
1584
1585 setState(current_state);
1586 }
1587 }
1588
1589
1590 void BlackboxWindow::setFocusFlag(bool focus) {
1591 flags.focused = focus;
1592
1593 if (decorations & Decor_Titlebar) {
1594 if (flags.focused) {
1595 if (frame.ftitle)
1596 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1597 frame.title, frame.ftitle);
1598 else
1599 XSetWindowBackground(blackbox->getXDisplay(),
1600 frame.title, frame.ftitle_pixel);
1601 } else {
1602 if (frame.utitle)
1603 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1604 frame.title, frame.utitle);
1605 else
1606 XSetWindowBackground(blackbox->getXDisplay(),
1607 frame.title, frame.utitle_pixel);
1608 }
1609 XClearWindow(blackbox->getXDisplay(), frame.title);
1610
1611 redrawLabel();
1612 redrawAllButtons();
1613 }
1614
1615 if (decorations & Decor_Handle) {
1616 if (flags.focused) {
1617 if (frame.fhandle)
1618 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1619 frame.handle, frame.fhandle);
1620 else
1621 XSetWindowBackground(blackbox->getXDisplay(),
1622 frame.handle, frame.fhandle_pixel);
1623
1624 if (frame.fgrip) {
1625 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1626 frame.left_grip, frame.fgrip);
1627 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1628 frame.right_grip, frame.fgrip);
1629 } else {
1630 XSetWindowBackground(blackbox->getXDisplay(),
1631 frame.left_grip, frame.fgrip_pixel);
1632 XSetWindowBackground(blackbox->getXDisplay(),
1633 frame.right_grip, frame.fgrip_pixel);
1634 }
1635 } else {
1636 if (frame.uhandle)
1637 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1638 frame.handle, frame.uhandle);
1639 else
1640 XSetWindowBackground(blackbox->getXDisplay(),
1641 frame.handle, frame.uhandle_pixel);
1642
1643 if (frame.ugrip) {
1644 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1645 frame.left_grip, frame.ugrip);
1646 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1647 frame.right_grip, frame.ugrip);
1648 } else {
1649 XSetWindowBackground(blackbox->getXDisplay(),
1650 frame.left_grip, frame.ugrip_pixel);
1651 XSetWindowBackground(blackbox->getXDisplay(),
1652 frame.right_grip, frame.ugrip_pixel);
1653 }
1654 }
1655 XClearWindow(blackbox->getXDisplay(), frame.handle);
1656 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
1657 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
1658 }
1659
1660 if (decorations & Decor_Border) {
1661 if (flags.focused)
1662 XSetWindowBorder(blackbox->getXDisplay(),
1663 frame.plate, frame.fborder_pixel);
1664 else
1665 XSetWindowBorder(blackbox->getXDisplay(),
1666 frame.plate, frame.uborder_pixel);
1667 }
1668
1669 if (screen->isSloppyFocus() && screen->doAutoRaise()) {
1670 if (isFocused()) timer->start();
1671 else timer->stop();
1672 }
1673
1674 if (isFocused())
1675 blackbox->setFocusedWindow(this);
1676 }
1677
1678
1679 void BlackboxWindow::installColormap(bool install) {
1680 int i = 0, ncmap = 0;
1681 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
1682 client.window, &ncmap);
1683 XWindowAttributes wattrib;
1684 if (cmaps) {
1685 if (XGetWindowAttributes(blackbox->getXDisplay(),
1686 client.window, &wattrib)) {
1687 if (install) {
1688 // install the window's colormap
1689 for (i = 0; i < ncmap; i++) {
1690 if (*(cmaps + i) == wattrib.colormap)
1691 // this window is using an installed color map... do not install
1692 install = False;
1693 }
1694 // otherwise, install the window's colormap
1695 if (install)
1696 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1697 } else {
1698 // uninstall the window's colormap
1699 for (i = 0; i < ncmap; i++) {
1700 if (*(cmaps + i) == wattrib.colormap)
1701 // we found the colormap to uninstall
1702 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
1703 }
1704 }
1705 }
1706
1707 XFree(cmaps);
1708 }
1709 }
1710
1711
1712 void BlackboxWindow::setState(unsigned long new_state) {
1713 current_state = new_state;
1714
1715 unsigned long state[2];
1716 state[0] = current_state;
1717 state[1] = None;
1718 XChangeProperty(blackbox->getXDisplay(), client.window,
1719 blackbox->getWMStateAtom(), blackbox->getWMStateAtom(), 32,
1720 PropModeReplace, (unsigned char *) state, 2);
1721
1722 XChangeProperty(blackbox->getXDisplay(), client.window,
1723 blackbox->getBlackboxAttributesAtom(),
1724 blackbox->getBlackboxAttributesAtom(), 32, PropModeReplace,
1725 (unsigned char *) &blackbox_attrib,
1726 PropBlackboxAttributesElements);
1727 }
1728
1729
1730 bool BlackboxWindow::getState(void) {
1731 current_state = 0;
1732
1733 Atom atom_return;
1734 bool ret = False;
1735 int foo;
1736 unsigned long *state, ulfoo, nitems;
1737
1738 if ((XGetWindowProperty(blackbox->getXDisplay(), client.window,
1739 blackbox->getWMStateAtom(),
1740 0l, 2l, False, blackbox->getWMStateAtom(),
1741 &atom_return, &foo, &nitems, &ulfoo,
1742 (unsigned char **) &state) != Success) ||
1743 (! state)) {
1744 return False;
1745 }
1746
1747 if (nitems >= 1) {
1748 current_state = static_cast<unsigned long>(state[0]);
1749
1750 ret = True;
1751 }
1752
1753 XFree((void *) state);
1754
1755 return ret;
1756 }
1757
1758
1759 void BlackboxWindow::restoreAttributes(void) {
1760 if (! getState()) current_state = NormalState;
1761
1762 Atom atom_return;
1763 int foo;
1764 unsigned long ulfoo, nitems;
1765
1766 BlackboxAttributes *net;
1767 int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
1768 blackbox->getBlackboxAttributesAtom(), 0l,
1769 PropBlackboxAttributesElements, False,
1770 blackbox->getBlackboxAttributesAtom(),
1771 &atom_return, &foo, &nitems, &ulfoo,
1772 (unsigned char **) &net);
1773 if (ret != Success || ! net || nitems != PropBlackboxAttributesElements)
1774 return;
1775
1776 if (net->flags & AttribShaded &&
1777 net->attrib & AttribShaded) {
1778 int save_state =
1779 ((current_state == IconicState) ? NormalState : current_state);
1780
1781 flags.shaded = False;
1782 shade();
1783
1784 current_state = save_state;
1785 }
1786
1787 if ((net->workspace != screen->getCurrentWorkspaceID()) &&
1788 (net->workspace < screen->getWorkspaceCount())) {
1789 screen->reassociateWindow(this, net->workspace, True);
1790
1791 if (current_state == NormalState) current_state = WithdrawnState;
1792 } else if (current_state == WithdrawnState) {
1793 current_state = NormalState;
1794 }
1795
1796 if (net->flags & AttribOmnipresent &&
1797 net->attrib & AttribOmnipresent) {
1798 flags.stuck = False;
1799 stick();
1800
1801 current_state = NormalState;
1802 }
1803
1804 if ((net->flags & AttribMaxHoriz) ||
1805 (net->flags & AttribMaxVert)) {
1806 int x = net->premax_x, y = net->premax_y;
1807 unsigned int w = net->premax_w, h = net->premax_h;
1808 flags.maximized = 0;
1809
1810 unsigned int m = 0;
1811 if ((net->flags & AttribMaxHoriz) &&
1812 (net->flags & AttribMaxVert))
1813 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1814 else if (net->flags & AttribMaxVert)
1815 m = (net->attrib & AttribMaxVert) ? 2 : 0;
1816 else if (net->flags & AttribMaxHoriz)
1817 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
1818
1819 if (m) maximize(m);
1820
1821 blackbox_attrib.premax_x = x;
1822 blackbox_attrib.premax_y = y;
1823 blackbox_attrib.premax_w = w;
1824 blackbox_attrib.premax_h = h;
1825 }
1826
1827 setState(current_state);
1828
1829 XFree((void *) net);
1830 }
1831
1832
1833 /*
1834 * Positions the frame according the the client window position and window
1835 * gravity.
1836 */
1837 void BlackboxWindow::setGravityOffsets(void) {
1838 // x coordinates for each gravity type
1839 const int x_west = client.rect.x();
1840 const int x_east = client.rect.right() - frame.inside_w + 1;
1841 const int x_center = client.rect.right() - (frame.rect.width()/2) + 1;
1842 // y coordinates for each gravity type
1843 const int y_north = client.rect.y();
1844 const int y_south = client.rect.bottom() - frame.inside_h + 1;
1845 const int y_center = client.rect.bottom() - (frame.rect.height()/2) + 1;
1846
1847 switch (client.win_gravity) {
1848 default:
1849 case NorthWestGravity: frame.rect.setPos(x_west, y_north); break;
1850 case NorthGravity: frame.rect.setPos(x_center, y_north); break;
1851 case NorthEastGravity: frame.rect.setPos(x_east, y_north); break;
1852 case SouthWestGravity: frame.rect.setPos(x_west, y_south); break;
1853 case SouthGravity: frame.rect.setPos(x_center, y_south); break;
1854 case SouthEastGravity: frame.rect.setPos(x_east, y_south); break;
1855 case WestGravity: frame.rect.setPos(x_west, y_center); break;
1856 case CenterGravity: frame.rect.setPos(x_center, y_center); break;
1857 case EastGravity: frame.rect.setPos(x_east, y_center); break;
1858
1859 case ForgetGravity:
1860 case StaticGravity:
1861 frame.rect.setPos(client.rect.x() - frame.margin.left,
1862 client.rect.y() - frame.margin.top);
1863 break;
1864 }
1865 }
1866
1867
1868 /*
1869 * The reverse of the setGravityOffsets function. Uses the frame window's
1870 * position to find the window's reference point.
1871 */
1872 void BlackboxWindow::restoreGravity(void) {
1873 // x coordinates for each gravity type
1874 const int x_west = frame.rect.x();
1875 const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
1876 const int x_center = frame.rect.x() + (frame.rect.width()/2) -
1877 client.rect.width();
1878 // y coordinates for each gravity type
1879 const int y_north = frame.rect.y();
1880 const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
1881 const int y_center = frame.rect.y() + (frame.rect.height()/2) -
1882 client.rect.height();
1883
1884 switch(client.win_gravity) {
1885 default:
1886 case NorthWestGravity: client.rect.setPos(x_west, y_north); break;
1887 case NorthGravity: client.rect.setPos(x_center, y_north); break;
1888 case NorthEastGravity: client.rect.setPos(x_east, y_north); break;
1889 case SouthWestGravity: client.rect.setPos(x_west, y_south); break;
1890 case SouthGravity: client.rect.setPos(x_center, y_south); break;
1891 case SouthEastGravity: client.rect.setPos(x_east, y_south); break;
1892 case WestGravity: client.rect.setPos(x_west, y_center); break;
1893 case CenterGravity: client.rect.setPos(x_center, y_center); break;
1894 case EastGravity: client.rect.setPos(x_east, y_center); break;
1895
1896 case ForgetGravity:
1897 case StaticGravity:
1898 client.rect.setPos(frame.rect.left() + frame.margin.left,
1899 frame.rect.top() + frame.margin.top);
1900 break;
1901 }
1902 }
1903
1904
1905 void BlackboxWindow::redrawLabel(void) {
1906 if (flags.focused) {
1907 if (frame.flabel)
1908 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1909 frame.label, frame.flabel);
1910 else
1911 XSetWindowBackground(blackbox->getXDisplay(),
1912 frame.label, frame.flabel_pixel);
1913 } else {
1914 if (frame.ulabel)
1915 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1916 frame.label, frame.ulabel);
1917 else
1918 XSetWindowBackground(blackbox->getXDisplay(),
1919 frame.label, frame.ulabel_pixel);
1920 }
1921 XClearWindow(blackbox->getXDisplay(), frame.label);
1922
1923 WindowStyle *style = screen->getWindowStyle();
1924
1925 int pos = frame.bevel_w * 2,
1926 dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
1927 frame.bevel_w * 4, i18n.multibyte());
1928
1929 BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
1930 style->font);
1931 if (i18n.multibyte())
1932 XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
1933 pen.gc(), pos,
1934 (1 - style->fontset_extents->max_ink_extent.y),
1935 client.title.c_str(), dlen);
1936 else
1937 XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
1938 (style->font->ascent + 1), client.title.c_str(), dlen);
1939 }
1940
1941
1942 void BlackboxWindow::redrawAllButtons(void) {
1943 if (frame.iconify_button) redrawIconifyButton(False);
1944 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
1945 if (frame.close_button) redrawCloseButton(False);
1946 }
1947
1948
1949 void BlackboxWindow::redrawIconifyButton(bool pressed) {
1950 if (! pressed) {
1951 if (flags.focused) {
1952 if (frame.fbutton)
1953 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1954 frame.iconify_button, frame.fbutton);
1955 else
1956 XSetWindowBackground(blackbox->getXDisplay(),
1957 frame.iconify_button, frame.fbutton_pixel);
1958 } else {
1959 if (frame.ubutton)
1960 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1961 frame.iconify_button, frame.ubutton);
1962 else
1963 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
1964 frame.ubutton_pixel);
1965 }
1966 } else {
1967 if (frame.pbutton)
1968 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1969 frame.iconify_button, frame.pbutton);
1970 else
1971 XSetWindowBackground(blackbox->getXDisplay(),
1972 frame.iconify_button, frame.pbutton_pixel);
1973 }
1974 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
1975
1976 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
1977 screen->getWindowStyle()->b_pic_unfocus);
1978 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
1979 2, (frame.button_w - 5), (frame.button_w - 5), 2);
1980 }
1981
1982
1983 void BlackboxWindow::redrawMaximizeButton(bool pressed) {
1984 if (! pressed) {
1985 if (flags.focused) {
1986 if (frame.fbutton)
1987 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1988 frame.maximize_button, frame.fbutton);
1989 else
1990 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
1991 frame.fbutton_pixel);
1992 } else {
1993 if (frame.ubutton)
1994 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
1995 frame.maximize_button, frame.ubutton);
1996 else
1997 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
1998 frame.ubutton_pixel);
1999 }
2000 } else {
2001 if (frame.pbutton)
2002 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2003 frame.maximize_button, frame.pbutton);
2004 else
2005 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2006 frame.pbutton_pixel);
2007 }
2008 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2009
2010 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2011 screen->getWindowStyle()->b_pic_unfocus);
2012 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2013 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2014 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2015 2, 3, (frame.button_w - 3), 3);
2016 }
2017
2018
2019 void BlackboxWindow::redrawCloseButton(bool pressed) {
2020 if (! pressed) {
2021 if (flags.focused) {
2022 if (frame.fbutton)
2023 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2024 frame.fbutton);
2025 else
2026 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2027 frame.fbutton_pixel);
2028 } else {
2029 if (frame.ubutton)
2030 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2031 frame.ubutton);
2032 else
2033 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2034 frame.ubutton_pixel);
2035 }
2036 } else {
2037 if (frame.pbutton)
2038 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2039 frame.close_button, frame.pbutton);
2040 else
2041 XSetWindowBackground(blackbox->getXDisplay(),
2042 frame.close_button, frame.pbutton_pixel);
2043 }
2044 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2045
2046 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2047 screen->getWindowStyle()->b_pic_unfocus);
2048 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2049 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2050 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2051 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2052 }
2053
2054
2055 void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
2056 if (re->window != client.window)
2057 return;
2058
2059 #ifdef DEBUG
2060 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2061 client.window);
2062 #endif // DEBUG
2063
2064 bool get_state_ret = getState();
2065 if (! (get_state_ret && blackbox->isStartup())) {
2066 if ((client.wm_hint_flags & StateHint) &&
2067 (! (current_state == NormalState || current_state == IconicState)))
2068 current_state = client.initial_state;
2069 else
2070 current_state = NormalState;
2071 } else if (flags.iconic) {
2072 current_state = NormalState;
2073 }
2074
2075 switch (current_state) {
2076 case IconicState:
2077 iconify();
2078 break;
2079
2080 case WithdrawnState:
2081 withdraw();
2082 break;
2083
2084 case NormalState:
2085 case InactiveState:
2086 case ZoomState:
2087 default:
2088 show();
2089 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2090 if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
2091 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
2092 setInputFocus();
2093 }
2094 break;
2095 }
2096 }
2097
2098
2099 void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
2100 if (ue->window != client.window)
2101 return;
2102
2103 #ifdef DEBUG
2104 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2105 client.window);
2106 #endif // DEBUG
2107
2108 screen->unmanageWindow(this, False);
2109 }
2110
2111
2112 void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
2113 if (de->window != client.window)
2114 return;
2115
2116 #ifdef DEBUG
2117 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2118 client.window);
2119 #endif // DEBUG
2120
2121 screen->unmanageWindow(this, False);
2122 }
2123
2124
2125 void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
2126 if (re->window != client.window || re->parent == frame.plate)
2127 return;
2128
2129 #ifdef DEBUG
2130 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2131 "0x%lx.\n", client.window, re->parent);
2132 #endif // DEBUG
2133
2134 XEvent ev;
2135 ev.xreparent = *re;
2136 XPutBackEvent(blackbox->getXDisplay(), &ev);
2137 screen->unmanageWindow(this, True);
2138 }
2139
2140
2141 void BlackboxWindow::propertyNotifyEvent(Atom atom) {
2142 switch(atom) {
2143 case XA_WM_CLASS:
2144 case XA_WM_CLIENT_MACHINE:
2145 case XA_WM_COMMAND:
2146 break;
2147
2148 case XA_WM_TRANSIENT_FOR: {
2149 // determine if this is a transient window
2150 getTransientInfo();
2151
2152 // adjust the window decorations based on transience
2153 if (isTransient()) {
2154 decorations &= ~(Decor_Maximize | Decor_Handle);
2155 functions &= ~Func_Maximize;
2156 }
2157
2158 reconfigure();
2159 }
2160 break;
2161
2162 case XA_WM_HINTS:
2163 getWMHints();
2164 break;
2165
2166 case XA_WM_ICON_NAME:
2167 getWMIconName();
2168 if (flags.iconic) screen->propagateWindowName(this);
2169 break;
2170
2171 case XA_WM_NAME:
2172 getWMName();
2173
2174 if (decorations & Decor_Titlebar)
2175 redrawLabel();
2176
2177 screen->propagateWindowName(this);
2178 break;
2179
2180 case XA_WM_NORMAL_HINTS: {
2181 getWMNormalHints();
2182
2183 if ((client.normal_hint_flags & PMinSize) &&
2184 (client.normal_hint_flags & PMaxSize)) {
2185 if (client.max_width <= client.min_width &&
2186 client.max_height <= client.min_height) {
2187 decorations &= ~(Decor_Maximize | Decor_Handle);
2188 functions &= ~(Func_Resize | Func_Maximize);
2189 } else {
2190 decorations |= Decor_Maximize | Decor_Handle;
2191 functions |= Func_Resize | Func_Maximize;
2192 }
2193 }
2194
2195 Rect old_rect = frame.rect;
2196
2197 upsize();
2198
2199 if (old_rect != frame.rect)
2200 reconfigure();
2201
2202 break;
2203 }
2204
2205 default:
2206 if (atom == blackbox->getWMProtocolsAtom()) {
2207 getWMProtocols();
2208
2209 if ((decorations & Decor_Close) && (! frame.close_button)) {
2210 createCloseButton();
2211 if (decorations & Decor_Titlebar) {
2212 positionButtons(True);
2213 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2214 }
2215 if (windowmenu) windowmenu->reconfigure();
2216 }
2217 }
2218
2219 break;
2220 }
2221 }
2222
2223
2224 void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
2225 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2226 redrawLabel();
2227 else if (frame.close_button == ee->window)
2228 redrawCloseButton(False);
2229 else if (frame.maximize_button == ee->window)
2230 redrawMaximizeButton(flags.maximized);
2231 else if (frame.iconify_button == ee->window)
2232 redrawIconifyButton(False);
2233 }
2234
2235
2236 void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
2237 if (cr->window != client.window || flags.iconic)
2238 return;
2239
2240 int cx = frame.rect.x(), cy = frame.rect.y();
2241 unsigned int cw = frame.rect.width(), ch = frame.rect.height();
2242
2243 if (cr->value_mask & CWBorderWidth)
2244 client.old_bw = cr->border_width;
2245
2246 if (cr->value_mask & CWX)
2247 cx = cr->x - frame.margin.left;
2248
2249 if (cr->value_mask & CWY)
2250 cy = cr->y - frame.margin.top;
2251
2252 if (cr->value_mask & CWWidth)
2253 cw = cr->width + frame.margin.left + frame.margin.right;
2254
2255 if (cr->value_mask & CWHeight)
2256 ch = cr->height + frame.margin.top + frame.margin.bottom;
2257
2258 if (frame.rect != Rect(cx, cy, cw, ch))
2259 configure(cx, cy, cw, ch);
2260
2261 if (cr->value_mask & CWStackMode) {
2262 switch (cr->detail) {
2263 case Below:
2264 case BottomIf:
2265 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2266 break;
2267
2268 case Above:
2269 case TopIf:
2270 default:
2271 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2272 break;
2273 }
2274 }
2275 }
2276
2277
2278 void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
2279 if (frame.maximize_button == be->window) {
2280 redrawMaximizeButton(True);
2281 } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
2282 if (! flags.focused)
2283 setInputFocus();
2284
2285 if (frame.iconify_button == be->window) {
2286 redrawIconifyButton(True);
2287 } else if (frame.close_button == be->window) {
2288 redrawCloseButton(True);
2289 } else if (frame.plate == be->window) {
2290 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2291
2292 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2293
2294 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2295 } else {
2296 if (frame.title == be->window || frame.label == be->window) {
2297 if (((be->time - lastButtonPressTime) <=
2298 blackbox->getDoubleClickInterval()) ||
2299 (be->state & ControlMask)) {
2300 lastButtonPressTime = 0;
2301 shade();
2302 } else {
2303 lastButtonPressTime = be->time;
2304 }
2305 }
2306
2307 frame.grab_x = be->x_root - frame.rect.x() - frame.border_w;
2308 frame.grab_y = be->y_root - frame.rect.y() - frame.border_w;
2309
2310 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2311
2312 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2313 }
2314 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2315 (be->window != frame.close_button)) {
2316 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2317 } else if (windowmenu && be->button == 3 &&
2318 (frame.title == be->window || frame.label == be->window ||
2319 frame.handle == be->window || frame.window == be->window)) {
2320 int mx = 0, my = 0;
2321
2322 if (frame.title == be->window || frame.label == be->window) {
2323 mx = be->x_root - (windowmenu->getWidth() / 2);
2324 my = frame.rect.y() + frame.title_h + frame.border_w;
2325 } else if (frame.handle == be->window) {
2326 mx = be->x_root - (windowmenu->getWidth() / 2);
2327 my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
2328 windowmenu->getHeight();
2329 } else {
2330 mx = be->x_root - (windowmenu->getWidth() / 2);
2331
2332 if (be->y <= static_cast<signed>(frame.bevel_w))
2333 my = frame.rect.y() + frame.title_h;
2334 else
2335 my = be->y_root - (windowmenu->getHeight() / 2);
2336 }
2337
2338 // snap the window menu into a corner if necessary - we check the
2339 // position of the menu with the coordinates of the client to
2340 // make the comparisions easier.
2341 // ### this needs some work!
2342 if (mx > client.rect.right() -
2343 static_cast<signed>(windowmenu->getWidth()))
2344 mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
2345 if (mx < client.rect.left())
2346 mx = frame.rect.x();
2347
2348 if (my > client.rect.bottom() -
2349 static_cast<signed>(windowmenu->getHeight()))
2350 my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
2351 if (my < client.rect.top())
2352 my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
2353 frame.title_h : 0);
2354
2355 if (windowmenu) {
2356 if (! windowmenu->isVisible()) {
2357 windowmenu->move(mx, my);
2358 windowmenu->show();
2359 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2360 XRaiseWindow(blackbox->getXDisplay(),
2361 windowmenu->getSendToMenu()->getWindowID());
2362 } else {
2363 windowmenu->hide();
2364 }
2365 }
2366 }
2367 }
2368
2369
2370 void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
2371 if (re->window == frame.maximize_button) {
2372 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2373 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2374 maximize(re->button);
2375 } else {
2376 redrawMaximizeButton(flags.maximized);
2377 }
2378 } else if (re->window == frame.iconify_button) {
2379 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2380 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2381 iconify();
2382 } else {
2383 redrawIconifyButton(False);
2384 }
2385 } else if (re->window == frame.close_button) {
2386 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2387 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2388 close();
2389 redrawCloseButton(False);
2390 } else if (flags.moving) {
2391 flags.moving = False;
2392
2393 if (! screen->doOpaqueMove()) {
2394 /* when drawing the rubber band, we need to make sure we only draw inside
2395 * the frame... frame.changing_* contain the new coords for the window,
2396 * so we need to subtract 1 from changing_w/changing_h every where we
2397 * draw the rubber band (for both moving and resizing)
2398 */
2399 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2400 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2401 frame.changing.width() - 1, frame.changing.height() - 1);
2402 XUngrabServer(blackbox->getXDisplay());
2403
2404 configure(frame.changing.x(), frame.changing.y(),
2405 frame.changing.width(), frame.changing.height());
2406 } else {
2407 configure(frame.rect.x(), frame.rect.y(),
2408 frame.rect.width(), frame.rect.height());
2409 }
2410 screen->hideGeometry();
2411 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2412 } else if (flags.resizing) {
2413 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2414 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2415 frame.changing.width() - 1, frame.changing.height() - 1);
2416 XUngrabServer(blackbox->getXDisplay());
2417
2418 screen->hideGeometry();
2419
2420 constrain((re->window == frame.left_grip) ? TopRight : TopLeft);
2421
2422 // unset maximized state when resized after fully maximized
2423 if (flags.maximized == 1)
2424 maximize(0);
2425 flags.resizing = False;
2426 configure(frame.changing.x(), frame.changing.y(),
2427 frame.changing.width(), frame.changing.height());
2428
2429 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2430 } else if (re->window == frame.window) {
2431 if (re->button == 2 && re->state == Mod1Mask)
2432 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2433 }
2434 }
2435
2436
2437 void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
2438 if (! flags.resizing && (me->state & Button1Mask) &&
2439 (functions & Func_Move) &&
2440 (frame.title == me->window || frame.label == me->window ||
2441 frame.handle == me->window || frame.window == me->window)) {
2442 if (! flags.moving) {
2443 XGrabPointer(blackbox->getXDisplay(), me->window, False,
2444 Button1MotionMask | ButtonReleaseMask,
2445 GrabModeAsync, GrabModeAsync,
2446 None, blackbox->getMoveCursor(), CurrentTime);
2447
2448 if (windowmenu && windowmenu->isVisible())
2449 windowmenu->hide();
2450
2451 flags.moving = True;
2452
2453 if (! screen->doOpaqueMove()) {
2454 XGrabServer(blackbox->getXDisplay());
2455
2456 frame.changing = frame.rect;
2457 screen->showPosition(frame.changing.x(), frame.changing.y());
2458
2459 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2460 screen->getOpGC(),
2461 frame.changing.x(),
2462 frame.changing.y(),
2463 frame.changing.width() - 1,
2464 frame.changing.height() - 1);
2465 }
2466 } else {
2467 int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y;
2468 dx -= frame.border_w;
2469 dy -= frame.border_w;
2470
2471 const int snap_distance = screen->getEdgeSnapThreshold();
2472
2473 if (snap_distance) {
2474 Rect srect = screen->availableArea();
2475 // window corners
2476 const int wleft = dx,
2477 wright = dx + frame.rect.width() - 1,
2478 wtop = dy,
2479 wbottom = dy + frame.rect.height() - 1;
2480
2481 int dleft = std::abs(wleft - srect.left()),
2482 dright = std::abs(wright - srect.right()),
2483 dtop = std::abs(wtop - srect.top()),
2484 dbottom = std::abs(wbottom - srect.bottom());
2485
2486 // snap left?
2487 if (dleft < snap_distance && dleft < dright)
2488 dx = srect.left();
2489 // snap right?
2490 else if (dright < snap_distance && dright < dleft)
2491 dx = srect.right() - frame.rect.width() + 1;
2492
2493 // snap top?
2494 if (dtop < snap_distance && dtop < dbottom)
2495 dy = srect.top();
2496 // snap bottom?
2497 else if (dbottom < snap_distance && dbottom < dtop)
2498 dy = srect.bottom() - frame.rect.height() + 1;
2499
2500 srect = screen->getRect(); // now get the full screen
2501
2502 dleft = std::abs(wleft - srect.left()),
2503 dright = std::abs(wright - srect.right()),
2504 dtop = std::abs(wtop - srect.top()),
2505 dbottom = std::abs(wbottom - srect.bottom());
2506
2507 // snap left?
2508 if (dleft < snap_distance && dleft < dright)
2509 dx = srect.left();
2510 // snap right?
2511 else if (dright < snap_distance && dright < dleft)
2512 dx = srect.right() - frame.rect.width() + 1;
2513
2514 // snap top?
2515 if (dtop < snap_distance && dtop < dbottom)
2516 dy = srect.top();
2517 // snap bottom?
2518 else if (dbottom < snap_distance && dbottom < dtop)
2519 dy = srect.bottom() - frame.rect.height() + 1;
2520 }
2521
2522 if (screen->doOpaqueMove()) {
2523 configure(dx, dy, frame.rect.width(), frame.rect.height());
2524 } else {
2525 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2526 screen->getOpGC(),
2527 frame.changing.x(),
2528 frame.changing.y(),
2529 frame.changing.width() - 1,
2530 frame.changing.height() - 1);
2531
2532 frame.changing.setPos(dx, dy);
2533
2534 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2535 screen->getOpGC(),
2536 frame.changing.x(),
2537 frame.changing.y(),
2538 frame.changing.width() - 1,
2539 frame.changing.height() - 1);
2540 }
2541
2542 screen->showPosition(dx, dy);
2543 }
2544 } else if ((functions & Func_Resize) &&
2545 (((me->state & Button1Mask) &&
2546 (me->window == frame.right_grip ||
2547 me->window == frame.left_grip)) ||
2548 (me->state & (Mod1Mask | Button3Mask) &&
2549 me->window == frame.window))) {
2550 bool left = (me->window == frame.left_grip);
2551
2552 if (! flags.resizing) {
2553 XGrabServer(blackbox->getXDisplay());
2554 XGrabPointer(blackbox->getXDisplay(), me->window, False,
2555 ButtonMotionMask | ButtonReleaseMask,
2556 GrabModeAsync, GrabModeAsync, None,
2557 ((left) ? blackbox->getLowerLeftAngleCursor() :
2558 blackbox->getLowerRightAngleCursor()),
2559 CurrentTime);
2560
2561 flags.resizing = True;
2562
2563 int gw, gh;
2564 frame.grab_x = me->x;
2565 frame.grab_y = me->y;
2566 frame.changing = frame.rect;
2567
2568 constrain((left) ? TopRight : TopLeft, &gw, &gh);
2569
2570 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2571 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2572 frame.changing.width() - 1, frame.changing.height() - 1);
2573
2574 screen->showGeometry(gw, gh);
2575 } else {
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 int gw, gh;
2581
2582 Corner anchor;
2583
2584 if (left) {
2585 anchor = TopRight;
2586 frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(),
2587 frame.rect.right(), frame.rect.bottom());
2588 frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y));
2589 } else {
2590 anchor = TopLeft;
2591 frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x),
2592 frame.rect.height() + (me->y - frame.grab_y));
2593 }
2594
2595 constrain(anchor, &gw, &gh);
2596
2597 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
2598 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
2599 frame.changing.width() - 1, frame.changing.height() - 1);
2600
2601 screen->showGeometry(gw, gh);
2602 }
2603 }
2604 }
2605
2606
2607 #ifdef SHAPE
2608 void BlackboxWindow::shapeEvent(XShapeEvent *) {
2609 if (blackbox->hasShapeExtensions() && flags.shaped) {
2610 configureShape();
2611 }
2612 }
2613 #endif // SHAPE
2614
2615
2616 bool BlackboxWindow::validateClient(void) {
2617 XSync(blackbox->getXDisplay(), False);
2618
2619 XEvent e;
2620 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2621 DestroyNotify, &e) ||
2622 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2623 UnmapNotify, &e)) {
2624 XPutBackEvent(blackbox->getXDisplay(), &e);
2625
2626 return False;
2627 }
2628
2629 return True;
2630 }
2631
2632
2633 void BlackboxWindow::restore(bool remap) {
2634 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
2635 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
2636 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
2637
2638 restoreGravity();
2639
2640 XUnmapWindow(blackbox->getXDisplay(), frame.window);
2641 XUnmapWindow(blackbox->getXDisplay(), client.window);
2642
2643 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
2644
2645 XEvent ev;
2646 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
2647 ReparentNotify, &ev)) {
2648 remap = True;
2649 } else {
2650 // according to the ICCCM - if the client doesn't reparent to
2651 // root, then we have to do it for them
2652 XReparentWindow(blackbox->getXDisplay(), client.window,
2653 screen->getRootWindow(),
2654 client.rect.x(), client.rect.y());
2655 }
2656
2657 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
2658 }
2659
2660
2661 // timer for autoraise
2662 void BlackboxWindow::timeout(void) {
2663 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2664 }
2665
2666
2667 void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) {
2668 if ((net->flags & AttribShaded) &&
2669 ((blackbox_attrib.attrib & AttribShaded) !=
2670 (net->attrib & AttribShaded)))
2671 shade();
2672
2673 if (flags.visible && // watch out for requests when we can not be seen
2674 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
2675 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
2676 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
2677 if (flags.maximized) {
2678 maximize(0);
2679 } else {
2680 int button = 0;
2681
2682 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
2683 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
2684 else if (net->flags & AttribMaxVert)
2685 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
2686 else if (net->flags & AttribMaxHoriz)
2687 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
2688
2689 maximize(button);
2690 }
2691 }
2692
2693 if ((net->flags & AttribOmnipresent) &&
2694 ((blackbox_attrib.attrib & AttribOmnipresent) !=
2695 (net->attrib & AttribOmnipresent)))
2696 stick();
2697
2698 if ((net->flags & AttribWorkspace) &&
2699 (blackbox_attrib.workspace != net->workspace)) {
2700 screen->reassociateWindow(this, net->workspace, True);
2701
2702 if (screen->getCurrentWorkspaceID() != net->workspace) {
2703 withdraw();
2704 } else {
2705 show();
2706 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2707 }
2708 }
2709
2710 if (net->flags & AttribDecoration) {
2711 switch (net->decoration) {
2712 case DecorNone:
2713 // clear all decorations except close
2714 decorations &= Decor_Close;
2715
2716 break;
2717
2718 default:
2719 case DecorNormal:
2720 decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2721 Decor_Iconify | Decor_Maximize;
2722
2723 break;
2724
2725 case DecorTiny:
2726 decorations |= Decor_Titlebar | Decor_Iconify;
2727 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2728
2729 break;
2730
2731 case DecorTool:
2732 decorations |= Decor_Titlebar;
2733 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2734 functions |= Func_Move;
2735
2736 break;
2737 }
2738 if (frame.window) {
2739 XMapSubwindows(blackbox->getXDisplay(), frame.window);
2740 XMapWindow(blackbox->getXDisplay(), frame.window);
2741 }
2742
2743 reconfigure();
2744 setState(current_state);
2745 }
2746 }
2747
2748
2749 /*
2750 * Set the sizes of all components of the window frame
2751 * (the window decorations).
2752 * These values are based upon the current style settings and the client
2753 * window's dimensions.
2754 */
2755 void BlackboxWindow::upsize(void) {
2756 frame.bevel_w = screen->getBevelWidth();
2757
2758 if (decorations & Decor_Border) {
2759 frame.border_w = screen->getBorderWidth();
2760 if (! isTransient())
2761 frame.mwm_border_w = screen->getFrameWidth();
2762 else
2763 frame.mwm_border_w = 0;
2764 } else {
2765 frame.mwm_border_w = frame.border_w = 0;
2766 }
2767
2768 if (decorations & Decor_Titlebar) {
2769 // the height of the titlebar is based upon the height of the font being
2770 // used to display the window's title
2771 WindowStyle *style = screen->getWindowStyle();
2772 if (i18n.multibyte())
2773 frame.title_h = (style->fontset_extents->max_ink_extent.height +
2774 (frame.bevel_w * 2) + 2);
2775 else
2776 frame.title_h = (style->font->ascent + style->font->descent +
2777 (frame.bevel_w * 2) + 2);
2778
2779 frame.label_h = frame.title_h - (frame.bevel_w * 2);
2780 frame.button_w = (frame.label_h - 2);
2781
2782 // set the top frame margin
2783 frame.margin.top = frame.border_w + frame.title_h +
2784 frame.border_w + frame.mwm_border_w;
2785 } else {
2786 frame.title_h = 0;
2787 frame.label_h = 0;
2788 frame.button_w = 0;
2789
2790 // set the top frame margin
2791 frame.margin.top = frame.border_w + frame.mwm_border_w;
2792 }
2793
2794 // set the left/right frame margin
2795 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
2796
2797 if (decorations & Decor_Handle) {
2798 frame.grip_w = frame.button_w * 2;
2799 frame.handle_h = screen->getHandleWidth();
2800
2801 // set the bottom frame margin
2802 frame.margin.bottom = frame.border_w + frame.handle_h +
2803 frame.border_w + frame.mwm_border_w;
2804 } else {
2805 frame.handle_h = 0;
2806 frame.grip_w = 0;
2807
2808 // set the bottom frame margin
2809 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
2810 }
2811
2812 // set the frame rect
2813 frame.rect.setSize(client.rect.width() + frame.margin.left +
2814 frame.margin.right,
2815 client.rect.height() + frame.margin.top +
2816 frame.margin.bottom);
2817 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
2818 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
2819 }
2820
2821
2822 /*
2823 * Calculate the size of the client window and constrain it to the
2824 * size specified by the size hints of the client window.
2825 *
2826 * The logical width and height are placed into pw and ph, if they
2827 * are non-zero. Logical size refers to the users perception of
2828 * the window size (for example an xterm has resizes in cells, not in
2829 * pixels).
2830 *
2831 * The physical geometry is placed into frame.changing_{x,y,width,height}.
2832 * Physical geometry refers to the geometry of the window in pixels.
2833 */
2834 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
2835 // frame.changing represents the requested frame size, we need to
2836 // strip the frame margin off and constrain the client size
2837 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
2838 frame.changing.top() + frame.margin.top,
2839 frame.changing.right() - frame.margin.right,
2840 frame.changing.bottom() - frame.margin.bottom);
2841
2842 int dw = frame.changing.width(), dh = frame.changing.height(),
2843 base_width = (client.base_width) ? client.base_width : client.min_width,
2844 base_height = (client.base_height) ? client.base_height :
2845 client.min_height;
2846
2847 // constrain
2848 if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
2849 if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
2850 if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
2851 if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
2852
2853 dw -= base_width;
2854 dw /= client.width_inc;
2855 dh -= base_height;
2856 dh /= client.height_inc;
2857
2858 if (pw) *pw = dw;
2859 if (ph) *ph = dh;
2860
2861 dw *= client.width_inc;
2862 dw += base_width;
2863 dh *= client.height_inc;
2864 dh += base_height;
2865
2866 frame.changing.setSize(dw, dh);
2867
2868 // add the frame margin back onto frame.changing
2869 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
2870 frame.changing.top() - frame.margin.top,
2871 frame.changing.right() + frame.margin.right,
2872 frame.changing.bottom() + frame.margin.bottom);
2873
2874 // move frame.changing to the specified anchor
2875 switch (anchor) {
2876 case TopLeft:
2877 // nothing to do
2878 break;
2879
2880 case TopRight:
2881 int dx = frame.rect.right() - frame.changing.right();
2882 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y());
2883 break;
2884 }
2885 }
2886
2887
2888 int WindowStyle::doJustify(const char *text, int &start_pos,
2889 unsigned int max_length, unsigned int modifier,
2890 bool multibyte) const {
2891 size_t text_len = strlen(text);
2892 unsigned int length;
2893
2894 do {
2895 if (multibyte) {
2896 XRectangle ink, logical;
2897 XmbTextExtents(fontset, text, text_len, &ink, &logical);
2898 length = logical.width;
2899 } else {
2900 length = XTextWidth(font, text, text_len);
2901 }
2902 length += modifier;
2903 } while (length > max_length && text_len-- > 0);
2904
2905 switch (justify) {
2906 case RightJustify:
2907 start_pos += max_length - length;
2908 break;
2909
2910 case CenterJustify:
2911 start_pos += (max_length - length) / 2;
2912 break;
2913
2914 case LeftJustify:
2915 default:
2916 break;
2917 }
2918
2919 return text_len;
2920 }
2921
2922
2923 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
2924 : blackbox(b), group(_group) {
2925 // watch for destroy notify on the group window
2926 XSelectInput(blackbox->getXDisplay(), group, StructureNotifyMask);
2927 blackbox->saveGroupSearch(group, this);
2928 }
2929
2930
2931 BWindowGroup::~BWindowGroup(void) {
2932 blackbox->removeGroupSearch(group);
2933 }
2934
2935
2936 BlackboxWindow *
2937 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
2938 BlackboxWindow *ret = blackbox->getFocusedWindow();
2939
2940 // does the focus window match (or any transient_fors)?
2941 while (ret) {
2942 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
2943 if (ret->isTransient() && allow_transients) break;
2944 else if (! ret->isTransient()) break;
2945 }
2946
2947 ret = ret->getTransientFor();
2948 }
2949
2950 if (ret) return ret;
2951
2952 // the focus window didn't match, look in the group's window list
2953 BlackboxWindowList::const_iterator it, end = windowList.end();
2954 for (it = windowList.begin(); it != end; ++it) {
2955 ret = *it;
2956 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
2957 if (ret->isTransient() && allow_transients) break;
2958 else if (! ret->isTransient()) break;
2959 }
2960 }
2961
2962 return ret;
2963 }
This page took 0.165587 seconds and 4 git commands to generate.