]> Dogcows Code - chaz/openbox/blob - src/Window.cc
fixed menu placement on menus with very small windows
[chaz/openbox] / src / Window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Window.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef HAVE_CONFIG_H
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31
32 #ifdef HAVE_STRING_H
33 # include <string.h>
34 #endif // HAVE_STRING_H
35
36 #ifdef DEBUG
37 # ifdef HAVE_STDIO_H
38 # include <stdio.h>
39 # endif // HAVE_STDIO_H
40 #endif // DEBUG
41
42 #ifdef HAVE_STDLIB_H
43 # include <stdlib.h>
44 #endif // HAVE_STDLIB_H
45 }
46
47 #include "i18n.hh"
48 #include "blackbox.hh"
49 #include "Clientmenu.hh"
50 #include "Font.hh"
51 #include "GCCache.hh"
52 #include "Iconmenu.hh"
53 #include "Image.hh"
54 #include "Screen.hh"
55 #include "Toolbar.hh"
56 #include "Util.hh"
57 #include "Window.hh"
58 #include "Windowmenu.hh"
59 #include "Workspace.hh"
60 #include "Slit.hh"
61
62 using std::string;
63 using std::abs;
64
65 /*
66 * Initializes the class with default values/the window's set initial values.
67 */
68 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
69 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
70 // sizeof(BlackboxWindow));
71
72 #ifdef DEBUG
73 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
74 #endif // DEBUG
75
76 /*
77 set timer to zero... it is initialized properly later, so we check
78 if timer is zero in the destructor, and assume that the window is not
79 fully constructed if timer is zero...
80 */
81 timer = 0;
82 blackbox = b;
83 client.window = w;
84 screen = s;
85 xatom = blackbox->getXAtom();
86
87 if (! validateClient()) {
88 delete this;
89 return;
90 }
91
92 // fetch client size and placement
93 XWindowAttributes wattrib;
94 if (! XGetWindowAttributes(blackbox->getXDisplay(),
95 client.window, &wattrib) ||
96 ! wattrib.screen || wattrib.override_redirect) {
97 #ifdef DEBUG
98 fprintf(stderr,
99 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
100 #endif // DEBUG
101
102 delete this;
103 return;
104 }
105
106 // set the eventmask early in the game so that we make sure we get
107 // all the events we are interested in
108 XSetWindowAttributes attrib_set;
109 attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
110 StructureNotifyMask;
111 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
112 ButtonMotionMask;
113 XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
114 CWEventMask|CWDontPropagate, &attrib_set);
115
116 flags.moving = flags.resizing = flags.shaded = flags.visible =
117 flags.iconic = flags.focused = flags.stuck = flags.modal =
118 flags.send_focus_message = flags.shaped = flags.skip_taskbar =
119 flags.skip_pager = flags.fullscreen = False;
120 flags.maximized = 0;
121
122 blackbox_attrib.workspace = window_number = BSENTINEL;
123
124 blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l;
125 blackbox_attrib.decoration = DecorNormal;
126 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
127 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
128
129 frame.border_w = 1;
130 frame.window = frame.plate = frame.title = frame.handle = None;
131 frame.close_button = frame.iconify_button = frame.maximize_button =
132 frame.stick_button = None;
133 frame.right_grip = frame.left_grip = None;
134
135 frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
136 frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
137 frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
138 frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
139 frame.fgrip_pixel = 0;
140 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
141 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
142 frame.pbutton = frame.ugrip = frame.fgrip = None;
143
144 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
145 mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
146 Decor_Iconify | Decor_Maximize;
147
148 client.normal_hint_flags = 0;
149 client.window_group = None;
150 client.transient_for = 0;
151
152 current_state = NormalState;
153
154 windowmenu = 0;
155
156 /*
157 set the initial size and location of client window (relative to the
158 _root window_). This position is the reference point used with the
159 window's gravity to find the window's initial position.
160 */
161 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
162 client.old_bw = wattrib.border_width;
163
164 lastButtonPressTime = 0;
165
166 timer = new BTimer(blackbox, this);
167 timer->setTimeout(blackbox->getAutoRaiseDelay());
168
169 // get size, aspect, minimum/maximum size and other hints set by the
170 // client
171
172 if (! getBlackboxHints())
173 getNetWMHints();
174
175 getWMProtocols();
176 getWMHints();
177 getWMNormalHints();
178
179 frame.window = createToplevelWindow();
180
181 blackbox->saveWindowSearch(frame.window, this);
182
183 frame.plate = createChildWindow(frame.window, ExposureMask);
184 blackbox->saveWindowSearch(frame.plate, this);
185
186 // determine if this is a transient window
187 getTransientInfo();
188
189 // determine the window's type, so we can decide its decorations and
190 // functionality, or if we should not manage it at all
191 if (getWindowType()) {
192 // adjust the window decorations/behavior based on the window type
193 switch (window_type) {
194 case Type_Desktop:
195 case Type_Dock:
196 case Type_Menu:
197 blackbox_attrib.workspace = 0; // we do need to belong to a workspace
198 flags.stuck = True; // we show up on all workspaces
199 case Type_Splash:
200 // none of these windows are manipulated by the window manager
201 functions = 0;
202 break;
203
204 case Type_Toolbar:
205 case Type_Utility:
206 // these windows get less functionality
207 functions &= ~(Func_Maximize | Func_Resize | Func_Iconify);
208 break;
209
210 case Type_Dialog:
211 // dialogs cannot be maximized
212 functions &= ~Func_Maximize;
213 break;
214
215 case Type_Normal:
216 // normal windows retain all of the possible decorations and
217 // functionality
218 break;
219 }
220 } else {
221 getMWMHints();
222 }
223
224 // further adjeust the window's decorations/behavior based on window sizes
225 if ((client.normal_hint_flags & PMinSize) &&
226 (client.normal_hint_flags & PMaxSize) &&
227 client.max_width <= client.min_width &&
228 client.max_height <= client.min_height) {
229 functions &= ~(Func_Resize | Func_Maximize);
230 }
231
232 setAllowedActions();
233
234 setupDecor();
235
236 if (decorations & Decor_Titlebar)
237 createTitlebar();
238
239 if (decorations & Decor_Handle)
240 createHandle();
241
242 // apply the size and gravity hint to the frame
243
244 upsize();
245
246 bool place_window = True;
247 if (blackbox->isStartup() || isTransient() ||
248 client.normal_hint_flags & (PPosition|USPosition)) {
249 applyGravity(frame.rect);
250
251 if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
252 place_window = False;
253 }
254
255 // add the window's strut. note this is done *after* placing the window.
256 screen->addStrut(&client.strut);
257 updateStrut();
258
259 /*
260 the server needs to be grabbed here to prevent client's from sending
261 events while we are in the process of configuring their window.
262 We hold the grab until after we are done moving the window around.
263 */
264
265 XGrabServer(blackbox->getXDisplay());
266
267 associateClientWindow();
268
269 blackbox->saveWindowSearch(client.window, this);
270
271 if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
272 screen->getCurrentWorkspace()->addWindow(this, place_window);
273 else
274 screen->getWorkspace(blackbox_attrib.workspace)->
275 addWindow(this, place_window);
276
277 if (! place_window) {
278 // don't need to call configure if we are letting the workspace
279 // place the window
280 configure(frame.rect.x(), frame.rect.y(),
281 frame.rect.width(), frame.rect.height());
282
283 }
284
285 positionWindows();
286
287 XUngrabServer(blackbox->getXDisplay());
288
289 #ifdef SHAPE
290 if (blackbox->hasShapeExtensions() && flags.shaped)
291 configureShape();
292 #endif // SHAPE
293
294 // now that we know where to put the window and what it should look like
295 // we apply the decorations
296 decorate();
297
298 grabButtons();
299
300 XMapSubwindows(blackbox->getXDisplay(), frame.window);
301
302 // this ensures the title, buttons, and other decor are properly displayed
303 redrawWindowFrame();
304
305 // preserve the window's initial state on first map, and its current state
306 // across a restart
307 unsigned long initial_state = current_state;
308 if (! getState())
309 current_state = initial_state;
310
311 // get sticky state from our parent window if we've got one
312 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
313 client.transient_for->isStuck() != flags.stuck)
314 flags.stuck = True;
315
316 if (flags.shaded) {
317 flags.shaded = False;
318 initial_state = current_state;
319 shade();
320
321 /*
322 At this point in the life of a window, current_state should only be set
323 to IconicState if the window was an *icon*, not if it was shaded.
324 */
325 if (initial_state != IconicState)
326 current_state = NormalState;
327 }
328
329 if (flags.stuck) {
330 flags.stuck = False;
331 stick();
332 }
333
334 if (flags.maximized && (functions & Func_Maximize))
335 remaximize();
336
337 // create this last so it only needs to be configured once
338 windowmenu = new Windowmenu(this);
339 }
340
341
342 BlackboxWindow::~BlackboxWindow(void) {
343 #ifdef DEBUG
344 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
345 client.window);
346 #endif // DEBUG
347
348 if (! timer) // window not managed...
349 return;
350
351 if (flags.moving)
352 endMove();
353
354 screen->removeStrut(&client.strut);
355 screen->updateAvailableArea();
356
357 // We don't need to worry about resizing because resizing always grabs the X
358 // server. This should only ever happen if using opaque moving.
359 if (flags.moving)
360 endMove();
361
362 delete timer;
363
364 delete windowmenu;
365
366 if (client.window_group) {
367 BWindowGroup *group = blackbox->searchGroup(client.window_group);
368 if (group) group->removeWindow(this);
369 }
370
371 // remove ourselves from our transient_for
372 if (isTransient()) {
373 if (client.transient_for != (BlackboxWindow *) ~0ul)
374 client.transient_for->client.transientList.remove(this);
375 client.transient_for = (BlackboxWindow*) 0;
376 }
377
378 if (client.transientList.size() > 0) {
379 // reset transient_for for all transients
380 BlackboxWindowList::iterator it, end = client.transientList.end();
381 for (it = client.transientList.begin(); it != end; ++it)
382 (*it)->client.transient_for = (BlackboxWindow*) 0;
383 }
384
385 if (frame.title)
386 destroyTitlebar();
387
388 if (frame.handle)
389 destroyHandle();
390
391 if (frame.plate) {
392 blackbox->removeWindowSearch(frame.plate);
393 XDestroyWindow(blackbox->getXDisplay(), frame.plate);
394 }
395
396 if (frame.window) {
397 blackbox->removeWindowSearch(frame.window);
398 XDestroyWindow(blackbox->getXDisplay(), frame.window);
399 }
400
401 blackbox->removeWindowSearch(client.window);
402 }
403
404
405 void BlackboxWindow::enableDecor(bool enable) {
406 blackbox_attrib.flags |= AttribDecoration;
407 blackbox_attrib.decoration = enable ? DecorNormal : DecorNone;
408 setupDecor();
409
410 // we can not be shaded if we lack a titlebar
411 if (! (decorations & Decor_Titlebar) && flags.shaded)
412 shade();
413
414 if (flags.visible && frame.window) {
415 XMapSubwindows(blackbox->getXDisplay(), frame.window);
416 XMapWindow(blackbox->getXDisplay(), frame.window);
417 }
418
419 reconfigure();
420 setState(current_state);
421 }
422
423
424 void BlackboxWindow::setupDecor() {
425 if (blackbox_attrib.decoration != DecorNone) {
426 // start with everything on
427 decorations = Decor_Close |
428 (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) |
429 (mwm_decorations & Decor_Border ? Decor_Border : 0) |
430 (mwm_decorations & Decor_Handle ? Decor_Handle : 0) |
431 (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) |
432 (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0);
433
434 if (! (functions & Func_Close)) decorations &= ~Decor_Close;
435 if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize;
436 if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify;
437 if (! (functions & Func_Resize)) decorations &= ~Decor_Handle;
438
439 switch (window_type) {
440 case Type_Desktop:
441 case Type_Dock:
442 case Type_Menu:
443 case Type_Splash:
444 // none of these windows are decorated by the window manager at all
445 decorations = 0;
446 break;
447
448 case Type_Toolbar:
449 case Type_Utility:
450 decorations &= ~(Decor_Border);
451 break;
452
453 case Type_Dialog:
454 decorations &= ~Decor_Handle;
455 break;
456
457 case Type_Normal:
458 break;
459 }
460 } else {
461 decorations = 0;
462 }
463 }
464
465 /*
466 * Creates a new top level window, with a given location, size, and border
467 * width.
468 * Returns: the newly created window
469 */
470 Window BlackboxWindow::createToplevelWindow(void) {
471 XSetWindowAttributes attrib_create;
472 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
473 CWOverrideRedirect | CWEventMask;
474
475 attrib_create.background_pixmap = None;
476 attrib_create.colormap = screen->getColormap();
477 attrib_create.override_redirect = True;
478 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask |
479 ButtonPress;
480 /*
481 We catch button presses because other wise they get passed down to the
482 root window, which will then cause root menus to show when you click the
483 window's frame.
484 */
485
486 return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
487 0, 0, 1, 1, frame.border_w, screen->getDepth(),
488 InputOutput, screen->getVisual(), create_mask,
489 &attrib_create);
490 }
491
492
493 /*
494 * Creates a child window, and optionally associates a given cursor with
495 * the new window.
496 */
497 Window BlackboxWindow::createChildWindow(Window parent,
498 unsigned long event_mask,
499 Cursor cursor) {
500 XSetWindowAttributes attrib_create;
501 unsigned long create_mask = CWBackPixmap | CWBorderPixel |
502 CWEventMask;
503
504 attrib_create.background_pixmap = None;
505 attrib_create.event_mask = event_mask;
506
507 if (cursor) {
508 create_mask |= CWCursor;
509 attrib_create.cursor = cursor;
510 }
511
512 return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
513 screen->getDepth(), InputOutput, screen->getVisual(),
514 create_mask, &attrib_create);
515 }
516
517
518 void BlackboxWindow::associateClientWindow(void) {
519 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
520 getWMName();
521 getWMIconName();
522
523 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
524
525 XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
526
527 /*
528 note we used to grab around this call to XReparentWindow however the
529 server is now grabbed before this method is called
530 */
531 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
532 StructureNotifyMask;
533 XSelectInput(blackbox->getXDisplay(), client.window,
534 event_mask & ~StructureNotifyMask);
535 XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
536 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
537
538 XRaiseWindow(blackbox->getXDisplay(), frame.plate);
539 XMapSubwindows(blackbox->getXDisplay(), frame.plate);
540
541 #ifdef SHAPE
542 if (blackbox->hasShapeExtensions()) {
543 XShapeSelectInput(blackbox->getXDisplay(), client.window,
544 ShapeNotifyMask);
545
546 Bool shaped = False;
547 int foo;
548 unsigned int ufoo;
549
550 XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
551 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
552 &ufoo, &ufoo);
553 flags.shaped = shaped;
554 }
555 #endif // SHAPE
556 }
557
558
559 void BlackboxWindow::decorate(void) {
560 BTexture* texture;
561
562 texture = &(screen->getWindowStyle()->b_focus);
563 frame.fbutton = texture->render(frame.button_w, frame.button_w,
564 frame.fbutton);
565 if (! frame.fbutton)
566 frame.fbutton_pixel = texture->color().pixel();
567
568 texture = &(screen->getWindowStyle()->b_unfocus);
569 frame.ubutton = texture->render(frame.button_w, frame.button_w,
570 frame.ubutton);
571 if (! frame.ubutton)
572 frame.ubutton_pixel = texture->color().pixel();
573
574 texture = &(screen->getWindowStyle()->b_pressed);
575 frame.pbutton = texture->render(frame.button_w, frame.button_w,
576 frame.pbutton);
577 if (! frame.pbutton)
578 frame.pbutton_pixel = texture->color().pixel();
579
580 if (decorations & Decor_Titlebar) {
581 texture = &(screen->getWindowStyle()->t_focus);
582 frame.ftitle = texture->render(frame.inside_w, frame.title_h,
583 frame.ftitle);
584 if (! frame.ftitle)
585 frame.ftitle_pixel = texture->color().pixel();
586
587 texture = &(screen->getWindowStyle()->t_unfocus);
588 frame.utitle = texture->render(frame.inside_w, frame.title_h,
589 frame.utitle);
590 if (! frame.utitle)
591 frame.utitle_pixel = texture->color().pixel();
592
593 XSetWindowBorder(blackbox->getXDisplay(), frame.title,
594 screen->getBorderColor()->pixel());
595
596 decorateLabel();
597 }
598
599 if (decorations & Decor_Border) {
600 frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel();
601 frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel();
602 }
603
604 if (decorations & Decor_Handle) {
605 texture = &(screen->getWindowStyle()->h_focus);
606 frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
607 frame.fhandle);
608 if (! frame.fhandle)
609 frame.fhandle_pixel = texture->color().pixel();
610
611 texture = &(screen->getWindowStyle()->h_unfocus);
612 frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
613 frame.uhandle);
614 if (! frame.uhandle)
615 frame.uhandle_pixel = texture->color().pixel();
616
617 texture = &(screen->getWindowStyle()->g_focus);
618 frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
619 if (! frame.fgrip)
620 frame.fgrip_pixel = texture->color().pixel();
621
622 texture = &(screen->getWindowStyle()->g_unfocus);
623 frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
624 if (! frame.ugrip)
625 frame.ugrip_pixel = texture->color().pixel();
626
627 XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
628 screen->getBorderColor()->pixel());
629 XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
630 screen->getBorderColor()->pixel());
631 XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
632 screen->getBorderColor()->pixel());
633 }
634
635 XSetWindowBorder(blackbox->getXDisplay(), frame.window,
636 screen->getBorderColor()->pixel());
637 }
638
639
640 void BlackboxWindow::decorateLabel(void) {
641 BTexture *texture;
642
643 texture = &(screen->getWindowStyle()->l_focus);
644 frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
645 if (! frame.flabel)
646 frame.flabel_pixel = texture->color().pixel();
647
648 texture = &(screen->getWindowStyle()->l_unfocus);
649 frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
650 if (! frame.ulabel)
651 frame.ulabel_pixel = texture->color().pixel();
652 }
653
654
655 void BlackboxWindow::createHandle(void) {
656 frame.handle = createChildWindow(frame.window,
657 ButtonPressMask | ButtonReleaseMask |
658 ButtonMotionMask | ExposureMask);
659 blackbox->saveWindowSearch(frame.handle, this);
660
661 frame.left_grip =
662 createChildWindow(frame.handle,
663 ButtonPressMask | ButtonReleaseMask |
664 ButtonMotionMask | ExposureMask,
665 blackbox->getLowerLeftAngleCursor());
666 blackbox->saveWindowSearch(frame.left_grip, this);
667
668 frame.right_grip =
669 createChildWindow(frame.handle,
670 ButtonPressMask | ButtonReleaseMask |
671 ButtonMotionMask | ExposureMask,
672 blackbox->getLowerRightAngleCursor());
673 blackbox->saveWindowSearch(frame.right_grip, this);
674 }
675
676
677 void BlackboxWindow::destroyHandle(void) {
678 if (frame.fhandle)
679 screen->getImageControl()->removeImage(frame.fhandle);
680
681 if (frame.uhandle)
682 screen->getImageControl()->removeImage(frame.uhandle);
683
684 if (frame.fgrip)
685 screen->getImageControl()->removeImage(frame.fgrip);
686
687 if (frame.ugrip)
688 screen->getImageControl()->removeImage(frame.ugrip);
689
690 blackbox->removeWindowSearch(frame.left_grip);
691 blackbox->removeWindowSearch(frame.right_grip);
692
693 XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
694 XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
695 frame.left_grip = frame.right_grip = None;
696
697 blackbox->removeWindowSearch(frame.handle);
698 XDestroyWindow(blackbox->getXDisplay(), frame.handle);
699 frame.handle = None;
700 }
701
702
703 void BlackboxWindow::createTitlebar(void) {
704 frame.title = createChildWindow(frame.window,
705 ButtonPressMask | ButtonReleaseMask |
706 ButtonMotionMask | ExposureMask);
707 frame.label = createChildWindow(frame.title,
708 ButtonPressMask | ButtonReleaseMask |
709 ButtonMotionMask | ExposureMask);
710 blackbox->saveWindowSearch(frame.title, this);
711 blackbox->saveWindowSearch(frame.label, this);
712
713 if (decorations & Decor_Iconify) createIconifyButton();
714 if (decorations & Decor_Maximize) createMaximizeButton();
715 if (decorations & Decor_Close) createCloseButton();
716 }
717
718
719 void BlackboxWindow::destroyTitlebar(void) {
720 if (frame.close_button)
721 destroyCloseButton();
722
723 if (frame.iconify_button)
724 destroyIconifyButton();
725
726 if (frame.maximize_button)
727 destroyMaximizeButton();
728
729 if (frame.stick_button)
730 destroyStickyButton();
731
732 if (frame.ftitle)
733 screen->getImageControl()->removeImage(frame.ftitle);
734
735 if (frame.utitle)
736 screen->getImageControl()->removeImage(frame.utitle);
737
738 if (frame.flabel)
739 screen->getImageControl()->removeImage(frame.flabel);
740
741 if( frame.ulabel)
742 screen->getImageControl()->removeImage(frame.ulabel);
743
744 if (frame.fbutton)
745 screen->getImageControl()->removeImage(frame.fbutton);
746
747 if (frame.ubutton)
748 screen->getImageControl()->removeImage(frame.ubutton);
749
750 if (frame.pbutton)
751 screen->getImageControl()->removeImage(frame.pbutton);
752
753 blackbox->removeWindowSearch(frame.title);
754 blackbox->removeWindowSearch(frame.label);
755
756 XDestroyWindow(blackbox->getXDisplay(), frame.label);
757 XDestroyWindow(blackbox->getXDisplay(), frame.title);
758 frame.title = frame.label = None;
759 }
760
761
762 void BlackboxWindow::createCloseButton(void) {
763 if (frame.title != None) {
764 frame.close_button = createChildWindow(frame.title,
765 ButtonPressMask |
766 ButtonReleaseMask |
767 ButtonMotionMask | ExposureMask);
768 blackbox->saveWindowSearch(frame.close_button, this);
769 }
770 }
771
772
773 void BlackboxWindow::destroyCloseButton(void) {
774 blackbox->removeWindowSearch(frame.close_button);
775 XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
776 frame.close_button = None;
777 }
778
779
780 void BlackboxWindow::createIconifyButton(void) {
781 if (frame.title != None) {
782 frame.iconify_button = createChildWindow(frame.title,
783 ButtonPressMask |
784 ButtonReleaseMask |
785 ButtonMotionMask | ExposureMask);
786 blackbox->saveWindowSearch(frame.iconify_button, this);
787 }
788 }
789
790
791 void BlackboxWindow::destroyIconifyButton(void) {
792 blackbox->removeWindowSearch(frame.iconify_button);
793 XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
794 frame.iconify_button = None;
795 }
796
797
798 void BlackboxWindow::createMaximizeButton(void) {
799 if (frame.title != None) {
800 frame.maximize_button = createChildWindow(frame.title,
801 ButtonPressMask |
802 ButtonReleaseMask |
803 ButtonMotionMask | ExposureMask);
804 blackbox->saveWindowSearch(frame.maximize_button, this);
805 }
806 }
807
808
809 void BlackboxWindow::destroyMaximizeButton(void) {
810 blackbox->removeWindowSearch(frame.maximize_button);
811 XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
812 frame.maximize_button = None;
813 }
814
815 void BlackboxWindow::createStickyButton(void) {
816 if (frame.title != None) {
817 frame.stick_button = createChildWindow(frame.title,
818 ButtonPressMask |
819 ButtonReleaseMask |
820 ButtonMotionMask | ExposureMask);
821 blackbox->saveWindowSearch(frame.stick_button, this);
822 }
823 }
824
825 void BlackboxWindow::destroyStickyButton(void) {
826 blackbox->removeWindowSearch(frame.stick_button);
827 XDestroyWindow(blackbox->getXDisplay(), frame.stick_button);
828 frame.stick_button = None;
829 }
830
831 void BlackboxWindow::positionButtons(bool redecorate_label) {
832 string layout = blackbox->getTitlebarLayout();
833 string parsed;
834
835 bool hasclose, hasiconify, hasmaximize, haslabel, hasstick;
836 hasclose = hasiconify = hasmaximize = haslabel = hasstick = false;
837
838 string::const_iterator it, end;
839 for (it = layout.begin(), end = layout.end(); it != end; ++it) {
840 switch(*it) {
841 case 'C':
842 if (! hasclose && (decorations & Decor_Close)) {
843 hasclose = true;
844 parsed += *it;
845 }
846 break;
847 case 'I':
848 if (! hasiconify && (decorations & Decor_Iconify)) {
849 hasiconify = true;
850 parsed += *it;
851 }
852 break;
853 case 'S':
854 if (!hasstick) {
855 hasstick = true;
856 parsed += *it;
857 }
858 break;
859 case 'M':
860 if (! hasmaximize && (decorations & Decor_Maximize)) {
861 hasmaximize = true;
862 parsed += *it;
863 }
864 break;
865 case 'L':
866 if (! haslabel) {
867 haslabel = true;
868 parsed += *it;
869 }
870 break;
871 }
872 }
873
874 if (! hasclose && frame.close_button)
875 destroyCloseButton();
876 if (! hasiconify && frame.iconify_button)
877 destroyIconifyButton();
878 if (! hasmaximize && frame.maximize_button)
879 destroyMaximizeButton();
880 if (! hasstick && frame.stick_button)
881 destroyStickyButton();
882 if (! haslabel)
883 parsed += 'L'; // require that the label be in the layout
884
885 const unsigned int bsep = frame.bevel_w + 1; // separation between elements
886 const unsigned int by = frame.bevel_w + 1;
887 const unsigned int ty = frame.bevel_w;
888
889 frame.label_w = frame.inside_w - bsep * 2 -
890 (frame.button_w + bsep) * (parsed.size() - 1);
891
892 unsigned int x = bsep;
893 for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
894 switch(*it) {
895 case 'C':
896 if (! frame.close_button) createCloseButton();
897 XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
898 frame.button_w, frame.button_w);
899 x += frame.button_w + bsep;
900 break;
901 case 'I':
902 if (! frame.iconify_button) createIconifyButton();
903 XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
904 frame.button_w, frame.button_w);
905 x += frame.button_w + bsep;
906 break;
907 case 'S':
908 if (! frame.stick_button) createStickyButton();
909 XMoveResizeWindow(blackbox->getXDisplay(), frame.stick_button, x, by,
910 frame.button_w, frame.button_w);
911 x += frame.button_w + bsep;
912 break;
913 case 'M':
914 if (! frame.maximize_button) createMaximizeButton();
915 XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
916 frame.button_w, frame.button_w);
917 x += frame.button_w + bsep;
918 break;
919 case 'L':
920 XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
921 frame.label_w, frame.label_h);
922 x += frame.label_w + bsep;
923 break;
924 }
925 }
926
927 if (redecorate_label) decorateLabel();
928 redrawLabel();
929 redrawAllButtons();
930 }
931
932
933 void BlackboxWindow::reconfigure(void) {
934 restoreGravity(client.rect);
935 upsize();
936 applyGravity(frame.rect);
937 positionWindows();
938 decorate();
939 redrawWindowFrame();
940
941 ungrabButtons();
942 grabButtons();
943
944 if (windowmenu) {
945 windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
946 windowmenu->reconfigure();
947 }
948 }
949
950
951 void BlackboxWindow::grabButtons(void) {
952 mod_mask = blackbox->getMouseModMask();
953
954 if (! screen->isSloppyFocus() || screen->doClickRaise())
955 // grab button 1 for changing focus/raising
956 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
957 GrabModeSync, GrabModeSync, frame.plate, None,
958 screen->allowScrollLock());
959
960 if (functions & Func_Move)
961 blackbox->grabButton(Button1, mod_mask, frame.window, True,
962 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
963 GrabModeAsync, frame.window, None,
964 screen->allowScrollLock());
965 if (functions & Func_Resize)
966 blackbox->grabButton(Button3, mod_mask, frame.window, True,
967 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
968 GrabModeAsync, frame.window, None,
969 screen->allowScrollLock());
970 // alt+middle lowers the window
971 blackbox->grabButton(Button2, mod_mask, frame.window, True,
972 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
973 frame.window, None, screen->allowScrollLock());
974 }
975
976
977 void BlackboxWindow::ungrabButtons(void) {
978 blackbox->ungrabButton(Button1, 0, frame.plate);
979 blackbox->ungrabButton(Button1, mod_mask, frame.window);
980 blackbox->ungrabButton(Button2, mod_mask, frame.window);
981 blackbox->ungrabButton(Button3, mod_mask, frame.window);
982 }
983
984
985 void BlackboxWindow::positionWindows(void) {
986 XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
987 frame.rect.x(), frame.rect.y(), frame.inside_w,
988 (flags.shaded) ? frame.title_h : frame.inside_h);
989 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
990 frame.border_w);
991 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
992 frame.mwm_border_w);
993 XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
994 frame.margin.left - frame.mwm_border_w - frame.border_w,
995 frame.margin.top - frame.mwm_border_w - frame.border_w,
996 client.rect.width(), client.rect.height());
997 XMoveResizeWindow(blackbox->getXDisplay(), client.window,
998 0, 0, client.rect.width(), client.rect.height());
999 // ensure client.rect contains the real location
1000 client.rect.setPos(frame.rect.left() + frame.margin.left,
1001 frame.rect.top() + frame.margin.top);
1002
1003 if (decorations & Decor_Titlebar) {
1004 if (frame.title == None) createTitlebar();
1005
1006 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
1007 frame.border_w);
1008 XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
1009 -frame.border_w, frame.inside_w, frame.title_h);
1010
1011 positionButtons();
1012 XMapSubwindows(blackbox->getXDisplay(), frame.title);
1013 XMapWindow(blackbox->getXDisplay(), frame.title);
1014 } else if (frame.title) {
1015 destroyTitlebar();
1016 }
1017 if (decorations & Decor_Handle) {
1018 if (frame.handle == None) createHandle();
1019 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
1020 frame.border_w);
1021 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
1022 frame.border_w);
1023 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
1024 frame.border_w);
1025
1026 // use client.rect here so the value is correct even if shaded
1027 XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
1028 -frame.border_w,
1029 client.rect.height() + frame.margin.top +
1030 frame.mwm_border_w - frame.border_w,
1031 frame.inside_w, frame.handle_h);
1032 XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
1033 -frame.border_w, -frame.border_w,
1034 frame.grip_w, frame.handle_h);
1035 XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
1036 frame.inside_w - frame.grip_w - frame.border_w,
1037 -frame.border_w, frame.grip_w, frame.handle_h);
1038
1039 XMapSubwindows(blackbox->getXDisplay(), frame.handle);
1040 XMapWindow(blackbox->getXDisplay(), frame.handle);
1041 } else if (frame.handle) {
1042 destroyHandle();
1043 }
1044 XSync(blackbox->getXDisplay(), False);
1045 }
1046
1047
1048 void BlackboxWindow::updateStrut(void) {
1049 unsigned long num = 4;
1050 unsigned long *data;
1051 if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
1052 num, &data))
1053 return;
1054
1055 if (num == 4) {
1056 client.strut.left = data[0];
1057 client.strut.right = data[1];
1058 client.strut.top = data[2];
1059 client.strut.bottom = data[3];
1060
1061 screen->updateAvailableArea();
1062 }
1063
1064 delete [] data;
1065 }
1066
1067
1068 bool BlackboxWindow::getWindowType(void) {
1069 window_type = (WindowType) -1;
1070
1071 unsigned long *val;
1072 unsigned long num = (unsigned) -1;
1073 if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
1074 num, &val)) {
1075 for (unsigned long i = 0; i < num; ++i) {
1076 if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_desktop))
1077 window_type = Type_Desktop;
1078 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dock))
1079 window_type = Type_Dock;
1080 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
1081 window_type = Type_Toolbar;
1082 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_menu))
1083 window_type = Type_Menu;
1084 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_utility))
1085 window_type = Type_Utility;
1086 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_splash))
1087 window_type = Type_Splash;
1088 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dialog))
1089 window_type = Type_Dialog;
1090 else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_normal))
1091 window_type = Type_Normal;
1092 else if (val[i] ==
1093 xatom->getAtom(XAtom::kde_net_wm_window_type_override))
1094 mwm_decorations = 0; // prevent this window from getting any decor
1095 }
1096 delete val;
1097 }
1098
1099 if (window_type == (WindowType) -1) {
1100 /*
1101 * the window type hint was not set, which means we either classify ourself
1102 * as a normal window or a dialog, depending on if we are a transient.
1103 */
1104 if (isTransient())
1105 window_type = Type_Dialog;
1106 else
1107 window_type = Type_Normal;
1108
1109 return False;
1110 }
1111
1112 return True;
1113 }
1114
1115
1116 void BlackboxWindow::getWMName(void) {
1117 if (xatom->getValue(client.window, XAtom::net_wm_name,
1118 XAtom::utf8, client.title) &&
1119 !client.title.empty()) {
1120 xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1121 return;
1122 }
1123 //fall through to using WM_NAME
1124 if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
1125 && !client.title.empty()) {
1126 xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1127 return;
1128 }
1129 // fall back to an internal default
1130 client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
1131 xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
1132 client.title);
1133
1134 #ifdef DEBUG_WITH_ID
1135 // the 16 is the 8 chars of the debug text plus the number
1136 char *tmp = new char[client.title.length() + 16];
1137 sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window);
1138 client.title = tmp;
1139 delete tmp;
1140 #endif
1141 }
1142
1143
1144 void BlackboxWindow::getWMIconName(void) {
1145 if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1146 XAtom::utf8, client.icon_title) &&
1147 !client.icon_title.empty()) {
1148 xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1149 return;
1150 }
1151 //fall through to using WM_ICON_NAME
1152 if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1153 client.icon_title) &&
1154 !client.icon_title.empty()) {
1155 xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1156 return;
1157 }
1158 // fall back to using the main name
1159 client.icon_title = client.title;
1160 xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1161 client.icon_title);
1162 }
1163
1164
1165 /*
1166 * Retrieve which WM Protocols are supported by the client window.
1167 * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1168 * window's decorations and allow the close behavior.
1169 * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1170 * this.
1171 */
1172 void BlackboxWindow::getWMProtocols(void) {
1173 Atom *proto;
1174 int num_return = 0;
1175
1176 if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1177 &proto, &num_return)) {
1178 for (int i = 0; i < num_return; ++i) {
1179 if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1180 decorations |= Decor_Close;
1181 functions |= Func_Close;
1182 } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1183 flags.send_focus_message = True;
1184 else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1185 screen->addNetizen(new Netizen(screen, client.window));
1186 }
1187
1188 XFree(proto);
1189 }
1190 }
1191
1192
1193 /*
1194 * Gets the value of the WM_HINTS property.
1195 * If the property is not set, then use a set of default values.
1196 */
1197 void BlackboxWindow::getWMHints(void) {
1198 focus_mode = F_Passive;
1199
1200 // remove from current window group
1201 if (client.window_group) {
1202 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1203 if (group) group->removeWindow(this);
1204 }
1205 client.window_group = None;
1206
1207 XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1208 if (! wmhint) {
1209 return;
1210 }
1211
1212 if (wmhint->flags & InputHint) {
1213 if (wmhint->input == True) {
1214 if (flags.send_focus_message)
1215 focus_mode = F_LocallyActive;
1216 } else {
1217 if (flags.send_focus_message)
1218 focus_mode = F_GloballyActive;
1219 else
1220 focus_mode = F_NoInput;
1221 }
1222 }
1223
1224 if (wmhint->flags & StateHint)
1225 current_state = wmhint->initial_state;
1226
1227 if (wmhint->flags & WindowGroupHint) {
1228 client.window_group = wmhint->window_group;
1229
1230 // add window to the appropriate group
1231 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1232 if (! group) { // no group found, create it!
1233 new BWindowGroup(blackbox, client.window_group);
1234 group = blackbox->searchGroup(client.window_group);
1235 }
1236 if (group)
1237 group->addWindow(this);
1238 }
1239
1240 XFree(wmhint);
1241 }
1242
1243
1244 /*
1245 * Gets the value of the WM_NORMAL_HINTS property.
1246 * If the property is not set, then use a set of default values.
1247 */
1248 void BlackboxWindow::getWMNormalHints(void) {
1249 long icccm_mask;
1250 XSizeHints sizehint;
1251
1252 client.min_width = client.min_height =
1253 client.width_inc = client.height_inc = 1;
1254 client.base_width = client.base_height = 0;
1255 client.win_gravity = NorthWestGravity;
1256 #if 0
1257 client.min_aspect_x = client.min_aspect_y =
1258 client.max_aspect_x = client.max_aspect_y = 1;
1259 #endif
1260
1261 // don't limit the size of a window, the default max width is the biggest
1262 // possible
1263 client.max_width = (unsigned) -1;
1264 client.max_height = (unsigned) -1;
1265
1266
1267 if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1268 &sizehint, &icccm_mask))
1269 return;
1270
1271 client.normal_hint_flags = sizehint.flags;
1272
1273 if (sizehint.flags & PMinSize) {
1274 if (sizehint.min_width >= 0)
1275 client.min_width = sizehint.min_width;
1276 if (sizehint.min_height >= 0)
1277 client.min_height = sizehint.min_height;
1278 }
1279
1280 if (sizehint.flags & PMaxSize) {
1281 if (sizehint.max_width > static_cast<signed>(client.min_width))
1282 client.max_width = sizehint.max_width;
1283 else
1284 client.max_width = client.min_width;
1285
1286 if (sizehint.max_height > static_cast<signed>(client.min_height))
1287 client.max_height = sizehint.max_height;
1288 else
1289 client.max_height = client.min_height;
1290 }
1291
1292 if (sizehint.flags & PResizeInc) {
1293 client.width_inc = sizehint.width_inc;
1294 client.height_inc = sizehint.height_inc;
1295 }
1296
1297 #if 0 // we do not support this at the moment
1298 if (sizehint.flags & PAspect) {
1299 client.min_aspect_x = sizehint.min_aspect.x;
1300 client.min_aspect_y = sizehint.min_aspect.y;
1301 client.max_aspect_x = sizehint.max_aspect.x;
1302 client.max_aspect_y = sizehint.max_aspect.y;
1303 }
1304 #endif
1305
1306 if (sizehint.flags & PBaseSize) {
1307 client.base_width = sizehint.base_width;
1308 client.base_height = sizehint.base_height;
1309 }
1310
1311 if (sizehint.flags & PWinGravity)
1312 client.win_gravity = sizehint.win_gravity;
1313 }
1314
1315
1316 /*
1317 * Gets the NETWM hints for the class' contained window.
1318 */
1319 void BlackboxWindow::getNetWMHints(void) {
1320 unsigned long workspace;
1321
1322 if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1323 workspace)) {
1324 if (workspace == 0xffffffff)
1325 flags.stuck = True;
1326 else
1327 blackbox_attrib.workspace = workspace;
1328 }
1329
1330 unsigned long *state;
1331 unsigned long num = (unsigned) -1;
1332 if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1333 num, &state)) {
1334 bool vert = False,
1335 horz = False;
1336 for (unsigned long i = 0; i < num; ++i) {
1337 if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1338 flags.modal = True;
1339 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1340 flags.shaded = True;
1341 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1342 flags.skip_taskbar = True;
1343 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1344 flags.skip_pager = True;
1345 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1346 flags.fullscreen = True;
1347 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1348 setState(IconicState);
1349 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1350 vert = True;
1351 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1352 horz = True;
1353 }
1354 if (vert && horz)
1355 flags.maximized = 1;
1356 else if (vert)
1357 flags.maximized = 2;
1358 else if (horz)
1359 flags.maximized = 3;
1360
1361 delete [] state;
1362 }
1363 }
1364
1365
1366 /*
1367 * Gets the MWM hints for the class' contained window.
1368 * This is used while initializing the window to its first state, and not
1369 * thereafter.
1370 * Returns: true if the MWM hints are successfully retreived and applied;
1371 * false if they are not.
1372 */
1373 void BlackboxWindow::getMWMHints(void) {
1374 unsigned long num;
1375 MwmHints *mwm_hint;
1376
1377 num = PropMwmHintsElements;
1378 if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1379 XAtom::motif_wm_hints, num,
1380 (unsigned long **)&mwm_hint))
1381 return;
1382 if (num < PropMwmHintsElements) {
1383 delete [] mwm_hint;
1384 return;
1385 }
1386
1387 if (mwm_hint->flags & MwmHintsDecorations) {
1388 if (mwm_hint->decorations & MwmDecorAll) {
1389 mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1390 Decor_Iconify | Decor_Maximize;
1391 } else {
1392 mwm_decorations = 0;
1393
1394 if (mwm_hint->decorations & MwmDecorBorder)
1395 mwm_decorations |= Decor_Border;
1396 if (mwm_hint->decorations & MwmDecorHandle)
1397 mwm_decorations |= Decor_Handle;
1398 if (mwm_hint->decorations & MwmDecorTitle)
1399 mwm_decorations |= Decor_Titlebar;
1400 if (mwm_hint->decorations & MwmDecorIconify)
1401 mwm_decorations |= Decor_Iconify;
1402 if (mwm_hint->decorations & MwmDecorMaximize)
1403 mwm_decorations |= Decor_Maximize;
1404 }
1405 }
1406
1407 if (mwm_hint->flags & MwmHintsFunctions) {
1408 if (mwm_hint->functions & MwmFuncAll) {
1409 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1410 Func_Close;
1411 } else {
1412 functions = 0;
1413
1414 if (mwm_hint->functions & MwmFuncResize)
1415 functions |= Func_Resize;
1416 if (mwm_hint->functions & MwmFuncMove)
1417 functions |= Func_Move;
1418 if (mwm_hint->functions & MwmFuncIconify)
1419 functions |= Func_Iconify;
1420 if (mwm_hint->functions & MwmFuncMaximize)
1421 functions |= Func_Maximize;
1422 if (mwm_hint->functions & MwmFuncClose)
1423 functions |= Func_Close;
1424 }
1425 }
1426 delete [] mwm_hint;
1427 }
1428
1429
1430 /*
1431 * Gets the blackbox hints from the class' contained window.
1432 * This is used while initializing the window to its first state, and not
1433 * thereafter.
1434 * Returns: true if the hints are successfully retreived and applied; false if
1435 * they are not.
1436 */
1437 bool BlackboxWindow::getBlackboxHints(void) {
1438 unsigned long num;
1439 BlackboxHints *blackbox_hint;
1440
1441 num = PropBlackboxHintsElements;
1442 if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1443 XAtom::blackbox_hints, num,
1444 (unsigned long **)&blackbox_hint))
1445 return False;
1446 if (num < PropBlackboxHintsElements) {
1447 delete [] blackbox_hint;
1448 return False;
1449 }
1450
1451 if (blackbox_hint->flags & AttribShaded)
1452 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1453
1454 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1455 (blackbox_hint->flags & AttribMaxVert))
1456 flags.maximized = (blackbox_hint->attrib &
1457 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1458 else if (blackbox_hint->flags & AttribMaxVert)
1459 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1460 else if (blackbox_hint->flags & AttribMaxHoriz)
1461 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1462
1463 if (blackbox_hint->flags & AttribOmnipresent)
1464 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1465
1466 if (blackbox_hint->flags & AttribWorkspace)
1467 blackbox_attrib.workspace = blackbox_hint->workspace;
1468
1469 // if (blackbox_hint->flags & AttribStack)
1470 // don't yet have always on top/bottom for blackbox yet... working
1471 // on that
1472
1473 if (blackbox_hint->flags & AttribDecoration) {
1474 switch (blackbox_hint->decoration) {
1475 case DecorNone:
1476 blackbox_attrib.decoration = DecorNone;
1477 break;
1478
1479 case DecorTiny:
1480 case DecorTool:
1481 case DecorNormal:
1482 default:
1483 // blackbox_attrib.decoration defaults to DecorNormal
1484 break;
1485 }
1486 }
1487
1488 delete [] blackbox_hint;
1489
1490 return True;
1491 }
1492
1493
1494 void BlackboxWindow::getTransientInfo(void) {
1495 if (client.transient_for &&
1496 client.transient_for != (BlackboxWindow *) ~0ul) {
1497 // reset transient_for in preparation of looking for a new owner
1498 client.transient_for->client.transientList.remove(this);
1499 }
1500
1501 // we have no transient_for until we find a new one
1502 client.transient_for = (BlackboxWindow *) 0;
1503
1504 Window trans_for;
1505 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1506 &trans_for)) {
1507 // transient_for hint not set
1508 return;
1509 }
1510
1511 if (trans_for == client.window) {
1512 // wierd client... treat this window as a normal window
1513 return;
1514 }
1515
1516 if (trans_for == None || trans_for == screen->getRootWindow()) {
1517 // this is an undocumented interpretation of the ICCCM. a transient
1518 // associated with None/Root/itself is assumed to be a modal root
1519 // transient. we don't support the concept of a global transient,
1520 // so we just associate this transient with nothing, and perhaps
1521 // we will add support later for global modality.
1522 client.transient_for = (BlackboxWindow *) ~0ul;
1523 flags.modal = True;
1524 return;
1525 }
1526
1527 client.transient_for = blackbox->searchWindow(trans_for);
1528 if (! client.transient_for &&
1529 client.window_group && trans_for == client.window_group) {
1530 // no direct transient_for, perhaps this is a group transient?
1531 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1532 if (group) client.transient_for = group->find(screen);
1533 }
1534
1535 if (! client.transient_for || client.transient_for == this) {
1536 // no transient_for found, or we have a wierd client that wants to be
1537 // a transient for itself, so we treat this window as a normal window
1538 client.transient_for = (BlackboxWindow*) 0;
1539 return;
1540 }
1541
1542 // Check for a circular transient state: this can lock up Blackbox
1543 // when it tries to find the non-transient window for a transient.
1544 BlackboxWindow *w = this;
1545 while(w->client.transient_for &&
1546 w->client.transient_for != (BlackboxWindow *) ~0ul) {
1547 if(w->client.transient_for == this) {
1548 client.transient_for = (BlackboxWindow*) 0;
1549 break;
1550 }
1551 w = w->client.transient_for;
1552 }
1553
1554 if (client.transient_for &&
1555 client.transient_for != (BlackboxWindow *) ~0ul) {
1556 // register ourselves with our new transient_for
1557 client.transient_for->client.transientList.push_back(this);
1558 flags.stuck = client.transient_for->flags.stuck;
1559 }
1560 }
1561
1562
1563 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1564 if (client.transient_for &&
1565 client.transient_for != (BlackboxWindow*) ~0ul)
1566 return client.transient_for;
1567 return 0;
1568 }
1569
1570
1571 /*
1572 * This function is responsible for updating both the client and the frame
1573 * rectangles.
1574 * According to the ICCCM a client message is not sent for a resize, only a
1575 * move.
1576 */
1577 void BlackboxWindow::configure(int dx, int dy,
1578 unsigned int dw, unsigned int dh) {
1579 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1580 ! flags.moving);
1581
1582 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1583 frame.rect.setRect(dx, dy, dw, dh);
1584 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1585 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1586
1587 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1588 frame.rect.setPos(0, 0);
1589
1590 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1591 frame.rect.top() + frame.margin.top,
1592 frame.rect.right() - frame.margin.right,
1593 frame.rect.bottom() - frame.margin.bottom);
1594
1595 #ifdef SHAPE
1596 if (blackbox->hasShapeExtensions() && flags.shaped) {
1597 configureShape();
1598 }
1599 #endif // SHAPE
1600
1601 positionWindows();
1602 decorate();
1603 redrawWindowFrame();
1604 } else {
1605 frame.rect.setPos(dx, dy);
1606
1607 XMoveWindow(blackbox->getXDisplay(), frame.window,
1608 frame.rect.x(), frame.rect.y());
1609 /*
1610 we may have been called just after an opaque window move, so even though
1611 the old coords match the new ones no ConfigureNotify has been sent yet.
1612 There are likely other times when this will be relevant as well.
1613 */
1614 if (! flags.moving) send_event = True;
1615 }
1616
1617 if (send_event) {
1618 // if moving, the update and event will occur when the move finishes
1619 client.rect.setPos(frame.rect.left() + frame.margin.left,
1620 frame.rect.top() + frame.margin.top);
1621
1622 XEvent event;
1623 event.type = ConfigureNotify;
1624
1625 event.xconfigure.display = blackbox->getXDisplay();
1626 event.xconfigure.event = client.window;
1627 event.xconfigure.window = client.window;
1628 event.xconfigure.x = client.rect.x();
1629 event.xconfigure.y = client.rect.y();
1630 event.xconfigure.width = client.rect.width();
1631 event.xconfigure.height = client.rect.height();
1632 event.xconfigure.border_width = client.old_bw;
1633 event.xconfigure.above = frame.window;
1634 event.xconfigure.override_redirect = False;
1635
1636 XSendEvent(blackbox->getXDisplay(), client.window, False,
1637 StructureNotifyMask, &event);
1638 screen->updateNetizenConfigNotify(&event);
1639 XFlush(blackbox->getXDisplay());
1640 }
1641 }
1642
1643
1644 #ifdef SHAPE
1645 void BlackboxWindow::configureShape(void) {
1646 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1647 frame.margin.left - frame.border_w,
1648 frame.margin.top - frame.border_w,
1649 client.window, ShapeBounding, ShapeSet);
1650
1651 int num = 0;
1652 XRectangle xrect[2];
1653
1654 if (decorations & Decor_Titlebar) {
1655 xrect[0].x = xrect[0].y = -frame.border_w;
1656 xrect[0].width = frame.rect.width();
1657 xrect[0].height = frame.title_h + (frame.border_w * 2);
1658 ++num;
1659 }
1660
1661 if (decorations & Decor_Handle) {
1662 xrect[1].x = -frame.border_w;
1663 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1664 frame.mwm_border_w - frame.border_w;
1665 xrect[1].width = frame.rect.width();
1666 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1667 ++num;
1668 }
1669
1670 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1671 ShapeBounding, 0, 0, xrect, num,
1672 ShapeUnion, Unsorted);
1673 }
1674
1675
1676 void BlackboxWindow::clearShape(void) {
1677 XShapeCombineMask(blackbox->getXDisplay(), frame.window, ShapeBounding,
1678 frame.margin.left - frame.border_w,
1679 frame.margin.top - frame.border_w,
1680 None, ShapeSet);
1681 }
1682 #endif // SHAPE
1683
1684
1685 bool BlackboxWindow::setInputFocus(void) {
1686 if (flags.focused) return True;
1687
1688 assert(flags.stuck || // window must be on the current workspace or sticky
1689 blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1690
1691 /*
1692 We only do this check for normal windows and dialogs because other windows
1693 do this on purpose, such as kde's kicker, and we don't want to go moving
1694 it.
1695 */
1696 if (window_type == Type_Normal || window_type == Type_Dialog)
1697 if (! frame.rect.intersects(screen->getRect())) {
1698 // client is outside the screen, move it to the center
1699 configure((screen->getWidth() - frame.rect.width()) / 2,
1700 (screen->getHeight() - frame.rect.height()) / 2,
1701 frame.rect.width(), frame.rect.height());
1702 }
1703
1704 if (client.transientList.size() > 0) {
1705 // transfer focus to any modal transients
1706 BlackboxWindowList::iterator it, end = client.transientList.end();
1707 for (it = client.transientList.begin(); it != end; ++it)
1708 if ((*it)->flags.modal) return (*it)->setInputFocus();
1709 }
1710
1711 bool ret = True;
1712 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1713 XSetInputFocus(blackbox->getXDisplay(), client.window,
1714 RevertToPointerRoot, CurrentTime);
1715 } else {
1716 /* we could set the focus to none, since the window doesn't accept focus,
1717 * but we shouldn't set focus to nothing since this would surely make
1718 * someone angry
1719 */
1720 ret = False;
1721 }
1722
1723 if (flags.send_focus_message) {
1724 XEvent ce;
1725 ce.xclient.type = ClientMessage;
1726 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1727 ce.xclient.display = blackbox->getXDisplay();
1728 ce.xclient.window = client.window;
1729 ce.xclient.format = 32;
1730 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1731 ce.xclient.data.l[1] = blackbox->getLastTime();
1732 ce.xclient.data.l[2] = 0l;
1733 ce.xclient.data.l[3] = 0l;
1734 ce.xclient.data.l[4] = 0l;
1735 XSendEvent(blackbox->getXDisplay(), client.window, False,
1736 NoEventMask, &ce);
1737 XFlush(blackbox->getXDisplay());
1738 }
1739
1740 return ret;
1741 }
1742
1743
1744 void BlackboxWindow::iconify(void) {
1745 if (flags.iconic || ! (functions & Func_Iconify)) return;
1746
1747 // We don't need to worry about resizing because resizing always grabs the X
1748 // server. This should only ever happen if using opaque moving.
1749 if (flags.moving)
1750 endMove();
1751
1752 if (windowmenu) windowmenu->hide();
1753
1754 /*
1755 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1756 * we need to clear the event mask on client.window for a split second.
1757 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1758 * split second, leaving us with a ghost window... so, we need to do this
1759 * while the X server is grabbed
1760 */
1761 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1762 StructureNotifyMask;
1763 XGrabServer(blackbox->getXDisplay());
1764 XSelectInput(blackbox->getXDisplay(), client.window,
1765 event_mask & ~StructureNotifyMask);
1766 XUnmapWindow(blackbox->getXDisplay(), client.window);
1767 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1768 XUngrabServer(blackbox->getXDisplay());
1769
1770 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1771 flags.visible = False;
1772 flags.iconic = True;
1773
1774 setState(IconicState);
1775
1776 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1777 if (flags.stuck) {
1778 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1779 if (i != blackbox_attrib.workspace)
1780 screen->getWorkspace(i)->removeWindow(this, True);
1781 }
1782
1783 if (isTransient()) {
1784 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1785 ! client.transient_for->flags.iconic) {
1786 // iconify our transient_for
1787 client.transient_for->iconify();
1788 }
1789 }
1790
1791 screen->addIcon(this);
1792
1793 if (client.transientList.size() > 0) {
1794 // iconify all transients
1795 BlackboxWindowList::iterator it, end = client.transientList.end();
1796 for (it = client.transientList.begin(); it != end; ++it) {
1797 if (! (*it)->flags.iconic) (*it)->iconify();
1798 }
1799 }
1800 screen->updateStackingList();
1801 }
1802
1803
1804 void BlackboxWindow::show(void) {
1805 flags.visible = True;
1806 flags.iconic = False;
1807
1808 current_state = (flags.shaded) ? IconicState : NormalState;
1809 setState(current_state);
1810
1811 XMapWindow(blackbox->getXDisplay(), client.window);
1812 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1813 XMapWindow(blackbox->getXDisplay(), frame.window);
1814
1815 #if 0
1816 int real_x, real_y;
1817 Window child;
1818 XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1819 screen->getRootWindow(),
1820 0, 0, &real_x, &real_y, &child);
1821 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1822 client.rect.left(), client.rect.top(), real_x, real_y);
1823 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1824 #endif
1825 }
1826
1827
1828 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1829 if (flags.iconic || reassoc)
1830 screen->reassociateWindow(this, BSENTINEL, False);
1831 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1832 return;
1833
1834 show();
1835
1836 // reassociate and deiconify all transients
1837 if (reassoc && client.transientList.size() > 0) {
1838 BlackboxWindowList::iterator it, end = client.transientList.end();
1839 for (it = client.transientList.begin(); it != end; ++it)
1840 (*it)->deiconify(True, False);
1841 }
1842
1843 if (raise)
1844 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1845 }
1846
1847
1848 void BlackboxWindow::close(void) {
1849 if (! (functions & Func_Close)) return;
1850
1851 XEvent ce;
1852 ce.xclient.type = ClientMessage;
1853 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1854 ce.xclient.display = blackbox->getXDisplay();
1855 ce.xclient.window = client.window;
1856 ce.xclient.format = 32;
1857 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1858 ce.xclient.data.l[1] = CurrentTime;
1859 ce.xclient.data.l[2] = 0l;
1860 ce.xclient.data.l[3] = 0l;
1861 ce.xclient.data.l[4] = 0l;
1862 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1863 XFlush(blackbox->getXDisplay());
1864 }
1865
1866
1867 void BlackboxWindow::withdraw(void) {
1868 // We don't need to worry about resizing because resizing always grabs the X
1869 // server. This should only ever happen if using opaque moving.
1870 if (flags.moving)
1871 endMove();
1872
1873 flags.visible = False;
1874 flags.iconic = False;
1875
1876 setState(current_state);
1877
1878 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1879
1880 XGrabServer(blackbox->getXDisplay());
1881
1882 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1883 StructureNotifyMask;
1884 XSelectInput(blackbox->getXDisplay(), client.window,
1885 event_mask & ~StructureNotifyMask);
1886 XUnmapWindow(blackbox->getXDisplay(), client.window);
1887 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1888
1889 XUngrabServer(blackbox->getXDisplay());
1890
1891 if (windowmenu) windowmenu->hide();
1892 }
1893
1894
1895 void BlackboxWindow::maximize(unsigned int button) {
1896 if (! (functions & Func_Maximize)) return;
1897
1898 // We don't need to worry about resizing because resizing always grabs the X
1899 // server. This should only ever happen if using opaque moving.
1900 if (flags.moving)
1901 endMove();
1902
1903 // handle case where menu is open then the max button is used instead
1904 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1905
1906 if (flags.maximized) {
1907 flags.maximized = 0;
1908
1909 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1910 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1911
1912 /*
1913 when a resize finishes, maximize(0) is called to clear any maximization
1914 flags currently set. Otherwise it still thinks it is maximized.
1915 so we do not need to call configure() because resizing will handle it
1916 */
1917 if (! flags.resizing)
1918 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1919 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1920
1921 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1922 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1923
1924 redrawAllButtons(); // in case it is not called in configure()
1925 setState(current_state);
1926 return;
1927 }
1928
1929 blackbox_attrib.premax_x = frame.rect.x();
1930 blackbox_attrib.premax_y = frame.rect.y();
1931 blackbox_attrib.premax_w = frame.rect.width();
1932 // use client.rect so that clients can be restored even if shaded
1933 blackbox_attrib.premax_h =
1934 client.rect.height() + frame.margin.top + frame.margin.bottom;
1935
1936 #ifdef XINERAMA
1937 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1938 // find the area to use
1939 RectList availableAreas = screen->allAvailableAreas();
1940 RectList::iterator it, end = availableAreas.end();
1941
1942 for (it = availableAreas.begin(); it != end; ++it)
1943 if (it->intersects(frame.rect)) break;
1944 if (it == end) // the window isn't inside an area
1945 it = availableAreas.begin(); // so just default to the first one
1946
1947 frame.changing = *it;
1948 } else
1949 #endif // XINERAMA
1950 frame.changing = screen->availableArea();
1951
1952 switch(button) {
1953 case 1:
1954 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1955 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1956 break;
1957
1958 case 2:
1959 blackbox_attrib.flags |= AttribMaxVert;
1960 blackbox_attrib.attrib |= AttribMaxVert;
1961
1962 frame.changing.setX(frame.rect.x());
1963 frame.changing.setWidth(frame.rect.width());
1964 break;
1965
1966 case 3:
1967 blackbox_attrib.flags |= AttribMaxHoriz;
1968 blackbox_attrib.attrib |= AttribMaxHoriz;
1969
1970 frame.changing.setY(frame.rect.y());
1971 frame.changing.setHeight(frame.rect.height());
1972 break;
1973 }
1974
1975 constrain(TopLeft);
1976
1977 if (flags.shaded) {
1978 blackbox_attrib.flags ^= AttribShaded;
1979 blackbox_attrib.attrib ^= AttribShaded;
1980 flags.shaded = False;
1981 }
1982
1983 flags.maximized = button;
1984
1985 configure(frame.changing.x(), frame.changing.y(),
1986 frame.changing.width(), frame.changing.height());
1987 if (flags.focused)
1988 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1989 redrawAllButtons(); // in case it is not called in configure()
1990 setState(current_state);
1991 }
1992
1993
1994 // re-maximizes the window to take into account availableArea changes
1995 void BlackboxWindow::remaximize(void) {
1996 if (flags.shaded) {
1997 // we only update the window's attributes otherwise we lose the shade bit
1998 switch(flags.maximized) {
1999 case 1:
2000 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
2001 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
2002 break;
2003
2004 case 2:
2005 blackbox_attrib.flags |= AttribMaxVert;
2006 blackbox_attrib.attrib |= AttribMaxVert;
2007 break;
2008
2009 case 3:
2010 blackbox_attrib.flags |= AttribMaxHoriz;
2011 blackbox_attrib.attrib |= AttribMaxHoriz;
2012 break;
2013 }
2014 return;
2015 }
2016
2017 // save the original dimensions because maximize will wipe them out
2018 int premax_x = blackbox_attrib.premax_x,
2019 premax_y = blackbox_attrib.premax_y,
2020 premax_w = blackbox_attrib.premax_w,
2021 premax_h = blackbox_attrib.premax_h;
2022
2023 unsigned int button = flags.maximized;
2024 flags.maximized = 0; // trick maximize() into working
2025 maximize(button);
2026
2027 // restore saved values
2028 blackbox_attrib.premax_x = premax_x;
2029 blackbox_attrib.premax_y = premax_y;
2030 blackbox_attrib.premax_w = premax_w;
2031 blackbox_attrib.premax_h = premax_h;
2032 }
2033
2034
2035 void BlackboxWindow::setWorkspace(unsigned int n) {
2036 blackbox_attrib.flags |= AttribWorkspace;
2037 blackbox_attrib.workspace = n;
2038 if (n == BSENTINEL) { // iconified window
2039 /*
2040 we set the workspace to 'all workspaces' so that taskbars will show the
2041 window. otherwise, it made uniconifying a window imposible without the
2042 blackbox workspace menu
2043 */
2044 n = 0xffffffff;
2045 }
2046 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
2047 }
2048
2049
2050 void BlackboxWindow::shade(void) {
2051 if (flags.shaded) {
2052 XResizeWindow(blackbox->getXDisplay(), frame.window,
2053 frame.inside_w, frame.inside_h);
2054 flags.shaded = False;
2055 blackbox_attrib.flags ^= AttribShaded;
2056 blackbox_attrib.attrib ^= AttribShaded;
2057
2058 setState(NormalState);
2059
2060 // set the frame rect to the normal size
2061 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2062 frame.margin.bottom);
2063 } else {
2064 if (! (decorations & Decor_Titlebar))
2065 return; // can't shade it without a titlebar!
2066
2067 XResizeWindow(blackbox->getXDisplay(), frame.window,
2068 frame.inside_w, frame.title_h);
2069 flags.shaded = True;
2070 blackbox_attrib.flags |= AttribShaded;
2071 blackbox_attrib.attrib |= AttribShaded;
2072
2073 setState(IconicState);
2074
2075 // set the frame rect to the shaded size
2076 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
2077 }
2078 }
2079
2080
2081 /*
2082 * (Un)Sticks a window and its relatives.
2083 */
2084 void BlackboxWindow::stick(void) {
2085 if (flags.stuck) {
2086 blackbox_attrib.flags ^= AttribOmnipresent;
2087 blackbox_attrib.attrib ^= AttribOmnipresent;
2088
2089 flags.stuck = False;
2090
2091 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2092 if (i != blackbox_attrib.workspace)
2093 screen->getWorkspace(i)->removeWindow(this, True);
2094
2095 if (! flags.iconic)
2096 screen->reassociateWindow(this, BSENTINEL, True);
2097 // temporary fix since sticky windows suck. set the hint to what we
2098 // actually hold in our data.
2099 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2100 blackbox_attrib.workspace);
2101
2102 setState(current_state);
2103 } else {
2104 flags.stuck = True;
2105
2106 blackbox_attrib.flags |= AttribOmnipresent;
2107 blackbox_attrib.attrib |= AttribOmnipresent;
2108
2109 // temporary fix since sticky windows suck. set the hint to a different
2110 // value than that contained in the class' data.
2111 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
2112 0xffffffff);
2113
2114 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2115 if (i != blackbox_attrib.workspace)
2116 screen->getWorkspace(i)->addWindow(this, False, True);
2117
2118 setState(current_state);
2119 }
2120
2121 redrawAllButtons();
2122
2123 // go up the chain
2124 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2125 client.transient_for->isStuck() != flags.stuck)
2126 client.transient_for->stick();
2127 // go down the chain
2128 BlackboxWindowList::iterator it;
2129 const BlackboxWindowList::iterator end = client.transientList.end();
2130 for (it = client.transientList.begin(); it != end; ++it)
2131 if ((*it)->isStuck() != flags.stuck)
2132 (*it)->stick();
2133 }
2134
2135
2136 void BlackboxWindow::redrawWindowFrame(void) const {
2137 if (decorations & Decor_Titlebar) {
2138 if (flags.focused) {
2139 if (frame.ftitle)
2140 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2141 frame.title, frame.ftitle);
2142 else
2143 XSetWindowBackground(blackbox->getXDisplay(),
2144 frame.title, frame.ftitle_pixel);
2145 } else {
2146 if (frame.utitle)
2147 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2148 frame.title, frame.utitle);
2149 else
2150 XSetWindowBackground(blackbox->getXDisplay(),
2151 frame.title, frame.utitle_pixel);
2152 }
2153 XClearWindow(blackbox->getXDisplay(), frame.title);
2154
2155 redrawLabel();
2156 redrawAllButtons();
2157 }
2158
2159 if (decorations & Decor_Handle) {
2160 if (flags.focused) {
2161 if (frame.fhandle)
2162 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2163 frame.handle, frame.fhandle);
2164 else
2165 XSetWindowBackground(blackbox->getXDisplay(),
2166 frame.handle, frame.fhandle_pixel);
2167
2168 if (frame.fgrip) {
2169 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2170 frame.left_grip, frame.fgrip);
2171 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2172 frame.right_grip, frame.fgrip);
2173 } else {
2174 XSetWindowBackground(blackbox->getXDisplay(),
2175 frame.left_grip, frame.fgrip_pixel);
2176 XSetWindowBackground(blackbox->getXDisplay(),
2177 frame.right_grip, frame.fgrip_pixel);
2178 }
2179 } else {
2180 if (frame.uhandle)
2181 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2182 frame.handle, frame.uhandle);
2183 else
2184 XSetWindowBackground(blackbox->getXDisplay(),
2185 frame.handle, frame.uhandle_pixel);
2186
2187 if (frame.ugrip) {
2188 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2189 frame.left_grip, frame.ugrip);
2190 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2191 frame.right_grip, frame.ugrip);
2192 } else {
2193 XSetWindowBackground(blackbox->getXDisplay(),
2194 frame.left_grip, frame.ugrip_pixel);
2195 XSetWindowBackground(blackbox->getXDisplay(),
2196 frame.right_grip, frame.ugrip_pixel);
2197 }
2198 }
2199 XClearWindow(blackbox->getXDisplay(), frame.handle);
2200 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2201 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2202 }
2203
2204 if (decorations & Decor_Border) {
2205 if (flags.focused)
2206 XSetWindowBorder(blackbox->getXDisplay(),
2207 frame.plate, frame.fborder_pixel);
2208 else
2209 XSetWindowBorder(blackbox->getXDisplay(),
2210 frame.plate, frame.uborder_pixel);
2211 }
2212 }
2213
2214
2215 void BlackboxWindow::setFocusFlag(bool focus) {
2216 // only focus a window if it is visible
2217 if (focus && ! flags.visible)
2218 return;
2219
2220 flags.focused = focus;
2221
2222 redrawWindowFrame();
2223
2224 if (flags.focused)
2225 blackbox->setFocusedWindow(this);
2226
2227 if (! flags.iconic) {
2228 // iconic windows arent in a workspace menu!
2229 if (flags.stuck)
2230 screen->getCurrentWorkspace()->setFocused(this, isFocused());
2231 else
2232 screen->getWorkspace(blackbox_attrib.workspace)->
2233 setFocused(this, flags.focused);
2234 }
2235 }
2236
2237
2238 void BlackboxWindow::installColormap(bool install) {
2239 int i = 0, ncmap = 0;
2240 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2241 client.window, &ncmap);
2242 if (cmaps) {
2243 XWindowAttributes wattrib;
2244 if (XGetWindowAttributes(blackbox->getXDisplay(),
2245 client.window, &wattrib)) {
2246 if (install) {
2247 // install the window's colormap
2248 for (i = 0; i < ncmap; i++) {
2249 if (*(cmaps + i) == wattrib.colormap)
2250 // this window is using an installed color map... do not install
2251 install = False;
2252 }
2253 // otherwise, install the window's colormap
2254 if (install)
2255 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2256 } else {
2257 // uninstall the window's colormap
2258 for (i = 0; i < ncmap; i++) {
2259 if (*(cmaps + i) == wattrib.colormap)
2260 // we found the colormap to uninstall
2261 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2262 }
2263 }
2264 }
2265
2266 XFree(cmaps);
2267 }
2268 }
2269
2270
2271 void BlackboxWindow::setAllowedActions(void) {
2272 Atom actions[7];
2273 int num = 0;
2274
2275 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2276 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2277 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2278
2279 if (functions & Func_Move)
2280 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2281 if (functions & Func_Resize)
2282 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2283 if (functions & Func_Maximize) {
2284 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2285 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2286 }
2287
2288 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2289 actions, num);
2290 }
2291
2292
2293 void BlackboxWindow::setState(unsigned long new_state) {
2294 current_state = new_state;
2295
2296 unsigned long state[2];
2297 state[0] = current_state;
2298 state[1] = None;
2299 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2300
2301 xatom->setValue(client.window, XAtom::blackbox_attributes,
2302 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2303 PropBlackboxAttributesElements);
2304
2305 Atom netstate[8];
2306 int num = 0;
2307 if (flags.modal)
2308 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2309 if (flags.shaded)
2310 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2311 if (flags.iconic)
2312 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2313 if (flags.skip_taskbar)
2314 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2315 if (flags.skip_pager)
2316 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2317 if (flags.fullscreen)
2318 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2319 if (flags.maximized == 1 || flags.maximized == 2)
2320 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2321 if (flags.maximized == 1 || flags.maximized == 3)
2322 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2323 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2324 netstate, num);
2325 }
2326
2327
2328 bool BlackboxWindow::getState(void) {
2329 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2330 current_state);
2331 if (! ret) current_state = 0;
2332 return ret;
2333 }
2334
2335
2336 void BlackboxWindow::restoreAttributes(void) {
2337 unsigned long num = PropBlackboxAttributesElements;
2338 BlackboxAttributes *net;
2339 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2340 XAtom::blackbox_attributes, num,
2341 (unsigned long **)&net))
2342 return;
2343 if (num < PropBlackboxAttributesElements) {
2344 delete [] net;
2345 return;
2346 }
2347
2348 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2349 flags.shaded = False;
2350 unsigned long orig_state = current_state;
2351 shade();
2352
2353 /*
2354 At this point in the life of a window, current_state should only be set
2355 to IconicState if the window was an *icon*, not if it was shaded.
2356 */
2357 if (orig_state != IconicState)
2358 current_state = WithdrawnState;
2359 }
2360
2361 if (net->workspace != screen->getCurrentWorkspaceID() &&
2362 net->workspace < screen->getWorkspaceCount())
2363 screen->reassociateWindow(this, net->workspace, True);
2364
2365 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2366 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2367 // set to WithdrawnState so it will be mapped on the new workspace
2368 if (current_state == NormalState) current_state = WithdrawnState;
2369 } else if (current_state == WithdrawnState) {
2370 // the window is on this workspace and is Withdrawn, so it is waiting to
2371 // be mapped
2372 current_state = NormalState;
2373 }
2374
2375 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2376 ! flags.stuck) {
2377 stick();
2378
2379 // if the window was on another workspace, it was going to be hidden. this
2380 // specifies that the window should be mapped since it is sticky.
2381 if (current_state == WithdrawnState) current_state = NormalState;
2382 }
2383
2384 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2385 int x = net->premax_x, y = net->premax_y;
2386 unsigned int w = net->premax_w, h = net->premax_h;
2387 flags.maximized = 0;
2388
2389 unsigned int m = 0;
2390 if ((net->flags & AttribMaxHoriz) &&
2391 (net->flags & AttribMaxVert))
2392 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2393 else if (net->flags & AttribMaxVert)
2394 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2395 else if (net->flags & AttribMaxHoriz)
2396 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2397
2398 if (m) maximize(m);
2399
2400 blackbox_attrib.premax_x = x;
2401 blackbox_attrib.premax_y = y;
2402 blackbox_attrib.premax_w = w;
2403 blackbox_attrib.premax_h = h;
2404 }
2405
2406 if (net->flags & AttribDecoration) {
2407 switch (net->decoration) {
2408 case DecorNone:
2409 enableDecor(False);
2410 break;
2411
2412 /* since tools only let you toggle this anyways, we'll just make that all
2413 it supports for now.
2414 */
2415 default:
2416 case DecorNormal:
2417 case DecorTiny:
2418 case DecorTool:
2419 enableDecor(True);
2420 break;
2421 }
2422 }
2423
2424 // with the state set it will then be the map event's job to read the
2425 // window's state and behave accordingly
2426
2427 delete [] net;
2428 }
2429
2430
2431 /*
2432 * Positions the Rect r according the the client window position and
2433 * window gravity.
2434 */
2435 void BlackboxWindow::applyGravity(Rect &r) {
2436 // apply horizontal window gravity
2437 switch (client.win_gravity) {
2438 default:
2439 case NorthWestGravity:
2440 case SouthWestGravity:
2441 case WestGravity:
2442 r.setX(client.rect.x());
2443 break;
2444
2445 case NorthGravity:
2446 case SouthGravity:
2447 case CenterGravity:
2448 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2449 break;
2450
2451 case NorthEastGravity:
2452 case SouthEastGravity:
2453 case EastGravity:
2454 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2455 break;
2456
2457 case ForgetGravity:
2458 case StaticGravity:
2459 r.setX(client.rect.x() - frame.margin.left);
2460 break;
2461 }
2462
2463 // apply vertical window gravity
2464 switch (client.win_gravity) {
2465 default:
2466 case NorthWestGravity:
2467 case NorthEastGravity:
2468 case NorthGravity:
2469 r.setY(client.rect.y());
2470 break;
2471
2472 case CenterGravity:
2473 case EastGravity:
2474 case WestGravity:
2475 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2476 break;
2477
2478 case SouthWestGravity:
2479 case SouthEastGravity:
2480 case SouthGravity:
2481 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2482 break;
2483
2484 case ForgetGravity:
2485 case StaticGravity:
2486 r.setY(client.rect.y() - frame.margin.top);
2487 break;
2488 }
2489 }
2490
2491
2492 /*
2493 * The reverse of the applyGravity function.
2494 *
2495 * Positions the Rect r according to the frame window position and
2496 * window gravity.
2497 */
2498 void BlackboxWindow::restoreGravity(Rect &r) {
2499 // restore horizontal window gravity
2500 switch (client.win_gravity) {
2501 default:
2502 case NorthWestGravity:
2503 case SouthWestGravity:
2504 case WestGravity:
2505 r.setX(frame.rect.x());
2506 break;
2507
2508 case NorthGravity:
2509 case SouthGravity:
2510 case CenterGravity:
2511 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2512 break;
2513
2514 case NorthEastGravity:
2515 case SouthEastGravity:
2516 case EastGravity:
2517 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2518 break;
2519
2520 case ForgetGravity:
2521 case StaticGravity:
2522 r.setX(frame.rect.x() + frame.margin.left);
2523 break;
2524 }
2525
2526 // restore vertical window gravity
2527 switch (client.win_gravity) {
2528 default:
2529 case NorthWestGravity:
2530 case NorthEastGravity:
2531 case NorthGravity:
2532 r.setY(frame.rect.y());
2533 break;
2534
2535 case CenterGravity:
2536 case EastGravity:
2537 case WestGravity:
2538 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2539 break;
2540
2541 case SouthWestGravity:
2542 case SouthEastGravity:
2543 case SouthGravity:
2544 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2545 break;
2546
2547 case ForgetGravity:
2548 case StaticGravity:
2549 r.setY(frame.rect.y() + frame.margin.top);
2550 break;
2551 }
2552 }
2553
2554
2555 void BlackboxWindow::redrawLabel(void) const {
2556 if (flags.focused) {
2557 if (frame.flabel)
2558 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2559 frame.label, frame.flabel);
2560 else
2561 XSetWindowBackground(blackbox->getXDisplay(),
2562 frame.label, frame.flabel_pixel);
2563 } else {
2564 if (frame.ulabel)
2565 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2566 frame.label, frame.ulabel);
2567 else
2568 XSetWindowBackground(blackbox->getXDisplay(),
2569 frame.label, frame.ulabel_pixel);
2570 }
2571 XClearWindow(blackbox->getXDisplay(), frame.label);
2572
2573 WindowStyle *style = screen->getWindowStyle();
2574
2575 int pos = frame.bevel_w * 2;
2576 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2577 style->font->drawString(frame.label, pos, 1,
2578 (flags.focused ? style->l_text_focus :
2579 style->l_text_unfocus),
2580 client.title);
2581 }
2582
2583
2584 void BlackboxWindow::redrawAllButtons(void) const {
2585 if (frame.iconify_button) redrawIconifyButton(False);
2586 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2587 if (frame.close_button) redrawCloseButton(False);
2588 if (frame.stick_button) redrawStickyButton(flags.stuck);
2589 }
2590
2591
2592 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2593 if (! pressed) {
2594 if (flags.focused) {
2595 if (frame.fbutton)
2596 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2597 frame.iconify_button, frame.fbutton);
2598 else
2599 XSetWindowBackground(blackbox->getXDisplay(),
2600 frame.iconify_button, frame.fbutton_pixel);
2601 } else {
2602 if (frame.ubutton)
2603 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2604 frame.iconify_button, frame.ubutton);
2605 else
2606 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2607 frame.ubutton_pixel);
2608 }
2609 } else {
2610 if (frame.pbutton)
2611 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2612 frame.iconify_button, frame.pbutton);
2613 else
2614 XSetWindowBackground(blackbox->getXDisplay(),
2615 frame.iconify_button, frame.pbutton_pixel);
2616 }
2617
2618 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2619 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2620 screen->getWindowStyle()->b_pic_unfocus);
2621 #ifdef BITMAPBUTTONS
2622 PixmapMask pm = screen->getWindowStyle()->icon_button;
2623
2624 if (screen->getWindowStyle()->icon_button.mask != None) {
2625 XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2626 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2627 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2628
2629 XFillRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2630 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2631 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2632
2633 XSetClipMask(blackbox->getXDisplay(), pen.gc(), None);
2634 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0);
2635 } else {
2636 #endif // BITMAPBUTTONS
2637 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2638 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2639 #ifdef BITMAPBUTTONS
2640 }
2641 #endif // BITMAPBUTTONS
2642 }
2643
2644
2645 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2646 if (! pressed) {
2647 if (flags.focused) {
2648 if (frame.fbutton)
2649 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2650 frame.maximize_button, frame.fbutton);
2651 else
2652 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2653 frame.fbutton_pixel);
2654 } else {
2655 if (frame.ubutton)
2656 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2657 frame.maximize_button, frame.ubutton);
2658 else
2659 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2660 frame.ubutton_pixel);
2661 }
2662 } else {
2663 if (frame.pbutton)
2664 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2665 frame.maximize_button, frame.pbutton);
2666 else
2667 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2668 frame.pbutton_pixel);
2669 }
2670 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2671
2672 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2673 screen->getWindowStyle()->b_pic_unfocus);
2674
2675 #ifdef BITMAPBUTTONS
2676 PixmapMask pm = screen->getWindowStyle()->max_button;
2677
2678 if (pm.mask != None) {
2679 XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2680 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2681 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2682
2683 XFillRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2684 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2685 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2686
2687 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2688 XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2689 } else {
2690 #endif // BITMAPBUTTONS
2691 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2692 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2693 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2694 2, 3, (frame.button_w - 3), 3);
2695 #ifdef BITMAPBUTTONS
2696 }
2697 #endif // BITMAPBUTTONS
2698 }
2699
2700
2701 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2702 if (! pressed) {
2703 if (flags.focused) {
2704 if (frame.fbutton)
2705 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2706 frame.close_button, frame.fbutton);
2707 else
2708 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2709 frame.fbutton_pixel);
2710 } else {
2711 if (frame.ubutton)
2712 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2713 frame.ubutton);
2714 else
2715 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2716 frame.ubutton_pixel);
2717 }
2718 } else {
2719 if (frame.pbutton)
2720 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2721 frame.close_button, frame.pbutton);
2722 else
2723 XSetWindowBackground(blackbox->getXDisplay(),
2724 frame.close_button, frame.pbutton_pixel);
2725 }
2726 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2727
2728 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2729 screen->getWindowStyle()->b_pic_unfocus);
2730
2731 #ifdef BITMAPBUTTONS
2732 PixmapMask pm = screen->getWindowStyle()->close_button;
2733
2734 if (pm.mask != None) {
2735 XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2736 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2737 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2738
2739 XFillRectangle(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2740 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2741 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2742
2743
2744 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2745 XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2746 } else {
2747 #endif // BITMAPBUTTONS
2748 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2749 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2750 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2751 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2752 #ifdef BITMAPBUTTONS
2753 }
2754 #endif // BITMAPBUTTONS
2755 }
2756
2757 void BlackboxWindow::redrawStickyButton(bool pressed) const {
2758 if (! pressed) {
2759 if (flags.focused) {
2760 if (frame.fbutton)
2761 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2762 frame.stick_button, frame.fbutton);
2763 else
2764 XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2765 frame.fbutton_pixel);
2766 } else {
2767 if (frame.ubutton)
2768 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2769 frame.stick_button, frame.ubutton);
2770 else
2771 XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2772 frame.ubutton_pixel);
2773 }
2774 } else {
2775 if (frame.pbutton)
2776 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2777 frame.stick_button, frame.pbutton);
2778 else
2779 XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button,
2780 frame.pbutton_pixel);
2781 }
2782 XClearWindow(blackbox->getXDisplay(), frame.stick_button);
2783
2784 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2785 screen->getWindowStyle()->b_pic_unfocus);
2786
2787 #ifdef BITMAPBUTTONS
2788 PixmapMask pm = screen->getWindowStyle()->stick_button;
2789
2790 if (pm.mask != None) {
2791 XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask);
2792 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(),
2793 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2794
2795 XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2796 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2797 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2798
2799
2800 XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 );
2801 XSetClipMask( blackbox->getXDisplay(), pen.gc(), None );
2802 } else {
2803 #endif // BITMAPBUTTONS
2804 XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(),
2805 frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 );
2806 #ifdef BITMAPBUTTONS
2807 }
2808 #endif
2809 }
2810
2811 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2812 if (re->window != client.window)
2813 return;
2814
2815 #ifdef DEBUG
2816 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2817 client.window);
2818 #endif // DEBUG
2819
2820 /*
2821 Even though the window wants to be shown, if it is not on the current
2822 workspace, then it isn't going to be shown right now.
2823 */
2824 if (! flags.stuck &&
2825 blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2826 blackbox_attrib.workspace < screen->getWorkspaceCount())
2827 if (current_state == NormalState) current_state = WithdrawnState;
2828
2829 switch (current_state) {
2830 case IconicState:
2831 iconify();
2832 break;
2833
2834 case WithdrawnState:
2835 withdraw();
2836 break;
2837
2838 case NormalState:
2839 case InactiveState:
2840 case ZoomState:
2841 default:
2842 show();
2843 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2844 if (isNormal()) {
2845 if (! blackbox->isStartup()) {
2846 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2847 if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2848 getTransientFor()->isFocused())) {
2849 setInputFocus();
2850 }
2851 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2852 int x, y, rx, ry;
2853 Window c, r;
2854 unsigned int m;
2855 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2856 &r, &c, &rx, &ry, &x, &y, &m);
2857 beginMove(rx, ry);
2858 }
2859 }
2860 }
2861 break;
2862 }
2863 }
2864
2865
2866 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2867 if (ue->window != client.window)
2868 return;
2869
2870 #ifdef DEBUG
2871 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2872 client.window);
2873 #endif // DEBUG
2874
2875 screen->unmanageWindow(this, False);
2876 }
2877
2878
2879 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2880 if (de->window != client.window)
2881 return;
2882
2883 #ifdef DEBUG
2884 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2885 client.window);
2886 #endif // DEBUG
2887
2888 screen->unmanageWindow(this, False);
2889 }
2890
2891
2892 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2893 if (re->window != client.window || re->parent == frame.plate)
2894 return;
2895
2896 #ifdef DEBUG
2897 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2898 "0x%lx.\n", client.window, re->parent);
2899 #endif // DEBUG
2900
2901 XEvent ev;
2902 ev.xreparent = *re;
2903 XPutBackEvent(blackbox->getXDisplay(), &ev);
2904 screen->unmanageWindow(this, True);
2905 }
2906
2907
2908 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2909 if (pe->state == PropertyDelete || ! validateClient())
2910 return;
2911
2912 #if 0
2913 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2914 client.window);
2915 #endif
2916
2917 switch(pe->atom) {
2918 case XA_WM_CLASS:
2919 case XA_WM_CLIENT_MACHINE:
2920 case XA_WM_COMMAND:
2921 break;
2922
2923 case XA_WM_TRANSIENT_FOR: {
2924 bool s = flags.stuck;
2925
2926 // determine if this is a transient window
2927 getTransientInfo();
2928
2929 if (flags.stuck != s) stick();
2930
2931 // adjust the window decorations based on transience
2932 if (isTransient()) {
2933 functions &= ~Func_Maximize;
2934 setAllowedActions();
2935 setupDecor();
2936 }
2937
2938 reconfigure();
2939 }
2940 break;
2941
2942 case XA_WM_HINTS:
2943 getWMHints();
2944 break;
2945
2946 case XA_WM_ICON_NAME:
2947 getWMIconName();
2948 if (flags.iconic) screen->propagateWindowName(this);
2949 break;
2950
2951 case XAtom::net_wm_name:
2952 case XA_WM_NAME:
2953 getWMName();
2954
2955 if (decorations & Decor_Titlebar)
2956 redrawLabel();
2957
2958 screen->propagateWindowName(this);
2959 break;
2960
2961 case XA_WM_NORMAL_HINTS: {
2962 getWMNormalHints();
2963
2964 if ((client.normal_hint_flags & PMinSize) &&
2965 (client.normal_hint_flags & PMaxSize)) {
2966 // the window now can/can't resize itself, so the buttons need to be
2967 // regrabbed.
2968 ungrabButtons();
2969 if (client.max_width <= client.min_width &&
2970 client.max_height <= client.min_height) {
2971 functions &= ~(Func_Resize | Func_Maximize);
2972 } else {
2973 if (! isTransient())
2974 functions |= Func_Maximize;
2975 functions |= Func_Resize;
2976 }
2977 grabButtons();
2978 setAllowedActions();
2979 setupDecor();
2980 }
2981
2982 Rect old_rect = frame.rect;
2983
2984 upsize();
2985
2986 if (old_rect != frame.rect)
2987 reconfigure();
2988
2989 break;
2990 }
2991
2992 default:
2993 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2994 getWMProtocols();
2995
2996 if ((decorations & Decor_Close) && (! frame.close_button)) {
2997 createCloseButton();
2998 if (decorations & Decor_Titlebar) {
2999 positionButtons(True);
3000 XMapSubwindows(blackbox->getXDisplay(), frame.title);
3001 }
3002 if (windowmenu) windowmenu->reconfigure();
3003 }
3004 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
3005 updateStrut();
3006 }
3007
3008 break;
3009 }
3010 }
3011
3012
3013 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
3014 #if 0
3015 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
3016 #endif
3017
3018 if (frame.label == ee->window && (decorations & Decor_Titlebar))
3019 redrawLabel();
3020 else if (frame.close_button == ee->window)
3021 redrawCloseButton(False);
3022 else if (frame.maximize_button == ee->window)
3023 redrawMaximizeButton(flags.maximized);
3024 else if (frame.iconify_button == ee->window)
3025 redrawIconifyButton(False);
3026 else if (frame.stick_button == ee->window)
3027 redrawStickyButton(flags.stuck);
3028 }
3029
3030
3031 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
3032 if (cr->window != client.window || flags.iconic)
3033 return;
3034
3035 if (cr->value_mask & CWBorderWidth)
3036 client.old_bw = cr->border_width;
3037
3038 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
3039 frame.changing = frame.rect;
3040
3041 if (cr->value_mask & (CWX | CWY)) {
3042 if (cr->value_mask & CWX)
3043 client.rect.setX(cr->x);
3044 if (cr->value_mask & CWY)
3045 client.rect.setY(cr->y);
3046
3047 applyGravity(frame.changing);
3048 }
3049
3050 if (cr->value_mask & (CWWidth | CWHeight)) {
3051 if (cr->value_mask & CWWidth)
3052 frame.changing.setWidth(cr->width +
3053 frame.margin.left + frame.margin.right);
3054
3055 if (cr->value_mask & CWHeight)
3056 frame.changing.setHeight(cr->height +
3057 frame.margin.top + frame.margin.bottom);
3058
3059 /*
3060 if a position change has been specified, then that position will be
3061 used instead of determining a position based on the window's gravity.
3062 */
3063 if (! (cr->value_mask & (CWX | CWY))) {
3064 Corner corner;
3065 switch (client.win_gravity) {
3066 case NorthEastGravity:
3067 case EastGravity:
3068 corner = TopRight;
3069 break;
3070 case SouthWestGravity:
3071 case SouthGravity:
3072 corner = BottomLeft;
3073 break;
3074 case SouthEastGravity:
3075 corner = BottomRight;
3076 break;
3077 default: // NorthWest, Static, etc
3078 corner = TopLeft;
3079 }
3080 constrain(corner);
3081 }
3082 }
3083
3084 configure(frame.changing.x(), frame.changing.y(),
3085 frame.changing.width(), frame.changing.height());
3086 }
3087
3088 if (cr->value_mask & CWStackMode && !isDesktop()) {
3089 switch (cr->detail) {
3090 case Below:
3091 case BottomIf:
3092 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3093 break;
3094
3095 case Above:
3096 case TopIf:
3097 default:
3098 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3099 break;
3100 }
3101 }
3102 }
3103
3104
3105 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
3106 #ifdef DEBUG
3107 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3108 client.window);
3109 #endif
3110
3111 if (frame.maximize_button == be->window && be->button <= 3) {
3112 redrawMaximizeButton(True);
3113 } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
3114 if (! flags.focused)
3115 setInputFocus();
3116
3117 if (frame.iconify_button == be->window) {
3118 redrawIconifyButton(True);
3119 } else if (frame.close_button == be->window) {
3120 redrawCloseButton(True);
3121 } else if (frame.stick_button == be->window) {
3122 redrawStickyButton(True);
3123 } else if (frame.plate == be->window) {
3124 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3125
3126 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3127
3128 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
3129 } else {
3130 if (frame.title == be->window || frame.label == be->window) {
3131 if (((be->time - lastButtonPressTime) <=
3132 blackbox->getDoubleClickInterval()) ||
3133 (be->state == ControlMask)) {
3134 lastButtonPressTime = 0;
3135 shade();
3136 } else {
3137 lastButtonPressTime = be->time;
3138 }
3139 }
3140
3141 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
3142
3143 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3144 }
3145 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3146 (be->window != frame.close_button) &&
3147 (be->window != frame.stick_button)) {
3148 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3149 } else if (windowmenu && be->button == 3 &&
3150 (frame.title == be->window || frame.label == be->window ||
3151 frame.handle == be->window || frame.window == be->window)) {
3152 if (windowmenu->isVisible()) {
3153 windowmenu->hide();
3154 } else {
3155 int mx = be->x_root - windowmenu->getWidth() / 2,
3156 my = be->y_root - windowmenu->getHeight() / 2;
3157
3158 // snap the window menu into a corner/side if necessary
3159 int left_edge, right_edge, top_edge, bottom_edge;
3160
3161 /*
3162 the " + (frame.border_w * 2) - 1" bits are to get the proper width
3163 and height of the menu, as the sizes returned by it do not include
3164 the borders.
3165 */
3166 left_edge = frame.rect.x();
3167 right_edge = frame.rect.right() -
3168 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
3169 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
3170 bottom_edge = client.rect.bottom() -
3171 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
3172 (frame.border_w + frame.mwm_border_w);
3173
3174 if (mx < left_edge)
3175 mx = left_edge;
3176 else if (mx > right_edge)
3177 mx = right_edge;
3178 if (my < top_edge)
3179 my = top_edge;
3180 else if (my > bottom_edge)
3181 my = bottom_edge;
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 assert(dw >= base_width && dh >= base_height);
4229
4230 if (client.width_inc > 1) {
4231 dw -= base_width;
4232 dw /= client.width_inc;
4233 }
4234 if (client.height_inc > 1) {
4235 dh -= base_height;
4236 dh /= client.height_inc;
4237 }
4238
4239 if (pw)
4240 *pw = dw;
4241
4242 if (ph)
4243 *ph = dh;
4244
4245 if (client.width_inc > 1) {
4246 dw *= client.width_inc;
4247 dw += base_width;
4248 }
4249 if (client.height_inc > 1) {
4250 dh *= client.height_inc;
4251 dh += base_height;
4252 }
4253
4254 frame.changing.setSize(dw, dh);
4255
4256 // add the frame margin back onto frame.changing
4257 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4258 frame.changing.top() - frame.margin.top,
4259 frame.changing.right() + frame.margin.right,
4260 frame.changing.bottom() + frame.margin.bottom);
4261
4262 // move frame.changing to the specified anchor
4263 int dx = 0,
4264 dy = 0;
4265 switch (anchor) {
4266 case TopLeft:
4267 break;
4268
4269 case TopRight:
4270 dx = frame.rect.right() - frame.changing.right();
4271 break;
4272
4273 case BottomLeft:
4274 dy = frame.rect.bottom() - frame.changing.bottom();
4275 break;
4276
4277 case BottomRight:
4278 dx = frame.rect.right() - frame.changing.right();
4279 dy = frame.rect.bottom() - frame.changing.bottom();
4280 break;
4281
4282 default:
4283 assert(false); // unhandled corner
4284 }
4285 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4286 }
4287
4288
4289 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4290 unsigned int max_length,
4291 unsigned int modifier) const {
4292 size_t text_len = text.size();
4293 unsigned int length;
4294
4295 do {
4296 length = font->measureString(string(text, 0, text_len)) + modifier;
4297 } while (length > max_length && text_len-- > 0);
4298
4299 switch (justify) {
4300 case RightJustify:
4301 start_pos += max_length - length;
4302 break;
4303
4304 case CenterJustify:
4305 start_pos += (max_length - length) / 2;
4306 break;
4307
4308 case LeftJustify:
4309 default:
4310 break;
4311 }
4312 }
4313
4314
4315 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4316 : blackbox(b), group(_group) {
4317 XWindowAttributes wattrib;
4318 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4319 // group window doesn't seem to exist anymore
4320 delete this;
4321 return;
4322 }
4323
4324 XSelectInput(blackbox->getXDisplay(), group,
4325 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4326
4327 blackbox->saveGroupSearch(group, this);
4328 }
4329
4330
4331 BWindowGroup::~BWindowGroup(void) {
4332 blackbox->removeGroupSearch(group);
4333 }
4334
4335
4336 BlackboxWindow *
4337 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4338 BlackboxWindow *ret = blackbox->getFocusedWindow();
4339
4340 // does the focus window match (or any transient_fors)?
4341 for (; ret; ret = ret->getTransientFor()) {
4342 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4343 (! ret->isTransient() || allow_transients))
4344 break;
4345 }
4346
4347 if (ret) return ret;
4348
4349 // the focus window didn't match, look in the group's window list
4350 BlackboxWindowList::const_iterator it, end = windowList.end();
4351 for (it = windowList.begin(); it != end; ++it) {
4352 ret = *it;
4353 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4354 (! ret->isTransient() || allow_transients))
4355 break;
4356 }
4357
4358 return ret;
4359 }
This page took 0.255388 seconds and 4 git commands to generate.