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