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