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