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