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