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