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