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