]> Dogcows Code - chaz/openbox/blob - src/Window.cc
2e465a0bb87f88936f37ef963930e6079b355408
[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 (blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2740 blackbox_attrib.workspace < screen->getWorkspaceCount())
2741 if (current_state == NormalState) current_state = WithdrawnState;
2742
2743 switch (current_state) {
2744 case IconicState:
2745 iconify();
2746 break;
2747
2748 case WithdrawnState:
2749 withdraw();
2750 break;
2751
2752 case NormalState:
2753 case InactiveState:
2754 case ZoomState:
2755 default:
2756 show();
2757 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2758 if (isNormal()) {
2759 if (! blackbox->isStartup()) {
2760 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2761 if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2762 getTransientFor()->isFocused())) {
2763 setInputFocus();
2764 }
2765 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2766 int x, y, rx, ry;
2767 Window c, r;
2768 unsigned int m;
2769 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2770 &r, &c, &rx, &ry, &x, &y, &m);
2771 beginMove(rx, ry);
2772 }
2773 }
2774 }
2775 break;
2776 }
2777 }
2778
2779
2780 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2781 if (ue->window != client.window)
2782 return;
2783
2784 #ifdef DEBUG
2785 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2786 client.window);
2787 #endif // DEBUG
2788
2789 screen->unmanageWindow(this, False);
2790 }
2791
2792
2793 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2794 if (de->window != client.window)
2795 return;
2796
2797 #ifdef DEBUG
2798 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2799 client.window);
2800 #endif // DEBUG
2801
2802 screen->unmanageWindow(this, False);
2803 }
2804
2805
2806 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2807 if (re->window != client.window || re->parent == frame.plate)
2808 return;
2809
2810 #ifdef DEBUG
2811 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2812 "0x%lx.\n", client.window, re->parent);
2813 #endif // DEBUG
2814
2815 XEvent ev;
2816 ev.xreparent = *re;
2817 XPutBackEvent(blackbox->getXDisplay(), &ev);
2818 screen->unmanageWindow(this, True);
2819 }
2820
2821
2822 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2823 if (pe->state == PropertyDelete || ! validateClient())
2824 return;
2825
2826 #if 0
2827 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2828 client.window);
2829 #endif
2830
2831 switch(pe->atom) {
2832 case XA_WM_CLASS:
2833 case XA_WM_CLIENT_MACHINE:
2834 case XA_WM_COMMAND:
2835 break;
2836
2837 case XA_WM_TRANSIENT_FOR: {
2838 bool s = flags.stuck;
2839
2840 // determine if this is a transient window
2841 getTransientInfo();
2842
2843 if (flags.stuck != s) stick();
2844
2845 // adjust the window decorations based on transience
2846 if (isTransient()) {
2847 functions &= ~Func_Maximize;
2848 setAllowedActions();
2849 setupDecor();
2850 }
2851
2852 reconfigure();
2853 }
2854 break;
2855
2856 case XA_WM_HINTS:
2857 getWMHints();
2858 break;
2859
2860 case XA_WM_ICON_NAME:
2861 getWMIconName();
2862 if (flags.iconic) screen->propagateWindowName(this);
2863 break;
2864
2865 case XAtom::net_wm_name:
2866 case XA_WM_NAME:
2867 getWMName();
2868
2869 if (decorations & Decor_Titlebar)
2870 redrawLabel();
2871
2872 screen->propagateWindowName(this);
2873 break;
2874
2875 case XA_WM_NORMAL_HINTS: {
2876 getWMNormalHints();
2877
2878 if ((client.normal_hint_flags & PMinSize) &&
2879 (client.normal_hint_flags & PMaxSize)) {
2880 // the window now can/can't resize itself, so the buttons need to be
2881 // regrabbed.
2882 ungrabButtons();
2883 if (client.max_width <= client.min_width &&
2884 client.max_height <= client.min_height) {
2885 functions &= ~(Func_Resize | Func_Maximize);
2886 } else {
2887 if (! isTransient())
2888 functions |= Func_Maximize;
2889 functions |= Func_Resize;
2890 }
2891 grabButtons();
2892 setAllowedActions();
2893 setupDecor();
2894 }
2895
2896 Rect old_rect = frame.rect;
2897
2898 upsize();
2899
2900 if (old_rect != frame.rect)
2901 reconfigure();
2902
2903 break;
2904 }
2905
2906 default:
2907 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2908 getWMProtocols();
2909
2910 if ((decorations & Decor_Close) && (! frame.close_button)) {
2911 createCloseButton();
2912 if (decorations & Decor_Titlebar) {
2913 positionButtons(True);
2914 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2915 }
2916 if (windowmenu) windowmenu->reconfigure();
2917 }
2918 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2919 updateStrut();
2920 }
2921
2922 break;
2923 }
2924 }
2925
2926
2927 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2928 #if 0
2929 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2930 #endif
2931
2932 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2933 redrawLabel();
2934 else if (frame.close_button == ee->window)
2935 redrawCloseButton(False);
2936 else if (frame.maximize_button == ee->window)
2937 redrawMaximizeButton(flags.maximized);
2938 else if (frame.iconify_button == ee->window)
2939 redrawIconifyButton(False);
2940 else if (frame.stick_button == ee->window)
2941 redrawStickyButton(flags.stuck);
2942 }
2943
2944
2945 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2946 if (cr->window != client.window || flags.iconic)
2947 return;
2948
2949 if (cr->value_mask & CWBorderWidth)
2950 client.old_bw = cr->border_width;
2951
2952 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2953 frame.changing = frame.rect;
2954
2955 if (cr->value_mask & (CWX | CWY)) {
2956 if (cr->value_mask & CWX)
2957 client.rect.setX(cr->x);
2958 if (cr->value_mask & CWY)
2959 client.rect.setY(cr->y);
2960
2961 applyGravity(frame.changing);
2962 }
2963
2964 if (cr->value_mask & (CWWidth | CWHeight)) {
2965 if (cr->value_mask & CWWidth)
2966 frame.changing.setWidth(cr->width +
2967 frame.margin.left + frame.margin.right);
2968
2969 if (cr->value_mask & CWHeight)
2970 frame.changing.setHeight(cr->height +
2971 frame.margin.top + frame.margin.bottom);
2972
2973 /*
2974 if a position change ha been specified, then that position will be used
2975 instead of determining a position based on the window's gravity.
2976 */
2977 if (cr->value_mask & (CWX | CWY)) {
2978 Corner corner;
2979 switch (client.win_gravity) {
2980 case NorthEastGravity:
2981 case EastGravity:
2982 corner = TopRight;
2983 break;
2984 case SouthWestGravity:
2985 case SouthGravity:
2986 corner = BottomLeft;
2987 break;
2988 case SouthEastGravity:
2989 corner = BottomRight;
2990 break;
2991 default: // NorthWest, Static, etc
2992 corner = TopLeft;
2993 }
2994 constrain(corner);
2995 }
2996 }
2997
2998 configure(frame.changing.x(), frame.changing.y(),
2999 frame.changing.width(), frame.changing.height());
3000 }
3001
3002 if (cr->value_mask & CWStackMode && !isDesktop()) {
3003 switch (cr->detail) {
3004 case Below:
3005 case BottomIf:
3006 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3007 break;
3008
3009 case Above:
3010 case TopIf:
3011 default:
3012 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3013 break;
3014 }
3015 }
3016 }
3017
3018
3019 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
3020 #ifdef DEBUG
3021 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3022 client.window);
3023 #endif
3024
3025 if (frame.maximize_button == be->window && be->button <= 3) {
3026 redrawMaximizeButton(True);
3027 } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
3028 if (! flags.focused)
3029 setInputFocus();
3030
3031 if (frame.iconify_button == be->window) {
3032 redrawIconifyButton(True);
3033 } else if (frame.close_button == be->window) {
3034 redrawCloseButton(True);
3035 } else if (frame.stick_button == be->window) {
3036 redrawStickyButton(True);
3037 } else if (frame.plate == be->window) {
3038 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3039
3040 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3041
3042 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
3043 } else {
3044 if (frame.title == be->window || frame.label == be->window) {
3045 if (((be->time - lastButtonPressTime) <=
3046 blackbox->getDoubleClickInterval()) ||
3047 (be->state == ControlMask)) {
3048 lastButtonPressTime = 0;
3049 shade();
3050 } else {
3051 lastButtonPressTime = be->time;
3052 }
3053 }
3054
3055 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3056
3057 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3058 }
3059 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3060 (be->window != frame.close_button) &&
3061 (be->window != frame.stick_button)) {
3062 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3063 } else if (windowmenu && be->button == 3 &&
3064 (frame.title == be->window || frame.label == be->window ||
3065 frame.handle == be->window || frame.window == be->window)) {
3066 if (windowmenu->isVisible()) {
3067 windowmenu->hide();
3068 } else {
3069 int mx = be->x_root - windowmenu->getWidth() / 2,
3070 my = be->y_root - windowmenu->getHeight() / 2;
3071
3072 // snap the window menu into a corner/side if necessary
3073 int left_edge, right_edge, top_edge, bottom_edge;
3074
3075 /*
3076 the " + (frame.border_w * 2) - 1" bits are to get the proper width
3077 and height of the menu, as the sizes returned by it do not include
3078 the borders.
3079 */
3080 left_edge = frame.rect.x();
3081 right_edge = frame.rect.right() -
3082 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
3083 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
3084 bottom_edge = client.rect.bottom() -
3085 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
3086 (frame.border_w + frame.mwm_border_w);
3087
3088 if (mx < left_edge)
3089 mx = left_edge;
3090 if (mx > right_edge)
3091 mx = right_edge;
3092 if (my < top_edge)
3093 my = top_edge;
3094 if (my > bottom_edge)
3095 my = bottom_edge;
3096
3097 windowmenu->move(mx, my);
3098 windowmenu->show();
3099 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
3100 XRaiseWindow(blackbox->getXDisplay(),
3101 windowmenu->getSendToMenu()->getWindowID());
3102 }
3103 // mouse wheel up
3104 } else if (be->button == 4) {
3105 if ((be->window == frame.label ||
3106 be->window == frame.title ||
3107 be->window == frame.maximize_button ||
3108 be->window == frame.iconify_button ||
3109 be->window == frame.close_button ||
3110 be->window == frame.stick_button) &&
3111 ! flags.shaded)
3112 shade();
3113 // mouse wheel down
3114 } else if (be->button == 5) {
3115 if ((be->window == frame.label ||
3116 be->window == frame.title ||
3117 be->window == frame.maximize_button ||
3118 be->window == frame.iconify_button ||
3119 be->window == frame.close_button ||
3120 be->window == frame.stick_button) &&
3121 flags.shaded)
3122 shade();
3123 }
3124 }
3125
3126
3127 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
3128 #ifdef DEBUG
3129 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3130 client.window);
3131 #endif
3132
3133 if (re->window == frame.maximize_button &&
3134 re->button >= 1 && re->button <= 3) {
3135 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3136 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3137 maximize(re->button);
3138 } else {
3139 redrawMaximizeButton(flags.maximized);
3140 }
3141 } else if (re->window == frame.iconify_button && re->button == 1) {
3142 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3143 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3144 iconify();
3145 } else {
3146 redrawIconifyButton(False);
3147 }
3148 } else if (re->window == frame.stick_button && re->button == 1) {
3149 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3150 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3151 stick();
3152 } else {
3153 redrawStickyButton(False);
3154 }
3155 } else if (re->window == frame.close_button & re->button == 1) {
3156 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3157 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3158 close();
3159 redrawCloseButton(False);
3160 } else if (flags.moving) {
3161 endMove();
3162 } else if (flags.resizing) {
3163 endResize();
3164 } else if (re->window == frame.window) {
3165 if (re->button == 2 && re->state == mod_mask)
3166 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3167 }
3168 }
3169
3170
3171
3172 void BlackboxWindow::beginMove(int x_root, int y_root) {
3173 if (! (functions & Func_Move)) return;
3174
3175 assert(! (flags.resizing || flags.moving));
3176
3177 /*
3178 Only one window can be moved/resized at a time. If another window is already
3179 being moved or resized, then stop it before whating to work with this one.
3180 */
3181 BlackboxWindow *changing = blackbox->getChangingWindow();
3182 if (changing && changing != this) {
3183 if (changing->flags.moving)
3184 changing->endMove();
3185 else // if (changing->flags.resizing)
3186 changing->endResize();
3187 }
3188
3189 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3190 PointerMotionMask | ButtonReleaseMask,
3191 GrabModeAsync, GrabModeAsync,
3192 None, blackbox->getMoveCursor(), CurrentTime);
3193
3194 if (windowmenu && windowmenu->isVisible())
3195 windowmenu->hide();
3196
3197 flags.moving = True;
3198 blackbox->setChangingWindow(this);
3199
3200 if (! screen->doOpaqueMove()) {
3201 XGrabServer(blackbox->getXDisplay());
3202
3203 frame.changing = frame.rect;
3204 screen->showPosition(frame.changing.x(), frame.changing.y());
3205
3206 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3207 screen->getOpGC(),
3208 frame.changing.x(),
3209 frame.changing.y(),
3210 frame.changing.width() - 1,
3211 frame.changing.height() - 1);
3212 }
3213
3214 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3215 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3216 }
3217
3218
3219 void BlackboxWindow::doMove(int x_root, int y_root) {
3220 assert(flags.moving);
3221 assert(blackbox->getChangingWindow() == this);
3222
3223 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3224 dx -= frame.border_w;
3225 dy -= frame.border_w;
3226
3227 doWindowSnapping(dx, dy);
3228
3229 if (screen->doOpaqueMove()) {
3230 if (screen->doWorkspaceWarping())
3231 doWorkspaceWarping(x_root, y_root, dx);
3232
3233 configure(dx, dy, frame.rect.width(), frame.rect.height());
3234 } else {
3235 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3236 screen->getOpGC(),
3237 frame.changing.x(),
3238 frame.changing.y(),
3239 frame.changing.width() - 1,
3240 frame.changing.height() - 1);
3241
3242 if (screen->doWorkspaceWarping())
3243 doWorkspaceWarping(x_root, y_root, dx);
3244
3245 frame.changing.setPos(dx, dy);
3246
3247 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3248 screen->getOpGC(),
3249 frame.changing.x(),
3250 frame.changing.y(),
3251 frame.changing.width() - 1,
3252 frame.changing.height() - 1);
3253 }
3254
3255 screen->showPosition(dx, dy);
3256 }
3257
3258
3259 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
3260 // workspace warping
3261 bool warp = False;
3262 unsigned int dest = screen->getCurrentWorkspaceID();
3263 if (x_root <= 0) {
3264 warp = True;
3265
3266 if (dest > 0) dest--;
3267 else dest = screen->getNumberOfWorkspaces() - 1;
3268
3269 } else if (x_root >= screen->getRect().right()) {
3270 warp = True;
3271
3272 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3273 else dest = 0;
3274 }
3275 if (! warp)
3276 return;
3277
3278 bool focus = flags.focused; // had focus while moving?
3279
3280 int dest_x = x_root;
3281 if (x_root <= 0) {
3282 dest_x += screen->getRect().width() - 1;
3283 dx += screen->getRect().width() - 1;
3284 } else {
3285 dest_x -= screen->getRect().width() - 1;
3286 dx -= screen->getRect().width() - 1;
3287 }
3288
3289 if (! flags.stuck)
3290 screen->reassociateWindow(this, dest, False);
3291 screen->changeWorkspaceID(dest);
3292
3293 if (screen->doOpaqueMove())
3294 XGrabServer(blackbox->getXDisplay());
3295
3296 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3297 XWarpPointer(blackbox->getXDisplay(), None,
3298 screen->getRootWindow(), 0, 0, 0, 0,
3299 dest_x, y_root);
3300 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3301 PointerMotionMask | ButtonReleaseMask,
3302 GrabModeAsync, GrabModeAsync,
3303 None, blackbox->getMoveCursor(), CurrentTime);
3304
3305 if (screen->doOpaqueMove())
3306 XUngrabServer(blackbox->getXDisplay());
3307
3308 if (focus)
3309 setInputFocus();
3310
3311 }
3312
3313
3314 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3315 // how much resistance to edges to provide
3316 const int resistance_size = screen->getResistanceSize();
3317
3318 // how far away to snap
3319 const int snap_distance = screen->getSnapThreshold();
3320
3321 // how to snap windows
3322 const int snap_to_windows = screen->getWindowToWindowSnap();
3323 const int snap_to_edges = screen->getWindowToEdgeSnap();
3324 // the amount of space away from the edge to provide resistance/snap
3325 const int snap_offset = screen->getSnapOffset();
3326
3327 // find the geomeetery where the moving window currently is
3328 const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3329
3330 // window corners
3331 const int wleft = dx,
3332 wright = dx + frame.rect.width() - 1,
3333 wtop = dy,
3334 wbottom = dy + frame.rect.height() - 1;
3335
3336 if (snap_to_windows) {
3337 RectList rectlist;
3338
3339 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3340 assert(w);
3341
3342 // add windows on the workspace to the rect list
3343 const BlackboxWindowList& stack_list = w->getStackingList();
3344 BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3345 for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3346 if (*st_it != this) // don't snap to ourself
3347 rectlist.push_back( (*st_it)->frameRect() );
3348
3349 // add the toolbar and the slit to the rect list.
3350 // (only if they are not hidden)
3351 Toolbar *tbar = screen->getToolbar();
3352 Slit *slit = screen->getSlit();
3353 Rect tbar_rect, slit_rect;
3354 unsigned int bwidth = screen->getBorderWidth() * 2;
3355
3356 if (! (screen->doHideToolbar() || tbar->isHidden())) {
3357 tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3358 tbar->getHeight() + bwidth);
3359 rectlist.push_back(tbar_rect);
3360 }
3361
3362 if (! slit->isHidden()) {
3363 slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3364 slit->getHeight() + bwidth);
3365 rectlist.push_back(slit_rect);
3366 }
3367
3368 RectList::const_iterator it, end = rectlist.end();
3369 for (it = rectlist.begin(); it != end; ++it) {
3370 bool snapped = False;
3371 const Rect &winrect = *it;
3372 Rect offsetrect;
3373 offsetrect.setCoords(winrect.left() - snap_offset,
3374 winrect.top() - snap_offset,
3375 winrect.right() + snap_offset,
3376 winrect.bottom() + snap_offset);
3377
3378 if (snap_to_windows == BScreen::WindowResistance)
3379 // if the window is already over top of this snap target, then
3380 // resistance is futile, so just ignore it
3381 if (winrect.intersects(moving))
3382 continue;
3383
3384 int dleft, dright, dtop, dbottom;
3385
3386 // if the windows are in the same plane vertically
3387 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3388 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3389
3390 if (snap_to_windows == BScreen::WindowResistance) {
3391 dleft = wright - offsetrect.left();
3392 dright = offsetrect.right() - wleft;
3393
3394 // snap left of other window?
3395 if (dleft >= 0 && dleft < resistance_size &&
3396 dleft < (wright - wleft)) {
3397 dx = offsetrect.left() - frame.rect.width();
3398 snapped = True;
3399 }
3400 // snap right of other window?
3401 else if (dright >= 0 && dright < resistance_size &&
3402 dright < (wright - wleft)) {
3403 dx = offsetrect.right() + 1;
3404 snapped = True;
3405 }
3406 } else { // BScreen::WindowSnap
3407 dleft = abs(wright - offsetrect.left());
3408 dright = abs(wleft - offsetrect.right());
3409
3410 // snap left of other window?
3411 if (dleft < snap_distance && dleft <= dright) {
3412 dx = offsetrect.left() - frame.rect.width();
3413 snapped = True;
3414 }
3415 // snap right of other window?
3416 else if (dright < snap_distance) {
3417 dx = offsetrect.right() + 1;
3418 snapped = True;
3419 }
3420 }
3421
3422 if (snapped) {
3423 if (screen->getWindowCornerSnap()) {
3424 // try corner-snap to its other sides
3425 if (snap_to_windows == BScreen::WindowResistance) {
3426 dtop = winrect.top() - wtop;
3427 dbottom = wbottom - winrect.bottom();
3428 if (dtop > 0 && dtop < resistance_size) {
3429 // if we're already past the top edge, then don't provide
3430 // resistance
3431 if (moving.top() >= winrect.top())
3432 dy = winrect.top();
3433 } else if (dbottom > 0 && dbottom < resistance_size) {
3434 // if we're already past the bottom edge, then don't provide
3435 // resistance
3436 if (moving.bottom() <= winrect.bottom())
3437 dy = winrect.bottom() - frame.rect.height() + 1;
3438 }
3439 } else { // BScreen::WindowSnap
3440 dtop = abs(wtop - winrect.top());
3441 dbottom = abs(wbottom - winrect.bottom());
3442 if (dtop < snap_distance && dtop <= dbottom)
3443 dy = winrect.top();
3444 else if (dbottom < snap_distance)
3445 dy = winrect.bottom() - frame.rect.height() + 1;
3446 }
3447 }
3448
3449 continue;
3450 }
3451 }
3452
3453 // if the windows are on the same plane horizontally
3454 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3455 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3456
3457 if (snap_to_windows == BScreen::WindowResistance) {
3458 dtop = wbottom - offsetrect.top();
3459 dbottom = offsetrect.bottom() - wtop;
3460
3461 // snap top of other window?
3462 if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3463 dy = offsetrect.top() - frame.rect.height();
3464 snapped = True;
3465 }
3466 // snap bottom of other window?
3467 else if (dbottom >= 0 && dbottom < resistance_size &&
3468 dbottom < (wbottom - wtop)) {
3469 dy = offsetrect.bottom() + 1;
3470 snapped = True;
3471 }
3472 } else { // BScreen::WindowSnap
3473 dtop = abs(wbottom - offsetrect.top());
3474 dbottom = abs(wtop - offsetrect.bottom());
3475
3476 // snap top of other window?
3477 if (dtop < snap_distance && dtop <= dbottom) {
3478 dy = offsetrect.top() - frame.rect.height();
3479 snapped = True;
3480 }
3481 // snap bottom of other window?
3482 else if (dbottom < snap_distance) {
3483 dy = offsetrect.bottom() + 1;
3484 snapped = True;
3485 }
3486
3487 }
3488
3489 if (snapped) {
3490 if (screen->getWindowCornerSnap()) {
3491 // try corner-snap to its other sides
3492 if (snap_to_windows == BScreen::WindowResistance) {
3493 dleft = winrect.left() - wleft;
3494 dright = wright - winrect.right();
3495 if (dleft > 0 && dleft < resistance_size) {
3496 // if we're already past the left edge, then don't provide
3497 // resistance
3498 if (moving.left() >= winrect.left())
3499 dx = winrect.left();
3500 } else if (dright > 0 && dright < resistance_size) {
3501 // if we're already past the right edge, then don't provide
3502 // resistance
3503 if (moving.right() <= winrect.right())
3504 dx = winrect.right() - frame.rect.width() + 1;
3505 }
3506 } else { // BScreen::WindowSnap
3507 dleft = abs(wleft - winrect.left());
3508 dright = abs(wright - winrect.right());
3509 if (dleft < snap_distance && dleft <= dright)
3510 dx = winrect.left();
3511 else if (dright < snap_distance)
3512 dx = winrect.right() - frame.rect.width() + 1;
3513 }
3514 }
3515
3516 continue;
3517 }
3518 }
3519 }
3520 }
3521
3522 if (snap_to_edges) {
3523 RectList rectlist;
3524
3525 // snap to the screen edges (and screen boundaries for xinerama)
3526 #ifdef XINERAMA
3527 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3528 rectlist.insert(rectlist.begin(),
3529 screen->getXineramaAreas().begin(),
3530 screen->getXineramaAreas().end());
3531 } else
3532 #endif // XINERAMA
3533 rectlist.push_back(screen->getRect());
3534
3535 RectList::const_iterator it, end = rectlist.end();
3536 for (it = rectlist.begin(); it != end; ++it) {
3537 const Rect &srect = *it;
3538 Rect offsetrect;
3539 offsetrect.setCoords(srect.left() + snap_offset,
3540 srect.top() + snap_offset,
3541 srect.right() - snap_offset,
3542 srect.bottom() - snap_offset);
3543
3544 if (snap_to_edges == BScreen::WindowResistance) {
3545 // if we're not in the rectangle then don't snap to it.
3546 if (! srect.contains(moving))
3547 continue;
3548 } else { // BScreen::WindowSnap
3549 // if we're not in the rectangle then don't snap to it.
3550 if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3551 frame.rect.height())))
3552 continue;
3553 }
3554
3555 if (snap_to_edges == BScreen::WindowResistance) {
3556 int dleft = offsetrect.left() - wleft,
3557 dright = wright - offsetrect.right(),
3558 dtop = offsetrect.top() - wtop,
3559 dbottom = wbottom - offsetrect.bottom();
3560
3561 // snap left?
3562 if (dleft > 0 && dleft < resistance_size)
3563 dx = offsetrect.left();
3564 // snap right?
3565 else if (dright > 0 && dright < resistance_size)
3566 dx = offsetrect.right() - frame.rect.width() + 1;
3567
3568 // snap top?
3569 if (dtop > 0 && dtop < resistance_size)
3570 dy = offsetrect.top();
3571 // snap bottom?
3572 else if (dbottom > 0 && dbottom < resistance_size)
3573 dy = offsetrect.bottom() - frame.rect.height() + 1;
3574 } else { // BScreen::WindowSnap
3575 int dleft = abs(wleft - offsetrect.left()),
3576 dright = abs(wright - offsetrect.right()),
3577 dtop = abs(wtop - offsetrect.top()),
3578 dbottom = abs(wbottom - offsetrect.bottom());
3579
3580 // snap left?
3581 if (dleft < snap_distance && dleft <= dright)
3582 dx = offsetrect.left();
3583 // snap right?
3584 else if (dright < snap_distance)
3585 dx = offsetrect.right() - frame.rect.width() + 1;
3586
3587 // snap top?
3588 if (dtop < snap_distance && dtop <= dbottom)
3589 dy = offsetrect.top();
3590 // snap bottom?
3591 else if (dbottom < snap_distance)
3592 dy = offsetrect.bottom() - frame.rect.height() + 1;
3593 }
3594 }
3595 }
3596 }
3597
3598
3599 void BlackboxWindow::endMove(void) {
3600 assert(flags.moving);
3601 assert(blackbox->getChangingWindow() == this);
3602
3603 flags.moving = False;
3604 blackbox->setChangingWindow(0);
3605
3606 if (! screen->doOpaqueMove()) {
3607 /* when drawing the rubber band, we need to make sure we only draw inside
3608 * the frame... frame.changing_* contain the new coords for the window,
3609 * so we need to subtract 1 from changing_w/changing_h every where we
3610 * draw the rubber band (for both moving and resizing)
3611 */
3612 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3613 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3614 frame.changing.width() - 1, frame.changing.height() - 1);
3615 XUngrabServer(blackbox->getXDisplay());
3616
3617 configure(frame.changing.x(), frame.changing.y(),
3618 frame.changing.width(), frame.changing.height());
3619 } else {
3620 configure(frame.rect.x(), frame.rect.y(),
3621 frame.rect.width(), frame.rect.height());
3622 }
3623 screen->hideGeometry();
3624
3625 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3626
3627 // if there are any left over motions from the move, drop them now
3628 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3629 XEvent e;
3630 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3631 MotionNotify, &e));
3632 }
3633
3634
3635 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3636 if (! (functions & Func_Resize)) return;
3637
3638 assert(! (flags.resizing || flags.moving));
3639
3640 /*
3641 Only one window can be moved/resized at a time. If another window is
3642 already being moved or resized, then stop it before whating to work with
3643 this one.
3644 */
3645 BlackboxWindow *changing = blackbox->getChangingWindow();
3646 if (changing && changing != this) {
3647 if (changing->flags.moving)
3648 changing->endMove();
3649 else // if (changing->flags.resizing)
3650 changing->endResize();
3651 }
3652
3653 resize_dir = dir;
3654
3655 Cursor cursor;
3656 Corner anchor;
3657
3658 switch (resize_dir) {
3659 case BottomLeft:
3660 anchor = TopRight;
3661 cursor = blackbox->getLowerLeftAngleCursor();
3662 break;
3663
3664 case BottomRight:
3665 anchor = TopLeft;
3666 cursor = blackbox->getLowerRightAngleCursor();
3667 break;
3668
3669 case TopLeft:
3670 anchor = BottomRight;
3671 cursor = blackbox->getUpperLeftAngleCursor();
3672 break;
3673
3674 case TopRight:
3675 anchor = BottomLeft;
3676 cursor = blackbox->getUpperRightAngleCursor();
3677 break;
3678
3679 default:
3680 assert(false); // unhandled Corner
3681 return; // unreachable, for the compiler
3682 }
3683
3684 XGrabServer(blackbox->getXDisplay());
3685 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3686 PointerMotionMask | ButtonReleaseMask,
3687 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3688
3689 flags.resizing = True;
3690 blackbox->setChangingWindow(this);
3691
3692 unsigned int gw, gh;
3693 frame.changing = frame.rect;
3694
3695 constrain(anchor, &gw, &gh);
3696
3697 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3698 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3699 frame.changing.width() - 1, frame.changing.height() - 1);
3700
3701 screen->showGeometry(gw, gh);
3702
3703 frame.grab_x = x_root;
3704 frame.grab_y = y_root;
3705 }
3706
3707
3708 void BlackboxWindow::doResize(int x_root, int y_root) {
3709 assert(flags.resizing);
3710 assert(blackbox->getChangingWindow() == this);
3711
3712 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3713 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3714 frame.changing.width() - 1, frame.changing.height() - 1);
3715
3716 unsigned int gw, gh;
3717 Corner anchor;
3718 int dx, dy; // the amount of change in the size of the window
3719
3720 switch (resize_dir) {
3721 case BottomLeft:
3722 anchor = TopRight;
3723 dx = - (x_root - frame.grab_x);
3724 dy = + (y_root - frame.grab_y);
3725 break;
3726 case BottomRight:
3727 anchor = TopLeft;
3728 dx = + (x_root - frame.grab_x);
3729 dy = + (y_root - frame.grab_y);
3730 break;
3731 case TopLeft:
3732 anchor = BottomRight;
3733 dx = - (x_root - frame.grab_x);
3734 dy = - (y_root - frame.grab_y);
3735 break;
3736 case TopRight:
3737 anchor = BottomLeft;
3738 dx = + (x_root - frame.grab_x);
3739 dy = - (y_root - frame.grab_y);
3740 break;
3741
3742 default:
3743 assert(false); // unhandled Corner
3744 return; // unreachable, for the compiler
3745 }
3746
3747 // make sure the user cant resize the window smaller than 0, which makes it
3748 // wrap around and become huge
3749 if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width();
3750 if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height();
3751
3752 frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy);
3753
3754 constrain(anchor, &gw, &gh);
3755
3756 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3757 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3758 frame.changing.width() - 1, frame.changing.height() - 1);
3759
3760 screen->showGeometry(gw, gh);
3761 }
3762
3763
3764 void BlackboxWindow::endResize(void) {
3765 assert(flags.resizing);
3766 assert(blackbox->getChangingWindow() == this);
3767
3768 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3769 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3770 frame.changing.width() - 1, frame.changing.height() - 1);
3771 XUngrabServer(blackbox->getXDisplay());
3772
3773 // unset maximized state after resized when fully maximized
3774 if (flags.maximized == 1)
3775 maximize(0);
3776
3777 flags.resizing = False;
3778 blackbox->setChangingWindow(0);
3779
3780 configure(frame.changing.x(), frame.changing.y(),
3781 frame.changing.width(), frame.changing.height());
3782 screen->hideGeometry();
3783
3784 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3785
3786 // if there are any left over motions from the resize, drop them now
3787 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3788 XEvent e;
3789 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3790 MotionNotify, &e));
3791 }
3792
3793
3794 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3795 #if 0
3796 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3797 client.window);
3798 #endif
3799
3800 if (flags.moving) {
3801 doMove(me->x_root, me->y_root);
3802 } else if (flags.resizing) {
3803 doResize(me->x_root, me->y_root);
3804 } else {
3805 if ((functions & Func_Move) &&
3806 (me->state & Button1Mask) &&
3807 (frame.title == me->window || frame.label == me->window ||
3808 frame.handle == me->window || frame.window == me->window)) {
3809 beginMove(me->x_root, me->y_root);
3810 } else if ((functions & Func_Resize) &&
3811 ((me->state & Button1Mask) && (me->window == frame.right_grip ||
3812 me->window == frame.left_grip)) ||
3813 ((me->state & Button3Mask) && (me->state & mod_mask) &&
3814 (frame.title == me->window || frame.label == me->window ||
3815 frame.handle == me->window || frame.window == me->window))) {
3816 unsigned int zones = screen->getResizeZones();
3817 Corner corner;
3818
3819 if (me->window == frame.left_grip) {
3820 corner = BottomLeft;
3821 } else if (me->window == frame.right_grip || zones == 1) {
3822 corner = BottomRight;
3823 } else {
3824 bool top;
3825 bool left = (me->x_root - frame.rect.x() <=
3826 static_cast<signed>(frame.rect.width() / 2));
3827 if (zones == 2)
3828 top = False;
3829 else // (zones == 4)
3830 top = (me->y_root - frame.rect.y() <=
3831 static_cast<signed>(frame.rect.height() / 2));
3832 corner = (top ? (left ? TopLeft : TopRight) :
3833 (left ? BottomLeft : BottomRight));
3834 }
3835
3836 beginResize(me->x_root, me->y_root, corner);
3837 }
3838 }
3839 }
3840
3841
3842 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3843 if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3844 return;
3845
3846 XEvent e;
3847 bool leave = False, inferior = False;
3848
3849 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3850 LeaveNotify, &e)) {
3851 if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3852 leave = True;
3853 inferior = (e.xcrossing.detail == NotifyInferior);
3854 }
3855 }
3856
3857 if (! leave || inferior) {
3858 if (! isFocused()) {
3859 bool success = setInputFocus();
3860 if (success) // if focus succeeded install the colormap
3861 installColormap(True); // XXX: shouldnt we honour no install?
3862 }
3863
3864 if (screen->doAutoRaise())
3865 timer->start();
3866 }
3867 }
3868
3869
3870 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3871 if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3872 return;
3873
3874 installColormap(False);
3875
3876 if (timer->isTiming())
3877 timer->stop();
3878 }
3879
3880
3881 #ifdef SHAPE
3882 void BlackboxWindow::shapeEvent(XShapeEvent *e) {
3883 if (blackbox->hasShapeExtensions()) {
3884 if (! e->shaped && flags.shaped) {
3885 clearShape();
3886 flags.shaped = False;
3887 } else if (e->shaped) {
3888 configureShape();
3889 flags.shaped = True;
3890 }
3891 }
3892 }
3893 #endif // SHAPE
3894
3895
3896 bool BlackboxWindow::validateClient(void) const {
3897 XSync(blackbox->getXDisplay(), False);
3898
3899 XEvent e;
3900 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3901 DestroyNotify, &e) ||
3902 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3903 UnmapNotify, &e)) {
3904 XPutBackEvent(blackbox->getXDisplay(), &e);
3905
3906 return False;
3907 }
3908
3909 return True;
3910 }
3911
3912
3913 void BlackboxWindow::restore(bool remap) {
3914 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3915 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3916 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3917
3918 // do not leave a shaded window as an icon unless it was an icon
3919 if (flags.shaded && ! flags.iconic)
3920 setState(NormalState);
3921
3922 // erase the netwm stuff that we read when a window maps, so that it
3923 // doesn't persist between mappings.
3924 // (these are the ones read in getNetWMFlags().)
3925 xatom->eraseValue(client.window, XAtom::net_wm_desktop);
3926 xatom->eraseValue(client.window, XAtom::net_wm_state);
3927
3928 restoreGravity(client.rect);
3929
3930 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3931 XUnmapWindow(blackbox->getXDisplay(), client.window);
3932
3933 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3934
3935 XEvent ev;
3936 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3937 ReparentNotify, &ev)) {
3938 remap = True;
3939 } else {
3940 // according to the ICCCM - if the client doesn't reparent to
3941 // root, then we have to do it for them
3942 XReparentWindow(blackbox->getXDisplay(), client.window,
3943 screen->getRootWindow(),
3944 client.rect.x(), client.rect.y());
3945 }
3946
3947 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3948 }
3949
3950
3951 // timer for autoraise
3952 void BlackboxWindow::timeout(void) {
3953 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3954 }
3955
3956
3957 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3958 if ((net->flags & AttribShaded) &&
3959 ((blackbox_attrib.attrib & AttribShaded) !=
3960 (net->attrib & AttribShaded)))
3961 shade();
3962
3963 if (flags.visible && // watch out for requests when we can not be seen
3964 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3965 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3966 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3967 if (flags.maximized) {
3968 maximize(0);
3969 } else {
3970 int button = 0;
3971
3972 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3973 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3974 else if (net->flags & AttribMaxVert)
3975 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3976 else if (net->flags & AttribMaxHoriz)
3977 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3978
3979 maximize(button);
3980 }
3981 }
3982
3983 if ((net->flags & AttribOmnipresent) &&
3984 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3985 (net->attrib & AttribOmnipresent)))
3986 stick();
3987
3988 if ((net->flags & AttribWorkspace) &&
3989 (blackbox_attrib.workspace != net->workspace)) {
3990 screen->reassociateWindow(this, net->workspace, True);
3991
3992 if (screen->getCurrentWorkspaceID() != net->workspace) {
3993 withdraw();
3994 } else {
3995 show();
3996 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3997 }
3998 }
3999
4000 if (net->flags & AttribDecoration) {
4001 switch (net->decoration) {
4002 case DecorNone:
4003 enableDecor(False);
4004 break;
4005
4006 default:
4007 case DecorNormal:
4008 case DecorTiny:
4009 case DecorTool:
4010 enableDecor(True);
4011 break;
4012 }
4013 }
4014 }
4015
4016
4017 /*
4018 * Set the sizes of all components of the window frame
4019 * (the window decorations).
4020 * These values are based upon the current style settings and the client
4021 * window's dimensions.
4022 */
4023 void BlackboxWindow::upsize(void) {
4024 frame.bevel_w = screen->getBevelWidth();
4025
4026 if (decorations & Decor_Border) {
4027 frame.border_w = screen->getBorderWidth();
4028 if (! isTransient())
4029 frame.mwm_border_w = screen->getFrameWidth();
4030 else
4031 frame.mwm_border_w = 0;
4032 } else {
4033 frame.mwm_border_w = frame.border_w = 0;
4034 }
4035
4036 if (decorations & Decor_Titlebar) {
4037 // the height of the titlebar is based upon the height of the font being
4038 // used to display the window's title
4039 WindowStyle *style = screen->getWindowStyle();
4040 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
4041
4042 frame.label_h = frame.title_h - (frame.bevel_w * 2);
4043 frame.button_w = (frame.label_h - 2);
4044
4045 // set the top frame margin
4046 frame.margin.top = frame.border_w + frame.title_h +
4047 frame.border_w + frame.mwm_border_w;
4048 } else {
4049 frame.title_h = 0;
4050 frame.label_h = 0;
4051 frame.button_w = 0;
4052
4053 // set the top frame margin
4054 frame.margin.top = frame.border_w + frame.mwm_border_w;
4055 }
4056
4057 // set the left/right frame margin
4058 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
4059
4060 if (decorations & Decor_Handle) {
4061 frame.grip_w = frame.button_w * 2;
4062 frame.handle_h = screen->getHandleWidth();
4063
4064 // set the bottom frame margin
4065 frame.margin.bottom = frame.border_w + frame.handle_h +
4066 frame.border_w + frame.mwm_border_w;
4067 } else {
4068 frame.handle_h = 0;
4069 frame.grip_w = 0;
4070
4071 // set the bottom frame margin
4072 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
4073 }
4074
4075 /*
4076 We first get the normal dimensions and use this to define the inside_w/h
4077 then we modify the height if shading is in effect.
4078 If the shade state is not considered then frame.rect gets reset to the
4079 normal window size on a reconfigure() call resulting in improper
4080 dimensions appearing in move/resize and other events.
4081 */
4082 unsigned int
4083 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
4084 width = client.rect.width() + frame.margin.left + frame.margin.right;
4085
4086 frame.inside_w = width - (frame.border_w * 2);
4087 frame.inside_h = height - (frame.border_w * 2);
4088
4089 if (flags.shaded)
4090 height = frame.title_h + (frame.border_w * 2);
4091 frame.rect.setSize(width, height);
4092 }
4093
4094
4095 /*
4096 * Calculate the size of the client window and constrain it to the
4097 * size specified by the size hints of the client window.
4098 *
4099 * The logical width and height are placed into pw and ph, if they
4100 * are non-zero. Logical size refers to the users perception of
4101 * the window size (for example an xterm resizes in cells, not in pixels).
4102 * pw and ph are then used to display the geometry during window moves, resize,
4103 * etc.
4104 *
4105 * The physical geometry is placed into frame.changing_{x,y,width,height}.
4106 * Physical geometry refers to the geometry of the window in pixels.
4107 */
4108 void BlackboxWindow::constrain(Corner anchor,
4109 unsigned int *pw, unsigned int *ph) {
4110 // frame.changing represents the requested frame size, we need to
4111 // strip the frame margin off and constrain the client size
4112 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
4113 frame.changing.top() + frame.margin.top,
4114 frame.changing.right() - frame.margin.right,
4115 frame.changing.bottom() - frame.margin.bottom);
4116
4117 unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
4118 base_width = (client.base_width) ? client.base_width : client.min_width,
4119 base_height = (client.base_height) ? client.base_height :
4120 client.min_height;
4121
4122 // constrain
4123 if (dw < client.min_width) dw = client.min_width;
4124 if (dh < client.min_height) dh = client.min_height;
4125 if (dw > client.max_width) dw = client.max_width;
4126 if (dh > client.max_height) dh = client.max_height;
4127
4128 assert(dw >= base_width && dh >= base_height);
4129
4130 if (client.width_inc > 1) {
4131 dw -= base_width;
4132 dw /= client.width_inc;
4133 }
4134 if (client.height_inc > 1) {
4135 dh -= base_height;
4136 dh /= client.height_inc;
4137 }
4138
4139 if (pw)
4140 *pw = dw;
4141
4142 if (ph)
4143 *ph = dh;
4144
4145 if (client.width_inc > 1) {
4146 dw *= client.width_inc;
4147 dw += base_width;
4148 }
4149 if (client.height_inc > 1) {
4150 dh *= client.height_inc;
4151 dh += base_height;
4152 }
4153
4154 frame.changing.setSize(dw, dh);
4155
4156 // add the frame margin back onto frame.changing
4157 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4158 frame.changing.top() - frame.margin.top,
4159 frame.changing.right() + frame.margin.right,
4160 frame.changing.bottom() + frame.margin.bottom);
4161
4162 // move frame.changing to the specified anchor
4163 int dx = 0,
4164 dy = 0;
4165 switch (anchor) {
4166 case TopLeft:
4167 break;
4168
4169 case TopRight:
4170 dx = frame.rect.right() - frame.changing.right();
4171 break;
4172
4173 case BottomLeft:
4174 dy = frame.rect.bottom() - frame.changing.bottom();
4175 break;
4176
4177 case BottomRight:
4178 dx = frame.rect.right() - frame.changing.right();
4179 dy = frame.rect.bottom() - frame.changing.bottom();
4180 break;
4181
4182 default:
4183 assert(false); // unhandled corner
4184 }
4185 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4186 }
4187
4188
4189 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4190 unsigned int max_length,
4191 unsigned int modifier) const {
4192 size_t text_len = text.size();
4193 unsigned int length;
4194
4195 do {
4196 length = font->measureString(string(text, 0, text_len)) + modifier;
4197 } while (length > max_length && text_len-- > 0);
4198
4199 switch (justify) {
4200 case RightJustify:
4201 start_pos += max_length - length;
4202 break;
4203
4204 case CenterJustify:
4205 start_pos += (max_length - length) / 2;
4206 break;
4207
4208 case LeftJustify:
4209 default:
4210 break;
4211 }
4212 }
4213
4214
4215 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4216 : blackbox(b), group(_group) {
4217 XWindowAttributes wattrib;
4218 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4219 // group window doesn't seem to exist anymore
4220 delete this;
4221 return;
4222 }
4223
4224 XSelectInput(blackbox->getXDisplay(), group,
4225 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4226
4227 blackbox->saveGroupSearch(group, this);
4228 }
4229
4230
4231 BWindowGroup::~BWindowGroup(void) {
4232 blackbox->removeGroupSearch(group);
4233 }
4234
4235
4236 BlackboxWindow *
4237 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4238 BlackboxWindow *ret = blackbox->getFocusedWindow();
4239
4240 // does the focus window match (or any transient_fors)?
4241 for (; ret; ret = ret->getTransientFor()) {
4242 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4243 (! ret->isTransient() || allow_transients))
4244 break;
4245 }
4246
4247 if (ret) return ret;
4248
4249 // the focus window didn't match, look in the group's window list
4250 BlackboxWindowList::const_iterator it, end = windowList.end();
4251 for (it = windowList.begin(); it != end; ++it) {
4252 ret = *it;
4253 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4254 (! ret->isTransient() || allow_transients))
4255 break;
4256 }
4257
4258 return ret;
4259 }
This page took 0.218691 seconds and 3 git commands to generate.