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