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