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