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