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>
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
34 #endif // HAVE_STRING_H
39 # endif // HAVE_STDIO_H
44 #endif // HAVE_STDLIB_H
48 #include "blackbox.hh"
49 #include "Clientmenu.hh"
52 #include "Iconmenu.hh"
58 #include "Windowmenu.hh"
59 #include "Workspace.hh"
64 // change this to change what modifier keys openbox uses for mouse bindings
65 // for example: Mod1Mask | ControlMask
66 // or: ControlMask| ShiftMask
67 const unsigned int ModMask = Mod1Mask;
70 * Initializes the class with default values/the window's set initial values.
72 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
73 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
74 // sizeof(BlackboxWindow));
77 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
81 set timer to zero... it is initialized properly later, so we check
82 if timer is zero in the destructor, and assume that the window is not
83 fully constructed if timer is zero...
89 xatom = blackbox->getXAtom();
91 if (! validateClient()) {
96 // fetch client size and placement
97 XWindowAttributes wattrib;
98 if (! XGetWindowAttributes(blackbox->getXDisplay(),
99 client.window, &wattrib) ||
100 ! wattrib.screen || wattrib.override_redirect) {
103 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
111 fprintf(stderr, "0x%lx: initial (%d, %d) w: %d, h: %d\n", client.window,
112 wattrib.x, wattrib.y, wattrib.width, wattrib.height);
115 // set the eventmask early in the game so that we make sure we get
116 // all the events we are interested in
117 XSetWindowAttributes attrib_set;
118 attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
120 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
122 XChangeWindowAttributes(blackbox->getXDisplay(), client.window,
123 CWEventMask|CWDontPropagate, &attrib_set);
125 flags.moving = flags.resizing = flags.shaded = flags.visible =
126 flags.iconic = flags.focused = flags.stuck = flags.modal =
127 flags.send_focus_message = flags.shaped = flags.skip_taskbar =
128 flags.skip_pager = flags.fullscreen = False;
131 blackbox_attrib.workspace = window_number = BSENTINEL;
133 blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack
134 = blackbox_attrib.decoration = 0l;
135 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
136 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
139 frame.window = frame.plate = frame.title = frame.handle = None;
140 frame.close_button = frame.iconify_button = frame.maximize_button = None;
141 frame.right_grip = frame.left_grip = None;
143 frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
144 frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
145 frame.ubutton_pixel = frame.fbutton_pixel = frame.pbutton_pixel =
146 frame.uborder_pixel = frame.fborder_pixel = frame.ugrip_pixel =
147 frame.fgrip_pixel = 0;
148 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
149 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
150 frame.pbutton = frame.ugrip = frame.fgrip = None;
152 decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
153 Decor_Iconify | Decor_Maximize;
154 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
156 client.normal_hint_flags = 0;
157 client.window_group = None;
158 client.transient_for = 0;
160 current_state = NormalState;
163 get the initial size and location of client window (relative to the
164 _root window_). This position is the reference point used with the
165 window's gravity to find the window's initial position.
167 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
168 client.old_bw = wattrib.border_width;
171 lastButtonPressTime = 0;
173 timer = new BTimer(blackbox, this);
174 timer->setTimeout(blackbox->getAutoRaiseDelay());
176 if (! getBlackboxHints()) {
181 // get size, aspect, minimum/maximum size and other hints set by the
188 fprintf(stderr, "0x%lx: after hints (%d, %d) w: %d, h: %d\n", client.window,
189 client.rect.x(), client.rect.y(),
190 client.rect.width(), client.rect.height());
193 frame.window = createToplevelWindow();
194 frame.plate = createChildWindow(frame.window);
195 associateClientWindow();
197 blackbox->saveWindowSearch(frame.window, this);
198 blackbox->saveWindowSearch(frame.plate, this);
199 blackbox->saveWindowSearch(client.window, this);
201 // determine if this is a transient window
204 // determine the window's type, so we can decide its decorations and
205 // functionality, or if we should not manage it at all
208 // adjust the window decorations/behavior based on the window type
209 switch (window_type) {
216 // none of these windows are decorated or manipulated by the window manager
219 blackbox_attrib.workspace = 0; // we do need to belong to a workspace
220 flags.stuck = True; // we show up on all workspaces
224 // dialogs cannot be maximized, and don't display a handle
225 decorations &= ~(Decor_Maximize | Decor_Handle);
226 functions &= ~Func_Maximize;
230 // normal windows retain all of the possible decorations and functionality
234 // further adjeust the window's decorations/behavior based on window sizes
235 if ((client.normal_hint_flags & PMinSize) &&
236 (client.normal_hint_flags & PMaxSize) &&
237 client.max_width <= client.min_width &&
238 client.max_height <= client.min_height) {
239 decorations &= ~(Decor_Maximize | Decor_Handle);
240 functions &= ~(Func_Resize | Func_Maximize);
245 fprintf(stderr, "0x%lx: sizes reflect the frame from now on\n",
247 fprintf(stderr, "0x%lx: after upsize (%d, %d) w: %d, h: %d\n", client.window,
248 frame.rect.x(), frame.rect.y(),
249 frame.rect.width(), frame.rect.height());
254 bool place_window = True;
255 if (blackbox->isStartup() || isTransient() ||
256 client.normal_hint_flags & (PPosition|USPosition)) {
257 applyGravity(frame.rect);
259 if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
260 place_window = False;
264 fprintf(stderr, "0x%lx: after gravity (%d, %d) w: %d, h: %d\n",
266 frame.rect.x(), frame.rect.y(),
267 frame.rect.width(), frame.rect.height());
270 // add the window's strut. note this is done *after* placing the window.
271 screen->addStrut(&client.strut);
274 if (decorations & Decor_Titlebar)
277 if (decorations & Decor_Handle)
281 if (blackbox->hasShapeExtensions() && flags.shaped) {
286 windowmenu = new Windowmenu(this);
288 if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
289 screen->getCurrentWorkspace()->addWindow(this, place_window);
291 screen->getWorkspace(blackbox_attrib.workspace)->
292 addWindow(this, place_window);
294 if (! place_window) {
295 // don't need to call configure if we are letting the workspace
297 configure(frame.rect.x(), frame.rect.y(),
298 frame.rect.width(), frame.rect.height());
301 fprintf(stderr, "0x%lx: after configure (%d, %d) w: %d, h: %d\n",
303 frame.rect.x(), frame.rect.y(),
304 frame.rect.width(), frame.rect.height());
308 // preserve the window's initial state on first map, and its current state
310 unsigned long initial_state = current_state;
312 current_state = initial_state;
314 // get sticky state from our parent window if we've got one
315 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
316 client.transient_for->isStuck() != flags.stuck)
320 flags.shaded = False;
321 initial_state = current_state;
325 At this point in the life of a window, current_state should only be set
326 to IconicState if the window was an *icon*, not if it was shaded.
328 if (initial_state != IconicState)
329 current_state = NormalState;
337 if (flags.maximized && (functions & Func_Maximize))
341 When the window is mapped (and also when its attributes are restored), the
342 current_state that was set here will be used.
343 It is set to Normal if the window is to be mapped or it is set to Iconic
344 if the window is to be iconified.
345 *Note* that for sticky windows, the same rules apply here, they are in
346 fact never set to Iconic since there is no way for us to tell if a sticky
347 window was iconified previously.
354 XMapSubwindows(blackbox->getXDisplay(), frame.window);
359 fprintf(stderr, "0x%lx: end of constructor (%d, %d) w: %d, h: %d\n",
361 frame.rect.x(), frame.rect.y(),
362 frame.rect.width(), frame.rect.height());
367 BlackboxWindow::~BlackboxWindow(void) {
369 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
373 if (! timer) // window not managed...
376 screen->removeStrut(&client.strut);
377 screen->updateAvailableArea();
379 // We don't need to worry about resizing because resizing always grabs the X
380 // server. This should only ever happen if using opaque moving.
388 if (client.window_group) {
389 BWindowGroup *group = blackbox->searchGroup(client.window_group);
390 if (group) group->removeWindow(this);
393 // remove ourselves from our transient_for
395 if (client.transient_for != (BlackboxWindow *) ~0ul) {
396 client.transient_for->client.transientList.remove(this);
398 client.transient_for = (BlackboxWindow*) 0;
401 if (client.transientList.size() > 0) {
402 // reset transient_for for all transients
403 BlackboxWindowList::iterator it, end = client.transientList.end();
404 for (it = client.transientList.begin(); it != end; ++it) {
405 (*it)->client.transient_for = (BlackboxWindow*) 0;
416 blackbox->removeWindowSearch(frame.plate);
417 XDestroyWindow(blackbox->getXDisplay(), frame.plate);
421 blackbox->removeWindowSearch(frame.window);
422 XDestroyWindow(blackbox->getXDisplay(), frame.window);
425 blackbox->removeWindowSearch(client.window);
430 * Creates a new top level window, with a given location, size, and border
432 * Returns: the newly created window
434 Window BlackboxWindow::createToplevelWindow(void) {
435 XSetWindowAttributes attrib_create;
436 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
437 CWOverrideRedirect | CWEventMask;
439 attrib_create.background_pixmap = None;
440 attrib_create.colormap = screen->getColormap();
441 attrib_create.override_redirect = True;
442 attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
443 ButtonMotionMask | EnterWindowMask;
445 return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
446 0, 0, 1, 1, frame.border_w, screen->getDepth(),
447 InputOutput, screen->getVisual(), create_mask,
453 * Creates a child window, and optionally associates a given cursor with
456 Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) {
457 XSetWindowAttributes attrib_create;
458 unsigned long create_mask = CWBackPixmap | CWBorderPixel |
461 attrib_create.background_pixmap = None;
462 attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
463 ButtonMotionMask | ExposureMask;
466 create_mask |= CWCursor;
467 attrib_create.cursor = cursor;
470 return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0,
471 screen->getDepth(), InputOutput, screen->getVisual(),
472 create_mask, &attrib_create);
476 void BlackboxWindow::associateClientWindow(void) {
477 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0);
481 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert);
483 XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
485 XGrabServer(blackbox->getXDisplay());
487 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
489 XSelectInput(blackbox->getXDisplay(), client.window,
490 event_mask & ~StructureNotifyMask);
491 XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
492 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
494 XUngrabServer(blackbox->getXDisplay());
496 XRaiseWindow(blackbox->getXDisplay(), frame.plate);
497 XMapSubwindows(blackbox->getXDisplay(), frame.plate);
501 if (blackbox->hasShapeExtensions()) {
502 XShapeSelectInput(blackbox->getXDisplay(), client.window,
509 XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped,
510 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
512 flags.shaped = shaped;
518 void BlackboxWindow::decorate(void) {
521 texture = &(screen->getWindowStyle()->b_focus);
522 frame.fbutton = texture->render(frame.button_w, frame.button_w,
525 frame.fbutton_pixel = texture->color().pixel();
527 texture = &(screen->getWindowStyle()->b_unfocus);
528 frame.ubutton = texture->render(frame.button_w, frame.button_w,
531 frame.ubutton_pixel = texture->color().pixel();
533 texture = &(screen->getWindowStyle()->b_pressed);
534 frame.pbutton = texture->render(frame.button_w, frame.button_w,
537 frame.pbutton_pixel = texture->color().pixel();
539 if (decorations & Decor_Titlebar) {
540 texture = &(screen->getWindowStyle()->t_focus);
541 frame.ftitle = texture->render(frame.inside_w, frame.title_h,
544 frame.ftitle_pixel = texture->color().pixel();
546 texture = &(screen->getWindowStyle()->t_unfocus);
547 frame.utitle = texture->render(frame.inside_w, frame.title_h,
550 frame.utitle_pixel = texture->color().pixel();
552 XSetWindowBorder(blackbox->getXDisplay(), frame.title,
553 screen->getBorderColor()->pixel());
558 if (decorations & Decor_Border) {
559 frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel();
560 frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel();
561 blackbox_attrib.flags |= AttribDecoration;
562 blackbox_attrib.decoration = DecorNormal;
564 blackbox_attrib.flags |= AttribDecoration;
565 blackbox_attrib.decoration = DecorNone;
568 if (decorations & Decor_Handle) {
569 texture = &(screen->getWindowStyle()->h_focus);
570 frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
573 frame.fhandle_pixel = texture->color().pixel();
575 texture = &(screen->getWindowStyle()->h_unfocus);
576 frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
579 frame.uhandle_pixel = texture->color().pixel();
581 texture = &(screen->getWindowStyle()->g_focus);
582 frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
584 frame.fgrip_pixel = texture->color().pixel();
586 texture = &(screen->getWindowStyle()->g_unfocus);
587 frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
589 frame.ugrip_pixel = texture->color().pixel();
591 XSetWindowBorder(blackbox->getXDisplay(), frame.handle,
592 screen->getBorderColor()->pixel());
593 XSetWindowBorder(blackbox->getXDisplay(), frame.left_grip,
594 screen->getBorderColor()->pixel());
595 XSetWindowBorder(blackbox->getXDisplay(), frame.right_grip,
596 screen->getBorderColor()->pixel());
599 XSetWindowBorder(blackbox->getXDisplay(), frame.window,
600 screen->getBorderColor()->pixel());
604 void BlackboxWindow::decorateLabel(void) {
607 texture = &(screen->getWindowStyle()->l_focus);
608 frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
610 frame.flabel_pixel = texture->color().pixel();
612 texture = &(screen->getWindowStyle()->l_unfocus);
613 frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
615 frame.ulabel_pixel = texture->color().pixel();
619 void BlackboxWindow::createHandle(void) {
620 frame.handle = createChildWindow(frame.window);
621 blackbox->saveWindowSearch(frame.handle, this);
624 createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor());
625 blackbox->saveWindowSearch(frame.left_grip, this);
628 createChildWindow(frame.handle, blackbox->getLowerRightAngleCursor());
629 blackbox->saveWindowSearch(frame.right_grip, this);
633 void BlackboxWindow::destroyHandle(void) {
635 screen->getImageControl()->removeImage(frame.fhandle);
638 screen->getImageControl()->removeImage(frame.uhandle);
641 screen->getImageControl()->removeImage(frame.fgrip);
644 screen->getImageControl()->removeImage(frame.ugrip);
646 blackbox->removeWindowSearch(frame.left_grip);
647 blackbox->removeWindowSearch(frame.right_grip);
649 XDestroyWindow(blackbox->getXDisplay(), frame.left_grip);
650 XDestroyWindow(blackbox->getXDisplay(), frame.right_grip);
651 frame.left_grip = frame.right_grip = None;
653 blackbox->removeWindowSearch(frame.handle);
654 XDestroyWindow(blackbox->getXDisplay(), frame.handle);
659 void BlackboxWindow::createTitlebar(void) {
660 frame.title = createChildWindow(frame.window);
661 frame.label = createChildWindow(frame.title);
662 blackbox->saveWindowSearch(frame.title, this);
663 blackbox->saveWindowSearch(frame.label, this);
665 if (decorations & Decor_Iconify) createIconifyButton();
666 if (decorations & Decor_Maximize) createMaximizeButton();
667 if (decorations & Decor_Close) createCloseButton();
671 void BlackboxWindow::destroyTitlebar(void) {
672 if (frame.close_button)
673 destroyCloseButton();
675 if (frame.iconify_button)
676 destroyIconifyButton();
678 if (frame.maximize_button)
679 destroyMaximizeButton();
682 screen->getImageControl()->removeImage(frame.ftitle);
685 screen->getImageControl()->removeImage(frame.utitle);
688 screen->getImageControl()->removeImage(frame.flabel);
691 screen->getImageControl()->removeImage(frame.ulabel);
694 screen->getImageControl()->removeImage(frame.fbutton);
697 screen->getImageControl()->removeImage(frame.ubutton);
700 screen->getImageControl()->removeImage(frame.pbutton);
702 blackbox->removeWindowSearch(frame.title);
703 blackbox->removeWindowSearch(frame.label);
705 XDestroyWindow(blackbox->getXDisplay(), frame.label);
706 XDestroyWindow(blackbox->getXDisplay(), frame.title);
707 frame.title = frame.label = None;
711 void BlackboxWindow::createCloseButton(void) {
712 if (frame.title != None) {
713 frame.close_button = createChildWindow(frame.title);
714 blackbox->saveWindowSearch(frame.close_button, this);
719 void BlackboxWindow::destroyCloseButton(void) {
720 blackbox->removeWindowSearch(frame.close_button);
721 XDestroyWindow(blackbox->getXDisplay(), frame.close_button);
722 frame.close_button = None;
726 void BlackboxWindow::createIconifyButton(void) {
727 if (frame.title != None) {
728 frame.iconify_button = createChildWindow(frame.title);
729 blackbox->saveWindowSearch(frame.iconify_button, this);
734 void BlackboxWindow::destroyIconifyButton(void) {
735 blackbox->removeWindowSearch(frame.iconify_button);
736 XDestroyWindow(blackbox->getXDisplay(), frame.iconify_button);
737 frame.iconify_button = None;
741 void BlackboxWindow::createMaximizeButton(void) {
742 if (frame.title != None) {
743 frame.maximize_button = createChildWindow(frame.title);
744 blackbox->saveWindowSearch(frame.maximize_button, this);
749 void BlackboxWindow::destroyMaximizeButton(void) {
750 blackbox->removeWindowSearch(frame.maximize_button);
751 XDestroyWindow(blackbox->getXDisplay(), frame.maximize_button);
752 frame.maximize_button = None;
756 void BlackboxWindow::positionButtons(bool redecorate_label) {
757 string layout = blackbox->getTitlebarLayout();
760 bool hasclose, hasiconify, hasmaximize, haslabel;
761 hasclose = hasiconify = hasmaximize = haslabel = false;
763 string::const_iterator it, end;
764 for (it = layout.begin(), end = layout.end(); it != end; ++it) {
767 if (! hasclose && (decorations & Decor_Close)) {
773 if (! hasiconify && (decorations & Decor_Iconify)) {
779 if (! hasmaximize && (decorations & Decor_Maximize)) {
791 if (! hasclose && frame.close_button)
792 destroyCloseButton();
793 if (! hasiconify && frame.iconify_button)
794 destroyIconifyButton();
795 if (! hasmaximize && frame.maximize_button)
796 destroyMaximizeButton();
798 parsed += 'L'; // require that the label be in the layout
800 const unsigned int bsep = frame.bevel_w + 1; // separation between elements
801 const unsigned int by = frame.bevel_w + 1;
802 const unsigned int ty = frame.bevel_w;
804 frame.label_w = frame.inside_w - bsep * 2 -
805 (frame.button_w + bsep) * (parsed.size() - 1);
807 unsigned int x = bsep;
808 for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
811 if (! frame.close_button) createCloseButton();
812 XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
813 frame.button_w, frame.button_w);
814 x += frame.button_w + bsep;
817 if (! frame.iconify_button) createIconifyButton();
818 XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
819 frame.button_w, frame.button_w);
820 x += frame.button_w + bsep;
823 if (! frame.maximize_button) createMaximizeButton();
824 XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
825 frame.button_w, frame.button_w);
826 x += frame.button_w + bsep;
829 XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
830 frame.label_w, frame.label_h);
831 x += frame.label_w + bsep;
836 if (redecorate_label) decorateLabel();
842 void BlackboxWindow::reconfigure(void) {
843 restoreGravity(client.rect);
845 applyGravity(frame.rect);
854 windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
855 windowmenu->reconfigure();
860 void BlackboxWindow::grabButtons(void) {
861 if (! screen->isSloppyFocus() || screen->doClickRaise())
862 // grab button 1 for changing focus/raising
863 blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
864 GrabModeSync, GrabModeSync, frame.plate, None,
865 screen->allowScrollLock());
867 if (functions & Func_Move)
868 blackbox->grabButton(Button1, ModMask, frame.window, True,
869 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
870 GrabModeAsync, frame.window, None,
871 screen->allowScrollLock());
872 if (functions & Func_Resize)
873 blackbox->grabButton(Button3, ModMask, frame.window, True,
874 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
875 GrabModeAsync, frame.window, None,
876 screen->allowScrollLock());
877 // alt+middle lowers the window
878 blackbox->grabButton(Button2, ModMask, frame.window, True,
879 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
881 screen->allowScrollLock());
885 void BlackboxWindow::ungrabButtons(void) {
886 if (! screen->isSloppyFocus() || screen->doClickRaise())
887 blackbox->ungrabButton(Button1, 0, frame.plate);
889 blackbox->ungrabButton(Button1, ModMask, frame.window);
890 blackbox->ungrabButton(Button2, ModMask, frame.window);
891 blackbox->ungrabButton(Button3, ModMask, frame.window);
895 void BlackboxWindow::positionWindows(void) {
896 XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
897 frame.rect.x(), frame.rect.y(), frame.inside_w,
898 (flags.shaded) ? frame.title_h : frame.inside_h);
899 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
901 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
903 XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
904 frame.margin.left - frame.mwm_border_w - frame.border_w,
905 frame.margin.top - frame.mwm_border_w - frame.border_w,
906 client.rect.width(), client.rect.height());
907 XMoveResizeWindow(blackbox->getXDisplay(), client.window,
908 0, 0, client.rect.width(), client.rect.height());
909 // ensure client.rect contains the real location
910 client.rect.setPos(frame.rect.left() + frame.margin.left,
911 frame.rect.top() + frame.margin.top);
913 if (decorations & Decor_Titlebar) {
914 if (frame.title == None) createTitlebar();
916 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.title,
918 XMoveResizeWindow(blackbox->getXDisplay(), frame.title, -frame.border_w,
919 -frame.border_w, frame.inside_w, frame.title_h);
922 XMapSubwindows(blackbox->getXDisplay(), frame.title);
923 XMapWindow(blackbox->getXDisplay(), frame.title);
924 } else if (frame.title) {
927 if (decorations & Decor_Handle) {
928 if (frame.handle == None) createHandle();
929 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.handle,
931 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.left_grip,
933 XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
936 // use client.rect here so the value is correct even if shaded
937 XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
939 client.rect.height() + frame.margin.top +
940 frame.mwm_border_w - frame.border_w,
941 frame.inside_w, frame.handle_h);
942 XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
943 -frame.border_w, -frame.border_w,
944 frame.grip_w, frame.handle_h);
945 XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
946 frame.inside_w - frame.grip_w - frame.border_w,
947 -frame.border_w, frame.grip_w, frame.handle_h);
949 XMapSubwindows(blackbox->getXDisplay(), frame.handle);
950 XMapWindow(blackbox->getXDisplay(), frame.handle);
951 } else if (frame.handle) {
954 XSync(blackbox->getXDisplay(), False);
958 void BlackboxWindow::updateStrut(void) {
959 unsigned long num = 4;
961 if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
966 client.strut.left = data[0];
967 client.strut.right = data[1];
968 client.strut.top = data[2];
969 client.strut.bottom = data[3];
971 screen->updateAvailableArea();
978 void BlackboxWindow::getWindowType(void) {
980 if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
982 if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop))
983 window_type = Type_Desktop;
984 else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock))
985 window_type = Type_Dock;
986 else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
987 window_type = Type_Toolbar;
988 else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu))
989 window_type = Type_Menu;
990 else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility))
991 window_type = Type_Utility;
992 else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash))
993 window_type = Type_Splash;
994 else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog))
995 window_type = Type_Dialog;
996 else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal))
997 window_type = Type_Normal;
1002 * the window type hint was not set, which means we either classify ourself
1003 * as a normal window or a dialog, depending on if we are a transient.
1006 window_type = Type_Dialog;
1008 window_type = Type_Normal;
1012 void BlackboxWindow::getWMName(void) {
1013 if (xatom->getValue(client.window, XAtom::net_wm_name,
1014 XAtom::utf8, client.title) &&
1015 !client.title.empty()) {
1016 xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1019 //fall through to using WM_NAME
1020 if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
1021 && !client.title.empty()) {
1022 xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
1025 // fall back to an internal default
1026 client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
1027 xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
1032 void BlackboxWindow::getWMIconName(void) {
1033 if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
1034 XAtom::utf8, client.icon_title) &&
1035 !client.icon_title.empty()) {
1036 xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1039 //fall through to using WM_ICON_NAME
1040 if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
1041 client.icon_title) &&
1042 !client.icon_title.empty()) {
1043 xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
1046 // fall back to using the main name
1047 client.icon_title = client.title;
1048 xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
1054 * Retrieve which WM Protocols are supported by the client window.
1055 * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1056 * window's decorations and allow the close behavior.
1057 * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1060 void BlackboxWindow::getWMProtocols(void) {
1064 if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
1065 &proto, &num_return)) {
1066 for (int i = 0; i < num_return; ++i) {
1067 if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
1068 decorations |= Decor_Close;
1069 functions |= Func_Close;
1070 } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
1071 flags.send_focus_message = True;
1072 else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
1073 screen->addNetizen(new Netizen(screen, client.window));
1082 * Gets the value of the WM_HINTS property.
1083 * If the property is not set, then use a set of default values.
1085 void BlackboxWindow::getWMHints(void) {
1086 focus_mode = F_Passive;
1088 // remove from current window group
1089 if (client.window_group) {
1090 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1091 if (group) group->removeWindow(this);
1093 client.window_group = None;
1095 XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1100 if (wmhint->flags & InputHint) {
1101 if (wmhint->input == True) {
1102 if (flags.send_focus_message)
1103 focus_mode = F_LocallyActive;
1105 if (flags.send_focus_message)
1106 focus_mode = F_GloballyActive;
1108 focus_mode = F_NoInput;
1112 if (wmhint->flags & StateHint)
1113 current_state = wmhint->initial_state;
1115 if (wmhint->flags & WindowGroupHint) {
1116 client.window_group = wmhint->window_group;
1118 // add window to the appropriate group
1119 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1120 if (! group) { // no group found, create it!
1121 new BWindowGroup(blackbox, client.window_group);
1122 group = blackbox->searchGroup(client.window_group);
1125 group->addWindow(this);
1133 * Gets the value of the WM_NORMAL_HINTS property.
1134 * If the property is not set, then use a set of default values.
1136 void BlackboxWindow::getWMNormalHints(void) {
1138 XSizeHints sizehint;
1140 client.min_width = client.min_height =
1141 client.width_inc = client.height_inc = 1;
1142 client.base_width = client.base_height = 0;
1143 client.win_gravity = NorthWestGravity;
1145 client.min_aspect_x = client.min_aspect_y =
1146 client.max_aspect_x = client.max_aspect_y = 1;
1150 use the full screen, not the strut modified size. otherwise when the
1151 availableArea changes max_width/height will be incorrect and lead to odd
1154 const Rect& screen_area = screen->getRect();
1155 client.max_width = screen_area.width();
1156 client.max_height = screen_area.height();
1158 if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1159 &sizehint, &icccm_mask))
1162 client.normal_hint_flags = sizehint.flags;
1164 if (sizehint.flags & PMinSize) {
1165 if (sizehint.min_width >= 0)
1166 client.min_width = sizehint.min_width;
1167 if (sizehint.min_height >= 0)
1168 client.min_height = sizehint.min_height;
1171 if (sizehint.flags & PMaxSize) {
1172 if (sizehint.max_width > static_cast<signed>(client.min_width))
1173 client.max_width = sizehint.max_width;
1175 client.max_width = client.min_width;
1177 if (sizehint.max_height > static_cast<signed>(client.min_height))
1178 client.max_height = sizehint.max_height;
1180 client.max_height = client.min_height;
1183 if (sizehint.flags & PResizeInc) {
1184 client.width_inc = sizehint.width_inc;
1185 client.height_inc = sizehint.height_inc;
1188 #if 0 // we do not support this at the moment
1189 if (sizehint.flags & PAspect) {
1190 client.min_aspect_x = sizehint.min_aspect.x;
1191 client.min_aspect_y = sizehint.min_aspect.y;
1192 client.max_aspect_x = sizehint.max_aspect.x;
1193 client.max_aspect_y = sizehint.max_aspect.y;
1197 if (sizehint.flags & PBaseSize) {
1198 client.base_width = sizehint.base_width;
1199 client.base_height = sizehint.base_height;
1202 if (sizehint.flags & PWinGravity)
1203 client.win_gravity = sizehint.win_gravity;
1208 * Gets the NETWM hints for the class' contained window.
1210 void BlackboxWindow::getNetWMHints(void) {
1211 unsigned long workspace;
1213 if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1215 if (workspace == 0xffffffff)
1218 blackbox_attrib.workspace = workspace;
1221 unsigned long *state;
1222 unsigned long num = (unsigned) -1;
1223 if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1227 for (unsigned long i = 0; i < num; ++i) {
1228 if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1230 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1231 flags.shaded = True;
1232 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1233 flags.skip_taskbar = True;
1234 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1235 flags.skip_pager = True;
1236 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1237 flags.fullscreen = True;
1238 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1239 setState(IconicState);
1240 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1242 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1246 flags.maximized = 1;
1248 flags.maximized = 2;
1250 flags.maximized = 3;
1258 * Gets the MWM hints for the class' contained window.
1259 * This is used while initializing the window to its first state, and not
1261 * Returns: true if the MWM hints are successfully retreived and applied;
1262 * false if they are not.
1264 void BlackboxWindow::getMWMHints(void) {
1268 num = PropMwmHintsElements;
1269 if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1270 XAtom::motif_wm_hints, num,
1271 (unsigned long **)&mwm_hint))
1273 if (num < PropMwmHintsElements) {
1278 if (mwm_hint->flags & MwmHintsDecorations) {
1279 if (mwm_hint->decorations & MwmDecorAll) {
1280 decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1281 Decor_Iconify | Decor_Maximize | Decor_Close;
1285 if (mwm_hint->decorations & MwmDecorBorder)
1286 decorations |= Decor_Border;
1287 if (mwm_hint->decorations & MwmDecorHandle)
1288 decorations |= Decor_Handle;
1289 if (mwm_hint->decorations & MwmDecorTitle)
1290 decorations |= Decor_Titlebar;
1291 if (mwm_hint->decorations & MwmDecorIconify)
1292 decorations |= Decor_Iconify;
1293 if (mwm_hint->decorations & MwmDecorMaximize)
1294 decorations |= Decor_Maximize;
1298 if (mwm_hint->flags & MwmHintsFunctions) {
1299 if (mwm_hint->functions & MwmFuncAll) {
1300 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1305 if (mwm_hint->functions & MwmFuncResize)
1306 functions |= Func_Resize;
1307 if (mwm_hint->functions & MwmFuncMove)
1308 functions |= Func_Move;
1309 if (mwm_hint->functions & MwmFuncIconify)
1310 functions |= Func_Iconify;
1311 if (mwm_hint->functions & MwmFuncMaximize)
1312 functions |= Func_Maximize;
1313 if (mwm_hint->functions & MwmFuncClose)
1314 functions |= Func_Close;
1322 * Gets the blackbox hints from the class' contained window.
1323 * This is used while initializing the window to its first state, and not
1325 * Returns: true if the hints are successfully retreived and applied; false if
1328 bool BlackboxWindow::getBlackboxHints(void) {
1330 BlackboxHints *blackbox_hint;
1332 num = PropBlackboxHintsElements;
1333 if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1334 XAtom::blackbox_hints, num,
1335 (unsigned long **)&blackbox_hint))
1337 if (num < PropBlackboxHintsElements) {
1338 delete [] blackbox_hint;
1342 if (blackbox_hint->flags & AttribShaded)
1343 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1345 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1346 (blackbox_hint->flags & AttribMaxVert))
1347 flags.maximized = (blackbox_hint->attrib &
1348 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1349 else if (blackbox_hint->flags & AttribMaxVert)
1350 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1351 else if (blackbox_hint->flags & AttribMaxHoriz)
1352 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1354 if (blackbox_hint->flags & AttribOmnipresent)
1355 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1357 if (blackbox_hint->flags & AttribWorkspace)
1358 blackbox_attrib.workspace = blackbox_hint->workspace;
1360 // if (blackbox_hint->flags & AttribStack)
1361 // don't yet have always on top/bottom for blackbox yet... working
1364 if (blackbox_hint->flags & AttribDecoration) {
1365 switch (blackbox_hint->decoration) {
1371 decorations |= Decor_Titlebar | Decor_Iconify;
1372 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1373 functions &= ~(Func_Resize | Func_Maximize);
1378 decorations |= Decor_Titlebar;
1379 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1380 functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1386 decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1387 Decor_Iconify | Decor_Maximize;
1394 delete [] blackbox_hint;
1400 void BlackboxWindow::getTransientInfo(void) {
1401 if (client.transient_for &&
1402 client.transient_for != (BlackboxWindow *) ~0ul) {
1403 // reset transient_for in preparation of looking for a new owner
1404 client.transient_for->client.transientList.remove(this);
1407 // we have no transient_for until we find a new one
1408 client.transient_for = 0;
1411 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1413 // transient_for hint not set
1417 if (trans_for == client.window) {
1418 // wierd client... treat this window as a normal window
1422 if (trans_for == None || trans_for == screen->getRootWindow()) {
1423 // this is an undocumented interpretation of the ICCCM. a transient
1424 // associated with None/Root/itself is assumed to be a modal root
1425 // transient. we don't support the concept of a global transient,
1426 // so we just associate this transient with nothing, and perhaps
1427 // we will add support later for global modality.
1428 client.transient_for = (BlackboxWindow *) ~0ul;
1433 client.transient_for = blackbox->searchWindow(trans_for);
1434 if (! client.transient_for &&
1435 client.window_group && trans_for == client.window_group) {
1436 // no direct transient_for, perhaps this is a group transient?
1437 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1438 if (group) client.transient_for = group->find(screen);
1441 if (! client.transient_for || client.transient_for == this) {
1442 // no transient_for found, or we have a wierd client that wants to be
1443 // a transient for itself, so we treat this window as a normal window
1444 client.transient_for = (BlackboxWindow*) 0;
1448 // register ourselves with our new transient_for
1449 client.transient_for->client.transientList.push_back(this);
1450 flags.stuck = client.transient_for->flags.stuck;
1454 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1455 if (client.transient_for &&
1456 client.transient_for != (BlackboxWindow*) ~0ul)
1457 return client.transient_for;
1463 * This function is responsible for updating both the client and the frame
1465 * According to the ICCCM a client message is not sent for a resize, only a
1468 void BlackboxWindow::configure(int dx, int dy,
1469 unsigned int dw, unsigned int dh) {
1470 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1473 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1474 frame.rect.setRect(dx, dy, dw, dh);
1475 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1476 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1478 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1479 frame.rect.setPos(0, 0);
1481 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1482 frame.rect.top() + frame.margin.top,
1483 frame.rect.right() - frame.margin.right,
1484 frame.rect.bottom() - frame.margin.bottom);
1487 if (blackbox->hasShapeExtensions() && flags.shaped) {
1494 redrawWindowFrame();
1496 frame.rect.setPos(dx, dy);
1498 XMoveWindow(blackbox->getXDisplay(), frame.window,
1499 frame.rect.x(), frame.rect.y());
1501 we may have been called just after an opaque window move, so even though
1502 the old coords match the new ones no ConfigureNotify has been sent yet.
1503 There are likely other times when this will be relevant as well.
1505 if (! flags.moving) send_event = True;
1509 // if moving, the update and event will occur when the move finishes
1510 client.rect.setPos(frame.rect.left() + frame.margin.left,
1511 frame.rect.top() + frame.margin.top);
1514 event.type = ConfigureNotify;
1516 event.xconfigure.display = blackbox->getXDisplay();
1517 event.xconfigure.event = client.window;
1518 event.xconfigure.window = client.window;
1519 event.xconfigure.x = client.rect.x();
1520 event.xconfigure.y = client.rect.y();
1521 event.xconfigure.width = client.rect.width();
1522 event.xconfigure.height = client.rect.height();
1523 event.xconfigure.border_width = client.old_bw;
1524 event.xconfigure.above = frame.window;
1525 event.xconfigure.override_redirect = False;
1527 XSendEvent(blackbox->getXDisplay(), client.window, False,
1528 StructureNotifyMask, &event);
1529 screen->updateNetizenConfigNotify(&event);
1530 XFlush(blackbox->getXDisplay());
1536 void BlackboxWindow::configureShape(void) {
1537 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1538 frame.margin.left - frame.border_w,
1539 frame.margin.top - frame.border_w,
1540 client.window, ShapeBounding, ShapeSet);
1543 XRectangle xrect[2];
1545 if (decorations & Decor_Titlebar) {
1546 xrect[0].x = xrect[0].y = -frame.border_w;
1547 xrect[0].width = frame.rect.width();
1548 xrect[0].height = frame.title_h + (frame.border_w * 2);
1552 if (decorations & Decor_Handle) {
1553 xrect[1].x = -frame.border_w;
1554 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1555 frame.mwm_border_w - frame.border_w;
1556 xrect[1].width = frame.rect.width();
1557 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1561 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1562 ShapeBounding, 0, 0, xrect, num,
1563 ShapeUnion, Unsorted);
1568 bool BlackboxWindow::setInputFocus(void) {
1569 if (flags.focused) return True;
1571 assert(! flags.iconic &&
1572 (flags.stuck || // window must be on the current workspace or sticky
1573 blackbox_attrib.workspace == screen->getCurrentWorkspaceID()));
1576 We only do this check for normal windows and dialogs because other windows
1577 do this on purpose, such as kde's kicker, and we don't want to go moving
1580 if (window_type == Type_Normal || window_type == Type_Dialog)
1581 if (! frame.rect.intersects(screen->getRect())) {
1582 // client is outside the screen, move it to the center
1583 configure((screen->getWidth() - frame.rect.width()) / 2,
1584 (screen->getHeight() - frame.rect.height()) / 2,
1585 frame.rect.width(), frame.rect.height());
1588 if (client.transientList.size() > 0) {
1589 // transfer focus to any modal transients
1590 BlackboxWindowList::iterator it, end = client.transientList.end();
1591 for (it = client.transientList.begin(); it != end; ++it) {
1592 if ((*it)->flags.modal) return (*it)->setInputFocus();
1597 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1598 XSetInputFocus(blackbox->getXDisplay(), client.window,
1599 RevertToPointerRoot, CurrentTime);
1601 /* we could set the focus to none, since the window doesn't accept focus,
1602 * but we shouldn't set focus to nothing since this would surely make
1608 if (flags.send_focus_message) {
1610 ce.xclient.type = ClientMessage;
1611 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1612 ce.xclient.display = blackbox->getXDisplay();
1613 ce.xclient.window = client.window;
1614 ce.xclient.format = 32;
1615 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1616 ce.xclient.data.l[1] = blackbox->getLastTime();
1617 ce.xclient.data.l[2] = 0l;
1618 ce.xclient.data.l[3] = 0l;
1619 ce.xclient.data.l[4] = 0l;
1620 XSendEvent(blackbox->getXDisplay(), client.window, False,
1622 XFlush(blackbox->getXDisplay());
1629 void BlackboxWindow::iconify(void) {
1630 if (flags.iconic) return;
1632 // We don't need to worry about resizing because resizing always grabs the X
1633 // server. This should only ever happen if using opaque moving.
1637 if (windowmenu) windowmenu->hide();
1640 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1641 * we need to clear the event mask on client.window for a split second.
1642 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1643 * split second, leaving us with a ghost window... so, we need to do this
1644 * while the X server is grabbed
1646 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1647 StructureNotifyMask;
1648 XGrabServer(blackbox->getXDisplay());
1649 XSelectInput(blackbox->getXDisplay(), client.window,
1650 event_mask & ~StructureNotifyMask);
1651 XUnmapWindow(blackbox->getXDisplay(), client.window);
1652 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1653 XUngrabServer(blackbox->getXDisplay());
1655 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1656 flags.visible = False;
1657 flags.iconic = True;
1659 setState(IconicState);
1661 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1663 if (isTransient()) {
1664 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1665 ! client.transient_for->flags.iconic) {
1666 // iconify our transient_for
1667 client.transient_for->iconify();
1671 screen->addIcon(this);
1673 if (client.transientList.size() > 0) {
1674 // iconify all transients
1675 BlackboxWindowList::iterator it, end = client.transientList.end();
1676 for (it = client.transientList.begin(); it != end; ++it) {
1677 if (! (*it)->flags.iconic) (*it)->iconify();
1680 screen->updateStackingList();
1684 void BlackboxWindow::show(void) {
1685 flags.visible = True;
1686 flags.iconic = False;
1688 current_state = (flags.shaded) ? IconicState : NormalState;
1689 setState(current_state);
1691 XMapWindow(blackbox->getXDisplay(), client.window);
1692 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1693 XMapWindow(blackbox->getXDisplay(), frame.window);
1698 XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1699 screen->getRootWindow(),
1700 0, 0, &real_x, &real_y, &child);
1701 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1702 client.rect.left(), client.rect.top(), real_x, real_y);
1703 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1708 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1709 if (flags.iconic || reassoc)
1710 screen->reassociateWindow(this, BSENTINEL, False);
1711 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1716 // reassociate and deiconify all transients
1717 if (reassoc && client.transientList.size() > 0) {
1718 BlackboxWindowList::iterator it, end = client.transientList.end();
1719 for (it = client.transientList.begin(); it != end; ++it) {
1720 (*it)->deiconify(True, False);
1725 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1729 void BlackboxWindow::close(void) {
1731 ce.xclient.type = ClientMessage;
1732 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1733 ce.xclient.display = blackbox->getXDisplay();
1734 ce.xclient.window = client.window;
1735 ce.xclient.format = 32;
1736 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1737 ce.xclient.data.l[1] = CurrentTime;
1738 ce.xclient.data.l[2] = 0l;
1739 ce.xclient.data.l[3] = 0l;
1740 ce.xclient.data.l[4] = 0l;
1741 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1742 XFlush(blackbox->getXDisplay());
1746 void BlackboxWindow::withdraw(void) {
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.
1752 flags.visible = False;
1753 flags.iconic = False;
1755 setState(current_state);
1757 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1759 XGrabServer(blackbox->getXDisplay());
1761 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1762 StructureNotifyMask;
1763 XSelectInput(blackbox->getXDisplay(), client.window,
1764 event_mask & ~StructureNotifyMask);
1765 XUnmapWindow(blackbox->getXDisplay(), client.window);
1766 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1768 XUngrabServer(blackbox->getXDisplay());
1770 if (windowmenu) windowmenu->hide();
1774 void BlackboxWindow::maximize(unsigned int button) {
1775 // We don't need to worry about resizing because resizing always grabs the X
1776 // server. This should only ever happen if using opaque moving.
1780 // handle case where menu is open then the max button is used instead
1781 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1783 if (flags.maximized) {
1784 flags.maximized = 0;
1786 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1787 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1790 when a resize finishes, maximize(0) is called to clear any maximization
1791 flags currently set. Otherwise it still thinks it is maximized.
1792 so we do not need to call configure() because resizing will handle it
1794 if (! flags.resizing)
1795 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1796 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1798 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1799 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1801 redrawAllButtons(); // in case it is not called in configure()
1802 setState(current_state);
1806 blackbox_attrib.premax_x = frame.rect.x();
1807 blackbox_attrib.premax_y = frame.rect.y();
1808 blackbox_attrib.premax_w = frame.rect.width();
1809 // use client.rect so that clients can be restored even if shaded
1810 blackbox_attrib.premax_h =
1811 client.rect.height() + frame.margin.top + frame.margin.bottom;
1814 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1815 // find the area to use
1816 RectList availableAreas = screen->allAvailableAreas();
1817 RectList::iterator it, end = availableAreas.end();
1819 for (it = availableAreas.begin(); it != end; ++it)
1820 if (it->intersects(frame.rect)) break;
1821 if (it == end) // the window isn't inside an area
1822 it = availableAreas.begin(); // so just default to the first one
1824 frame.changing = *it;
1827 frame.changing = screen->availableArea();
1831 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1832 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1836 blackbox_attrib.flags |= AttribMaxVert;
1837 blackbox_attrib.attrib |= AttribMaxVert;
1839 frame.changing.setX(frame.rect.x());
1840 frame.changing.setWidth(frame.rect.width());
1844 blackbox_attrib.flags |= AttribMaxHoriz;
1845 blackbox_attrib.attrib |= AttribMaxHoriz;
1847 frame.changing.setY(frame.rect.y());
1848 frame.changing.setHeight(frame.rect.height());
1855 blackbox_attrib.flags ^= AttribShaded;
1856 blackbox_attrib.attrib ^= AttribShaded;
1857 flags.shaded = False;
1860 flags.maximized = button;
1862 configure(frame.changing.x(), frame.changing.y(),
1863 frame.changing.width(), frame.changing.height());
1865 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1866 redrawAllButtons(); // in case it is not called in configure()
1867 setState(current_state);
1871 // re-maximizes the window to take into account availableArea changes
1872 void BlackboxWindow::remaximize(void) {
1874 // we only update the window's attributes otherwise we lose the shade bit
1875 switch(flags.maximized) {
1877 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1878 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1882 blackbox_attrib.flags |= AttribMaxVert;
1883 blackbox_attrib.attrib |= AttribMaxVert;
1887 blackbox_attrib.flags |= AttribMaxHoriz;
1888 blackbox_attrib.attrib |= AttribMaxHoriz;
1894 // save the original dimensions because maximize will wipe them out
1895 int premax_x = blackbox_attrib.premax_x,
1896 premax_y = blackbox_attrib.premax_y,
1897 premax_w = blackbox_attrib.premax_w,
1898 premax_h = blackbox_attrib.premax_h;
1900 unsigned int button = flags.maximized;
1901 flags.maximized = 0; // trick maximize() into working
1904 // restore saved values
1905 blackbox_attrib.premax_x = premax_x;
1906 blackbox_attrib.premax_y = premax_y;
1907 blackbox_attrib.premax_w = premax_w;
1908 blackbox_attrib.premax_h = premax_h;
1912 void BlackboxWindow::setWorkspace(unsigned int n) {
1913 blackbox_attrib.flags |= AttribWorkspace;
1914 blackbox_attrib.workspace = n;
1915 if (n == BSENTINEL) { // iconified window
1917 we set the workspace to 'all workspaces' so that taskbars will show the
1918 window. otherwise, it made uniconifying a window imposible without the
1919 blackbox workspace menu
1923 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1927 void BlackboxWindow::shade(void) {
1929 XResizeWindow(blackbox->getXDisplay(), frame.window,
1930 frame.inside_w, frame.inside_h);
1931 flags.shaded = False;
1932 blackbox_attrib.flags ^= AttribShaded;
1933 blackbox_attrib.attrib ^= AttribShaded;
1935 setState(NormalState);
1937 // set the frame rect to the normal size
1938 frame.rect.setHeight(client.rect.height() + frame.margin.top +
1939 frame.margin.bottom);
1941 if (! (decorations & Decor_Titlebar))
1942 return; // can't shade it without a titlebar!
1944 XResizeWindow(blackbox->getXDisplay(), frame.window,
1945 frame.inside_w, frame.title_h);
1946 flags.shaded = True;
1947 blackbox_attrib.flags |= AttribShaded;
1948 blackbox_attrib.attrib |= AttribShaded;
1950 setState(IconicState);
1952 // set the frame rect to the shaded size
1953 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1959 * (Un)Sticks a window and its relatives.
1961 void BlackboxWindow::stick(void) {
1963 blackbox_attrib.flags ^= AttribOmnipresent;
1964 blackbox_attrib.attrib ^= AttribOmnipresent;
1966 flags.stuck = False;
1969 screen->reassociateWindow(this, BSENTINEL, True);
1970 // temporary fix since sticky windows suck. set the hint to what we
1971 // actually hold in our data.
1972 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1973 blackbox_attrib.workspace);
1975 setState(current_state);
1979 blackbox_attrib.flags |= AttribOmnipresent;
1980 blackbox_attrib.attrib |= AttribOmnipresent;
1982 // temporary fix since sticky windows suck. set the hint to a different
1983 // value than that contained in the class' data.
1984 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1987 setState(current_state);
1990 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1991 client.transient_for->isStuck() != flags.stuck)
1992 client.transient_for->stick();
1993 // go down the chain
1994 BlackboxWindowList::iterator it;
1995 const BlackboxWindowList::iterator end = client.transientList.end();
1996 for (it = client.transientList.begin(); it != end; ++it)
1997 if ((*it)->isStuck() != flags.stuck)
2002 void BlackboxWindow::redrawWindowFrame(void) const {
2003 if (decorations & Decor_Titlebar) {
2004 if (flags.focused) {
2006 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2007 frame.title, frame.ftitle);
2009 XSetWindowBackground(blackbox->getXDisplay(),
2010 frame.title, frame.ftitle_pixel);
2013 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2014 frame.title, frame.utitle);
2016 XSetWindowBackground(blackbox->getXDisplay(),
2017 frame.title, frame.utitle_pixel);
2019 XClearWindow(blackbox->getXDisplay(), frame.title);
2025 if (decorations & Decor_Handle) {
2026 if (flags.focused) {
2028 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2029 frame.handle, frame.fhandle);
2031 XSetWindowBackground(blackbox->getXDisplay(),
2032 frame.handle, frame.fhandle_pixel);
2035 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2036 frame.left_grip, frame.fgrip);
2037 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2038 frame.right_grip, frame.fgrip);
2040 XSetWindowBackground(blackbox->getXDisplay(),
2041 frame.left_grip, frame.fgrip_pixel);
2042 XSetWindowBackground(blackbox->getXDisplay(),
2043 frame.right_grip, frame.fgrip_pixel);
2047 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2048 frame.handle, frame.uhandle);
2050 XSetWindowBackground(blackbox->getXDisplay(),
2051 frame.handle, frame.uhandle_pixel);
2054 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2055 frame.left_grip, frame.ugrip);
2056 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2057 frame.right_grip, frame.ugrip);
2059 XSetWindowBackground(blackbox->getXDisplay(),
2060 frame.left_grip, frame.ugrip_pixel);
2061 XSetWindowBackground(blackbox->getXDisplay(),
2062 frame.right_grip, frame.ugrip_pixel);
2065 XClearWindow(blackbox->getXDisplay(), frame.handle);
2066 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2067 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2070 if (decorations & Decor_Border) {
2072 XSetWindowBorder(blackbox->getXDisplay(),
2073 frame.plate, frame.fborder_pixel);
2075 XSetWindowBorder(blackbox->getXDisplay(),
2076 frame.plate, frame.uborder_pixel);
2081 void BlackboxWindow::setFocusFlag(bool focus) {
2082 // only focus a window if it is visible
2083 if (focus && !flags.visible)
2086 flags.focused = focus;
2088 redrawWindowFrame();
2090 if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2091 if (isFocused()) timer->start();
2096 blackbox->setFocusedWindow(this);
2098 if (! flags.iconic) {
2099 // iconic windows arent in a workspace menu!
2101 screen->getWorkspace(blackbox_attrib.workspace)->getMenu();
2102 menu->setItemSelected(window_number, isFocused());
2107 void BlackboxWindow::installColormap(bool install) {
2108 int i = 0, ncmap = 0;
2109 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2110 client.window, &ncmap);
2112 XWindowAttributes wattrib;
2113 if (XGetWindowAttributes(blackbox->getXDisplay(),
2114 client.window, &wattrib)) {
2116 // install the window's colormap
2117 for (i = 0; i < ncmap; i++) {
2118 if (*(cmaps + i) == wattrib.colormap)
2119 // this window is using an installed color map... do not install
2122 // otherwise, install the window's colormap
2124 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2126 // uninstall the window's colormap
2127 for (i = 0; i < ncmap; i++) {
2128 if (*(cmaps + i) == wattrib.colormap)
2129 // we found the colormap to uninstall
2130 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2140 void BlackboxWindow::setAllowedActions(void) {
2144 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2145 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2146 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2148 if (functions & Func_Move)
2149 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2150 if (functions & Func_Resize)
2151 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2152 if (functions & Func_Maximize) {
2153 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2154 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2157 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2162 void BlackboxWindow::setState(unsigned long new_state) {
2163 current_state = new_state;
2165 unsigned long state[2];
2166 state[0] = current_state;
2168 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2170 xatom->setValue(client.window, XAtom::blackbox_attributes,
2171 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2172 PropBlackboxAttributesElements);
2177 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2179 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2181 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2182 if (flags.skip_taskbar)
2183 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2184 if (flags.skip_pager)
2185 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2186 if (flags.fullscreen)
2187 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2188 if (flags.maximized == 1 || flags.maximized == 2)
2189 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2190 if (flags.maximized == 1 || flags.maximized == 3)
2191 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2192 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2197 bool BlackboxWindow::getState(void) {
2198 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2200 if (! ret) current_state = 0;
2205 void BlackboxWindow::restoreAttributes(void) {
2206 unsigned long num = PropBlackboxAttributesElements;
2207 BlackboxAttributes *net;
2208 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2209 XAtom::blackbox_attributes, num,
2210 (unsigned long **)&net))
2212 if (num < PropBlackboxAttributesElements) {
2217 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2218 flags.shaded = False;
2219 unsigned long orig_state = current_state;
2223 At this point in the life of a window, current_state should only be set
2224 to IconicState if the window was an *icon*, not if it was shaded.
2226 if (orig_state != IconicState)
2227 current_state = WithdrawnState;
2230 if (net->workspace != screen->getCurrentWorkspaceID() &&
2231 net->workspace < screen->getWorkspaceCount())
2232 screen->reassociateWindow(this, net->workspace, True);
2234 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2235 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2236 // set to WithdrawnState so it will be mapped on the new workspace
2237 if (current_state == NormalState) current_state = WithdrawnState;
2238 } else if (current_state == WithdrawnState) {
2239 // the window is on this workspace and is Withdrawn, so it is waiting to
2241 current_state = NormalState;
2244 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2245 flags.stuck = False;
2248 // if the window was on another workspace, it was going to be hidden. this
2249 // specifies that the window should be mapped since it is sticky.
2250 if (current_state == WithdrawnState) current_state = NormalState;
2253 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2254 int x = net->premax_x, y = net->premax_y;
2255 unsigned int w = net->premax_w, h = net->premax_h;
2256 flags.maximized = 0;
2259 if ((net->flags & AttribMaxHoriz) &&
2260 (net->flags & AttribMaxVert))
2261 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2262 else if (net->flags & AttribMaxVert)
2263 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2264 else if (net->flags & AttribMaxHoriz)
2265 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2269 blackbox_attrib.premax_x = x;
2270 blackbox_attrib.premax_y = y;
2271 blackbox_attrib.premax_w = w;
2272 blackbox_attrib.premax_h = h;
2275 if (net->flags & AttribDecoration) {
2276 switch (net->decoration) {
2284 decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2285 Decor_Iconify | Decor_Maximize;
2290 decorations |= Decor_Titlebar | Decor_Iconify;
2291 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2296 decorations |= Decor_Titlebar;
2297 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2302 // sanity check the new decor
2303 if (! (functions & Func_Resize) || isTransient())
2304 decorations &= ~(Decor_Maximize | Decor_Handle);
2305 if (! (functions & Func_Maximize))
2306 decorations &= ~Decor_Maximize;
2308 if (decorations & Decor_Titlebar) {
2309 if (functions & Func_Close) // close button is controlled by function
2310 decorations |= Decor_Close; // not decor type
2312 if (flags.shaded) // we can not be shaded if we lack a titlebar
2316 if (flags.visible && frame.window) {
2317 XMapSubwindows(blackbox->getXDisplay(), frame.window);
2318 XMapWindow(blackbox->getXDisplay(), frame.window);
2322 setState(current_state);
2325 // with the state set it will then be the map event's job to read the
2326 // window's state and behave accordingly
2333 * Positions the Rect r according the the client window position and
2336 void BlackboxWindow::applyGravity(Rect &r) {
2337 // apply horizontal window gravity
2338 switch (client.win_gravity) {
2340 case NorthWestGravity:
2341 case SouthWestGravity:
2343 r.setX(client.rect.x());
2349 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2352 case NorthEastGravity:
2353 case SouthEastGravity:
2355 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2360 r.setX(client.rect.x() - frame.margin.left);
2364 // apply vertical window gravity
2365 switch (client.win_gravity) {
2367 case NorthWestGravity:
2368 case NorthEastGravity:
2370 r.setY(client.rect.y());
2376 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2379 case SouthWestGravity:
2380 case SouthEastGravity:
2382 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2387 r.setY(client.rect.y() - frame.margin.top);
2394 * The reverse of the applyGravity function.
2396 * Positions the Rect r according to the frame window position and
2399 void BlackboxWindow::restoreGravity(Rect &r) {
2400 // restore horizontal window gravity
2401 switch (client.win_gravity) {
2403 case NorthWestGravity:
2404 case SouthWestGravity:
2406 r.setX(frame.rect.x());
2412 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2415 case NorthEastGravity:
2416 case SouthEastGravity:
2418 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2423 r.setX(frame.rect.x() + frame.margin.left);
2427 // restore vertical window gravity
2428 switch (client.win_gravity) {
2430 case NorthWestGravity:
2431 case NorthEastGravity:
2433 r.setY(frame.rect.y());
2439 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2442 case SouthWestGravity:
2443 case SouthEastGravity:
2445 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2450 r.setY(frame.rect.y() + frame.margin.top);
2456 void BlackboxWindow::redrawLabel(void) const {
2457 if (flags.focused) {
2459 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2460 frame.label, frame.flabel);
2462 XSetWindowBackground(blackbox->getXDisplay(),
2463 frame.label, frame.flabel_pixel);
2466 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2467 frame.label, frame.ulabel);
2469 XSetWindowBackground(blackbox->getXDisplay(),
2470 frame.label, frame.ulabel_pixel);
2472 XClearWindow(blackbox->getXDisplay(), frame.label);
2474 WindowStyle *style = screen->getWindowStyle();
2476 int pos = frame.bevel_w * 2;
2477 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2478 style->font->drawString(frame.label, pos, 1,
2479 (flags.focused ? style->l_text_focus :
2480 style->l_text_unfocus),
2485 void BlackboxWindow::redrawAllButtons(void) const {
2486 if (frame.iconify_button) redrawIconifyButton(False);
2487 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2488 if (frame.close_button) redrawCloseButton(False);
2492 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2494 if (flags.focused) {
2496 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2497 frame.iconify_button, frame.fbutton);
2499 XSetWindowBackground(blackbox->getXDisplay(),
2500 frame.iconify_button, frame.fbutton_pixel);
2503 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2504 frame.iconify_button, frame.ubutton);
2506 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2507 frame.ubutton_pixel);
2511 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2512 frame.iconify_button, frame.pbutton);
2514 XSetWindowBackground(blackbox->getXDisplay(),
2515 frame.iconify_button, frame.pbutton_pixel);
2517 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2519 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2520 screen->getWindowStyle()->b_pic_unfocus);
2521 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2522 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2526 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2528 if (flags.focused) {
2530 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2531 frame.maximize_button, frame.fbutton);
2533 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2534 frame.fbutton_pixel);
2537 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2538 frame.maximize_button, frame.ubutton);
2540 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2541 frame.ubutton_pixel);
2545 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2546 frame.maximize_button, frame.pbutton);
2548 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2549 frame.pbutton_pixel);
2551 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2553 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2554 screen->getWindowStyle()->b_pic_unfocus);
2555 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2556 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2557 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2558 2, 3, (frame.button_w - 3), 3);
2562 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2564 if (flags.focused) {
2566 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2569 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2570 frame.fbutton_pixel);
2573 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2576 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2577 frame.ubutton_pixel);
2581 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2582 frame.close_button, frame.pbutton);
2584 XSetWindowBackground(blackbox->getXDisplay(),
2585 frame.close_button, frame.pbutton_pixel);
2587 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2589 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2590 screen->getWindowStyle()->b_pic_unfocus);
2591 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2592 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2593 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2594 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2598 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2599 if (re->window != client.window)
2603 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2607 switch (current_state) {
2612 case WithdrawnState:
2621 fprintf(stderr, "0x%lx: just before show (%d, %d) w: %d, h: %d\n",
2623 frame.rect.x(), frame.rect.y(),
2624 frame.rect.width(), frame.rect.height());
2627 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2629 if (! blackbox->isStartup()) {
2630 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2631 if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2632 getTransientFor()->isFocused())) {
2635 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2639 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2640 &r, &c, &rx, &ry, &x, &y, &m);
2650 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2651 if (ue->window != client.window)
2655 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2659 screen->unmanageWindow(this, False);
2663 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2664 if (de->window != client.window)
2668 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2672 screen->unmanageWindow(this, False);
2676 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2677 if (re->window != client.window || re->parent == frame.plate)
2681 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2682 "0x%lx.\n", client.window, re->parent);
2687 XPutBackEvent(blackbox->getXDisplay(), &ev);
2688 screen->unmanageWindow(this, True);
2692 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2693 if (pe->state == PropertyDelete)
2697 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2703 case XA_WM_CLIENT_MACHINE:
2707 case XA_WM_TRANSIENT_FOR: {
2708 // determine if this is a transient window
2711 // adjust the window decorations based on transience
2712 if (isTransient()) {
2713 decorations &= ~(Decor_Maximize | Decor_Handle);
2714 functions &= ~Func_Maximize;
2715 setAllowedActions();
2720 fprintf(stderr, "0x%lx: transient hint (%d, %d) w: %d, h: %d\n",
2722 frame.rect.x(), frame.rect.y(),
2723 frame.rect.width(), frame.rect.height());
2732 case XA_WM_ICON_NAME:
2734 if (flags.iconic) screen->propagateWindowName(this);
2737 case XAtom::net_wm_name:
2741 if (decorations & Decor_Titlebar)
2744 screen->propagateWindowName(this);
2747 case XA_WM_NORMAL_HINTS: {
2750 if ((client.normal_hint_flags & PMinSize) &&
2751 (client.normal_hint_flags & PMaxSize)) {
2752 // the window now can/can't resize itself, so the buttons need to be
2755 if (client.max_width <= client.min_width &&
2756 client.max_height <= client.min_height) {
2757 decorations &= ~(Decor_Maximize | Decor_Handle);
2758 functions &= ~(Func_Resize | Func_Maximize);
2760 if (! isTransient()) {
2761 decorations |= Decor_Maximize | Decor_Handle;
2762 functions |= Func_Maximize;
2764 functions |= Func_Resize;
2767 setAllowedActions();
2770 Rect old_rect = frame.rect;
2774 if (old_rect != frame.rect)
2778 fprintf(stderr, "0x%lx: normal hint (%d, %d) w: %d, h: %d\n",
2780 frame.rect.x(), frame.rect.y(),
2781 frame.rect.width(), frame.rect.height());
2787 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2790 if ((decorations & Decor_Close) && (! frame.close_button)) {
2791 createCloseButton();
2792 if (decorations & Decor_Titlebar) {
2793 positionButtons(True);
2794 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2796 if (windowmenu) windowmenu->reconfigure();
2798 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2807 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2809 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2812 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2814 else if (frame.close_button == ee->window)
2815 redrawCloseButton(False);
2816 else if (frame.maximize_button == ee->window)
2817 redrawMaximizeButton(flags.maximized);
2818 else if (frame.iconify_button == ee->window)
2819 redrawIconifyButton(False);
2823 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2824 if (cr->window != client.window || flags.iconic)
2827 if (cr->value_mask & CWBorderWidth)
2828 client.old_bw = cr->border_width;
2830 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2831 Rect req = frame.rect;
2833 if (cr->value_mask & (CWX | CWY)) {
2834 if (cr->value_mask & CWX)
2835 client.rect.setX(cr->x);
2836 if (cr->value_mask & CWY)
2837 client.rect.setY(cr->y);
2842 if (cr->value_mask & CWWidth) {
2843 req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2845 fprintf(stderr, "0x%lx: new width - %d\n", client.window, cr->width);
2849 if (cr->value_mask & CWHeight) {
2850 req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2852 fprintf(stderr, "0x%lx: new height - %d\n", client.window, cr->height);
2856 configure(req.x(), req.y(), req.width(), req.height());
2859 if (cr->value_mask & CWStackMode && !isDesktop()) {
2860 switch (cr->detail) {
2863 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2869 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2875 fprintf(stderr, "0x%lx: change request (%d, %d) w: %d, h: %d\n",
2877 frame.rect.x(), frame.rect.y(),
2878 frame.rect.width(), frame.rect.height());
2883 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2885 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2889 if (frame.maximize_button == be->window && be->button <= 3) {
2890 redrawMaximizeButton(True);
2891 } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2892 if (! flags.focused)
2895 if (frame.iconify_button == be->window) {
2896 redrawIconifyButton(True);
2897 } else if (frame.close_button == be->window) {
2898 redrawCloseButton(True);
2899 } else if (frame.plate == be->window) {
2900 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2902 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2904 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2906 if (frame.title == be->window || frame.label == be->window) {
2907 if (((be->time - lastButtonPressTime) <=
2908 blackbox->getDoubleClickInterval()) ||
2909 (be->state == ControlMask)) {
2910 lastButtonPressTime = 0;
2913 lastButtonPressTime = be->time;
2917 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2919 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2921 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2922 (be->window != frame.close_button)) {
2923 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2924 } else if (windowmenu && be->button == 3 &&
2925 (frame.title == be->window || frame.label == be->window ||
2926 frame.handle == be->window || frame.window == be->window)) {
2927 if (windowmenu->isVisible()) {
2930 int mx = be->x_root - windowmenu->getWidth() / 2,
2931 my = be->y_root - windowmenu->getHeight() / 2;
2933 // snap the window menu into a corner/side if necessary
2934 int left_edge, right_edge, top_edge, bottom_edge;
2937 the " + (frame.border_w * 2) - 1" bits are to get the proper width
2938 and height of the menu, as the sizes returned by it do not include
2941 left_edge = frame.rect.x();
2942 right_edge = frame.rect.right() -
2943 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2944 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2945 bottom_edge = client.rect.bottom() -
2946 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2947 (frame.border_w + frame.mwm_border_w);
2951 if (mx > right_edge)
2955 if (my > bottom_edge)
2958 windowmenu->move(mx, my);
2960 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2961 XRaiseWindow(blackbox->getXDisplay(),
2962 windowmenu->getSendToMenu()->getWindowID());
2965 } else if (be->button == 4) {
2966 if ((be->window == frame.label ||
2967 be->window == frame.title ||
2968 be->window == frame.maximize_button ||
2969 be->window == frame.iconify_button ||
2970 be->window == frame.close_button) &&
2974 } else if (be->button == 5) {
2975 if ((be->window == frame.label ||
2976 be->window == frame.title ||
2977 be->window == frame.maximize_button ||
2978 be->window == frame.iconify_button ||
2979 be->window == frame.close_button) &&
2986 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2988 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2992 if (re->window == frame.maximize_button &&
2993 re->button >= 1 && re->button <= 3) {
2994 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2995 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2996 maximize(re->button);
2998 redrawMaximizeButton(flags.maximized);
3000 } else if (re->window == frame.iconify_button && re->button == 1) {
3001 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3002 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3005 redrawIconifyButton(False);
3007 } else if (re->window == frame.close_button & re->button == 1) {
3008 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3009 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3011 redrawCloseButton(False);
3012 } else if (flags.moving) {
3014 } else if (flags.resizing) {
3016 } else if (re->window == frame.window) {
3017 if (re->button == 2 && re->state == ModMask)
3018 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3024 void BlackboxWindow::beginMove(int x_root, int y_root) {
3025 assert(! (flags.resizing || flags.moving));
3028 Only one window can be moved/resized at a time. If another window is already
3029 being moved or resized, then stop it before whating to work with this one.
3031 BlackboxWindow *changing = blackbox->getChangingWindow();
3032 if (changing && changing != this) {
3033 if (changing->flags.moving)
3034 changing->endMove();
3035 else // if (changing->flags.resizing)
3036 changing->endResize();
3039 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3040 PointerMotionMask | ButtonReleaseMask,
3041 GrabModeAsync, GrabModeAsync,
3042 None, blackbox->getMoveCursor(), CurrentTime);
3044 if (windowmenu && windowmenu->isVisible())
3047 flags.moving = True;
3048 blackbox->setChangingWindow(this);
3050 if (! screen->doOpaqueMove()) {
3051 XGrabServer(blackbox->getXDisplay());
3053 frame.changing = frame.rect;
3054 screen->showPosition(frame.changing.x(), frame.changing.y());
3056 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3060 frame.changing.width() - 1,
3061 frame.changing.height() - 1);
3064 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3065 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3069 void BlackboxWindow::doMove(int x_root, int y_root) {
3070 assert(flags.moving);
3071 assert(blackbox->getChangingWindow() == this);
3073 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3074 dx -= frame.border_w;
3075 dy -= frame.border_w;
3077 if (screen->doWorkspaceWarping()) {
3078 // workspace warping
3080 unsigned int dest = screen->getCurrentWorkspaceID();
3084 if (dest > 0) dest--;
3085 else dest = screen->getNumberOfWorkspaces() - 1;
3087 } else if (x_root >= screen->getRect().right()) {
3090 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3095 bool focus = flags.focused; // had focus while moving?
3097 screen->reassociateWindow(this, dest, False);
3098 screen->changeWorkspaceID(dest);
3103 If the XWarpPointer is done after the configure, we can end up
3104 grabbing another window, so made sure you do it first.
3108 dest_x = screen->getRect().right() - 1;
3109 XWarpPointer(blackbox->getXDisplay(), None,
3110 screen->getRootWindow(), 0, 0, 0, 0,
3113 configure(dx + (screen->getRect().width() - 1), dy,
3114 frame.rect.width(), frame.rect.height());
3117 XWarpPointer(blackbox->getXDisplay(), None,
3118 screen->getRootWindow(), 0, 0, 0, 0,
3121 configure(dx - (screen->getRect().width() - 1), dy,
3122 frame.rect.width(), frame.rect.height());
3125 beginMove(dest_x, y_root);
3130 const int snap_distance = screen->getEdgeSnapThreshold();
3132 if (snap_distance) {
3134 const int wleft = dx,
3135 wright = dx + frame.rect.width() - 1,
3137 wbottom = dy + frame.rect.height() - 1;
3139 if (screen->getWindowToWindowSnap()) {
3140 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3143 // try snap to another window
3144 for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
3145 BlackboxWindow *snapwin = w->getWindow(i);
3146 if (snapwin == this)
3147 continue; // don't snap to self
3149 bool snapped = False;
3151 const Rect &winrect = snapwin->frameRect();
3152 int dleft = abs(wright - winrect.left()),
3153 dright = abs(wleft - winrect.right()),
3154 dtop = abs(wbottom - winrect.top()),
3155 dbottom = abs(wtop - winrect.bottom());
3157 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3158 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3160 // snap left of other window?
3161 if (dleft < snap_distance && dleft <= dright) {
3162 dx = winrect.left() - frame.rect.width();
3165 // snap right of other window?
3166 else if (dright < snap_distance) {
3167 dx = winrect.right() + 1;
3172 if (screen->getWindowCornerSnap()) {
3173 // try corner-snap to its other sides
3174 dtop = abs(wtop - winrect.top());
3175 dbottom = abs(wbottom - winrect.bottom());
3176 if (dtop < snap_distance && dtop <= dbottom)
3178 else if (dbottom < snap_distance)
3179 dy = winrect.bottom() - frame.rect.height() + 1;
3186 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3187 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3189 // snap top of other window?
3190 if (dtop < snap_distance && dtop <= dbottom) {
3191 dy = winrect.top() - frame.rect.height();
3194 // snap bottom of other window?
3195 else if (dbottom < snap_distance) {
3196 dy = winrect.bottom() + 1;
3201 if (screen->getWindowCornerSnap()) {
3202 // try corner-snap to its other sides
3203 dleft = abs(wleft - winrect.left());
3204 dright = abs(wright - winrect.right());
3205 if (dleft < snap_distance && dleft <= dright)
3206 dx = winrect.left();
3207 else if (dright < snap_distance)
3208 dx = winrect.right() - frame.rect.width() + 1;
3217 RectList snaplist; // the list of rects we will try to snap to
3219 // snap to the strut (and screen boundaries for xinerama)
3221 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3222 if (! screen->doFullMax())
3223 snaplist.insert(snaplist.begin(),
3224 screen->allAvailableAreas().begin(),
3225 screen->allAvailableAreas().end());
3227 // always snap to the screen edges
3228 snaplist.insert(snaplist.begin(),
3229 screen->getXineramaAreas().begin(),
3230 screen->getXineramaAreas().end());
3234 if (! screen->doFullMax())
3235 snaplist.push_back(screen->availableArea());
3237 // always snap to the screen edges
3238 snaplist.push_back(screen->getRect());
3241 RectList::const_iterator it, end = snaplist.end();
3242 for (it = snaplist.begin(); it != end; ++it) {
3243 const Rect &srect = *it;
3245 // if we're not in the rectangle then don't snap to it.
3246 if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3247 frame.rect.height())))
3250 int dleft = abs(wleft - srect.left()),
3251 dright = abs(wright - srect.right()),
3252 dtop = abs(wtop - srect.top()),
3253 dbottom = abs(wbottom - srect.bottom());
3256 if (dleft < snap_distance && dleft <= dright)
3259 else if (dright < snap_distance)
3260 dx = srect.right() - frame.rect.width() + 1;
3263 if (dtop < snap_distance && dtop <= dbottom)
3266 else if (dbottom < snap_distance)
3267 dy = srect.bottom() - frame.rect.height() + 1;
3271 if (screen->doOpaqueMove()) {
3272 configure(dx, dy, frame.rect.width(), frame.rect.height());
3274 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3278 frame.changing.width() - 1,
3279 frame.changing.height() - 1);
3281 frame.changing.setPos(dx, dy);
3283 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3287 frame.changing.width() - 1,
3288 frame.changing.height() - 1);
3291 screen->showPosition(dx, dy);
3295 void BlackboxWindow::endMove(void) {
3296 assert(flags.moving);
3297 assert(blackbox->getChangingWindow() == this);
3299 flags.moving = False;
3300 blackbox->setChangingWindow(0);
3302 if (! screen->doOpaqueMove()) {
3303 /* when drawing the rubber band, we need to make sure we only draw inside
3304 * the frame... frame.changing_* contain the new coords for the window,
3305 * so we need to subtract 1 from changing_w/changing_h every where we
3306 * draw the rubber band (for both moving and resizing)
3308 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3309 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3310 frame.changing.width() - 1, frame.changing.height() - 1);
3311 XUngrabServer(blackbox->getXDisplay());
3313 configure(frame.changing.x(), frame.changing.y(),
3314 frame.changing.width(), frame.changing.height());
3316 configure(frame.rect.x(), frame.rect.y(),
3317 frame.rect.width(), frame.rect.height());
3319 screen->hideGeometry();
3321 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3323 // if there are any left over motions from the move, drop them now
3324 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3326 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3331 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3332 assert(! (flags.resizing || flags.moving));
3335 Only one window can be moved/resized at a time. If another window is already
3336 being moved or resized, then stop it before whating to work with this one.
3338 BlackboxWindow *changing = blackbox->getChangingWindow();
3339 if (changing && changing != this) {
3340 if (changing->flags.moving)
3341 changing->endMove();
3342 else // if (changing->flags.resizing)
3343 changing->endResize();
3351 switch (resize_dir) {
3354 cursor = blackbox->getLowerLeftAngleCursor();
3359 cursor = blackbox->getLowerRightAngleCursor();
3363 anchor = BottomRight;
3364 cursor = blackbox->getUpperLeftAngleCursor();
3368 anchor = BottomLeft;
3369 cursor = blackbox->getUpperRightAngleCursor();
3373 assert(false); // unhandled Corner
3374 return; // unreachable, for the compiler
3377 XGrabServer(blackbox->getXDisplay());
3378 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3379 PointerMotionMask | ButtonReleaseMask,
3380 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3382 flags.resizing = True;
3383 blackbox->setChangingWindow(this);
3386 frame.changing = frame.rect;
3388 constrain(anchor, &gw, &gh);
3390 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3391 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3392 frame.changing.width() - 1, frame.changing.height() - 1);
3394 screen->showGeometry(gw, gh);
3396 frame.grab_x = x_root;
3397 frame.grab_y = y_root;
3401 void BlackboxWindow::doResize(int x_root, int y_root) {
3402 assert(flags.resizing);
3403 assert(blackbox->getChangingWindow() == this);
3405 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3406 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3407 frame.changing.width() - 1, frame.changing.height() - 1);
3412 switch (resize_dir) {
3415 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3416 frame.rect.height() + (y_root - frame.grab_y));
3420 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3421 frame.rect.height() + (y_root - frame.grab_y));
3424 anchor = BottomRight;
3425 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3426 frame.rect.height() - (y_root - frame.grab_y));
3429 anchor = BottomLeft;
3430 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3431 frame.rect.height() - (y_root - frame.grab_y));
3435 assert(false); // unhandled Corner
3436 return; // unreachable, for the compiler
3439 constrain(anchor, &gw, &gh);
3441 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3442 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3443 frame.changing.width() - 1, frame.changing.height() - 1);
3445 screen->showGeometry(gw, gh);
3449 void BlackboxWindow::endResize(void) {
3450 assert(flags.resizing);
3451 assert(blackbox->getChangingWindow() == this);
3453 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3454 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3455 frame.changing.width() - 1, frame.changing.height() - 1);
3456 XUngrabServer(blackbox->getXDisplay());
3458 // unset maximized state after resized when fully maximized
3459 if (flags.maximized == 1)
3462 flags.resizing = False;
3463 blackbox->setChangingWindow(0);
3465 configure(frame.changing.x(), frame.changing.y(),
3466 frame.changing.width(), frame.changing.height());
3467 screen->hideGeometry();
3469 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3471 // if there are any left over motions from the resize, drop them now
3472 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3474 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3479 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3481 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3486 doMove(me->x_root, me->y_root);
3487 } else if (flags.resizing) {
3488 doResize(me->x_root, me->y_root);
3490 if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3491 (frame.title == me->window || frame.label == me->window ||
3492 frame.handle == me->window || frame.window == me->window)) {
3493 beginMove(me->x_root, me->y_root);
3494 } else if ((functions & Func_Resize) &&
3495 (me->state & Button1Mask && (me->window == frame.right_grip ||
3496 me->window == frame.left_grip)) ||
3497 (me->state & Button3Mask && me->state & ModMask &&
3498 me->window == frame.window)) {
3499 unsigned int zones = screen->getResizeZones();
3502 if (me->window == frame.left_grip) {
3503 corner = BottomLeft;
3504 } else if (me->window == frame.right_grip || zones == 1) {
3505 corner = BottomRight;
3508 bool left = (me->x_root - frame.rect.x() <=
3509 static_cast<signed>(frame.rect.width() / 2));
3512 else // (zones == 4)
3513 top = (me->y_root - frame.rect.y() <=
3514 static_cast<signed>(frame.rect.height() / 2));
3515 corner = (top ? (left ? TopLeft : TopRight) :
3516 (left ? BottomLeft : BottomRight));
3519 beginResize(me->x_root, me->y_root, corner);
3526 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3527 if (blackbox->hasShapeExtensions() && flags.shaped) {
3534 bool BlackboxWindow::validateClient(void) const {
3535 XSync(blackbox->getXDisplay(), False);
3538 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3539 DestroyNotify, &e) ||
3540 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3542 XPutBackEvent(blackbox->getXDisplay(), &e);
3551 void BlackboxWindow::restore(bool remap) {
3552 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3553 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3554 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3556 // do not leave a shaded window as an icon unless it was an icon
3557 if (flags.shaded && ! flags.iconic) setState(NormalState);
3559 restoreGravity(client.rect);
3561 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3562 XUnmapWindow(blackbox->getXDisplay(), client.window);
3564 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3567 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3568 ReparentNotify, &ev)) {
3571 // according to the ICCCM - if the client doesn't reparent to
3572 // root, then we have to do it for them
3573 XReparentWindow(blackbox->getXDisplay(), client.window,
3574 screen->getRootWindow(),
3575 client.rect.x(), client.rect.y());
3578 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3582 // timer for autoraise
3583 void BlackboxWindow::timeout(void) {
3584 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3588 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3589 if ((net->flags & AttribShaded) &&
3590 ((blackbox_attrib.attrib & AttribShaded) !=
3591 (net->attrib & AttribShaded)))
3594 if (flags.visible && // watch out for requests when we can not be seen
3595 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3596 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3597 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3598 if (flags.maximized) {
3603 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3604 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3605 else if (net->flags & AttribMaxVert)
3606 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3607 else if (net->flags & AttribMaxHoriz)
3608 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3614 if ((net->flags & AttribOmnipresent) &&
3615 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3616 (net->attrib & AttribOmnipresent)))
3619 if ((net->flags & AttribWorkspace) &&
3620 (blackbox_attrib.workspace != net->workspace)) {
3621 screen->reassociateWindow(this, net->workspace, True);
3623 if (screen->getCurrentWorkspaceID() != net->workspace) {
3627 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3631 if (net->flags & AttribDecoration) {
3632 switch (net->decoration) {
3640 decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3642 decorations = ((functions & Func_Resize) && !isTransient() ?
3643 decorations | Decor_Handle :
3644 decorations &= ~Decor_Handle);
3645 decorations = (functions & Func_Maximize ?
3646 decorations | Decor_Maximize :
3647 decorations &= ~Decor_Maximize);
3652 decorations |= Decor_Titlebar | Decor_Iconify;
3653 decorations &= ~(Decor_Border | Decor_Handle);
3655 decorations = (functions & Func_Maximize ?
3656 decorations | Decor_Maximize :
3657 decorations &= ~Decor_Maximize);
3662 decorations |= Decor_Titlebar;
3663 decorations &= ~(Decor_Iconify | Decor_Border);
3665 decorations = ((functions & Func_Resize) && !isTransient() ?
3666 decorations | Decor_Handle :
3667 decorations &= ~Decor_Handle);
3668 decorations = (functions & Func_Maximize ?
3669 decorations | Decor_Maximize :
3670 decorations &= ~Decor_Maximize);
3675 // we can not be shaded if we lack a titlebar
3676 if (flags.shaded && ! (decorations & Decor_Titlebar))
3679 if (flags.visible && frame.window) {
3680 XMapSubwindows(blackbox->getXDisplay(), frame.window);
3681 XMapWindow(blackbox->getXDisplay(), frame.window);
3685 setState(current_state);
3691 * Set the sizes of all components of the window frame
3692 * (the window decorations).
3693 * These values are based upon the current style settings and the client
3694 * window's dimensions.
3696 void BlackboxWindow::upsize(void) {
3697 frame.bevel_w = screen->getBevelWidth();
3699 if (decorations & Decor_Border) {
3700 frame.border_w = screen->getBorderWidth();
3701 if (! isTransient())
3702 frame.mwm_border_w = screen->getFrameWidth();
3704 frame.mwm_border_w = 0;
3706 frame.mwm_border_w = frame.border_w = 0;
3709 if (decorations & Decor_Titlebar) {
3710 // the height of the titlebar is based upon the height of the font being
3711 // used to display the window's title
3712 WindowStyle *style = screen->getWindowStyle();
3713 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3715 frame.label_h = frame.title_h - (frame.bevel_w * 2);
3716 frame.button_w = (frame.label_h - 2);
3718 // set the top frame margin
3719 frame.margin.top = frame.border_w + frame.title_h +
3720 frame.border_w + frame.mwm_border_w;
3726 // set the top frame margin
3727 frame.margin.top = frame.border_w + frame.mwm_border_w;
3730 // set the left/right frame margin
3731 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3733 if (decorations & Decor_Handle) {
3734 frame.grip_w = frame.button_w * 2;
3735 frame.handle_h = screen->getHandleWidth();
3737 // set the bottom frame margin
3738 frame.margin.bottom = frame.border_w + frame.handle_h +
3739 frame.border_w + frame.mwm_border_w;
3744 // set the bottom frame margin
3745 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3749 We first get the normal dimensions and use this to define the inside_w/h
3750 then we modify the height if shading is in effect.
3751 If the shade state is not considered then frame.rect gets reset to the
3752 normal window size on a reconfigure() call resulting in improper
3753 dimensions appearing in move/resize and other events.
3756 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3757 width = client.rect.width() + frame.margin.left + frame.margin.right;
3759 frame.inside_w = width - (frame.border_w * 2);
3760 frame.inside_h = height - (frame.border_w * 2);
3763 height = frame.title_h + (frame.border_w * 2);
3764 frame.rect.setSize(width, height);
3769 * Calculate the size of the client window and constrain it to the
3770 * size specified by the size hints of the client window.
3772 * The logical width and height are placed into pw and ph, if they
3773 * are non-zero. Logical size refers to the users perception of
3774 * the window size (for example an xterm resizes in cells, not in pixels).
3776 * The physical geometry is placed into frame.changing_{x,y,width,height}.
3777 * Physical geometry refers to the geometry of the window in pixels.
3779 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3780 // frame.changing represents the requested frame size, we need to
3781 // strip the frame margin off and constrain the client size
3782 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3783 frame.changing.top() + frame.margin.top,
3784 frame.changing.right() - frame.margin.right,
3785 frame.changing.bottom() - frame.margin.bottom);
3787 int dw = frame.changing.width(), dh = frame.changing.height(),
3788 base_width = (client.base_width) ? client.base_width : client.min_width,
3789 base_height = (client.base_height) ? client.base_height :
3793 if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3794 if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3795 if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3796 if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3799 dw /= client.width_inc;
3801 dh /= client.height_inc;
3804 if (client.width_inc == 1)
3805 *pw = dw + base_width;
3810 if (client.height_inc == 1)
3811 *ph = dh + base_height;
3816 dw *= client.width_inc;
3818 dh *= client.height_inc;
3821 frame.changing.setSize(dw, dh);
3823 // add the frame margin back onto frame.changing
3824 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3825 frame.changing.top() - frame.margin.top,
3826 frame.changing.right() + frame.margin.right,
3827 frame.changing.bottom() + frame.margin.bottom);
3829 // move frame.changing to the specified anchor
3837 dx = frame.rect.right() - frame.changing.right();
3841 dy = frame.rect.bottom() - frame.changing.bottom();
3845 dx = frame.rect.right() - frame.changing.right();
3846 dy = frame.rect.bottom() - frame.changing.bottom();
3850 assert(false); // unhandled corner
3852 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3856 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3857 unsigned int max_length,
3858 unsigned int modifier) const {
3859 size_t text_len = text.size();
3860 unsigned int length;
3863 length = font->measureString(string(text, 0, text_len)) + modifier;
3864 } while (length > max_length && text_len-- > 0);
3868 start_pos += max_length - length;
3872 start_pos += (max_length - length) / 2;
3882 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3883 : blackbox(b), group(_group) {
3884 XWindowAttributes wattrib;
3885 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3886 // group window doesn't seem to exist anymore
3891 XSelectInput(blackbox->getXDisplay(), group,
3892 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3894 blackbox->saveGroupSearch(group, this);
3898 BWindowGroup::~BWindowGroup(void) {
3899 blackbox->removeGroupSearch(group);
3904 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3905 BlackboxWindow *ret = blackbox->getFocusedWindow();
3907 // does the focus window match (or any transient_fors)?
3909 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3910 if (ret->isTransient() && allow_transients) break;
3911 else if (! ret->isTransient()) break;
3914 ret = ret->getTransientFor();
3917 if (ret) return ret;
3919 // the focus window didn't match, look in the group's window list
3920 BlackboxWindowList::const_iterator it, end = windowList.end();
3921 for (it = windowList.begin(); it != end; ++it) {
3923 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3924 if (ret->isTransient() && allow_transients) break;
3925 else if (! ret->isTransient()) break;