]> Dogcows Code - chaz/openbox/blob - src/Window.cc
Added a optional omnipresent button, enabled using the title bar layout key "S".
[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 #define DEBUG_WITH_ID 1
1129 #ifdef DEBUG_WITH_ID
1130 // the 16 is the 8 chars of the debug text plus the number
1131 char *tmp = new char[client.title.length() + 16];
1132 sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window);
1133 client.title = tmp;
1134 delete tmp;
1135 #endif
1136 }
1137
1138
1139 void BlackboxWindow::getWMIconName(void) {
1140 if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1141 XAtom::utf8, client.icon_title) &&
1142 !client.icon_title.empty()) {
1143 xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1144 return;
1145 }
1146 //fall through to using WM_ICON_NAME
1147 if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1148 client.icon_title) &&
1149 !client.icon_title.empty()) {
1150 xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1151 return;
1152 }
1153 // fall back to using the main name
1154 client.icon_title = client.title;
1155 xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1156 client.icon_title);
1157 }
1158
1159
1160 /*
1161 * Retrieve which WM Protocols are supported by the client window.
1162 * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1163 * window's decorations and allow the close behavior.
1164 * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1165 * this.
1166 */
1167 void BlackboxWindow::getWMProtocols(void) {
1168 Atom *proto;
1169 int num_return = 0;
1170
1171 if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1172 &proto, &num_return)) {
1173 for (int i = 0; i < num_return; ++i) {
1174 if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1175 decorations |= Decor_Close;
1176 functions |= Func_Close;
1177 } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1178 flags.send_focus_message = True;
1179 else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1180 screen->addNetizen(new Netizen(screen, client.window));
1181 }
1182
1183 XFree(proto);
1184 }
1185 }
1186
1187
1188 /*
1189 * Gets the value of the WM_HINTS property.
1190 * If the property is not set, then use a set of default values.
1191 */
1192 void BlackboxWindow::getWMHints(void) {
1193 focus_mode = F_Passive;
1194
1195 // remove from current window group
1196 if (client.window_group) {
1197 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1198 if (group) group->removeWindow(this);
1199 }
1200 client.window_group = None;
1201
1202 XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1203 if (! wmhint) {
1204 return;
1205 }
1206
1207 if (wmhint->flags & InputHint) {
1208 if (wmhint->input == True) {
1209 if (flags.send_focus_message)
1210 focus_mode = F_LocallyActive;
1211 } else {
1212 if (flags.send_focus_message)
1213 focus_mode = F_GloballyActive;
1214 else
1215 focus_mode = F_NoInput;
1216 }
1217 }
1218
1219 if (wmhint->flags & StateHint)
1220 current_state = wmhint->initial_state;
1221
1222 if (wmhint->flags & WindowGroupHint) {
1223 client.window_group = wmhint->window_group;
1224
1225 // add window to the appropriate group
1226 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1227 if (! group) { // no group found, create it!
1228 new BWindowGroup(blackbox, client.window_group);
1229 group = blackbox->searchGroup(client.window_group);
1230 }
1231 if (group)
1232 group->addWindow(this);
1233 }
1234
1235 XFree(wmhint);
1236 }
1237
1238
1239 /*
1240 * Gets the value of the WM_NORMAL_HINTS property.
1241 * If the property is not set, then use a set of default values.
1242 */
1243 void BlackboxWindow::getWMNormalHints(void) {
1244 long icccm_mask;
1245 XSizeHints sizehint;
1246
1247 client.min_width = client.min_height =
1248 client.width_inc = client.height_inc = 1;
1249 client.base_width = client.base_height = 0;
1250 client.win_gravity = NorthWestGravity;
1251 #if 0
1252 client.min_aspect_x = client.min_aspect_y =
1253 client.max_aspect_x = client.max_aspect_y = 1;
1254 #endif
1255
1256 // don't limit the size of a window, the default max width is the biggest
1257 // possible
1258 client.max_width = (unsigned) -1;
1259 client.max_height = (unsigned) -1;
1260
1261
1262 if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1263 &sizehint, &icccm_mask))
1264 return;
1265
1266 client.normal_hint_flags = sizehint.flags;
1267
1268 if (sizehint.flags & PMinSize) {
1269 if (sizehint.min_width >= 0)
1270 client.min_width = sizehint.min_width;
1271 if (sizehint.min_height >= 0)
1272 client.min_height = sizehint.min_height;
1273 }
1274
1275 if (sizehint.flags & PMaxSize) {
1276 if (sizehint.max_width > static_cast<signed>(client.min_width))
1277 client.max_width = sizehint.max_width;
1278 else
1279 client.max_width = client.min_width;
1280
1281 if (sizehint.max_height > static_cast<signed>(client.min_height))
1282 client.max_height = sizehint.max_height;
1283 else
1284 client.max_height = client.min_height;
1285 }
1286
1287 if (sizehint.flags & PResizeInc) {
1288 client.width_inc = sizehint.width_inc;
1289 client.height_inc = sizehint.height_inc;
1290 }
1291
1292 #if 0 // we do not support this at the moment
1293 if (sizehint.flags & PAspect) {
1294 client.min_aspect_x = sizehint.min_aspect.x;
1295 client.min_aspect_y = sizehint.min_aspect.y;
1296 client.max_aspect_x = sizehint.max_aspect.x;
1297 client.max_aspect_y = sizehint.max_aspect.y;
1298 }
1299 #endif
1300
1301 if (sizehint.flags & PBaseSize) {
1302 client.base_width = sizehint.base_width;
1303 client.base_height = sizehint.base_height;
1304 }
1305
1306 if (sizehint.flags & PWinGravity)
1307 client.win_gravity = sizehint.win_gravity;
1308 }
1309
1310
1311 /*
1312 * Gets the NETWM hints for the class' contained window.
1313 */
1314 void BlackboxWindow::getNetWMHints(void) {
1315 unsigned long workspace;
1316
1317 if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1318 workspace)) {
1319 if (workspace == 0xffffffff)
1320 flags.stuck = True;
1321 else
1322 blackbox_attrib.workspace = workspace;
1323 }
1324
1325 unsigned long *state;
1326 unsigned long num = (unsigned) -1;
1327 if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1328 num, &state)) {
1329 bool vert = False,
1330 horz = False;
1331 for (unsigned long i = 0; i < num; ++i) {
1332 if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1333 flags.modal = True;
1334 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1335 flags.shaded = True;
1336 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1337 flags.skip_taskbar = True;
1338 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1339 flags.skip_pager = True;
1340 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1341 flags.fullscreen = True;
1342 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1343 setState(IconicState);
1344 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1345 vert = True;
1346 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1347 horz = True;
1348 }
1349 if (vert && horz)
1350 flags.maximized = 1;
1351 else if (vert)
1352 flags.maximized = 2;
1353 else if (horz)
1354 flags.maximized = 3;
1355
1356 delete [] state;
1357 }
1358 }
1359
1360
1361 /*
1362 * Gets the MWM hints for the class' contained window.
1363 * This is used while initializing the window to its first state, and not
1364 * thereafter.
1365 * Returns: true if the MWM hints are successfully retreived and applied;
1366 * false if they are not.
1367 */
1368 void BlackboxWindow::getMWMHints(void) {
1369 unsigned long num;
1370 MwmHints *mwm_hint;
1371
1372 num = PropMwmHintsElements;
1373 if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1374 XAtom::motif_wm_hints, num,
1375 (unsigned long **)&mwm_hint))
1376 return;
1377 if (num < PropMwmHintsElements) {
1378 delete [] mwm_hint;
1379 return;
1380 }
1381
1382 if (mwm_hint->flags & MwmHintsDecorations) {
1383 if (mwm_hint->decorations & MwmDecorAll) {
1384 mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1385 Decor_Iconify | Decor_Maximize;
1386 } else {
1387 mwm_decorations = 0;
1388
1389 if (mwm_hint->decorations & MwmDecorBorder)
1390 mwm_decorations |= Decor_Border;
1391 if (mwm_hint->decorations & MwmDecorHandle)
1392 mwm_decorations |= Decor_Handle;
1393 if (mwm_hint->decorations & MwmDecorTitle)
1394 mwm_decorations |= Decor_Titlebar;
1395 if (mwm_hint->decorations & MwmDecorIconify)
1396 mwm_decorations |= Decor_Iconify;
1397 if (mwm_hint->decorations & MwmDecorMaximize)
1398 mwm_decorations |= Decor_Maximize;
1399 }
1400 }
1401
1402 if (mwm_hint->flags & MwmHintsFunctions) {
1403 if (mwm_hint->functions & MwmFuncAll) {
1404 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1405 Func_Close;
1406 } else {
1407 functions = 0;
1408
1409 if (mwm_hint->functions & MwmFuncResize)
1410 functions |= Func_Resize;
1411 if (mwm_hint->functions & MwmFuncMove)
1412 functions |= Func_Move;
1413 if (mwm_hint->functions & MwmFuncIconify)
1414 functions |= Func_Iconify;
1415 if (mwm_hint->functions & MwmFuncMaximize)
1416 functions |= Func_Maximize;
1417 if (mwm_hint->functions & MwmFuncClose)
1418 functions |= Func_Close;
1419 }
1420 }
1421 delete [] mwm_hint;
1422 }
1423
1424
1425 /*
1426 * Gets the blackbox hints from the class' contained window.
1427 * This is used while initializing the window to its first state, and not
1428 * thereafter.
1429 * Returns: true if the hints are successfully retreived and applied; false if
1430 * they are not.
1431 */
1432 bool BlackboxWindow::getBlackboxHints(void) {
1433 unsigned long num;
1434 BlackboxHints *blackbox_hint;
1435
1436 num = PropBlackboxHintsElements;
1437 if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1438 XAtom::blackbox_hints, num,
1439 (unsigned long **)&blackbox_hint))
1440 return False;
1441 if (num < PropBlackboxHintsElements) {
1442 delete [] blackbox_hint;
1443 return False;
1444 }
1445
1446 if (blackbox_hint->flags & AttribShaded)
1447 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1448
1449 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1450 (blackbox_hint->flags & AttribMaxVert))
1451 flags.maximized = (blackbox_hint->attrib &
1452 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1453 else if (blackbox_hint->flags & AttribMaxVert)
1454 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1455 else if (blackbox_hint->flags & AttribMaxHoriz)
1456 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1457
1458 if (blackbox_hint->flags & AttribOmnipresent)
1459 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1460
1461 if (blackbox_hint->flags & AttribWorkspace)
1462 blackbox_attrib.workspace = blackbox_hint->workspace;
1463
1464 // if (blackbox_hint->flags & AttribStack)
1465 // don't yet have always on top/bottom for blackbox yet... working
1466 // on that
1467
1468 if (blackbox_hint->flags & AttribDecoration) {
1469 switch (blackbox_hint->decoration) {
1470 case DecorNone:
1471 blackbox_attrib.decoration = DecorNone;
1472 break;
1473
1474 case DecorTiny:
1475 case DecorTool:
1476 case DecorNormal:
1477 default:
1478 // blackbox_attrib.decoration defaults to DecorNormal
1479 break;
1480 }
1481 }
1482
1483 delete [] blackbox_hint;
1484
1485 return True;
1486 }
1487
1488
1489 void BlackboxWindow::getTransientInfo(void) {
1490 if (client.transient_for &&
1491 client.transient_for != (BlackboxWindow *) ~0ul) {
1492 // reset transient_for in preparation of looking for a new owner
1493 client.transient_for->client.transientList.remove(this);
1494 }
1495
1496 // we have no transient_for until we find a new one
1497 client.transient_for = (BlackboxWindow *) 0;
1498
1499 Window trans_for;
1500 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1501 &trans_for)) {
1502 // transient_for hint not set
1503 return;
1504 }
1505
1506 if (trans_for == client.window) {
1507 // wierd client... treat this window as a normal window
1508 return;
1509 }
1510
1511 if (trans_for == None || trans_for == screen->getRootWindow()) {
1512 // this is an undocumented interpretation of the ICCCM. a transient
1513 // associated with None/Root/itself is assumed to be a modal root
1514 // transient. we don't support the concept of a global transient,
1515 // so we just associate this transient with nothing, and perhaps
1516 // we will add support later for global modality.
1517 client.transient_for = (BlackboxWindow *) ~0ul;
1518 flags.modal = True;
1519 return;
1520 }
1521
1522 client.transient_for = blackbox->searchWindow(trans_for);
1523 if (! client.transient_for &&
1524 client.window_group && trans_for == client.window_group) {
1525 // no direct transient_for, perhaps this is a group transient?
1526 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1527 if (group) client.transient_for = group->find(screen);
1528 }
1529
1530 if (! client.transient_for || client.transient_for == this) {
1531 // no transient_for found, or we have a wierd client that wants to be
1532 // a transient for itself, so we treat this window as a normal window
1533 client.transient_for = (BlackboxWindow*) 0;
1534 return;
1535 }
1536
1537 // Check for a circular transient state: this can lock up Blackbox
1538 // when it tries to find the non-transient window for a transient.
1539 BlackboxWindow *w = this;
1540 while(w->client.transient_for &&
1541 w->client.transient_for != (BlackboxWindow *) ~0ul) {
1542 if(w->client.transient_for == this) {
1543 client.transient_for = (BlackboxWindow*) 0;
1544 break;
1545 }
1546 w = w->client.transient_for;
1547 }
1548
1549 if (client.transient_for &&
1550 client.transient_for != (BlackboxWindow *) ~0ul) {
1551 // register ourselves with our new transient_for
1552 client.transient_for->client.transientList.push_back(this);
1553 flags.stuck = client.transient_for->flags.stuck;
1554 }
1555 }
1556
1557
1558 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1559 if (client.transient_for &&
1560 client.transient_for != (BlackboxWindow*) ~0ul)
1561 return client.transient_for;
1562 return 0;
1563 }
1564
1565
1566 /*
1567 * This function is responsible for updating both the client and the frame
1568 * rectangles.
1569 * According to the ICCCM a client message is not sent for a resize, only a
1570 * move.
1571 */
1572 void BlackboxWindow::configure(int dx, int dy,
1573 unsigned int dw, unsigned int dh) {
1574 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1575 ! flags.moving);
1576
1577 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1578 frame.rect.setRect(dx, dy, dw, dh);
1579 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1580 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1581
1582 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1583 frame.rect.setPos(0, 0);
1584
1585 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1586 frame.rect.top() + frame.margin.top,
1587 frame.rect.right() - frame.margin.right,
1588 frame.rect.bottom() - frame.margin.bottom);
1589
1590 #ifdef SHAPE
1591 if (blackbox->hasShapeExtensions() && flags.shaped) {
1592 configureShape();
1593 }
1594 #endif // SHAPE
1595
1596 positionWindows();
1597 decorate();
1598 redrawWindowFrame();
1599 } else {
1600 frame.rect.setPos(dx, dy);
1601
1602 XMoveWindow(blackbox->getXDisplay(), frame.window,
1603 frame.rect.x(), frame.rect.y());
1604 /*
1605 we may have been called just after an opaque window move, so even though
1606 the old coords match the new ones no ConfigureNotify has been sent yet.
1607 There are likely other times when this will be relevant as well.
1608 */
1609 if (! flags.moving) send_event = True;
1610 }
1611
1612 if (send_event) {
1613 // if moving, the update and event will occur when the move finishes
1614 client.rect.setPos(frame.rect.left() + frame.margin.left,
1615 frame.rect.top() + frame.margin.top);
1616
1617 XEvent event;
1618 event.type = ConfigureNotify;
1619
1620 event.xconfigure.display = blackbox->getXDisplay();
1621 event.xconfigure.event = client.window;
1622 event.xconfigure.window = client.window;
1623 event.xconfigure.x = client.rect.x();
1624 event.xconfigure.y = client.rect.y();
1625 event.xconfigure.width = client.rect.width();
1626 event.xconfigure.height = client.rect.height();
1627 event.xconfigure.border_width = client.old_bw;
1628 event.xconfigure.above = frame.window;
1629 event.xconfigure.override_redirect = False;
1630
1631 XSendEvent(blackbox->getXDisplay(), client.window, False,
1632 StructureNotifyMask, &event);
1633 screen->updateNetizenConfigNotify(&event);
1634 XFlush(blackbox->getXDisplay());
1635 }
1636 }
1637
1638
1639 #ifdef SHAPE
1640 void BlackboxWindow::configureShape(void) {
1641 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1642 frame.margin.left - frame.border_w,
1643 frame.margin.top - frame.border_w,
1644 client.window, ShapeBounding, ShapeSet);
1645
1646 int num = 0;
1647 XRectangle xrect[2];
1648
1649 if (decorations & Decor_Titlebar) {
1650 xrect[0].x = xrect[0].y = -frame.border_w;
1651 xrect[0].width = frame.rect.width();
1652 xrect[0].height = frame.title_h + (frame.border_w * 2);
1653 ++num;
1654 }
1655
1656 if (decorations & Decor_Handle) {
1657 xrect[1].x = -frame.border_w;
1658 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1659 frame.mwm_border_w - frame.border_w;
1660 xrect[1].width = frame.rect.width();
1661 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1662 ++num;
1663 }
1664
1665 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1666 ShapeBounding, 0, 0, xrect, num,
1667 ShapeUnion, Unsorted);
1668 }
1669
1670
1671 void BlackboxWindow::clearShape(void) {
1672 XShapeCombineMask(blackbox->getXDisplay(), frame.window, ShapeBounding,
1673 frame.margin.left - frame.border_w,
1674 frame.margin.top - frame.border_w,
1675 None, ShapeSet);
1676 }
1677 #endif // SHAPE
1678
1679
1680 bool BlackboxWindow::setInputFocus(void) {
1681 if (flags.focused) return True;
1682
1683 assert(flags.stuck || // window must be on the current workspace or sticky
1684 blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1685
1686 /*
1687 We only do this check for normal windows and dialogs because other windows
1688 do this on purpose, such as kde's kicker, and we don't want to go moving
1689 it.
1690 */
1691 if (window_type == Type_Normal || window_type == Type_Dialog)
1692 if (! frame.rect.intersects(screen->getRect())) {
1693 // client is outside the screen, move it to the center
1694 configure((screen->getWidth() - frame.rect.width()) / 2,
1695 (screen->getHeight() - frame.rect.height()) / 2,
1696 frame.rect.width(), frame.rect.height());
1697 }
1698
1699 if (client.transientList.size() > 0) {
1700 // transfer focus to any modal transients
1701 BlackboxWindowList::iterator it, end = client.transientList.end();
1702 for (it = client.transientList.begin(); it != end; ++it)
1703 if ((*it)->flags.modal) return (*it)->setInputFocus();
1704 }
1705
1706 bool ret = True;
1707 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1708 XSetInputFocus(blackbox->getXDisplay(), client.window,
1709 RevertToPointerRoot, CurrentTime);
1710 } else {
1711 /* we could set the focus to none, since the window doesn't accept focus,
1712 * but we shouldn't set focus to nothing since this would surely make
1713 * someone angry
1714 */
1715 ret = False;
1716 }
1717
1718 if (flags.send_focus_message) {
1719 XEvent ce;
1720 ce.xclient.type = ClientMessage;
1721 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1722 ce.xclient.display = blackbox->getXDisplay();
1723 ce.xclient.window = client.window;
1724 ce.xclient.format = 32;
1725 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1726 ce.xclient.data.l[1] = blackbox->getLastTime();
1727 ce.xclient.data.l[2] = 0l;
1728 ce.xclient.data.l[3] = 0l;
1729 ce.xclient.data.l[4] = 0l;
1730 XSendEvent(blackbox->getXDisplay(), client.window, False,
1731 NoEventMask, &ce);
1732 XFlush(blackbox->getXDisplay());
1733 }
1734
1735 return ret;
1736 }
1737
1738
1739 void BlackboxWindow::iconify(void) {
1740 if (flags.iconic || ! (functions & Func_Iconify)) return;
1741
1742 // We don't need to worry about resizing because resizing always grabs the X
1743 // server. This should only ever happen if using opaque moving.
1744 if (flags.moving)
1745 endMove();
1746
1747 if (windowmenu) windowmenu->hide();
1748
1749 /*
1750 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1751 * we need to clear the event mask on client.window for a split second.
1752 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1753 * split second, leaving us with a ghost window... so, we need to do this
1754 * while the X server is grabbed
1755 */
1756 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1757 StructureNotifyMask;
1758 XGrabServer(blackbox->getXDisplay());
1759 XSelectInput(blackbox->getXDisplay(), client.window,
1760 event_mask & ~StructureNotifyMask);
1761 XUnmapWindow(blackbox->getXDisplay(), client.window);
1762 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1763 XUngrabServer(blackbox->getXDisplay());
1764
1765 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1766 flags.visible = False;
1767 flags.iconic = True;
1768
1769 setState(IconicState);
1770
1771 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1772 if (flags.stuck) {
1773 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1774 if (i != blackbox_attrib.workspace)
1775 screen->getWorkspace(i)->removeWindow(this, True);
1776 }
1777
1778 if (isTransient()) {
1779 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1780 ! client.transient_for->flags.iconic) {
1781 // iconify our transient_for
1782 client.transient_for->iconify();
1783 }
1784 }
1785
1786 screen->addIcon(this);
1787
1788 if (client.transientList.size() > 0) {
1789 // iconify all transients
1790 BlackboxWindowList::iterator it, end = client.transientList.end();
1791 for (it = client.transientList.begin(); it != end; ++it) {
1792 if (! (*it)->flags.iconic) (*it)->iconify();
1793 }
1794 }
1795 screen->updateStackingList();
1796 }
1797
1798
1799 void BlackboxWindow::show(void) {
1800 flags.visible = True;
1801 flags.iconic = False;
1802
1803 current_state = (flags.shaded) ? IconicState : NormalState;
1804 setState(current_state);
1805
1806 XMapWindow(blackbox->getXDisplay(), client.window);
1807 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1808 XMapWindow(blackbox->getXDisplay(), frame.window);
1809
1810 #if 0
1811 int real_x, real_y;
1812 Window child;
1813 XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1814 screen->getRootWindow(),
1815 0, 0, &real_x, &real_y, &child);
1816 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1817 client.rect.left(), client.rect.top(), real_x, real_y);
1818 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1819 #endif
1820 }
1821
1822
1823 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1824 if (flags.iconic || reassoc)
1825 screen->reassociateWindow(this, BSENTINEL, False);
1826 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1827 return;
1828
1829 show();
1830
1831 // reassociate and deiconify all transients
1832 if (reassoc && client.transientList.size() > 0) {
1833 BlackboxWindowList::iterator it, end = client.transientList.end();
1834 for (it = client.transientList.begin(); it != end; ++it)
1835 (*it)->deiconify(True, False);
1836 }
1837
1838 if (raise)
1839 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1840 }
1841
1842
1843 void BlackboxWindow::close(void) {
1844 if (! (functions & Func_Close)) return;
1845
1846 XEvent ce;
1847 ce.xclient.type = ClientMessage;
1848 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1849 ce.xclient.display = blackbox->getXDisplay();
1850 ce.xclient.window = client.window;
1851 ce.xclient.format = 32;
1852 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1853 ce.xclient.data.l[1] = CurrentTime;
1854 ce.xclient.data.l[2] = 0l;
1855 ce.xclient.data.l[3] = 0l;
1856 ce.xclient.data.l[4] = 0l;
1857 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1858 XFlush(blackbox->getXDisplay());
1859 }
1860
1861
1862 void BlackboxWindow::withdraw(void) {
1863 // We don't need to worry about resizing because resizing always grabs the X
1864 // server. This should only ever happen if using opaque moving.
1865 if (flags.moving)
1866 endMove();
1867
1868 flags.visible = False;
1869 flags.iconic = False;
1870
1871 setState(current_state);
1872
1873 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1874
1875 XGrabServer(blackbox->getXDisplay());
1876
1877 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1878 StructureNotifyMask;
1879 XSelectInput(blackbox->getXDisplay(), client.window,
1880 event_mask & ~StructureNotifyMask);
1881 XUnmapWindow(blackbox->getXDisplay(), client.window);
1882 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1883
1884 XUngrabServer(blackbox->getXDisplay());
1885
1886 if (windowmenu) windowmenu->hide();
1887 }
1888
1889
1890 void BlackboxWindow::maximize(unsigned int button) {
1891 if (! (functions & Func_Maximize)) return;
1892
1893 // We don't need to worry about resizing because resizing always grabs the X
1894 // server. This should only ever happen if using opaque moving.
1895 if (flags.moving)
1896 endMove();
1897
1898 // handle case where menu is open then the max button is used instead
1899 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1900
1901 if (flags.maximized) {
1902 flags.maximized = 0;
1903
1904 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1905 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1906
1907 /*
1908 when a resize finishes, maximize(0) is called to clear any maximization
1909 flags currently set. Otherwise it still thinks it is maximized.
1910 so we do not need to call configure() because resizing will handle it
1911 */
1912 if (! flags.resizing)
1913 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1914 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1915
1916 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1917 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1918
1919 redrawAllButtons(); // in case it is not called in configure()
1920 setState(current_state);
1921 return;
1922 }
1923
1924 blackbox_attrib.premax_x = frame.rect.x();
1925 blackbox_attrib.premax_y = frame.rect.y();
1926 blackbox_attrib.premax_w = frame.rect.width();
1927 // use client.rect so that clients can be restored even if shaded
1928 blackbox_attrib.premax_h =
1929 client.rect.height() + frame.margin.top + frame.margin.bottom;
1930
1931 #ifdef XINERAMA
1932 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1933 // find the area to use
1934 RectList availableAreas = screen->allAvailableAreas();
1935 RectList::iterator it, end = availableAreas.end();
1936
1937 for (it = availableAreas.begin(); it != end; ++it)
1938 if (it->intersects(frame.rect)) break;
1939 if (it == end) // the window isn't inside an area
1940 it = availableAreas.begin(); // so just default to the first one
1941
1942 frame.changing = *it;
1943 } else
1944 #endif // XINERAMA
1945 frame.changing = screen->availableArea();
1946
1947 switch(button) {
1948 case 1:
1949 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1950 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1951 break;
1952
1953 case 2:
1954 blackbox_attrib.flags |= AttribMaxVert;
1955 blackbox_attrib.attrib |= AttribMaxVert;
1956
1957 frame.changing.setX(frame.rect.x());
1958 frame.changing.setWidth(frame.rect.width());
1959 break;
1960
1961 case 3:
1962 blackbox_attrib.flags |= AttribMaxHoriz;
1963 blackbox_attrib.attrib |= AttribMaxHoriz;
1964
1965 frame.changing.setY(frame.rect.y());
1966 frame.changing.setHeight(frame.rect.height());
1967 break;
1968 }
1969
1970 constrain(TopLeft);
1971
1972 if (flags.shaded) {
1973 blackbox_attrib.flags ^= AttribShaded;
1974 blackbox_attrib.attrib ^= AttribShaded;
1975 flags.shaded = False;
1976 }
1977
1978 flags.maximized = button;
1979
1980 configure(frame.changing.x(), frame.changing.y(),
1981 frame.changing.width(), frame.changing.height());
1982 if (flags.focused)
1983 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1984 redrawAllButtons(); // in case it is not called in configure()
1985 setState(current_state);
1986 }
1987
1988
1989 // re-maximizes the window to take into account availableArea changes
1990 void BlackboxWindow::remaximize(void) {
1991 if (flags.shaded) {
1992 // we only update the window's attributes otherwise we lose the shade bit
1993 switch(flags.maximized) {
1994 case 1:
1995 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1996 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1997 break;
1998
1999 case 2:
2000 blackbox_attrib.flags |= AttribMaxVert;
2001 blackbox_attrib.attrib |= AttribMaxVert;
2002 break;
2003
2004 case 3:
2005 blackbox_attrib.flags |= AttribMaxHoriz;
2006 blackbox_attrib.attrib |= AttribMaxHoriz;
2007 break;
2008 }
2009 return;
2010 }
2011
2012 // save the original dimensions because maximize will wipe them out
2013 int premax_x = blackbox_attrib.premax_x,
2014 premax_y = blackbox_attrib.premax_y,
2015 premax_w = blackbox_attrib.premax_w,
2016 premax_h = blackbox_attrib.premax_h;
2017
2018 unsigned int button = flags.maximized;
2019 flags.maximized = 0; // trick maximize() into working
2020 maximize(button);
2021
2022 // restore saved values
2023 blackbox_attrib.premax_x = premax_x;
2024 blackbox_attrib.premax_y = premax_y;
2025 blackbox_attrib.premax_w = premax_w;
2026 blackbox_attrib.premax_h = premax_h;
2027 }
2028
2029
2030 void BlackboxWindow::setWorkspace(unsigned int n) {
2031 blackbox_attrib.flags |= AttribWorkspace;
2032 blackbox_attrib.workspace = n;
2033 if (n == BSENTINEL) { // iconified window
2034 /*
2035 we set the workspace to 'all workspaces' so that taskbars will show the
2036 window. otherwise, it made uniconifying a window imposible without the
2037 blackbox workspace menu
2038 */
2039 n = 0xffffffff;
2040 }
2041 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
2042 }
2043
2044
2045 void BlackboxWindow::shade(void) {
2046 if (flags.shaded) {
2047 XResizeWindow(blackbox->getXDisplay(), frame.window,
2048 frame.inside_w, frame.inside_h);
2049 flags.shaded = False;
2050 blackbox_attrib.flags ^= AttribShaded;
2051 blackbox_attrib.attrib ^= AttribShaded;
2052
2053 setState(NormalState);
2054
2055 // set the frame rect to the normal size
2056 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2057 frame.margin.bottom);
2058 } else {
2059 if (! (decorations & Decor_Titlebar))
2060 return; // can't shade it without a titlebar!
2061
2062 XResizeWindow(blackbox->getXDisplay(), frame.window,
2063 frame.inside_w, frame.title_h);
2064 flags.shaded = True;
2065 blackbox_attrib.flags |= AttribShaded;
2066 blackbox_attrib.attrib |= AttribShaded;
2067
2068 setState(IconicState);
2069
2070 // set the frame rect to the shaded size
2071 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
2072 }
2073 }
2074
2075
2076 /*
2077 * (Un)Sticks a window and its relatives.
2078 */
2079 void BlackboxWindow::stick(void) {
2080 if (flags.stuck) {
2081 blackbox_attrib.flags ^= AttribOmnipresent;
2082 blackbox_attrib.attrib ^= AttribOmnipresent;
2083
2084 flags.stuck = False;
2085
2086 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2087 if (i != blackbox_attrib.workspace)
2088 screen->getWorkspace(i)->removeWindow(this, True);
2089
2090 if (! flags.iconic)
2091 screen->reassociateWindow(this, BSENTINEL, True);
2092 // temporary fix since sticky windows suck. set the hint to what we
2093 // actually hold in our data.
2094 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2095 blackbox_attrib.workspace);
2096
2097 setState(current_state);
2098 } else {
2099 flags.stuck = True;
2100
2101 blackbox_attrib.flags |= AttribOmnipresent;
2102 blackbox_attrib.attrib |= AttribOmnipresent;
2103
2104 // temporary fix since sticky windows suck. set the hint to a different
2105 // value than that contained in the class' data.
2106 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2107 0xffffffff);
2108
2109 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2110 if (i != blackbox_attrib.workspace)
2111 screen->getWorkspace(i)->addWindow(this, False, True);
2112
2113 setState(current_state);
2114 }
2115
2116 redrawAllButtons();
2117
2118 // go up the chain
2119 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2120 client.transient_for->isStuck() != flags.stuck)
2121 client.transient_for->stick();
2122 // go down the chain
2123 BlackboxWindowList::iterator it;
2124 const BlackboxWindowList::iterator end = client.transientList.end();
2125 for (it = client.transientList.begin(); it != end; ++it)
2126 if ((*it)->isStuck() != flags.stuck)
2127 (*it)->stick();
2128 }
2129
2130
2131 void BlackboxWindow::redrawWindowFrame(void) const {
2132 if (decorations & Decor_Titlebar) {
2133 if (flags.focused) {
2134 if (frame.ftitle)
2135 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2136 frame.title, frame.ftitle);
2137 else
2138 XSetWindowBackground(blackbox->getXDisplay(),
2139 frame.title, frame.ftitle_pixel);
2140 } else {
2141 if (frame.utitle)
2142 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2143 frame.title, frame.utitle);
2144 else
2145 XSetWindowBackground(blackbox->getXDisplay(),
2146 frame.title, frame.utitle_pixel);
2147 }
2148 XClearWindow(blackbox->getXDisplay(), frame.title);
2149
2150 redrawLabel();
2151 redrawAllButtons();
2152 }
2153
2154 if (decorations & Decor_Handle) {
2155 if (flags.focused) {
2156 if (frame.fhandle)
2157 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2158 frame.handle, frame.fhandle);
2159 else
2160 XSetWindowBackground(blackbox->getXDisplay(),
2161 frame.handle, frame.fhandle_pixel);
2162
2163 if (frame.fgrip) {
2164 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2165 frame.left_grip, frame.fgrip);
2166 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2167 frame.right_grip, frame.fgrip);
2168 } else {
2169 XSetWindowBackground(blackbox->getXDisplay(),
2170 frame.left_grip, frame.fgrip_pixel);
2171 XSetWindowBackground(blackbox->getXDisplay(),
2172 frame.right_grip, frame.fgrip_pixel);
2173 }
2174 } else {
2175 if (frame.uhandle)
2176 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2177 frame.handle, frame.uhandle);
2178 else
2179 XSetWindowBackground(blackbox->getXDisplay(),
2180 frame.handle, frame.uhandle_pixel);
2181
2182 if (frame.ugrip) {
2183 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2184 frame.left_grip, frame.ugrip);
2185 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2186 frame.right_grip, frame.ugrip);
2187 } else {
2188 XSetWindowBackground(blackbox->getXDisplay(),
2189 frame.left_grip, frame.ugrip_pixel);
2190 XSetWindowBackground(blackbox->getXDisplay(),
2191 frame.right_grip, frame.ugrip_pixel);
2192 }
2193 }
2194 XClearWindow(blackbox->getXDisplay(), frame.handle);
2195 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2196 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2197 }
2198
2199 if (decorations & Decor_Border) {
2200 if (flags.focused)
2201 XSetWindowBorder(blackbox->getXDisplay(),
2202 frame.plate, frame.fborder_pixel);
2203 else
2204 XSetWindowBorder(blackbox->getXDisplay(),
2205 frame.plate, frame.uborder_pixel);
2206 }
2207 }
2208
2209
2210 void BlackboxWindow::setFocusFlag(bool focus) {
2211 // only focus a window if it is visible
2212 if (focus && ! flags.visible)
2213 return;
2214
2215 flags.focused = focus;
2216
2217 redrawWindowFrame();
2218
2219 if (flags.focused)
2220 blackbox->setFocusedWindow(this);
2221
2222 if (! flags.iconic) {
2223 // iconic windows arent in a workspace menu!
2224 if (flags.stuck)
2225 screen->getCurrentWorkspace()->setFocused(this, isFocused());
2226 else
2227 screen->getWorkspace(blackbox_attrib.workspace)->
2228 setFocused(this, flags.focused);
2229 }
2230 }
2231
2232
2233 void BlackboxWindow::installColormap(bool install) {
2234 int i = 0, ncmap = 0;
2235 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2236 client.window, &ncmap);
2237 if (cmaps) {
2238 XWindowAttributes wattrib;
2239 if (XGetWindowAttributes(blackbox->getXDisplay(),
2240 client.window, &wattrib)) {
2241 if (install) {
2242 // install the window's colormap
2243 for (i = 0; i < ncmap; i++) {
2244 if (*(cmaps + i) == wattrib.colormap)
2245 // this window is using an installed color map... do not install
2246 install = False;
2247 }
2248 // otherwise, install the window's colormap
2249 if (install)
2250 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2251 } else {
2252 // uninstall the window's colormap
2253 for (i = 0; i < ncmap; i++) {
2254 if (*(cmaps + i) == wattrib.colormap)
2255 // we found the colormap to uninstall
2256 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2257 }
2258 }
2259 }
2260
2261 XFree(cmaps);
2262 }
2263 }
2264
2265
2266 void BlackboxWindow::setAllowedActions(void) {
2267 Atom actions[7];
2268 int num = 0;
2269
2270 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2271 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2272 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2273
2274 if (functions & Func_Move)
2275 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2276 if (functions & Func_Resize)
2277 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2278 if (functions & Func_Maximize) {
2279 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2280 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2281 }
2282
2283 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2284 actions, num);
2285 }
2286
2287
2288 void BlackboxWindow::setState(unsigned long new_state) {
2289 current_state = new_state;
2290
2291 unsigned long state[2];
2292 state[0] = current_state;
2293 state[1] = None;
2294 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2295
2296 xatom->setValue(client.window, XAtom::blackbox_attributes,
2297 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2298 PropBlackboxAttributesElements);
2299
2300 Atom netstate[8];
2301 int num = 0;
2302 if (flags.modal)
2303 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2304 if (flags.shaded)
2305 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2306 if (flags.iconic)
2307 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2308 if (flags.skip_taskbar)
2309 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2310 if (flags.skip_pager)
2311 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2312 if (flags.fullscreen)
2313 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2314 if (flags.maximized == 1 || flags.maximized == 2)
2315 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2316 if (flags.maximized == 1 || flags.maximized == 3)
2317 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2318 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2319 netstate, num);
2320 }
2321
2322
2323 bool BlackboxWindow::getState(void) {
2324 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2325 current_state);
2326 if (! ret) current_state = 0;
2327 return ret;
2328 }
2329
2330
2331 void BlackboxWindow::restoreAttributes(void) {
2332 unsigned long num = PropBlackboxAttributesElements;
2333 BlackboxAttributes *net;
2334 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2335 XAtom::blackbox_attributes, num,
2336 (unsigned long **)&net))
2337 return;
2338 if (num < PropBlackboxAttributesElements) {
2339 delete [] net;
2340 return;
2341 }
2342
2343 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2344 flags.shaded = False;
2345 unsigned long orig_state = current_state;
2346 shade();
2347
2348 /*
2349 At this point in the life of a window, current_state should only be set
2350 to IconicState if the window was an *icon*, not if it was shaded.
2351 */
2352 if (orig_state != IconicState)
2353 current_state = WithdrawnState;
2354 }
2355
2356 if (net->workspace != screen->getCurrentWorkspaceID() &&
2357 net->workspace < screen->getWorkspaceCount())
2358 screen->reassociateWindow(this, net->workspace, True);
2359
2360 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2361 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2362 // set to WithdrawnState so it will be mapped on the new workspace
2363 if (current_state == NormalState) current_state = WithdrawnState;
2364 } else if (current_state == WithdrawnState) {
2365 // the window is on this workspace and is Withdrawn, so it is waiting to
2366 // be mapped
2367 current_state = NormalState;
2368 }
2369
2370 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2371 ! flags.stuck) {
2372 stick();
2373
2374 // if the window was on another workspace, it was going to be hidden. this
2375 // specifies that the window should be mapped since it is sticky.
2376 if (current_state == WithdrawnState) current_state = NormalState;
2377 }
2378
2379 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2380 int x = net->premax_x, y = net->premax_y;
2381 unsigned int w = net->premax_w, h = net->premax_h;
2382 flags.maximized = 0;
2383
2384 unsigned int m = 0;
2385 if ((net->flags & AttribMaxHoriz) &&
2386 (net->flags & AttribMaxVert))
2387 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2388 else if (net->flags & AttribMaxVert)
2389 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2390 else if (net->flags & AttribMaxHoriz)
2391 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2392
2393 if (m) maximize(m);
2394
2395 blackbox_attrib.premax_x = x;
2396 blackbox_attrib.premax_y = y;
2397 blackbox_attrib.premax_w = w;
2398 blackbox_attrib.premax_h = h;
2399 }
2400
2401 if (net->flags & AttribDecoration) {
2402 switch (net->decoration) {
2403 case DecorNone:
2404 enableDecor(False);
2405 break;
2406
2407 /* since tools only let you toggle this anyways, we'll just make that all
2408 it supports for now.
2409 */
2410 default:
2411 case DecorNormal:
2412 case DecorTiny:
2413 case DecorTool:
2414 enableDecor(True);
2415 break;
2416 }
2417 }
2418
2419 // with the state set it will then be the map event's job to read the
2420 // window's state and behave accordingly
2421
2422 delete [] net;
2423 }
2424
2425
2426 /*
2427 * Positions the Rect r according the the client window position and
2428 * window gravity.
2429 */
2430 void BlackboxWindow::applyGravity(Rect &r) {
2431 // apply horizontal window gravity
2432 switch (client.win_gravity) {
2433 default:
2434 case NorthWestGravity:
2435 case SouthWestGravity:
2436 case WestGravity:
2437 r.setX(client.rect.x());
2438 break;
2439
2440 case NorthGravity:
2441 case SouthGravity:
2442 case CenterGravity:
2443 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2444 break;
2445
2446 case NorthEastGravity:
2447 case SouthEastGravity:
2448 case EastGravity:
2449 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2450 break;
2451
2452 case ForgetGravity:
2453 case StaticGravity:
2454 r.setX(client.rect.x() - frame.margin.left);
2455 break;
2456 }
2457
2458 // apply vertical window gravity
2459 switch (client.win_gravity) {
2460 default:
2461 case NorthWestGravity:
2462 case NorthEastGravity:
2463 case NorthGravity:
2464 r.setY(client.rect.y());
2465 break;
2466
2467 case CenterGravity:
2468 case EastGravity:
2469 case WestGravity:
2470 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2471 break;
2472
2473 case SouthWestGravity:
2474 case SouthEastGravity:
2475 case SouthGravity:
2476 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2477 break;
2478
2479 case ForgetGravity:
2480 case StaticGravity:
2481 r.setY(client.rect.y() - frame.margin.top);
2482 break;
2483 }
2484 }
2485
2486
2487 /*
2488 * The reverse of the applyGravity function.
2489 *
2490 * Positions the Rect r according to the frame window position and
2491 * window gravity.
2492 */
2493 void BlackboxWindow::restoreGravity(Rect &r) {
2494 // restore horizontal window gravity
2495 switch (client.win_gravity) {
2496 default:
2497 case NorthWestGravity:
2498 case SouthWestGravity:
2499 case WestGravity:
2500 r.setX(frame.rect.x());
2501 break;
2502
2503 case NorthGravity:
2504 case SouthGravity:
2505 case CenterGravity:
2506 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2507 break;
2508
2509 case NorthEastGravity:
2510 case SouthEastGravity:
2511 case EastGravity:
2512 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2513 break;
2514
2515 case ForgetGravity:
2516 case StaticGravity:
2517 r.setX(frame.rect.x() + frame.margin.left);
2518 break;
2519 }
2520
2521 // restore vertical window gravity
2522 switch (client.win_gravity) {
2523 default:
2524 case NorthWestGravity:
2525 case NorthEastGravity:
2526 case NorthGravity:
2527 r.setY(frame.rect.y());
2528 break;
2529
2530 case CenterGravity:
2531 case EastGravity:
2532 case WestGravity:
2533 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2534 break;
2535
2536 case SouthWestGravity:
2537 case SouthEastGravity:
2538 case SouthGravity:
2539 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2540 break;
2541
2542 case ForgetGravity:
2543 case StaticGravity:
2544 r.setY(frame.rect.y() + frame.margin.top);
2545 break;
2546 }
2547 }
2548
2549
2550 void BlackboxWindow::redrawLabel(void) const {
2551 if (flags.focused) {
2552 if (frame.flabel)
2553 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2554 frame.label, frame.flabel);
2555 else
2556 XSetWindowBackground(blackbox->getXDisplay(),
2557 frame.label, frame.flabel_pixel);
2558 } else {
2559 if (frame.ulabel)
2560 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2561 frame.label, frame.ulabel);
2562 else
2563 XSetWindowBackground(blackbox->getXDisplay(),
2564 frame.label, frame.ulabel_pixel);
2565 }
2566 XClearWindow(blackbox->getXDisplay(), frame.label);
2567
2568 WindowStyle *style = screen->getWindowStyle();
2569
2570 int pos = frame.bevel_w * 2;
2571 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2572 style->font->drawString(frame.label, pos, 1,
2573 (flags.focused ? style->l_text_focus :
2574 style->l_text_unfocus),
2575 client.title);
2576 }
2577
2578
2579 void BlackboxWindow::redrawAllButtons(void) const {
2580 if (frame.iconify_button) redrawIconifyButton(False);
2581 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2582 if (frame.close_button) redrawCloseButton(False);
2583 if (frame.stick_button) redrawStickyButton(flags.stuck);
2584 }
2585
2586
2587 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2588 if (! pressed) {
2589 if (flags.focused) {
2590 if (frame.fbutton)
2591 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2592 frame.iconify_button, frame.fbutton);
2593 else
2594 XSetWindowBackground(blackbox->getXDisplay(),
2595 frame.iconify_button, frame.fbutton_pixel);
2596 } else {
2597 if (frame.ubutton)
2598 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2599 frame.iconify_button, frame.ubutton);
2600 else
2601 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2602 frame.ubutton_pixel);
2603 }
2604 } else {
2605 if (frame.pbutton)
2606 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2607 frame.iconify_button, frame.pbutton);
2608 else
2609 XSetWindowBackground(blackbox->getXDisplay(),
2610 frame.iconify_button, frame.pbutton_pixel);
2611 }
2612 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2613
2614 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2615 screen->getWindowStyle()->b_pic_unfocus);
2616 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2617 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2618 }
2619
2620
2621 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2622 if (! pressed) {
2623 if (flags.focused) {
2624 if (frame.fbutton)
2625 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2626 frame.maximize_button, frame.fbutton);
2627 else
2628 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2629 frame.fbutton_pixel);
2630 } else {
2631 if (frame.ubutton)
2632 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2633 frame.maximize_button, frame.ubutton);
2634 else
2635 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2636 frame.ubutton_pixel);
2637 }
2638 } else {
2639 if (frame.pbutton)
2640 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2641 frame.maximize_button, frame.pbutton);
2642 else
2643 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2644 frame.pbutton_pixel);
2645 }
2646 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2647
2648 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2649 screen->getWindowStyle()->b_pic_unfocus);
2650 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2651 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2652 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2653 2, 3, (frame.button_w - 3), 3);
2654 }
2655
2656
2657 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2658 if (! pressed) {
2659 if (flags.focused) {
2660 if (frame.fbutton)
2661 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2662 frame.fbutton);
2663 else
2664 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2665 frame.fbutton_pixel);
2666 } else {
2667 if (frame.ubutton)
2668 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2669 frame.ubutton);
2670 else
2671 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2672 frame.ubutton_pixel);
2673 }
2674 } else {
2675 if (frame.pbutton)
2676 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2677 frame.close_button, frame.pbutton);
2678 else
2679 XSetWindowBackground(blackbox->getXDisplay(),
2680 frame.close_button, frame.pbutton_pixel);
2681 }
2682 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2683
2684 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2685 screen->getWindowStyle()->b_pic_unfocus, 0, 2);
2686 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2687 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2688 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2689 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2690 }
2691
2692
2693 void BlackboxWindow::redrawStickyButton(bool pressed) const {
2694 if (! pressed) {
2695 if (flags.focused) {
2696 if (frame.fbutton)
2697 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2698 frame.stick_button, frame.fbutton);
2699 else
2700 XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2701 frame.fbutton_pixel);
2702 } else {
2703 if (frame.ubutton)
2704 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2705 frame.stick_button, frame.ubutton);
2706 else
2707 XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2708 frame.ubutton_pixel);
2709 }
2710 } else {
2711 if (frame.pbutton)
2712 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2713 frame.stick_button, frame.pbutton);
2714 else
2715 XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2716 frame.pbutton_pixel);
2717 }
2718 XClearWindow(blackbox->getXDisplay(), frame.stick_button);
2719
2720 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2721 screen->getWindowStyle()->b_pic_unfocus);
2722
2723 XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2724 frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 );
2725 }
2726
2727 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2728 if (re->window != client.window)
2729 return;
2730
2731 #ifdef DEBUG
2732 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2733 client.window);
2734 #endif // DEBUG
2735
2736 /*
2737 Even though the window wants to be shown, if it is not on the current
2738 workspace, then it isn't going to be shown right now.
2739 */
2740 if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2741 blackbox_attrib.workspace < screen->getWorkspaceCount())
2742 if (current_state == NormalState) current_state = WithdrawnState;
2743
2744 switch (current_state) {
2745 case IconicState:
2746 iconify();
2747 break;
2748
2749 case WithdrawnState:
2750 withdraw();
2751 break;
2752
2753 case NormalState:
2754 case InactiveState:
2755 case ZoomState:
2756 default:
2757 show();
2758 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2759 if (isNormal()) {
2760 if (! blackbox->isStartup()) {
2761 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2762 if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2763 getTransientFor()->isFocused())) {
2764 setInputFocus();
2765 }
2766 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2767 int x, y, rx, ry;
2768 Window c, r;
2769 unsigned int m;
2770 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2771 &r, &c, &rx, &ry, &x, &y, &m);
2772 beginMove(rx, ry);
2773 }
2774 }
2775 }
2776 break;
2777 }
2778 }
2779
2780
2781 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2782 if (ue->window != client.window)
2783 return;
2784
2785 #ifdef DEBUG
2786 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2787 client.window);
2788 #endif // DEBUG
2789
2790 screen->unmanageWindow(this, False);
2791 }
2792
2793
2794 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2795 if (de->window != client.window)
2796 return;
2797
2798 #ifdef DEBUG
2799 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2800 client.window);
2801 #endif // DEBUG
2802
2803 screen->unmanageWindow(this, False);
2804 }
2805
2806
2807 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2808 if (re->window != client.window || re->parent == frame.plate)
2809 return;
2810
2811 #ifdef DEBUG
2812 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2813 "0x%lx.\n", client.window, re->parent);
2814 #endif // DEBUG
2815
2816 XEvent ev;
2817 ev.xreparent = *re;
2818 XPutBackEvent(blackbox->getXDisplay(), &ev);
2819 screen->unmanageWindow(this, True);
2820 }
2821
2822
2823 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2824 if (pe->state == PropertyDelete || ! validateClient())
2825 return;
2826
2827 #if 0
2828 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2829 client.window);
2830 #endif
2831
2832 switch(pe->atom) {
2833 case XA_WM_CLASS:
2834 case XA_WM_CLIENT_MACHINE:
2835 case XA_WM_COMMAND:
2836 break;
2837
2838 case XA_WM_TRANSIENT_FOR: {
2839 bool s = flags.stuck;
2840
2841 // determine if this is a transient window
2842 getTransientInfo();
2843
2844 if (flags.stuck != s) stick();
2845
2846 // adjust the window decorations based on transience
2847 if (isTransient()) {
2848 functions &= ~Func_Maximize;
2849 setAllowedActions();
2850 setupDecor();
2851 }
2852
2853 reconfigure();
2854 }
2855 break;
2856
2857 case XA_WM_HINTS:
2858 getWMHints();
2859 break;
2860
2861 case XA_WM_ICON_NAME:
2862 getWMIconName();
2863 if (flags.iconic) screen->propagateWindowName(this);
2864 break;
2865
2866 case XAtom::net_wm_name:
2867 case XA_WM_NAME:
2868 getWMName();
2869
2870 if (decorations & Decor_Titlebar)
2871 redrawLabel();
2872
2873 screen->propagateWindowName(this);
2874 break;
2875
2876 case XA_WM_NORMAL_HINTS: {
2877 getWMNormalHints();
2878
2879 if ((client.normal_hint_flags & PMinSize) &&
2880 (client.normal_hint_flags & PMaxSize)) {
2881 // the window now can/can't resize itself, so the buttons need to be
2882 // regrabbed.
2883 ungrabButtons();
2884 if (client.max_width <= client.min_width &&
2885 client.max_height <= client.min_height) {
2886 functions &= ~(Func_Resize | Func_Maximize);
2887 } else {
2888 if (! isTransient())
2889 functions |= Func_Maximize;
2890 functions |= Func_Resize;
2891 }
2892 grabButtons();
2893 setAllowedActions();
2894 setupDecor();
2895 }
2896
2897 Rect old_rect = frame.rect;
2898
2899 upsize();
2900
2901 if (old_rect != frame.rect)
2902 reconfigure();
2903
2904 break;
2905 }
2906
2907 default:
2908 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2909 getWMProtocols();
2910
2911 if ((decorations & Decor_Close) && (! frame.close_button)) {
2912 createCloseButton();
2913 if (decorations & Decor_Titlebar) {
2914 positionButtons(True);
2915 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2916 }
2917 if (windowmenu) windowmenu->reconfigure();
2918 }
2919 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2920 updateStrut();
2921 }
2922
2923 break;
2924 }
2925 }
2926
2927
2928 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2929 #if 0
2930 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2931 #endif
2932
2933 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2934 redrawLabel();
2935 else if (frame.close_button == ee->window)
2936 redrawCloseButton(False);
2937 else if (frame.maximize_button == ee->window)
2938 redrawMaximizeButton(flags.maximized);
2939 else if (frame.iconify_button == ee->window)
2940 redrawIconifyButton(False);
2941 else if (frame.stick_button == ee->window)
2942 redrawStickyButton(flags.stuck);
2943 }
2944
2945
2946 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2947 if (cr->window != client.window || flags.iconic)
2948 return;
2949
2950 if (cr->value_mask & CWBorderWidth)
2951 client.old_bw = cr->border_width;
2952
2953 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2954 frame.changing = frame.rect;
2955
2956 if (cr->value_mask & (CWX | CWY)) {
2957 if (cr->value_mask & CWX)
2958 client.rect.setX(cr->x);
2959 if (cr->value_mask & CWY)
2960 client.rect.setY(cr->y);
2961
2962 applyGravity(frame.changing);
2963 }
2964
2965 if (cr->value_mask & (CWWidth | CWHeight)) {
2966 if (cr->value_mask & CWWidth)
2967 frame.changing.setWidth(cr->width +
2968 frame.margin.left + frame.margin.right);
2969
2970 if (cr->value_mask & CWHeight)
2971 frame.changing.setHeight(cr->height +
2972 frame.margin.top + frame.margin.bottom);
2973
2974 /*
2975 if a position change ha been specified, then that position will be used
2976 instead of determining a position based on the window's gravity.
2977 */
2978 if (cr->value_mask & (CWX | CWY)) {
2979 Corner corner;
2980 switch (client.win_gravity) {
2981 case NorthEastGravity:
2982 case EastGravity:
2983 corner = TopRight;
2984 break;
2985 case SouthWestGravity:
2986 case SouthGravity:
2987 corner = BottomLeft;
2988 break;
2989 case SouthEastGravity:
2990 corner = BottomRight;
2991 break;
2992 default: // NorthWest, Static, etc
2993 corner = TopLeft;
2994 }
2995 constrain(corner);
2996 }
2997 }
2998
2999 configure(frame.changing.x(), frame.changing.y(),
3000 frame.changing.width(), frame.changing.height());
3001 }
3002
3003 if (cr->value_mask & CWStackMode && !isDesktop()) {
3004 switch (cr->detail) {
3005 case Below:
3006 case BottomIf:
3007 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3008 break;
3009
3010 case Above:
3011 case TopIf:
3012 default:
3013 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3014 break;
3015 }
3016 }
3017 }
3018
3019
3020 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
3021 #ifdef DEBUG
3022 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3023 client.window);
3024 #endif
3025
3026 if (frame.maximize_button == be->window && be->button <= 3) {
3027 redrawMaximizeButton(True);
3028 } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
3029 if (! flags.focused)
3030 setInputFocus();
3031
3032 if (frame.iconify_button == be->window) {
3033 redrawIconifyButton(True);
3034 } else if (frame.close_button == be->window) {
3035 redrawCloseButton(True);
3036 } else if (frame.stick_button == be->window) {
3037 redrawStickyButton(True);
3038 } else if (frame.plate == be->window) {
3039 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3040
3041 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3042
3043 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
3044 } else {
3045 if (frame.title == be->window || frame.label == be->window) {
3046 if (((be->time - lastButtonPressTime) <=
3047 blackbox->getDoubleClickInterval()) ||
3048 (be->state == ControlMask)) {
3049 lastButtonPressTime = 0;
3050 shade();
3051 } else {
3052 lastButtonPressTime = be->time;
3053 }
3054 }
3055
3056 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3057
3058 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3059 }
3060 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3061 (be->window != frame.close_button) &&
3062 (be->window != frame.stick_button)) {
3063 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3064 } else if (windowmenu && be->button == 3 &&
3065 (frame.title == be->window || frame.label == be->window ||
3066 frame.handle == be->window || frame.window == be->window)) {
3067 if (windowmenu->isVisible()) {
3068 windowmenu->hide();
3069 } else {
3070 int mx = be->x_root - windowmenu->getWidth() / 2,
3071 my = be->y_root - windowmenu->getHeight() / 2;
3072
3073 // snap the window menu into a corner/side if necessary
3074 int left_edge, right_edge, top_edge, bottom_edge;
3075
3076 /*
3077 the " + (frame.border_w * 2) - 1" bits are to get the proper width
3078 and height of the menu, as the sizes returned by it do not include
3079 the borders.
3080 */
3081 left_edge = frame.rect.x();
3082 right_edge = frame.rect.right() -
3083 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
3084 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
3085 bottom_edge = client.rect.bottom() -
3086 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
3087 (frame.border_w + frame.mwm_border_w);
3088
3089 if (mx < left_edge)
3090 mx = left_edge;
3091 if (mx > right_edge)
3092 mx = right_edge;
3093 if (my < top_edge)
3094 my = top_edge;
3095 if (my > bottom_edge)
3096 my = bottom_edge;
3097
3098 windowmenu->move(mx, my);
3099 windowmenu->show();
3100 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
3101 XRaiseWindow(blackbox->getXDisplay(),
3102 windowmenu->getSendToMenu()->getWindowID());
3103 }
3104 // mouse wheel up
3105 } else if (be->button == 4) {
3106 if ((be->window == frame.label ||
3107 be->window == frame.title ||
3108 be->window == frame.maximize_button ||
3109 be->window == frame.iconify_button ||
3110 be->window == frame.close_button ||
3111 be->window == frame.stick_button) &&
3112 ! flags.shaded)
3113 shade();
3114 // mouse wheel down
3115 } else if (be->button == 5) {
3116 if ((be->window == frame.label ||
3117 be->window == frame.title ||
3118 be->window == frame.maximize_button ||
3119 be->window == frame.iconify_button ||
3120 be->window == frame.close_button ||
3121 be->window == frame.stick_button) &&
3122 flags.shaded)
3123 shade();
3124 }
3125 }
3126
3127
3128 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
3129 #ifdef DEBUG
3130 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3131 client.window);
3132 #endif
3133
3134 if (re->window == frame.maximize_button &&
3135 re->button >= 1 && re->button <= 3) {
3136 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3137 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3138 maximize(re->button);
3139 } else {
3140 redrawMaximizeButton(flags.maximized);
3141 }
3142 } else if (re->window == frame.iconify_button && re->button == 1) {
3143 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3144 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3145 iconify();
3146 } else {
3147 redrawIconifyButton(False);
3148 }
3149 } else if (re->window == frame.stick_button && re->button == 1) {
3150 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3151 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3152 stick();
3153 } else {
3154 redrawStickyButton(False);
3155 }
3156 } else if (re->window == frame.close_button & re->button == 1) {
3157 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3158 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3159 close();
3160 redrawCloseButton(False);
3161 } else if (flags.moving) {
3162 endMove();
3163 } else if (flags.resizing) {
3164 endResize();
3165 } else if (re->window == frame.window) {
3166 if (re->button == 2 && re->state == mod_mask)
3167 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3168 }
3169 }
3170
3171
3172
3173 void BlackboxWindow::beginMove(int x_root, int y_root) {
3174 if (! (functions & Func_Move)) return;
3175
3176 assert(! (flags.resizing || flags.moving));
3177
3178 /*
3179 Only one window can be moved/resized at a time. If another window is already
3180 being moved or resized, then stop it before whating to work with this one.
3181 */
3182 BlackboxWindow *changing = blackbox->getChangingWindow();
3183 if (changing && changing != this) {
3184 if (changing->flags.moving)
3185 changing->endMove();
3186 else // if (changing->flags.resizing)
3187 changing->endResize();
3188 }
3189
3190 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3191 PointerMotionMask | ButtonReleaseMask,
3192 GrabModeAsync, GrabModeAsync,
3193 None, blackbox->getMoveCursor(), CurrentTime);
3194
3195 if (windowmenu && windowmenu->isVisible())
3196 windowmenu->hide();
3197
3198 flags.moving = True;
3199 blackbox->setChangingWindow(this);
3200
3201 if (! screen->doOpaqueMove()) {
3202 XGrabServer(blackbox->getXDisplay());
3203
3204 frame.changing = frame.rect;
3205 screen->showPosition(frame.changing.x(), frame.changing.y());
3206
3207 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3208 screen->getOpGC(),
3209 frame.changing.x(),
3210 frame.changing.y(),
3211 frame.changing.width() - 1,
3212 frame.changing.height() - 1);
3213 }
3214
3215 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3216 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3217 }
3218
3219
3220 void BlackboxWindow::doMove(int x_root, int y_root) {
3221 assert(flags.moving);
3222 assert(blackbox->getChangingWindow() == this);
3223
3224 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3225 dx -= frame.border_w;
3226 dy -= frame.border_w;
3227
3228 doWindowSnapping(dx, dy);
3229
3230 if (screen->doOpaqueMove()) {
3231 if (screen->doWorkspaceWarping())
3232 doWorkspaceWarping(x_root, y_root, dx);
3233
3234 configure(dx, dy, frame.rect.width(), frame.rect.height());
3235 } else {
3236 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3237 screen->getOpGC(),
3238 frame.changing.x(),
3239 frame.changing.y(),
3240 frame.changing.width() - 1,
3241 frame.changing.height() - 1);
3242
3243 if (screen->doWorkspaceWarping())
3244 doWorkspaceWarping(x_root, y_root, dx);
3245
3246 frame.changing.setPos(dx, dy);
3247
3248 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3249 screen->getOpGC(),
3250 frame.changing.x(),
3251 frame.changing.y(),
3252 frame.changing.width() - 1,
3253 frame.changing.height() - 1);
3254 }
3255
3256 screen->showPosition(dx, dy);
3257 }
3258
3259
3260 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
3261 // workspace warping
3262 bool warp = False;
3263 unsigned int dest = screen->getCurrentWorkspaceID();
3264 if (x_root <= 0) {
3265 warp = True;
3266
3267 if (dest > 0) dest--;
3268 else dest = screen->getNumberOfWorkspaces() - 1;
3269
3270 } else if (x_root >= screen->getRect().right()) {
3271 warp = True;
3272
3273 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3274 else dest = 0;
3275 }
3276 if (! warp)
3277 return;
3278
3279 bool focus = flags.focused; // had focus while moving?
3280
3281 int dest_x = x_root;
3282 if (x_root <= 0) {
3283 dest_x += screen->getRect().width() - 1;
3284 dx += screen->getRect().width() - 1;
3285 } else {
3286 dest_x -= screen->getRect().width() - 1;
3287 dx -= screen->getRect().width() - 1;
3288 }
3289
3290 if (! flags.stuck)
3291 screen->reassociateWindow(this, dest, False);
3292 screen->changeWorkspaceID(dest);
3293
3294 if (screen->doOpaqueMove())
3295 XGrabServer(blackbox->getXDisplay());
3296
3297 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3298 XWarpPointer(blackbox->getXDisplay(), None,
3299 screen->getRootWindow(), 0, 0, 0, 0,
3300 dest_x, y_root);
3301 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3302 PointerMotionMask | ButtonReleaseMask,
3303 GrabModeAsync, GrabModeAsync,
3304 None, blackbox->getMoveCursor(), CurrentTime);
3305
3306 if (screen->doOpaqueMove())
3307 XUngrabServer(blackbox->getXDisplay());
3308
3309 if (focus)
3310 setInputFocus();
3311
3312 }
3313
3314
3315 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3316 // how much resistance to edges to provide
3317 const int resistance_size = screen->getResistanceSize();
3318
3319 // how far away to snap
3320 const int snap_distance = screen->getSnapThreshold();
3321
3322 // how to snap windows
3323 const int snap_to_windows = screen->getWindowToWindowSnap();
3324 const int snap_to_edges = screen->getWindowToEdgeSnap();
3325 // the amount of space away from the edge to provide resistance/snap
3326 const int snap_offset = screen->getSnapOffset();
3327
3328 // find the geomeetery where the moving window currently is
3329 const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3330
3331 // window corners
3332 const int wleft = dx,
3333 wright = dx + frame.rect.width() - 1,
3334 wtop = dy,
3335 wbottom = dy + frame.rect.height() - 1;
3336
3337 if (snap_to_windows) {
3338 RectList rectlist;
3339
3340 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3341 assert(w);
3342
3343 // add windows on the workspace to the rect list
3344 const BlackboxWindowList& stack_list = w->getStackingList();
3345 BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3346 for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3347 if (*st_it != this) // don't snap to ourself
3348 rectlist.push_back( (*st_it)->frameRect() );
3349
3350 // add the toolbar and the slit to the rect list.
3351 // (only if they are not hidden)
3352 Toolbar *tbar = screen->getToolbar();
3353 Slit *slit = screen->getSlit();
3354 Rect tbar_rect, slit_rect;
3355 unsigned int bwidth = screen->getBorderWidth() * 2;
3356
3357 if (! (screen->doHideToolbar() || tbar->isHidden())) {
3358 tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3359 tbar->getHeight() + bwidth);
3360 rectlist.push_back(tbar_rect);
3361 }
3362
3363 if (! slit->isHidden()) {
3364 slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3365 slit->getHeight() + bwidth);
3366 rectlist.push_back(slit_rect);
3367 }
3368
3369 RectList::const_iterator it, end = rectlist.end();
3370 for (it = rectlist.begin(); it != end; ++it) {
3371 bool snapped = False;
3372 const Rect &winrect = *it;
3373 Rect offsetrect;
3374 offsetrect.setCoords(winrect.left() - snap_offset,
3375 winrect.top() - snap_offset,
3376 winrect.right() + snap_offset,
3377 winrect.bottom() + snap_offset);
3378
3379 if (snap_to_windows == BScreen::WindowResistance)
3380 // if the window is already over top of this snap target, then
3381 // resistance is futile, so just ignore it
3382 if (winrect.intersects(moving))
3383 continue;
3384
3385 int dleft, dright, dtop, dbottom;
3386
3387 // if the windows are in the same plane vertically
3388 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3389 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3390
3391 if (snap_to_windows == BScreen::WindowResistance) {
3392 dleft = wright - offsetrect.left();
3393 dright = offsetrect.right() - wleft;
3394
3395 // snap left of other window?
3396 if (dleft >= 0 && dleft < resistance_size &&
3397 dleft < (wright - wleft)) {
3398 dx = offsetrect.left() - frame.rect.width();
3399 snapped = True;
3400 }
3401 // snap right of other window?
3402 else if (dright >= 0 && dright < resistance_size &&
3403 dright < (wright - wleft)) {
3404 dx = offsetrect.right() + 1;
3405 snapped = True;
3406 }
3407 } else { // BScreen::WindowSnap
3408 dleft = abs(wright - offsetrect.left());
3409 dright = abs(wleft - offsetrect.right());
3410
3411 // snap left of other window?
3412 if (dleft < snap_distance && dleft <= dright) {
3413 dx = offsetrect.left() - frame.rect.width();
3414 snapped = True;
3415 }
3416 // snap right of other window?
3417 else if (dright < snap_distance) {
3418 dx = offsetrect.right() + 1;
3419 snapped = True;
3420 }
3421 }
3422
3423 if (snapped) {
3424 if (screen->getWindowCornerSnap()) {
3425 // try corner-snap to its other sides
3426 if (snap_to_windows == BScreen::WindowResistance) {
3427 dtop = winrect.top() - wtop;
3428 dbottom = wbottom - winrect.bottom();
3429 if (dtop > 0 && dtop < resistance_size) {
3430 // if we're already past the top edge, then don't provide
3431 // resistance
3432 if (moving.top() >= winrect.top())
3433 dy = winrect.top();
3434 } else if (dbottom > 0 && dbottom < resistance_size) {
3435 // if we're already past the bottom edge, then don't provide
3436 // resistance
3437 if (moving.bottom() <= winrect.bottom())
3438 dy = winrect.bottom() - frame.rect.height() + 1;
3439 }
3440 } else { // BScreen::WindowSnap
3441 dtop = abs(wtop - winrect.top());
3442 dbottom = abs(wbottom - winrect.bottom());
3443 if (dtop < snap_distance && dtop <= dbottom)
3444 dy = winrect.top();
3445 else if (dbottom < snap_distance)
3446 dy = winrect.bottom() - frame.rect.height() + 1;
3447 }
3448 }
3449
3450 continue;
3451 }
3452 }
3453
3454 // if the windows are on the same plane horizontally
3455 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3456 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3457
3458 if (snap_to_windows == BScreen::WindowResistance) {
3459 dtop = wbottom - offsetrect.top();
3460 dbottom = offsetrect.bottom() - wtop;
3461
3462 // snap top of other window?
3463 if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3464 dy = offsetrect.top() - frame.rect.height();
3465 snapped = True;
3466 }
3467 // snap bottom of other window?
3468 else if (dbottom >= 0 && dbottom < resistance_size &&
3469 dbottom < (wbottom - wtop)) {
3470 dy = offsetrect.bottom() + 1;
3471 snapped = True;
3472 }
3473 } else { // BScreen::WindowSnap
3474 dtop = abs(wbottom - offsetrect.top());
3475 dbottom = abs(wtop - offsetrect.bottom());
3476
3477 // snap top of other window?
3478 if (dtop < snap_distance && dtop <= dbottom) {
3479 dy = offsetrect.top() - frame.rect.height();
3480 snapped = True;
3481 }
3482 // snap bottom of other window?
3483 else if (dbottom < snap_distance) {
3484 dy = offsetrect.bottom() + 1;
3485 snapped = True;
3486 }
3487
3488 }
3489
3490 if (snapped) {
3491 if (screen->getWindowCornerSnap()) {
3492 // try corner-snap to its other sides
3493 if (snap_to_windows == BScreen::WindowResistance) {
3494 dleft = winrect.left() - wleft;
3495 dright = wright - winrect.right();
3496 if (dleft > 0 && dleft < resistance_size) {
3497 // if we're already past the left edge, then don't provide
3498 // resistance
3499 if (moving.left() >= winrect.left())
3500 dx = winrect.left();
3501 } else if (dright > 0 && dright < resistance_size) {
3502 // if we're already past the right edge, then don't provide
3503 // resistance
3504 if (moving.right() <= winrect.right())
3505 dx = winrect.right() - frame.rect.width() + 1;
3506 }
3507 } else { // BScreen::WindowSnap
3508 dleft = abs(wleft - winrect.left());
3509 dright = abs(wright - winrect.right());
3510 if (dleft < snap_distance && dleft <= dright)
3511 dx = winrect.left();
3512 else if (dright < snap_distance)
3513 dx = winrect.right() - frame.rect.width() + 1;
3514 }
3515 }
3516
3517 continue;
3518 }
3519 }
3520 }
3521 }
3522
3523 if (snap_to_edges) {
3524 RectList rectlist;
3525
3526 // snap to the screen edges (and screen boundaries for xinerama)
3527 #ifdef XINERAMA
3528 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3529 rectlist.insert(rectlist.begin(),
3530 screen->getXineramaAreas().begin(),
3531 screen->getXineramaAreas().end());
3532 } else
3533 #endif // XINERAMA
3534 rectlist.push_back(screen->getRect());
3535
3536 RectList::const_iterator it, end = rectlist.end();
3537 for (it = rectlist.begin(); it != end; ++it) {
3538 const Rect &srect = *it;
3539 Rect offsetrect;
3540 offsetrect.setCoords(srect.left() + snap_offset,
3541 srect.top() + snap_offset,
3542 srect.right() - snap_offset,
3543 srect.bottom() - snap_offset);
3544
3545 if (snap_to_edges == BScreen::WindowResistance) {
3546 // if we're not in the rectangle then don't snap to it.
3547 if (! srect.contains(moving))
3548 continue;
3549 } else { // BScreen::WindowSnap
3550 // if we're not in the rectangle then don't snap to it.
3551 if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3552 frame.rect.height())))
3553 continue;
3554 }
3555
3556 if (snap_to_edges == BScreen::WindowResistance) {
3557 int dleft = offsetrect.left() - wleft,
3558 dright = wright - offsetrect.right(),
3559 dtop = offsetrect.top() - wtop,
3560 dbottom = wbottom - offsetrect.bottom();
3561
3562 // snap left?
3563 if (dleft > 0 && dleft < resistance_size)
3564 dx = offsetrect.left();
3565 // snap right?
3566 else if (dright > 0 && dright < resistance_size)
3567 dx = offsetrect.right() - frame.rect.width() + 1;
3568
3569 // snap top?
3570 if (dtop > 0 && dtop < resistance_size)
3571 dy = offsetrect.top();
3572 // snap bottom?
3573 else if (dbottom > 0 && dbottom < resistance_size)
3574 dy = offsetrect.bottom() - frame.rect.height() + 1;
3575 } else { // BScreen::WindowSnap
3576 int dleft = abs(wleft - offsetrect.left()),
3577 dright = abs(wright - offsetrect.right()),
3578 dtop = abs(wtop - offsetrect.top()),
3579 dbottom = abs(wbottom - offsetrect.bottom());
3580
3581 // snap left?
3582 if (dleft < snap_distance && dleft <= dright)
3583 dx = offsetrect.left();
3584 // snap right?
3585 else if (dright < snap_distance)
3586 dx = offsetrect.right() - frame.rect.width() + 1;
3587
3588 // snap top?
3589 if (dtop < snap_distance && dtop <= dbottom)
3590 dy = offsetrect.top();
3591 // snap bottom?
3592 else if (dbottom < snap_distance)
3593 dy = offsetrect.bottom() - frame.rect.height() + 1;
3594 }
3595 }
3596 }
3597 }
3598
3599
3600 void BlackboxWindow::endMove(void) {
3601 assert(flags.moving);
3602 assert(blackbox->getChangingWindow() == this);
3603
3604 flags.moving = False;
3605 blackbox->setChangingWindow(0);
3606
3607 if (! screen->doOpaqueMove()) {
3608 /* when drawing the rubber band, we need to make sure we only draw inside
3609 * the frame... frame.changing_* contain the new coords for the window,
3610 * so we need to subtract 1 from changing_w/changing_h every where we
3611 * draw the rubber band (for both moving and resizing)
3612 */
3613 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3614 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3615 frame.changing.width() - 1, frame.changing.height() - 1);
3616 XUngrabServer(blackbox->getXDisplay());
3617
3618 configure(frame.changing.x(), frame.changing.y(),
3619 frame.changing.width(), frame.changing.height());
3620 } else {
3621 configure(frame.rect.x(), frame.rect.y(),
3622 frame.rect.width(), frame.rect.height());
3623 }
3624 screen->hideGeometry();
3625
3626 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3627
3628 // if there are any left over motions from the move, drop them now
3629 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3630 XEvent e;
3631 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3632 MotionNotify, &e));
3633 }
3634
3635
3636 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3637 if (! (functions & Func_Resize)) return;
3638
3639 assert(! (flags.resizing || flags.moving));
3640
3641 /*
3642 Only one window can be moved/resized at a time. If another window is
3643 already being moved or resized, then stop it before whating to work with
3644 this one.
3645 */
3646 BlackboxWindow *changing = blackbox->getChangingWindow();
3647 if (changing && changing != this) {
3648 if (changing->flags.moving)
3649 changing->endMove();
3650 else // if (changing->flags.resizing)
3651 changing->endResize();
3652 }
3653
3654 resize_dir = dir;
3655
3656 Cursor cursor;
3657 Corner anchor;
3658
3659 switch (resize_dir) {
3660 case BottomLeft:
3661 anchor = TopRight;
3662 cursor = blackbox->getLowerLeftAngleCursor();
3663 break;
3664
3665 case BottomRight:
3666 anchor = TopLeft;
3667 cursor = blackbox->getLowerRightAngleCursor();
3668 break;
3669
3670 case TopLeft:
3671 anchor = BottomRight;
3672 cursor = blackbox->getUpperLeftAngleCursor();
3673 break;
3674
3675 case TopRight:
3676 anchor = BottomLeft;
3677 cursor = blackbox->getUpperRightAngleCursor();
3678 break;
3679
3680 default:
3681 assert(false); // unhandled Corner
3682 return; // unreachable, for the compiler
3683 }
3684
3685 XGrabServer(blackbox->getXDisplay());
3686 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3687 PointerMotionMask | ButtonReleaseMask,
3688 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3689
3690 flags.resizing = True;
3691 blackbox->setChangingWindow(this);
3692
3693 unsigned int gw, gh;
3694 frame.changing = frame.rect;
3695
3696 constrain(anchor, &gw, &gh);
3697
3698 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3699 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3700 frame.changing.width() - 1, frame.changing.height() - 1);
3701
3702 screen->showGeometry(gw, gh);
3703
3704 frame.grab_x = x_root;
3705 frame.grab_y = y_root;
3706 }
3707
3708
3709 void BlackboxWindow::doResize(int x_root, int y_root) {
3710 assert(flags.resizing);
3711 assert(blackbox->getChangingWindow() == this);
3712
3713 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3714 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3715 frame.changing.width() - 1, frame.changing.height() - 1);
3716
3717 unsigned int gw, gh;
3718 Corner anchor;
3719 int dx, dy; // the amount of change in the size of the window
3720
3721 switch (resize_dir) {
3722 case BottomLeft:
3723 anchor = TopRight;
3724 dx = - (x_root - frame.grab_x);
3725 dy = + (y_root - frame.grab_y);
3726 break;
3727 case BottomRight:
3728 anchor = TopLeft;
3729 dx = + (x_root - frame.grab_x);
3730 dy = + (y_root - frame.grab_y);
3731 break;
3732 case TopLeft:
3733 anchor = BottomRight;
3734 dx = - (x_root - frame.grab_x);
3735 dy = - (y_root - frame.grab_y);
3736 break;
3737 case TopRight:
3738 anchor = BottomLeft;
3739 dx = + (x_root - frame.grab_x);
3740 dy = - (y_root - frame.grab_y);
3741 break;
3742
3743 default:
3744 assert(false); // unhandled Corner
3745 return; // unreachable, for the compiler
3746 }
3747
3748 // make sure the user cant resize the window smaller than 0, which makes it
3749 // wrap around and become huge
3750 if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width();
3751 if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height();
3752
3753 frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy);
3754
3755 constrain(anchor, &gw, &gh);
3756
3757 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3758 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3759 frame.changing.width() - 1, frame.changing.height() - 1);
3760
3761 screen->showGeometry(gw, gh);
3762 }
3763
3764
3765 void BlackboxWindow::endResize(void) {
3766 assert(flags.resizing);
3767 assert(blackbox->getChangingWindow() == this);
3768
3769 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3770 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3771 frame.changing.width() - 1, frame.changing.height() - 1);
3772 XUngrabServer(blackbox->getXDisplay());
3773
3774 // unset maximized state after resized when fully maximized
3775 if (flags.maximized == 1)
3776 maximize(0);
3777
3778 flags.resizing = False;
3779 blackbox->setChangingWindow(0);
3780
3781 configure(frame.changing.x(), frame.changing.y(),
3782 frame.changing.width(), frame.changing.height());
3783 screen->hideGeometry();
3784
3785 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3786
3787 // if there are any left over motions from the resize, drop them now
3788 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3789 XEvent e;
3790 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3791 MotionNotify, &e));
3792 }
3793
3794
3795 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3796 #if 0
3797 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3798 client.window);
3799 #endif
3800
3801 if (flags.moving) {
3802 doMove(me->x_root, me->y_root);
3803 } else if (flags.resizing) {
3804 doResize(me->x_root, me->y_root);
3805 } else {
3806 if ((functions & Func_Move) &&
3807 (me->state & Button1Mask) &&
3808 (frame.title == me->window || frame.label == me->window ||
3809 frame.handle == me->window || frame.window == me->window)) {
3810 beginMove(me->x_root, me->y_root);
3811 } else if ((functions & Func_Resize) &&
3812 ((me->state & Button1Mask) && (me->window == frame.right_grip ||
3813 me->window == frame.left_grip)) ||
3814 ((me->state & Button3Mask) && (me->state & mod_mask) &&
3815 (frame.title == me->window || frame.label == me->window ||
3816 frame.handle == me->window || frame.window == me->window))) {
3817 unsigned int zones = screen->getResizeZones();
3818 Corner corner;
3819
3820 if (me->window == frame.left_grip) {
3821 corner = BottomLeft;
3822 } else if (me->window == frame.right_grip || zones == 1) {
3823 corner = BottomRight;
3824 } else {
3825 bool top;
3826 bool left = (me->x_root - frame.rect.x() <=
3827 static_cast<signed>(frame.rect.width() / 2));
3828 if (zones == 2)
3829 top = False;
3830 else // (zones == 4)
3831 top = (me->y_root - frame.rect.y() <=
3832 static_cast<signed>(frame.rect.height() / 2));
3833 corner = (top ? (left ? TopLeft : TopRight) :
3834 (left ? BottomLeft : BottomRight));
3835 }
3836
3837 beginResize(me->x_root, me->y_root, corner);
3838 }
3839 }
3840 }
3841
3842
3843 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3844 if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3845 return;
3846
3847 XEvent e;
3848 bool leave = False, inferior = False;
3849
3850 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3851 LeaveNotify, &e)) {
3852 if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3853 leave = True;
3854 inferior = (e.xcrossing.detail == NotifyInferior);
3855 }
3856 }
3857
3858 if (! leave || inferior) {
3859 if (! isFocused()) {
3860 bool success = setInputFocus();
3861 if (success) // if focus succeeded install the colormap
3862 installColormap(True); // XXX: shouldnt we honour no install?
3863 }
3864
3865 if (screen->doAutoRaise())
3866 timer->start();
3867 }
3868 }
3869
3870
3871 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3872 if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3873 return;
3874
3875 installColormap(False);
3876
3877 if (timer->isTiming())
3878 timer->stop();
3879 }
3880
3881
3882 #ifdef SHAPE
3883 void BlackboxWindow::shapeEvent(XShapeEvent *e) {
3884 if (blackbox->hasShapeExtensions()) {
3885 if (! e->shaped && flags.shaped) {
3886 clearShape();
3887 flags.shaped = False;
3888 } else if (e->shaped) {
3889 configureShape();
3890 flags.shaped = True;
3891 }
3892 }
3893 }
3894 #endif // SHAPE
3895
3896
3897 bool BlackboxWindow::validateClient(void) const {
3898 XSync(blackbox->getXDisplay(), False);
3899
3900 XEvent e;
3901 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3902 DestroyNotify, &e) ||
3903 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3904 UnmapNotify, &e)) {
3905 XPutBackEvent(blackbox->getXDisplay(), &e);
3906
3907 return False;
3908 }
3909
3910 return True;
3911 }
3912
3913
3914 void BlackboxWindow::restore(bool remap) {
3915 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3916 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3917 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3918
3919 // do not leave a shaded window as an icon unless it was an icon
3920 if (flags.shaded && ! flags.iconic)
3921 setState(NormalState);
3922
3923 // erase the netwm stuff that we read when a window maps, so that it
3924 // doesn't persist between mappings.
3925 // (these are the ones read in getNetWMFlags().)
3926 xatom->eraseValue(client.window, XAtom::net_wm_desktop);
3927 xatom->eraseValue(client.window, XAtom::net_wm_state);
3928
3929 restoreGravity(client.rect);
3930
3931 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3932 XUnmapWindow(blackbox->getXDisplay(), client.window);
3933
3934 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3935
3936 XEvent ev;
3937 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3938 ReparentNotify, &ev)) {
3939 remap = True;
3940 } else {
3941 // according to the ICCCM - if the client doesn't reparent to
3942 // root, then we have to do it for them
3943 XReparentWindow(blackbox->getXDisplay(), client.window,
3944 screen->getRootWindow(),
3945 client.rect.x(), client.rect.y());
3946 }
3947
3948 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3949 }
3950
3951
3952 // timer for autoraise
3953 void BlackboxWindow::timeout(void) {
3954 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3955 }
3956
3957
3958 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3959 if ((net->flags & AttribShaded) &&
3960 ((blackbox_attrib.attrib & AttribShaded) !=
3961 (net->attrib & AttribShaded)))
3962 shade();
3963
3964 if (flags.visible && // watch out for requests when we can not be seen
3965 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3966 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3967 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3968 if (flags.maximized) {
3969 maximize(0);
3970 } else {
3971 int button = 0;
3972
3973 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3974 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3975 else if (net->flags & AttribMaxVert)
3976 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3977 else if (net->flags & AttribMaxHoriz)
3978 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3979
3980 maximize(button);
3981 }
3982 }
3983
3984 if ((net->flags & AttribOmnipresent) &&
3985 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3986 (net->attrib & AttribOmnipresent)))
3987 stick();
3988
3989 if ((net->flags & AttribWorkspace) &&
3990 (blackbox_attrib.workspace != net->workspace)) {
3991 screen->reassociateWindow(this, net->workspace, True);
3992
3993 if (screen->getCurrentWorkspaceID() != net->workspace) {
3994 withdraw();
3995 } else {
3996 show();
3997 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3998 }
3999 }
4000
4001 if (net->flags & AttribDecoration) {
4002 switch (net->decoration) {
4003 case DecorNone:
4004 enableDecor(False);
4005 break;
4006
4007 default:
4008 case DecorNormal:
4009 case DecorTiny:
4010 case DecorTool:
4011 enableDecor(True);
4012 break;
4013 }
4014 }
4015 }
4016
4017
4018 /*
4019 * Set the sizes of all components of the window frame
4020 * (the window decorations).
4021 * These values are based upon the current style settings and the client
4022 * window's dimensions.
4023 */
4024 void BlackboxWindow::upsize(void) {
4025 frame.bevel_w = screen->getBevelWidth();
4026
4027 if (decorations & Decor_Border) {
4028 frame.border_w = screen->getBorderWidth();
4029 if (! isTransient())
4030 frame.mwm_border_w = screen->getFrameWidth();
4031 else
4032 frame.mwm_border_w = 0;
4033 } else {
4034 frame.mwm_border_w = frame.border_w = 0;
4035 }
4036
4037 if (decorations & Decor_Titlebar) {
4038 // the height of the titlebar is based upon the height of the font being
4039 // used to display the window's title
4040 WindowStyle *style = screen->getWindowStyle();
4041 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
4042
4043 frame.label_h = frame.title_h - (frame.bevel_w * 2);
4044 frame.button_w = (frame.label_h - 2);
4045
4046 // set the top frame margin
4047 frame.margin.top = frame.border_w + frame.title_h +
4048 frame.border_w + frame.mwm_border_w;
4049 } else {
4050 frame.title_h = 0;
4051 frame.label_h = 0;
4052 frame.button_w = 0;
4053
4054 // set the top frame margin
4055 frame.margin.top = frame.border_w + frame.mwm_border_w;
4056 }
4057
4058 // set the left/right frame margin
4059 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
4060
4061 if (decorations & Decor_Handle) {
4062 frame.grip_w = frame.button_w * 2;
4063 frame.handle_h = screen->getHandleWidth();
4064
4065 // set the bottom frame margin
4066 frame.margin.bottom = frame.border_w + frame.handle_h +
4067 frame.border_w + frame.mwm_border_w;
4068 } else {
4069 frame.handle_h = 0;
4070 frame.grip_w = 0;
4071
4072 // set the bottom frame margin
4073 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
4074 }
4075
4076 /*
4077 We first get the normal dimensions and use this to define the inside_w/h
4078 then we modify the height if shading is in effect.
4079 If the shade state is not considered then frame.rect gets reset to the
4080 normal window size on a reconfigure() call resulting in improper
4081 dimensions appearing in move/resize and other events.
4082 */
4083 unsigned int
4084 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
4085 width = client.rect.width() + frame.margin.left + frame.margin.right;
4086
4087 frame.inside_w = width - (frame.border_w * 2);
4088 frame.inside_h = height - (frame.border_w * 2);
4089
4090 if (flags.shaded)
4091 height = frame.title_h + (frame.border_w * 2);
4092 frame.rect.setSize(width, height);
4093 }
4094
4095
4096 /*
4097 * Calculate the size of the client window and constrain it to the
4098 * size specified by the size hints of the client window.
4099 *
4100 * The logical width and height are placed into pw and ph, if they
4101 * are non-zero. Logical size refers to the users perception of
4102 * the window size (for example an xterm resizes in cells, not in pixels).
4103 * pw and ph are then used to display the geometry during window moves, resize,
4104 * etc.
4105 *
4106 * The physical geometry is placed into frame.changing_{x,y,width,height}.
4107 * Physical geometry refers to the geometry of the window in pixels.
4108 */
4109 void BlackboxWindow::constrain(Corner anchor,
4110 unsigned int *pw, unsigned int *ph) {
4111 // frame.changing represents the requested frame size, we need to
4112 // strip the frame margin off and constrain the client size
4113 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
4114 frame.changing.top() + frame.margin.top,
4115 frame.changing.right() - frame.margin.right,
4116 frame.changing.bottom() - frame.margin.bottom);
4117
4118 unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
4119 base_width = (client.base_width) ? client.base_width : client.min_width,
4120 base_height = (client.base_height) ? client.base_height :
4121 client.min_height;
4122
4123 // constrain
4124 if (dw < client.min_width) dw = client.min_width;
4125 if (dh < client.min_height) dh = client.min_height;
4126 if (dw > client.max_width) dw = client.max_width;
4127 if (dh > client.max_height) dh = client.max_height;
4128
4129 assert(dw >= base_width && dh >= base_height);
4130
4131 if (client.width_inc > 1) {
4132 dw -= base_width;
4133 dw /= client.width_inc;
4134 }
4135 if (client.height_inc > 1) {
4136 dh -= base_height;
4137 dh /= client.height_inc;
4138 }
4139
4140 if (pw)
4141 *pw = dw;
4142
4143 if (ph)
4144 *ph = dh;
4145
4146 if (client.width_inc > 1) {
4147 dw *= client.width_inc;
4148 dw += base_width;
4149 }
4150 if (client.height_inc > 1) {
4151 dh *= client.height_inc;
4152 dh += base_height;
4153 }
4154
4155 frame.changing.setSize(dw, dh);
4156
4157 // add the frame margin back onto frame.changing
4158 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4159 frame.changing.top() - frame.margin.top,
4160 frame.changing.right() + frame.margin.right,
4161 frame.changing.bottom() + frame.margin.bottom);
4162
4163 // move frame.changing to the specified anchor
4164 int dx = 0,
4165 dy = 0;
4166 switch (anchor) {
4167 case TopLeft:
4168 break;
4169
4170 case TopRight:
4171 dx = frame.rect.right() - frame.changing.right();
4172 break;
4173
4174 case BottomLeft:
4175 dy = frame.rect.bottom() - frame.changing.bottom();
4176 break;
4177
4178 case BottomRight:
4179 dx = frame.rect.right() - frame.changing.right();
4180 dy = frame.rect.bottom() - frame.changing.bottom();
4181 break;
4182
4183 default:
4184 assert(false); // unhandled corner
4185 }
4186 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4187 }
4188
4189
4190 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4191 unsigned int max_length,
4192 unsigned int modifier) const {
4193 size_t text_len = text.size();
4194 unsigned int length;
4195
4196 do {
4197 length = font->measureString(string(text, 0, text_len)) + modifier;
4198 } while (length > max_length && text_len-- > 0);
4199
4200 switch (justify) {
4201 case RightJustify:
4202 start_pos += max_length - length;
4203 break;
4204
4205 case CenterJustify:
4206 start_pos += (max_length - length) / 2;
4207 break;
4208
4209 case LeftJustify:
4210 default:
4211 break;
4212 }
4213 }
4214
4215
4216 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4217 : blackbox(b), group(_group) {
4218 XWindowAttributes wattrib;
4219 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4220 // group window doesn't seem to exist anymore
4221 delete this;
4222 return;
4223 }
4224
4225 XSelectInput(blackbox->getXDisplay(), group,
4226 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4227
4228 blackbox->saveGroupSearch(group, this);
4229 }
4230
4231
4232 BWindowGroup::~BWindowGroup(void) {
4233 blackbox->removeGroupSearch(group);
4234 }
4235
4236
4237 BlackboxWindow *
4238 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4239 BlackboxWindow *ret = blackbox->getFocusedWindow();
4240
4241 // does the focus window match (or any transient_fors)?
4242 for (; ret; ret = ret->getTransientFor()) {
4243 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4244 (! ret->isTransient() || allow_transients))
4245 break;
4246 }
4247
4248 if (ret) return ret;
4249
4250 // the focus window didn't match, look in the group's window list
4251 BlackboxWindowList::const_iterator it, end = windowList.end();
4252 for (it = windowList.begin(); it != end; ++it) {
4253 ret = *it;
4254 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4255 (! ret->isTransient() || allow_transients))
4256 break;
4257 }
4258
4259 return ret;
4260 }
This page took 0.241833 seconds and 4 git commands to generate.