diff options
Diffstat (limited to 'src/Window.cc')
-rw-r--r-- | src/Window.cc | 3015 |
1 files changed, 3015 insertions, 0 deletions
diff --git a/src/Window.cc b/src/Window.cc new file mode 100644 index 0000000..d1db058 --- /dev/null +++ b/src/Window.cc @@ -0,0 +1,3015 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +// Window.cc for Blackbox - an X11 Window manager +// Copyright (c) 2003 Kensuke Matsuzaki <zakki@peppermint.jp> +// Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org> +// Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif // HAVE_CONFIG_H + +extern "C" { +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/keysym.h> + +#include <X11/extensions/windowswm.h> +#include <X11/extensions/windowswmstr.h> + +#ifdef HAVE_STRING_H +# include <string.h> +#endif // HAVE_STRING_H + +#ifdef DEBUG +# ifdef HAVE_STDIO_H +# include <stdio.h> +# endif // HAVE_STDIO_H +#endif // DEBUG + +#ifdef HAVE_STDLIB_H + #include <stdlib.h> +#endif // HAVE_STDLIB_H +} + +#include <assert.h> + +#include "i18n.hh" +#include "blackbox.hh" +#include "GCCache.hh" +#include "Screen.hh" +#include "Util.hh" +#include "Window.hh" +#include "Workspace.hh" + + +/* + * Initializes the class with default values/the window's set initial values. + */ +BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { + // fprintf(stderr, "BlackboxWindow size: %d bytes\n", + // sizeof(BlackboxWindow)); + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w); +#endif // DEBUG + + /* + set timer to zero... it is initialized properly later, so we check + if timer is zero in the destructor, and assume that the window is not + fully constructed if timer is zero... + */ + timer = 0; + blackbox = b; + client.window = w; + screen = s; + + if (! validateClient()) { + delete this; + return; + } + + // fetch client size and placement + XWindowAttributes wattrib; + if (! XGetWindowAttributes(blackbox->getXDisplay(), + client.window, &wattrib) || + ! wattrib.screen || wattrib.override_redirect) { +#ifdef DEBUG + fprintf(stderr, + "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n"); +#endif // DEBUG + + delete this; + return; + } + + // set the eventmask early in the game so that we make sure we get + // all the events we are interested in + XSetWindowAttributes attrib_set; + attrib_set.event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask; + XChangeWindowAttributes(blackbox->getXDisplay(), client.window, + CWEventMask|CWDontPropagate, &attrib_set); + + flags.moving = flags.resizing = flags.shaded = flags.visible = + flags.iconic = flags.focused = flags.stuck = flags.modal = + flags.send_focus_message = flags.shaped = False; + flags.maximized = 0; + + blackbox_attrib.workspace = window_number = BSENTINEL; + + blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack + = blackbox_attrib.decoration = 0l; + blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; + blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; + + frame.window = None; + + decorations = Decor_Titlebar | Decor_Border | Decor_Handle | + Decor_Iconify | Decor_Maximize; + functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; + frame_style = WindowsWMFrameStylePopup; + frame_style_ex = WindowsWMFrameStyleExAppWindow; + + client.normal_hint_flags = 0; + client.window_group = None; + client.transient_for = 0; + + current_state = NormalState; + + /* + set the initial size and location of client window (relative to the + _root window_). This position is the reference point used with the + window's gravity to find the window's initial position. + */ + client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height); + client.old_bw = wattrib.border_width; + + lastButtonPressTime = 0; + + timer = new BTimer(blackbox, this); + timer->setTimeout(blackbox->getAutoRaiseDelay()); + + // get size, aspect, minimum/maximum size and other hints set by the + // client + + if (! getBlackboxHints()) + getMWMHints(); + + getWMProtocols(); + getWMHints(); + getWMNormalHints(); + getWMClass(); + + frame.window = createToplevelWindow(); + blackbox->saveWindowSearch(frame.window, this); + + //FIXME: + if (! getBlackboxHints()) + getMWMHints(); + + getWMProtocols(); + getWMHints(); + getWMNormalHints(); + getWMClass(); + + XChangeProperty(blackbox->getXDisplay(), frame.window, + blackbox->WindowsWMClientWindow(), + XA_INTEGER, 32, + PropModeReplace, (unsigned char *) &client.window, 1); + + // determine if this is a transient window + getTransientInfo(); + + // adjust the window decorations based on transience and window sizes + + if (isTransient()) { + decorations &= ~(Decor_Maximize | Decor_Handle); + functions &= ~Func_Maximize; + } + + if ((client.normal_hint_flags & PMinSize) && + (client.normal_hint_flags & PMaxSize) && + client.max_width <= client.min_width && + client.max_height <= client.min_height) { + decorations &= ~(Decor_Maximize | Decor_Handle); + functions &= ~(Func_Resize | Func_Maximize); + } + + // now that we know what decorations are required, create them + + if (decorations & Decor_Titlebar) + createTitlebar(); + + if (decorations & Decor_Handle) + createHandle(); + + // apply the size and gravity hint to the frame + + upsize(); + + bool place_window = True; + if (blackbox->isStartup() || isTransient() || + client.normal_hint_flags & (PPosition|USPosition)) { + applyGravity(frame.rect); + + if (blackbox->isStartup() || client.rect.intersects(screen->getRect())) + place_window = False; + } + + /* + the server needs to be grabbed here to prevent client's from sending + events while we are in the process of configuring their window. + We hold the grab until after we are done moving the window around. + */ + + XGrabServer(blackbox->getXDisplay()); + + associateClientWindow(); + + blackbox->saveWindowSearch(client.window, this); + + if (blackbox_attrib.workspace >= screen->getWorkspaceCount()) + screen->getCurrentWorkspace()->addWindow(this, place_window); + else + screen->getWorkspace(blackbox_attrib.workspace)-> + addWindow(this, place_window); + + if (! place_window) { + // don't need to call configure if we are letting the workspace + // place the window + configure(frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height()); + } + + positionWindows(); + + XUngrabServer(blackbox->getXDisplay()); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions() && flags.shaped) + configureShape(); +#endif // SHAPE + + // now that we know where to put the window and what it should look like + // we apply the decorations + decorate(); + + grabButtons(); + + XMapSubwindows(blackbox->getXDisplay(), frame.window); + + // this ensures the title, buttons, and other decor are properly displayed + redrawWindowFrame(); + + // preserve the window's initial state on first map, and its current state + // across a restart + unsigned long initial_state = current_state; + if (! getState()) + current_state = initial_state; + + // the following flags are set by blackbox native apps only + if (flags.shaded) { + flags.shaded = False; + initial_state = current_state; + shade(); + + /* + At this point in the life of a window, current_state should only be set + to IconicState if the window was an *icon*, not if it was shaded. + */ + if (initial_state != IconicState) + current_state = NormalState; + } + + if (flags.stuck) { + flags.stuck = False; + stick(); + } + + if (flags.maximized && (functions & Func_Maximize)) + remaximize(); +} + + +BlackboxWindow::~BlackboxWindow(void) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n", + client.window); +#endif // DEBUG + + if (! timer) // window not managed... + return; + + if (flags.moving || flags.resizing) { + XUngrabPointer(blackbox->getXDisplay(), CurrentTime); + } + + delete timer; + + if (client.window_group) { + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (group) group->removeWindow(this); + } + + // remove ourselves from our transient_for + if (isTransient()) { + if (client.transient_for != (BlackboxWindow *) ~0ul) + client.transient_for->client.transientList.remove(this); + + client.transient_for = (BlackboxWindow*) 0; + } + + if (client.transientList.size() > 0) { + // reset transient_for for all transients + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + (*it)->client.transient_for = (BlackboxWindow*) 0; + } + + if (frame.window) { + blackbox->removeWindowSearch(frame.window); + XDestroyWindow(blackbox->getXDisplay(), frame.window); + } + + blackbox->removeWindowSearch(client.window); +} + + +/* + * Creates a new top level window, with a given location, size, and border + * width. + * Returns: the newly created window + */ +Window BlackboxWindow::createToplevelWindow(void) { + XSetWindowAttributes attrib_create; + unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap | + CWOverrideRedirect | CWEventMask; + + attrib_create.background_pixmap = None; + attrib_create.colormap = screen->getColormap(); + attrib_create.override_redirect = True; + attrib_create.event_mask = EnterWindowMask | LeaveWindowMask; + + return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(), + 0, 0, 1, 1, 0, screen->getDepth(), + InputOutput, screen->getVisual(), create_mask, + &attrib_create); +} + + +/* + * Creates a child window, and optionally associates a given cursor with + * the new window. + */ +Window BlackboxWindow::createChildWindow(Window parent, + unsigned long event_mask, + Cursor cursor) { + XSetWindowAttributes attrib_create; + unsigned long create_mask = CWBackPixmap | CWBorderPixel | + CWEventMask; + + attrib_create.background_pixmap = None; + attrib_create.event_mask = event_mask; + + if (cursor) { + create_mask |= CWCursor; + attrib_create.cursor = cursor; + } + + return XCreateWindow(blackbox->getXDisplay(), parent, 0, 0, 1, 1, 0, + screen->getDepth(), InputOutput, screen->getVisual(), + create_mask, &attrib_create); +} + + +void BlackboxWindow::associateClientWindow(void) { + XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, 0); + getWMName(); + getWMIconName(); + getWMClass(); + +#ifdef DEBUG + fprintf(stderr, "XWindowsWMFrameSetTitle %s\n", client.title.c_str()); +#endif + + XWindowsWMFrameSetTitle(blackbox->getXDisplay(), 0, frame.window, + client.title.length(), client.title.c_str()); + + XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeInsert); + + XSelectInput(blackbox->getXDisplay(), frame.window, + StructureNotifyMask|SubstructureRedirectMask); + + /* + note we used to grab around this call to XReparentWindow however the + server is now grabbed before this method is called + */ + unsigned long event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + XSelectInput(blackbox->getXDisplay(), client.window, + event_mask & ~StructureNotifyMask); + XReparentWindow(blackbox->getXDisplay(), client.window, frame.window, 0, 0); + XSelectInput(blackbox->getXDisplay(), client.window, event_mask); + + XMapSubwindows(blackbox->getXDisplay(), frame.window); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions()) { + XShapeSelectInput(blackbox->getXDisplay(), client.window, + ShapeNotifyMask); + + Bool shaped = False; + int foo; + unsigned int ufoo; + + XShapeQueryExtents(blackbox->getXDisplay(), client.window, &shaped, + &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo, + &ufoo, &ufoo); + flags.shaped = shaped; + } +#endif // SHAPE +} + + +void BlackboxWindow::decorate(void) { + if (decorations & Decor_Titlebar) { + frame_style |= WindowsWMFrameStyleCaption | WindowsWMFrameStyleSysMenu; + + decorateLabel(); + } else { + frame_style &= ~(WindowsWMFrameStyleCaption | WindowsWMFrameStyleSysMenu); + } + + if (decorations & Decor_Border) { + frame_style |= WindowsWMFrameStyleBorder; + blackbox_attrib.flags |= AttribDecoration; + blackbox_attrib.decoration = DecorNormal; + } else { + frame_style &= ~WindowsWMFrameStyleBorder; + blackbox_attrib.flags |= AttribDecoration; + blackbox_attrib.decoration = DecorNone; + } + + if (decorations & Decor_Handle) { + frame_style |= WindowsWMFrameStyleSizeBox; + } else { + frame_style &= ~WindowsWMFrameStyleSizeBox; + } + + XWindowsWMFrameDraw(blackbox->getXDisplay(), 0, frame.window, + frame_style, + frame_style_ex, + client.rect.x(), client.rect.y(), + client.rect.width(), client.rect.height()); + + XSetWindowBorder(blackbox->getXDisplay(), frame.window, + screen->getBorderColor()->pixel()); + + int prop = 1; + XChangeProperty(blackbox->getXDisplay(), frame.window, + blackbox->WindowsWMRaiseOnClick(), + XA_INTEGER, 32, + PropModeReplace, (unsigned char *) &prop, 1); + XChangeProperty(blackbox->getXDisplay(), frame.window, + blackbox->WindowsWMMouseActivate(), + XA_INTEGER, 32, + PropModeReplace, (unsigned char *) &prop, 1); +} + + +void BlackboxWindow::decorateLabel(void) { +} + + +void BlackboxWindow::createHandle(void) { +} + + +void BlackboxWindow::destroyHandle(void) { +} + + +void BlackboxWindow::createTitlebar(void) { +/* if (decorations & Decor_Iconify) createIconifyButton(); + if (decorations & Decor_Maximize) createMaximizeButton(); + if (decorations & Decor_Close) createCloseButton();*/ + if (decorations & Decor_Iconify) { + frame_style |= WindowsWMFrameStyleMinimizeBox; + } else { + frame_style &= ~WindowsWMFrameStyleMinimizeBox; + } + if (decorations & Decor_Maximize) { + frame_style |= WindowsWMFrameStyleMaximizeBox; + } else { + frame_style &= ~WindowsWMFrameStyleMaximizeBox; + } + //FIXME: how? + if (decorations & Decor_Close) { + } else { + } + XWindowsWMFrameDraw(blackbox->getXDisplay(), 0, frame.window, + frame_style, + frame_style_ex, + client.rect.x(), client.rect.y(), + client.rect.width(), client.rect.height()); +} + + +void BlackboxWindow::destroyTitlebar(void) { +} + + +void BlackboxWindow::createCloseButton(void) { +} + + +void BlackboxWindow::destroyCloseButton(void) { +} + + +void BlackboxWindow::createIconifyButton(void) { +} + + +void BlackboxWindow::destroyIconifyButton(void) { +} + + +void BlackboxWindow::createMaximizeButton(void) { +} + + +void BlackboxWindow::destroyMaximizeButton(void) { +} + + +void BlackboxWindow::positionButtons(bool /*redecorate_label*/) { +} + + +void BlackboxWindow::reconfigure(void) { + restoreGravity(client.rect); + upsize(); + applyGravity(frame.rect); + positionWindows(); + decorate(); + redrawWindowFrame(); + + ungrabButtons(); + grabButtons(); +} + + +void BlackboxWindow::grabButtons(void) { + if (! screen->isSloppyFocus() || screen->doClickRaise()) + // grab button 1 for changing focus/raising + blackbox->grabButton(Button1, 0, frame.window, True, ButtonPressMask, + GrabModeSync, GrabModeSync, frame.window, None, + screen->allowScrollLock()); + + if (functions & Func_Move) + blackbox->grabButton(Button1, Mod1Mask, frame.window, True, + ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, + GrabModeAsync, frame.window, + blackbox->getMoveCursor(), + screen->allowScrollLock()); + if (functions & Func_Resize) + blackbox->grabButton(Button3, Mod1Mask, frame.window, True, + ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, + GrabModeAsync, frame.window, + blackbox->getLowerRightAngleCursor(), + screen->allowScrollLock()); + // alt+middle lowers the window + blackbox->grabButton(Button2, Mod1Mask, frame.window, True, + ButtonReleaseMask, GrabModeAsync, GrabModeAsync, + frame.window, None, screen->allowScrollLock()); +} + + +void BlackboxWindow::ungrabButtons(void) { + blackbox->ungrabButton(Button1, 0, frame.window); + blackbox->ungrabButton(Button1, Mod1Mask, frame.window); + blackbox->ungrabButton(Button2, Mod1Mask, frame.window); + blackbox->ungrabButton(Button3, Mod1Mask, frame.window); +} + + +void BlackboxWindow::positionWindows(void) { +#if 1 + //FIXME: Get Windows window's rect and use it. + XMoveResizeWindow(blackbox->getXDisplay(), frame.window, + client.rect.x(), client.rect.y(), client.rect.width(), + (flags.shaded) ? 0 : client.rect.height()); + XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window, 0); + XMoveResizeWindow(blackbox->getXDisplay(), client.window, + 0, 0, client.rect.width(), client.rect.height()); + // ensure client.rect contains the real location + client.rect.setPos(frame.rect.left(), frame.rect.top()); + + if (decorations & Decor_Titlebar) { + //WindowsWM ext + } else { + } + if (decorations & Decor_Handle) { + //WindowsWM ext + } else { + } + XSync(blackbox->getXDisplay(), False); +#else + XMoveResizeWindow(blackbox->getXDisplay(), frame.window, + frame.rect.x(), frame.rect.y(), frame.inside_w, + (flags.shaded) ? frame.title_h : frame.inside_h); + XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window, + frame.border_w); + XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate, + frame.mwm_border_w); + XMoveResizeWindow(blackbox->getXDisplay(), frame.plate, + frame.margin.left - frame.mwm_border_w - frame.border_w, + frame.margin.top - frame.mwm_border_w - frame.border_w, + client.rect.width(), client.rect.height()); + XMoveResizeWindow(blackbox->getXDisplay(), client.window, + 0, 0, client.rect.width(), client.rect.height()); + // ensure client.rect contains the real location + client.rect.setPos(frame.rect.left() + frame.margin.left, + frame.rect.top() + frame.margin.top); + + if (decorations & Decor_Titlebar) { + //WindowsWM ext + } else { + } + if (decorations & Decor_Handle) { + //WindowsWM ext + } else { + } + XSync(blackbox->getXDisplay(), False); +#endif +} + + +void BlackboxWindow::getWMName(void) { + XTextProperty text_prop; + + std::string name; + + if (XGetWMName(blackbox->getXDisplay(), client.window, &text_prop)) { + name = textPropertyToString(blackbox->getXDisplay(), text_prop); + XFree((char *) text_prop.value); + } + if (! name.empty()) + client.title = name; + else + client.title = i18n(WindowSet, WindowUnnamed, "Unnamed"); + +#ifdef DEBUG_WITH_ID + // the 16 is the 8 chars of the debug text plus the number + char *tmp = new char[client.title.length() + 16]; + sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window); + client.title = tmp; + delete tmp; +#endif +} + + +void BlackboxWindow::getWMIconName(void) { + XTextProperty text_prop; + + std::string name; + + if (XGetWMIconName(blackbox->getXDisplay(), client.window, &text_prop)) { + name = textPropertyToString(blackbox->getXDisplay(), text_prop); + XFree((char *) text_prop.value); + } + if (! name.empty()) + client.icon_title = name; + else + client.icon_title = client.title; +} + + +/* + * Retrieve which WM Protocols are supported by the client window. + * If the WM_DELETE_WINDOW protocol is supported, add the close button to the + * window's decorations and allow the close behavior. + * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates + * this. + */ +void BlackboxWindow::getWMProtocols(void) { + Atom *proto; + int num_return = 0; + + if (XGetWMProtocols(blackbox->getXDisplay(), client.window, + &proto, &num_return)) { + for (int i = 0; i < num_return; ++i) { + if (proto[i] == blackbox->getWMDeleteAtom()) { + decorations |= Decor_Close; + functions |= Func_Close; + } else if (proto[i] == blackbox->getWMTakeFocusAtom()) { + flags.send_focus_message = True; + } else if (proto[i] == blackbox->getBlackboxStructureMessagesAtom()) { + screen->addNetizen(new Netizen(screen, client.window)); + } + } + + XFree(proto); + } +} + + +/* + * Gets the value of the WM_HINTS property. + * If the property is not set, then use a set of default values. + */ +void BlackboxWindow::getWMHints(void) { + focus_mode = F_Passive; + + // remove from current window group + if (client.window_group) { + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (group) group->removeWindow(this); + } + client.window_group = None; + + XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window); + if (! wmhint) + return; + + if (wmhint->flags & InputHint) { + if (wmhint->input == True) { + if (flags.send_focus_message) + focus_mode = F_LocallyActive; + } else { + if (flags.send_focus_message) + focus_mode = F_GloballyActive; + else + focus_mode = F_NoInput; + } + } + + if (wmhint->flags & StateHint) + current_state = wmhint->initial_state; + + if (wmhint->flags & WindowGroupHint) { + client.window_group = wmhint->window_group; + + // add window to the appropriate group + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (! group) { // no group found, create it! + new BWindowGroup(blackbox, client.window_group); + group = blackbox->searchGroup(client.window_group); + } + if (group) + group->addWindow(this); + } + + //FIXME: move to extension? + XSetWMHints(blackbox->getXDisplay(), frame.window, wmhint); + + XFree(wmhint); +} + + +/* + * Gets the value of the WM_NORMAL_HINTS property. + * If the property is not set, then use a set of default values. + */ +void BlackboxWindow::getWMNormalHints(void) { + long icccm_mask; + XSizeHints sizehint; + + client.min_width = client.min_height = + client.width_inc = client.height_inc = 1; + client.base_width = client.base_height = 0; + client.win_gravity = NorthWestGravity; +#if 0 + client.min_aspect_x = client.min_aspect_y = + client.max_aspect_x = client.max_aspect_y = 1; +#endif + + /* + use the full screen, not the strut modified size. otherwise when the + availableArea changes max_width/height will be incorrect and lead to odd + rendering bugs. + */ + const Rect& screen_area = screen->getRect(); + client.max_width = screen_area.width(); + client.max_height = screen_area.height(); + + if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window, + &sizehint, &icccm_mask)) + return; + + client.normal_hint_flags = sizehint.flags; + + if (sizehint.flags & PMinSize) { + if (sizehint.min_width >= 0) + client.min_width = sizehint.min_width; + if (sizehint.min_height >= 0) + client.min_height = sizehint.min_height; + } + + if (sizehint.flags & PMaxSize) { + if (sizehint.max_width > static_cast<signed>(client.min_width)) + client.max_width = sizehint.max_width; + else + client.max_width = client.min_width; + + if (sizehint.max_height > static_cast<signed>(client.min_height)) + client.max_height = sizehint.max_height; + else + client.max_height = client.min_height; + } + + if (sizehint.flags & PResizeInc) { + client.width_inc = sizehint.width_inc; + client.height_inc = sizehint.height_inc; + } + +#if 0 // we do not support this at the moment + if (sizehint.flags & PAspect) { + client.min_aspect_x = sizehint.min_aspect.x; + client.min_aspect_y = sizehint.min_aspect.y; + client.max_aspect_x = sizehint.max_aspect.x; + client.max_aspect_y = sizehint.max_aspect.y; + } +#endif + + if (sizehint.flags & PBaseSize) { + client.base_width = sizehint.base_width; + client.base_height = sizehint.base_height; + } + + if (sizehint.flags & PWinGravity) + client.win_gravity = sizehint.win_gravity; + + //FIXME: move to extension? + XSetWMNormalHints(blackbox->getXDisplay(), frame.window, &sizehint); + +#ifdef DEBUG + fprintf(stderr, "XSetWMNormalHints\n"); +#endif +} + + +/* + * Gets the MWM hints for the class' contained window. + * This is used while initializing the window to its first state, and not + * thereafter. + * Returns: true if the MWM hints are successfully retreived and applied; + * false if they are not. + */ +void BlackboxWindow::getMWMHints(void) { + int format; + Atom atom_return; + unsigned long num, len; + MwmHints *mwm_hint = 0; + + int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window, + blackbox->getMotifWMHintsAtom(), 0, + PropMwmHintsElements, False, + blackbox->getMotifWMHintsAtom(), &atom_return, + &format, &num, &len, + (unsigned char **) &mwm_hint); + + if (ret != Success || ! mwm_hint || num != PropMwmHintsElements) + return; + + if (mwm_hint->flags & MwmHintsDecorations) { + if (mwm_hint->decorations & MwmDecorAll) { + decorations = Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize | Decor_Close; + } else { + decorations = 0; + + if (mwm_hint->decorations & MwmDecorBorder) + decorations |= Decor_Border; + if (mwm_hint->decorations & MwmDecorHandle) + decorations |= Decor_Handle; + if (mwm_hint->decorations & MwmDecorTitle) + decorations |= Decor_Titlebar; + if (mwm_hint->decorations & MwmDecorIconify) + decorations |= Decor_Iconify; + if (mwm_hint->decorations & MwmDecorMaximize) + decorations |= Decor_Maximize; + } + } + + if (mwm_hint->flags & MwmHintsFunctions) { + if (mwm_hint->functions & MwmFuncAll) { + functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize | + Func_Close; + } else { + functions = 0; + + if (mwm_hint->functions & MwmFuncResize) + functions |= Func_Resize; + if (mwm_hint->functions & MwmFuncMove) + functions |= Func_Move; + if (mwm_hint->functions & MwmFuncIconify) + functions |= Func_Iconify; + if (mwm_hint->functions & MwmFuncMaximize) + functions |= Func_Maximize; + if (mwm_hint->functions & MwmFuncClose) + functions |= Func_Close; + } + } + XFree(mwm_hint); +} + + +/* + * Gets the value of the WM_CLASS property. + * If the property is not set, then use a set of default values. + */ +void BlackboxWindow::getWMClass(void) { + XClassHint classhints; + + //FIXME: move to extension? + if (XGetClassHint(blackbox->getXDisplay(), client.window, &classhints) == 0) + return; + + XSetClassHint(blackbox->getXDisplay(), frame.window, &classhints); + XFree(classhints.res_name); + XFree(classhints.res_class); +} + + +/* + * Gets the blackbox hints from the class' contained window. + * This is used while initializing the window to its first state, and not + * thereafter. + * Returns: true if the hints are successfully retreived and applied; false if + * they are not. + */ +bool BlackboxWindow::getBlackboxHints(void) { + int format; + Atom atom_return; + unsigned long num, len; + BlackboxHints *blackbox_hint = 0; + + int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window, + blackbox->getBlackboxHintsAtom(), 0, + PropBlackboxHintsElements, False, + blackbox->getBlackboxHintsAtom(), &atom_return, + &format, &num, &len, + (unsigned char **) &blackbox_hint); + if (ret != Success || ! blackbox_hint || num != PropBlackboxHintsElements) + return False; + + if (blackbox_hint->flags & AttribShaded) + flags.shaded = (blackbox_hint->attrib & AttribShaded); + + if ((blackbox_hint->flags & AttribMaxHoriz) && + (blackbox_hint->flags & AttribMaxVert)) + flags.maximized = (blackbox_hint->attrib & + (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0; + else if (blackbox_hint->flags & AttribMaxVert) + flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0; + else if (blackbox_hint->flags & AttribMaxHoriz) + flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0; + + if (blackbox_hint->flags & AttribOmnipresent) + flags.stuck = (blackbox_hint->attrib & AttribOmnipresent); + + if (blackbox_hint->flags & AttribWorkspace) + blackbox_attrib.workspace = blackbox_hint->workspace; + + // if (blackbox_hint->flags & AttribStack) + // don't yet have always on top/bottom for blackbox yet... working + // on that + + if (blackbox_hint->flags & AttribDecoration) { + switch (blackbox_hint->decoration) { + case DecorNone: + decorations = 0; + + break; + + case DecorTiny: + decorations |= Decor_Titlebar | Decor_Iconify; + decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize); + functions &= ~(Func_Resize | Func_Maximize); + + break; + + case DecorTool: + decorations |= Decor_Titlebar; + decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle); + functions &= ~(Func_Resize | Func_Maximize | Func_Iconify); + + break; + + case DecorNormal: + default: + decorations |= Decor_Titlebar | Decor_Border | Decor_Handle | + Decor_Iconify | Decor_Maximize; + break; + } + + reconfigure(); + } + XFree(blackbox_hint); + return True; +} + + +void BlackboxWindow::getTransientInfo(void) { + if (client.transient_for && + client.transient_for != (BlackboxWindow *) ~0ul) { + // reset transient_for in preparation of looking for a new owner + client.transient_for->client.transientList.remove(this); + } + + // we have no transient_for until we find a new one + client.transient_for = (BlackboxWindow *) 0; + + Window trans_for; + if (!XGetTransientForHint(blackbox->getXDisplay(), client.window, + &trans_for)) { + // transient_for hint not set + return; + } + + if (trans_for == client.window) { + // wierd client... treat this window as a normal window + return; + } + + if (trans_for == None || trans_for == screen->getRootWindow()) { + // this is an undocumented interpretation of the ICCCM. a transient + // associated with None/Root/itself is assumed to be a modal root + // transient. we don't support the concept of a global transient, + // so we just associate this transient with nothing, and perhaps + // we will add support later for global modality. + client.transient_for = (BlackboxWindow *) ~0ul; + flags.modal = True; + return; + } + + client.transient_for = blackbox->searchWindow(trans_for); + if (! client.transient_for && + client.window_group && trans_for == client.window_group) { + // no direct transient_for, perhaps this is a group transient? + BWindowGroup *group = blackbox->searchGroup(client.window_group); + if (group) client.transient_for = group->find(screen); + } + + if (! client.transient_for || client.transient_for == this) { + // no transient_for found, or we have a wierd client that wants to be + // a transient for itself, so we treat this window as a normal window + client.transient_for = (BlackboxWindow*) 0; + return; + } + + // Check for a circular transient state: this can lock up Blackbox + // when it tries to find the non-transient window for a transient. + BlackboxWindow *w = this; + while(w->client.transient_for && + w->client.transient_for != (BlackboxWindow *) ~0ul) { + if(w->client.transient_for == this) { + client.transient_for = (BlackboxWindow*) 0; + break; + } + w = w->client.transient_for; + } + + if (client.transient_for) { + // register ourselves with our new transient_for + client.transient_for->client.transientList.push_back(this); + flags.stuck = client.transient_for->flags.stuck; + } +} + + +BlackboxWindow *BlackboxWindow::getTransientFor(void) const { + if (client.transient_for && + client.transient_for != (BlackboxWindow*) ~0ul) + return client.transient_for; + return 0; +} + + +/* + * This function is responsible for updating both the client and the frame + * rectangles. + * According to the ICCCM a client message is not sent for a resize, only a + * move. + */ +void BlackboxWindow::configure(int dx, int dy, + unsigned int dw, unsigned int dh) { +#if 1 + bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) && + ! flags.moving); + + if (dw != frame.rect.width() || dh != frame.rect.height()) { + frame.rect.setRect(dx, dy, dw, dh); +#if 1 + short fx, fy, fw, fh; + XWindowsWMFrameGetRect(blackbox->getXDisplay(), + frame_style, frame_style_ex, 0, + frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height(), + &fx, &fy, &fw, &fh); + +#ifdef DEBUG + fprintf(stderr, "XWindowsWMFrameGetRect %d %d %d %d - %d %d %d %d\n", + frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height(), + fx, fy, fw, fh); +#endif + if (fx < 0 || fy <= 0) + frame.rect.setPos(frame.rect.x() - fx, frame.rect.y() - fy); +#endif + client.rect.setCoords(frame.rect.left(), + frame.rect.top(), + frame.rect.right(), + frame.rect.bottom()); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions() && flags.shaped) { + configureShape(); + } +#endif // SHAPE + + positionWindows(); + decorate(); + redrawWindowFrame(); + } else { + frame.rect.setPos(dx, dy); + + XMoveWindow(blackbox->getXDisplay(), frame.window, + frame.rect.x(), frame.rect.y()); + /* + we may have been called just after an opaque window move, so even though + the old coords match the new ones no ConfigureNotify has been sent yet. + There are likely other times when this will be relevant as well. + */ + if (! flags.moving) send_event = True; + } + + if (send_event) { + // if moving, the update and event will occur when the move finishes + client.rect.setPos(frame.rect.left(), frame.rect.top()); + + XEvent event; + event.type = ConfigureNotify; + + event.xconfigure.display = blackbox->getXDisplay(); + event.xconfigure.event = client.window; + event.xconfigure.window = client.window; + event.xconfigure.x = client.rect.x(); + event.xconfigure.y = client.rect.y(); + event.xconfigure.width = client.rect.width(); + event.xconfigure.height = client.rect.height(); + event.xconfigure.border_width = client.old_bw; + event.xconfigure.above = frame.window; + event.xconfigure.override_redirect = False; + + XSendEvent(blackbox->getXDisplay(), client.window, False, + StructureNotifyMask, &event); + screen->updateNetizenConfigNotify(&event); + XFlush(blackbox->getXDisplay()); + } +#else + bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) && + ! flags.moving); + + if (dw != frame.rect.width() || dh != frame.rect.height()) { + frame.rect.setRect(dx, dy, dw, dh); + frame.inside_w = frame.rect.width() - (frame.border_w * 2); + frame.inside_h = frame.rect.height() - (frame.border_w * 2); + + short fx, fy, fw, fh; + XWindowsWMFrameGetRect(blackbox->getXDisplay(), + frame_style, frame_style_ex, + frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height(), + &fx, &fy, &fw, &fh); + if (fx < 0 || fy <= 0) + frame.rect.setPos(frame.rect.x() - fx, frame.rect.y() - fy); + //if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0) + // frame.rect.setPos(0, 0); + + client.rect.setCoords(frame.rect.left() + frame.margin.left, + frame.rect.top() + frame.margin.top, + frame.rect.right() - frame.margin.right, + frame.rect.bottom() - frame.margin.bottom); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions() && flags.shaped) { + configureShape(); + } +#endif // SHAPE + + positionWindows(); + decorate(); + redrawWindowFrame(); + } else { + frame.rect.setPos(dx, dy); + + XMoveWindow(blackbox->getXDisplay(), frame.window, + frame.rect.x(), frame.rect.y()); + /* + we may have been called just after an opaque window move, so even though + the old coords match the new ones no ConfigureNotify has been sent yet. + There are likely other times when this will be relevant as well. + */ + if (! flags.moving) send_event = True; + } + + if (send_event) { + // if moving, the update and event will occur when the move finishes + client.rect.setPos(frame.rect.left() + frame.margin.left, + frame.rect.top() + frame.margin.top); + + XEvent event; + event.type = ConfigureNotify; + + event.xconfigure.display = blackbox->getXDisplay(); + event.xconfigure.event = client.window; + event.xconfigure.window = client.window; + event.xconfigure.x = client.rect.x(); + event.xconfigure.y = client.rect.y(); + event.xconfigure.width = client.rect.width(); + event.xconfigure.height = client.rect.height(); + event.xconfigure.border_width = client.old_bw; + event.xconfigure.above = frame.window; + event.xconfigure.override_redirect = False; + + XSendEvent(blackbox->getXDisplay(), client.window, False, + StructureNotifyMask, &event); + screen->updateNetizenConfigNotify(&event); + XFlush(blackbox->getXDisplay()); + } +#endif +} + + +#ifdef SHAPE +void BlackboxWindow::configureShape(void) { + XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding, + 0, 0, client.window, ShapeBounding, ShapeSet); + +#if 0 //FIXME + int num = 0; + XRectangle xrect[2]; + + if (decorations & Decor_Titlebar) { + xrect[0].x = xrect[0].y = -frame.border_w; + xrect[0].width = frame.rect.width(); + xrect[0].height = frame.title_h + (frame.border_w * 2); + ++num; + } + + if (decorations & Decor_Handle) { + xrect[1].x = -frame.border_w; + xrect[1].y = frame.rect.height() - frame.margin.bottom + + frame.mwm_border_w - frame.border_w; + xrect[1].width = frame.rect.width(); + xrect[1].height = frame.handle_h + (frame.border_w * 2); + ++num; + } + + XShapeCombineRectangles(blackbox->getXDisplay(), frame.window, + ShapeBounding, 0, 0, xrect, num, + ShapeUnion, Unsorted); +#endif +} +#endif // SHAPE + + +bool BlackboxWindow::setInputFocus(void) { + if (flags.focused) return True; + + // do not give focus to a window that is about to close + if (! validateClient()) return False; + + assert(! flags.iconic && + (flags.stuck || // window must be on the current workspace or sticky + blackbox_attrib.workspace == screen->getCurrentWorkspaceID())); + + if (! frame.rect.intersects(screen->getRect())) { + // client is outside the screen, move it to the center + configure((screen->getWidth() - frame.rect.width()) / 2, + (screen->getHeight() - frame.rect.height()) / 2, + frame.rect.width(), frame.rect.height()); + } + + if (client.transientList.size() > 0) { + // transfer focus to any modal transients + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + if ((*it)->flags.modal) return (*it)->setInputFocus(); + } + + bool ret = True; + switch (focus_mode) { + case F_Passive: + case F_LocallyActive: + XSetInputFocus(blackbox->getXDisplay(), client.window, + RevertToPointerRoot, CurrentTime); + blackbox->setFocusedWindow(this); + break; + + case F_GloballyActive: + case F_NoInput: + /* + * we could set the focus to none, since the window doesn't accept focus, + * but we shouldn't set focus to nothing since this would surely make + * someone angry + */ + ret = False; + break; + } + + if (flags.send_focus_message) { + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = blackbox->getWMProtocolsAtom(); + ce.xclient.display = blackbox->getXDisplay(); + ce.xclient.window = client.window; + ce.xclient.format = 32; + ce.xclient.data.l[0] = blackbox->getWMTakeFocusAtom(); + ce.xclient.data.l[1] = blackbox->getLastTime(); + ce.xclient.data.l[2] = 0l; + ce.xclient.data.l[3] = 0l; + ce.xclient.data.l[4] = 0l; + XSendEvent(blackbox->getXDisplay(), client.window, False, + NoEventMask, &ce); + XFlush(blackbox->getXDisplay()); + } + + return ret; +} + +//FIXME +void BlackboxWindow::iconify(void) { + // walk up to the topmost transient_for that is not iconified + if (isTransient() && + client.transient_for != (BlackboxWindow *) ~0ul && + ! client.transient_for->isIconic()) { + + client.transient_for->iconify(); + return; + } + + if (flags.iconic) return; + + /* + * unmap the frame window first, so when all the transients are + * unmapped, we don't get an enter event in sloppy focus mode + */ + XUnmapWindow(blackbox->getXDisplay(), frame.window); + flags.visible = False; + flags.iconic = True; + + setState(IconicState); + + // iconify all transients first + if (client.transientList.size() > 0) { + std::for_each(client.transientList.begin(), client.transientList.end(), + std::mem_fun(&BlackboxWindow::iconify)); + } + + /* + * remove the window from the workspace and add it to the screen's + * icons *AFTER* we have process all transients. since we always + * iconify transients, it's pointless to have focus reverted to one + * of them (since they are above their transient_for) for a split + * second + */ + screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this); + screen->addIcon(this); + + /* + * we don't want this XUnmapWindow call to generate an UnmapNotify event, so + * we need to clear the event mask on client.window for a split second. + * HOWEVER, since X11 is asynchronous, the window could be destroyed in that + * split second, leaving us with a ghost window... so, we need to do this + * while the X server is grabbed + */ + unsigned long event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + XGrabServer(blackbox->getXDisplay()); + XSelectInput(blackbox->getXDisplay(), client.window, + event_mask & ~StructureNotifyMask); + XUnmapWindow(blackbox->getXDisplay(), client.window); + XSelectInput(blackbox->getXDisplay(), client.window, event_mask); + XUngrabServer(blackbox->getXDisplay()); +} + + +void BlackboxWindow::show(void) { + current_state = (flags.shaded) ? IconicState : NormalState; + setState(current_state); + + XMapWindow(blackbox->getXDisplay(), client.window); + XMapSubwindows(blackbox->getXDisplay(), frame.window); + XMapWindow(blackbox->getXDisplay(), frame.window); + +#ifdef DEBUG + int real_x, real_y; + Window child; + XTranslateCoordinates(blackbox->getXDisplay(), client.window, + screen->getRootWindow(), + 0, 0, &real_x, &real_y, &child); + fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(), + client.rect.left(), client.rect.top(), real_x, real_y); + assert(client.rect.left() == real_x && client.rect.top() == real_y); +#endif + + flags.visible = True; + flags.iconic = False; +} + + +void BlackboxWindow::deiconify(bool reassoc, bool raise) { + if (flags.iconic || reassoc) + screen->reassociateWindow(this, BSENTINEL, False); + else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) + return; + + show(); + + // reassociate and deiconify all transients + if (reassoc && client.transientList.size() > 0) { + BlackboxWindowList::iterator it, end = client.transientList.end(); + for (it = client.transientList.begin(); it != end; ++it) + (*it)->deiconify(True, False); + } + + if (raise) + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); +} + + +void BlackboxWindow::close(void) { + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = blackbox->getWMProtocolsAtom(); + ce.xclient.display = blackbox->getXDisplay(); + ce.xclient.window = client.window; + ce.xclient.format = 32; + ce.xclient.data.l[0] = blackbox->getWMDeleteAtom(); + ce.xclient.data.l[1] = CurrentTime; + ce.xclient.data.l[2] = 0l; + ce.xclient.data.l[3] = 0l; + ce.xclient.data.l[4] = 0l; + XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce); + XFlush(blackbox->getXDisplay()); +} + + +void BlackboxWindow::withdraw(void) { + setState(current_state); + + flags.visible = False; + flags.iconic = False; + + XUnmapWindow(blackbox->getXDisplay(), frame.window); + + XGrabServer(blackbox->getXDisplay()); + + unsigned long event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + XSelectInput(blackbox->getXDisplay(), client.window, + event_mask & ~StructureNotifyMask); + XUnmapWindow(blackbox->getXDisplay(), client.window); + XSelectInput(blackbox->getXDisplay(), client.window, event_mask); + + XUngrabServer(blackbox->getXDisplay()); +} + + +void BlackboxWindow::maximize(unsigned int /*button*/) { +#if 0 + if (flags.maximized) { + flags.maximized = 0; + + blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert); + blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert); + + /* + when a resize is begun, maximize(0) is called to clear any maximization + flags currently set. Otherwise it still thinks it is maximized. + so we do not need to call configure() because resizing will handle it + */ + if (!flags.resizing) + configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y, + blackbox_attrib.premax_w, blackbox_attrib.premax_h); + + blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; + blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; + + redrawAllButtons(); // in case it is not called in configure() + setState(current_state); + return; + } + + blackbox_attrib.premax_x = frame.rect.x(); + blackbox_attrib.premax_y = frame.rect.y(); + blackbox_attrib.premax_w = frame.rect.width(); + // use client.rect so that clients can be restored even if shaded + blackbox_attrib.premax_h = + client.rect.height() + frame.margin.top + frame.margin.bottom; + + const Rect &screen_area = screen->availableArea(); + frame.changing = screen_area; + + switch(button) { + case 1: + blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; + break; + + case 2: + blackbox_attrib.flags |= AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxVert; + + frame.changing.setX(blackbox_attrib.premax_x); + frame.changing.setWidth(blackbox_attrib.premax_w); + break; + + case 3: + blackbox_attrib.flags |= AttribMaxHoriz; + blackbox_attrib.attrib |= AttribMaxHoriz; + + frame.changing.setY(blackbox_attrib.premax_y); + frame.changing.setHeight(blackbox_attrib.premax_h); + break; + } + + constrain(TopLeft); + + if (flags.shaded) { + blackbox_attrib.flags ^= AttribShaded; + blackbox_attrib.attrib ^= AttribShaded; + flags.shaded = False; + } + + flags.maximized = button; + + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + redrawAllButtons(); // in case it is not called in configure() + setState(current_state); +#endif +} + + +// re-maximizes the window to take into account availableArea changes +void BlackboxWindow::remaximize(void) { +#if 0 + if (flags.shaded) { + // we only update the window's attributes otherwise we lose the shade bit + switch(flags.maximized) { + case 1: + blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; + break; + + case 2: + blackbox_attrib.flags |= AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxVert; + break; + + case 3: + blackbox_attrib.flags |= AttribMaxHoriz; + blackbox_attrib.attrib |= AttribMaxHoriz; + break; + } + return; + } + + // save the original dimensions because maximize will wipe them out + int premax_x = blackbox_attrib.premax_x, + premax_y = blackbox_attrib.premax_y, + premax_w = blackbox_attrib.premax_w, + premax_h = blackbox_attrib.premax_h; + + unsigned int button = flags.maximized; + flags.maximized = 0; // trick maximize() into working + maximize(button); + + // restore saved values + blackbox_attrib.premax_x = premax_x; + blackbox_attrib.premax_y = premax_y; + blackbox_attrib.premax_w = premax_w; + blackbox_attrib.premax_h = premax_h; +#endif +} + + +void BlackboxWindow::setWorkspace(unsigned int n) { + blackbox_attrib.flags |= AttribWorkspace; + blackbox_attrib.workspace = n; +} + + +void BlackboxWindow::shade(void) { +#if 0 //needed? + if (flags.shaded) { + flags.shaded = False; + blackbox_attrib.flags ^= AttribShaded; + blackbox_attrib.attrib ^= AttribShaded; + + if (flags.maximized) { + remaximize(); + } else { + XResizeWindow(blackbox->getXDisplay(), frame.window, + frame.inside_w, frame.inside_h); + // set the frame rect to the normal size + frame.rect.setHeight(client.rect.height() + frame.margin.top + + frame.margin.bottom); + } + + setState(NormalState); + } else { + if (! (decorations & Decor_Titlebar)) + return; // can't shade it without a titlebar! + + XResizeWindow(blackbox->getXDisplay(), frame.window, + frame.inside_w, frame.title_h); + flags.shaded = True; + blackbox_attrib.flags |= AttribShaded; + blackbox_attrib.attrib |= AttribShaded; + + setState(IconicState); + + // set the frame rect to the shaded size + frame.rect.setHeight(frame.title_h + (frame.border_w * 2)); + } +#endif +} + + +void BlackboxWindow::stick(void) { + if (flags.stuck) { + blackbox_attrib.flags ^= AttribOmnipresent; + blackbox_attrib.attrib ^= AttribOmnipresent; + + flags.stuck = False; + + if (! flags.iconic) + screen->reassociateWindow(this, BSENTINEL, True); + + setState(current_state); + } else { + flags.stuck = True; + + blackbox_attrib.flags |= AttribOmnipresent; + blackbox_attrib.attrib |= AttribOmnipresent; + + setState(current_state); + } +} + + +void BlackboxWindow::redrawWindowFrame(void) const { + if (decorations & Decor_Titlebar) { + } + + if (decorations & Decor_Handle) { + } + + if (decorations & Decor_Border) { + } +} + + +void BlackboxWindow::setFocusFlag(bool focus) { + // only focus a window if it is visible + if (focus && ! flags.visible) + return; + + flags.focused = focus; + + redrawWindowFrame(); + + if (flags.focused) + blackbox->setFocusedWindow(this); +} + + +void BlackboxWindow::installColormap(bool install) { + int i = 0, ncmap = 0; + Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(), + client.window, &ncmap); + if (cmaps) { + XWindowAttributes wattrib; + if (XGetWindowAttributes(blackbox->getXDisplay(), + client.window, &wattrib)) { + if (install) { + // install the window's colormap + for (i = 0; i < ncmap; i++) { + if (*(cmaps + i) == wattrib.colormap) + // this window is using an installed color map... do not install + install = False; + } + // otherwise, install the window's colormap + if (install) + XInstallColormap(blackbox->getXDisplay(), wattrib.colormap); + } else { + // uninstall the window's colormap + for (i = 0; i < ncmap; i++) { + if (*(cmaps + i) == wattrib.colormap) + // we found the colormap to uninstall + XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap); + } + } + } + + XFree(cmaps); + } +} + + +void BlackboxWindow::setState(unsigned long new_state) { + current_state = new_state; + + unsigned long state[2]; + state[0] = current_state; + state[1] = None; + XChangeProperty(blackbox->getXDisplay(), client.window, + blackbox->getWMStateAtom(), blackbox->getWMStateAtom(), 32, + PropModeReplace, (unsigned char *) state, 2); + + XChangeProperty(blackbox->getXDisplay(), client.window, + blackbox->getBlackboxAttributesAtom(), + blackbox->getBlackboxAttributesAtom(), 32, PropModeReplace, + (unsigned char *) &blackbox_attrib, + PropBlackboxAttributesElements); +} + + +bool BlackboxWindow::getState(void) { + current_state = 0; + + Atom atom_return; + bool ret = False; + int foo; + unsigned long *state, ulfoo, nitems; + + if ((XGetWindowProperty(blackbox->getXDisplay(), client.window, + blackbox->getWMStateAtom(), + 0l, 2l, False, blackbox->getWMStateAtom(), + &atom_return, &foo, &nitems, &ulfoo, + (unsigned char **) &state) != Success) || + (! state)) { + return False; + } + + if (nitems >= 1) { + current_state = static_cast<unsigned long>(state[0]); + + ret = True; + } + + XFree((void *) state); + + return ret; +} + + +void BlackboxWindow::restoreAttributes(void) { + Atom atom_return; + int foo; + unsigned long ulfoo, nitems; + + BlackboxAttributes *net; + int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window, + blackbox->getBlackboxAttributesAtom(), 0l, + PropBlackboxAttributesElements, False, + blackbox->getBlackboxAttributesAtom(), + &atom_return, &foo, &nitems, &ulfoo, + (unsigned char **) &net); + if (ret != Success || !net || nitems != PropBlackboxAttributesElements) + return; + + if (net->flags & AttribShaded && net->attrib & AttribShaded) { + flags.shaded = False; + unsigned long orig_state = current_state; + shade(); + + /* + At this point in the life of a window, current_state should only be set + to IconicState if the window was an *icon*, not if it was shaded. + */ + if (orig_state != IconicState) + current_state = WithdrawnState; + } + + if (net->workspace != screen->getCurrentWorkspaceID() && + net->workspace < screen->getWorkspaceCount()) { + screen->reassociateWindow(this, net->workspace, True); + + // set to WithdrawnState so it will be mapped on the new workspace + if (current_state == NormalState) current_state = WithdrawnState; + } else if (current_state == WithdrawnState) { + // the window is on this workspace and is Withdrawn, so it is waiting to + // be mapped + current_state = NormalState; + } + + if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) { + flags.stuck = False; + stick(); + + // if the window was on another workspace, it was going to be hidden. this + // specifies that the window should be mapped since it is sticky. + if (current_state == WithdrawnState) current_state = NormalState; + } + + if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) { + blackbox_attrib.premax_x = net->premax_x; + blackbox_attrib.premax_y = net->premax_y; + blackbox_attrib.premax_w = net->premax_w; + blackbox_attrib.premax_h = net->premax_h; + + flags.maximized = 0; + + if (net->flags & AttribMaxHoriz && net->flags & AttribMaxVert && + net->attrib & (AttribMaxHoriz | AttribMaxVert)) + flags.maximized = 1; + else if (net->flags & AttribMaxVert && net->attrib & AttribMaxVert) + flags.maximized = 2; + else if (net->flags & AttribMaxHoriz && net->attrib & AttribMaxHoriz) + flags.maximized = 3; + + if (flags.maximized) remaximize(); + } + + if (net->flags & AttribDecoration) { + switch (net->decoration) { + case DecorNone: + decorations = 0; + + break; + + default: + case DecorNormal: + decorations |= Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize; + + break; + + case DecorTiny: + decorations |= Decor_Titlebar | Decor_Iconify; + decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize); + + break; + + case DecorTool: + decorations |= Decor_Titlebar; + decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle); + + break; + } + + // sanity check the new decor + if (! (functions & Func_Resize) || isTransient()) + decorations &= ~(Decor_Maximize | Decor_Handle); + if (! (functions & Func_Maximize)) + decorations &= ~Decor_Maximize; + + if (decorations & Decor_Titlebar) { + if (functions & Func_Close) // close button is controlled by function + decorations |= Decor_Close; // not decor type + } else { + if (flags.shaded) // we can not be shaded if we lack a titlebar + shade(); + } + + if (flags.visible && frame.window) { + XMapSubwindows(blackbox->getXDisplay(), frame.window); + XMapWindow(blackbox->getXDisplay(), frame.window); + } + + reconfigure(); + setState(current_state); + } + + // with the state set it will then be the map event's job to read the + // window's state and behave accordingly + + XFree((void *) net); +} + + +/* + * Positions the Rect r according the the client window position and + * window gravity. + */ +void BlackboxWindow::applyGravity(Rect &r) { + // apply horizontal window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case SouthWestGravity: + case WestGravity: + r.setX(client.rect.x()); + break; + + case NorthGravity: + case SouthGravity: + case CenterGravity: + r.setX(client.rect.x()); + break; + + case NorthEastGravity: + case SouthEastGravity: + case EastGravity: + r.setX(client.rect.x()); + break; + + case ForgetGravity: + case StaticGravity: + r.setX(client.rect.x()); + break; + } + + // apply vertical window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case NorthEastGravity: + case NorthGravity: + r.setY(client.rect.y()); + break; + + case CenterGravity: + case EastGravity: + case WestGravity: + r.setY(client.rect.y()); + break; + + case SouthWestGravity: + case SouthEastGravity: + case SouthGravity: + r.setY(client.rect.y()); + break; + + case ForgetGravity: + case StaticGravity: + r.setY(client.rect.y()); + break; + } +} + + +/* + * The reverse of the applyGravity function. + * + * Positions the Rect r according to the frame window position and + * window gravity. + */ +void BlackboxWindow::restoreGravity(Rect &r) { + // restore horizontal window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case SouthWestGravity: + case WestGravity: + r.setX(frame.rect.x()); + break; + + case NorthGravity: + case SouthGravity: + case CenterGravity: + r.setX(frame.rect.x()); + break; + + case NorthEastGravity: + case SouthEastGravity: + case EastGravity: + r.setX(frame.rect.x()); + break; + + case ForgetGravity: + case StaticGravity: + r.setX(frame.rect.x()); + break; + } + + // restore vertical window gravity + switch (client.win_gravity) { + default: + case NorthWestGravity: + case NorthEastGravity: + case NorthGravity: + r.setY(frame.rect.y()); + break; + + case CenterGravity: + case EastGravity: + case WestGravity: + r.setY(frame.rect.y()); + break; + + case SouthWestGravity: + case SouthEastGravity: + case SouthGravity: + r.setY(frame.rect.y()); + break; + + case ForgetGravity: + case StaticGravity: + r.setY(frame.rect.y()); + break; + } +} + + +void BlackboxWindow::redrawLabel(void) const { + //WindowsWM ext +#ifdef DEBUG + fprintf(stderr, "XWindowsWMFrameSetTitle %s\n", client.title.c_str()); +#endif + XWindowsWMFrameSetTitle(blackbox->getXDisplay(), 0, frame.window, + client.title.length(), client.title.c_str()); +} + + +void BlackboxWindow::redrawAllButtons(void) const { +} + + +void BlackboxWindow::redrawIconifyButton(bool /*pressed*/) const { +} + + +void BlackboxWindow::redrawMaximizeButton(bool /*pressed*/) const { +} + + +void BlackboxWindow::redrawCloseButton(bool /*pressed*/) const { +} + +//FIXME: +void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { + if (re->window != client.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n", + client.window); +#endif // DEBUG + + switch (current_state) { + case IconicState: + iconify(); + break; + + case WithdrawnState: + withdraw(); + break; + + case NormalState: + case InactiveState: + case ZoomState: + default: + show(); + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) { + XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped.. + setInputFocus(); + } + break; + } +} + + +void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) { + if (ue->window != client.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n", + client.window); +#endif // DEBUG + + screen->unmanageWindow(this, False); +} + + +void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) { + if (de->window != client.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n", + client.window); +#endif // DEBUG + + screen->unmanageWindow(this, False); +} + + +void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) { + if (re->window != client.window || re->parent == frame.window) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to " + "0x%lx.\n", client.window, re->parent); +#endif // DEBUG + + XEvent ev; + ev.xreparent = *re; + XPutBackEvent(blackbox->getXDisplay(), &ev); + screen->unmanageWindow(this, True); +} + + +void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { + if (pe->state == PropertyDelete || ! validateClient()) + return; + +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n", + client.window); +#endif + + switch(pe->atom) { + case XA_WM_CLASS: + getWMClass(); + break; + + case XA_WM_CLIENT_MACHINE: + case XA_WM_COMMAND: + break; + + case XA_WM_TRANSIENT_FOR: { + // determine if this is a transient window + getTransientInfo(); + + // adjust the window decorations based on transience + if (isTransient()) { + decorations &= ~(Decor_Maximize | Decor_Handle); + functions &= ~Func_Maximize; + } + + reconfigure(); + } + break; + + case XA_WM_HINTS: + getWMHints(); + break; + + case XA_WM_ICON_NAME: + getWMIconName(); + if (flags.iconic) screen->propagateWindowName(this); + break; + + case XA_WM_NAME: + getWMName(); + + if (decorations & Decor_Titlebar) + redrawLabel(); + + screen->propagateWindowName(this); + break; + + case XA_WM_NORMAL_HINTS: { + getWMNormalHints(); + + if ((client.normal_hint_flags & PMinSize) && + (client.normal_hint_flags & PMaxSize)) { + // the window now can/can't resize itself, so the buttons need to be + // regrabbed. + ungrabButtons(); + if (client.max_width <= client.min_width && + client.max_height <= client.min_height) { + decorations &= ~(Decor_Maximize | Decor_Handle); + functions &= ~(Func_Resize | Func_Maximize); + } else { + if (! isTransient()) { + decorations |= Decor_Maximize | Decor_Handle; + functions |= Func_Maximize; + } + functions |= Func_Resize; + } + grabButtons(); + } + + Rect old_rect = frame.rect; + + upsize(); + + if (old_rect != frame.rect) + reconfigure(); + + break; + } + + default: + if (pe->atom == blackbox->getWMProtocolsAtom()) { + getWMProtocols(); +/* + if ((decorations & Decor_Close) && (! frame.close_button)) { + createCloseButton(); + if (decorations & Decor_Titlebar) { + positionButtons(True); + XMapSubwindows(blackbox->getXDisplay(), frame.title); + } + if (windowmenu) windowmenu->reconfigure(); + }*/ + } + + break; + } +} + + +void BlackboxWindow::exposeEvent(const XExposeEvent */*ee*/) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window); +#endif +} + + +void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) { + if (cr->window != client.window || flags.iconic) + return; + + if (cr->value_mask & CWBorderWidth) + client.old_bw = cr->border_width; + + if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) { + Rect req = frame.rect; + + if (cr->value_mask & (CWX | CWY)) { + if (cr->value_mask & CWX) + client.rect.setX(cr->x); + if (cr->value_mask & CWY) + client.rect.setY(cr->y); + + applyGravity(req); + } + + if (cr->value_mask & CWWidth) + req.setWidth(cr->width); + + if (cr->value_mask & CWHeight) + req.setHeight(cr->height); + + configure(req.x(), req.y(), req.width(), req.height()); + } + + if (cr->value_mask & CWStackMode) { + switch (cr->detail) { + case Below: + case BottomIf: + screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); + break; + + case Above: + case TopIf: + default: + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + break; + } + } +} + + +void BlackboxWindow::configureNotifyEvent(const XConfigureEvent */*ce*/) { +#if 0 + if (ce->window != frame.window) + return; + + if (ce->width != client.rect.width() || ce->height != client.rect.height()) + { + XResizeWindow(blackbox->getXDisplay(), client.window, + ce->width, ce->height); + } + + if (ce->x != client.rect.x() || + ce->y != client.rect.y() || + ce->width != client.rect.width() || + ce->height != client.rect.height()) + { + bool send_event = ((frame.rect.x() != ce->x || frame.rect.y() != ce->y) && + ! flags.moving); + + frame.rect.setRect(ce->x, ce->y, ce->width, ce->height); + + if (send_event) { + // if moving, the update and event will occur when the move finishes + //client.rect.setPos(frame.rect.left(), frame.rect.top()); + client.rect.setCoords(frame.rect.left(), + frame.rect.top(), + frame.rect.right(), + frame.rect.bottom()); + + XEvent event; + event.type = ConfigureNotify; + + event.xconfigure.display = blackbox->getXDisplay(); + event.xconfigure.event = client.window; + event.xconfigure.window = client.window; + event.xconfigure.x = client.rect.x(); + event.xconfigure.y = client.rect.y(); + event.xconfigure.width = client.rect.width(); + event.xconfigure.height = client.rect.height(); + event.xconfigure.border_width = client.old_bw; + event.xconfigure.above = frame.window; + event.xconfigure.override_redirect = False; + + XSendEvent(blackbox->getXDisplay(), client.window, False, + StructureNotifyMask, &event); + screen->updateNetizenConfigNotify(&event); + XFlush(blackbox->getXDisplay()); + } + } +#endif +} + + +void BlackboxWindow::buttonPressEvent(const XButtonEvent */*be*/) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n", + client.window); +#endif +#if 0 + if (frame.maximize_button == be->window) { + redrawMaximizeButton(True); + } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) { + if (! flags.focused) + setInputFocus(); + + if (frame.iconify_button == be->window) { + redrawIconifyButton(True); + } else if (frame.close_button == be->window) { + redrawCloseButton(True); + } else if (frame.window == be->window) { + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + + XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time); + } else { + if (frame.title == be->window || frame.label == be->window) { + if (((be->time - lastButtonPressTime) <= + blackbox->getDoubleClickInterval()) || + (be->state == ControlMask)) { + lastButtonPressTime = 0; + shade(); + } else { + lastButtonPressTime = be->time; + } + } + + frame.grab_x = be->x_root - frame.rect.x() - frame.border_w; + frame.grab_y = be->y_root - frame.rect.y() - frame.border_w; + + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + } + } else if (be->button == 2 && (be->window != frame.iconify_button) && + (be->window != frame.close_button)) { + screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); + } +#endif +} + + +void BlackboxWindow::buttonReleaseEvent(const XButtonEvent */*re*/) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n", + client.window); +#endif +#if 0 + if (re->window == frame.maximize_button) { + if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) { + maximize(re->button); + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + } else { + redrawMaximizeButton(flags.maximized); + } + } else if (re->window == frame.iconify_button) { + if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) { + iconify(); + } else { + redrawIconifyButton(False); + } + } else if (re->window == frame.close_button) { + if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) + close(); + redrawCloseButton(False); + } else if (flags.moving) { + flags.moving = False; + + if (! screen->doOpaqueMove()) { + /* when drawing the rubber band, we need to make sure we only draw inside + * the frame... frame.changing_* contain the new coords for the window, + * so we need to subtract 1 from changing_w/changing_h every where we + * draw the rubber band (for both moving and resizing) + */ + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + XUngrabServer(blackbox->getXDisplay()); + + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + } else { + configure(frame.rect.x(), frame.rect.y(), + frame.rect.width(), frame.rect.height()); + } + XUngrabPointer(blackbox->getXDisplay(), CurrentTime); + } else if (flags.resizing) { + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + XUngrabServer(blackbox->getXDisplay()); + + constrain((re->window == frame.left_grip) ? TopRight : TopLeft); + + // unset maximized state when resized after fully maximized + if (flags.maximized == 1) + maximize(0); + flags.resizing = False; + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); + + XUngrabPointer(blackbox->getXDisplay(), CurrentTime); + } else if (re->window == frame.window) { + if (re->button == 2 && re->state == Mod1Mask) + XUngrabPointer(blackbox->getXDisplay(), CurrentTime); + } +#endif +} + + +void BlackboxWindow::motionNotifyEvent(const XMotionEvent */*me*/) { +#ifdef DEBUG + fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n", + client.window); +#endif +#if 0 + if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) && + (frame.title == me->window || frame.label == me->window || + frame.handle == me->window || frame.window == me->window)) { + if (! flags.moving) { + XGrabPointer(blackbox->getXDisplay(), me->window, False, + Button1MotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, + None, blackbox->getMoveCursor(), CurrentTime); + + flags.moving = True; + + if (! screen->doOpaqueMove()) { + XGrabServer(blackbox->getXDisplay()); + + frame.changing = frame.rect; + screen->showPosition(frame.changing.x(), frame.changing.y()); + + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + } + } else { + int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y; + dx -= frame.border_w; + dy -= frame.border_w; + + const int snap_distance = screen->getEdgeSnapThreshold(); + + if (snap_distance) { + Rect srect = screen->availableArea(); + // window corners + const int wleft = dx, + wright = dx + frame.rect.width() - 1, + wtop = dy, + wbottom = dy + frame.rect.height() - 1; + + int dleft = abs(wleft - srect.left()), + dright = abs(wright - srect.right()), + dtop = abs(wtop - srect.top()), + dbottom = abs(wbottom - srect.bottom()); + + // snap left? + if (dleft < snap_distance && dleft <= dright) + dx = srect.left(); + // snap right? + else if (dright < snap_distance) + dx = srect.right() - frame.rect.width() + 1; + + // snap top? + if (dtop < snap_distance && dtop <= dbottom) + dy = srect.top(); + // snap bottom? + else if (dbottom < snap_distance) + dy = srect.bottom() - frame.rect.height() + 1; + + if (! screen->doFullMax()) { + srect = screen->getRect(); // now get the full screen + + dleft = abs(wleft - srect.left()), + dright = abs(wright - srect.right()), + dtop = abs(wtop - srect.top()), + dbottom = abs(wbottom - srect.bottom()); + + // snap left? + if (dleft < snap_distance && dleft <= dright) + dx = srect.left(); + // snap right? + else if (dright < snap_distance) + dx = srect.right() - frame.rect.width() + 1; + + // snap top? + if (dtop < snap_distance && dtop <= dbottom) + dy = srect.top(); + // snap bottom? + else if (dbottom < snap_distance) + dy = srect.bottom() - frame.rect.height() + 1; + } + } + + if (screen->doOpaqueMove()) { + configure(dx, dy, frame.rect.width(), frame.rect.height()); + } else { + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + + frame.changing.setPos(dx, dy); + + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + } + + screen->showPosition(dx, dy); + } + } else if ((functions & Func_Resize) && + (me->state & Button1Mask && (me->window == frame.right_grip || + me->window == frame.left_grip)) || + (me->state & Button3Mask && me->state & Mod1Mask && + me->window == frame.window)) { + bool left = (me->window == frame.left_grip); + + if (! flags.resizing) { + XGrabServer(blackbox->getXDisplay()); + XGrabPointer(blackbox->getXDisplay(), me->window, False, + ButtonMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, + ((left) ? blackbox->getLowerLeftAngleCursor() : + blackbox->getLowerRightAngleCursor()), + CurrentTime); + + flags.resizing = True; + + unsigned int gw, gh; + frame.grab_x = me->x; + frame.grab_y = me->y; + frame.changing = frame.rect; + + constrain((left) ? TopRight : TopLeft, &gw, &gh); + + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + } else { + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + + unsigned int gw, gh; + + Corner anchor; + + if (left) { + anchor = TopRight; + frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(), + frame.rect.right(), frame.rect.bottom()); + frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y)); + } else { + anchor = TopLeft; + frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x), + frame.rect.height() + (me->y - frame.grab_y)); + } + + constrain(anchor, &gw, &gh); + + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), frame.changing.x(), frame.changing.y(), + frame.changing.width() - 1, frame.changing.height() - 1); + } + } +#endif +} + + +void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) { + if (! (screen->isSloppyFocus() && isVisible())) + return; + + XEvent e; + bool leave = False, inferior = False; + + while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window, + LeaveNotify, &e)) { + if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) { + leave = True; + inferior = (e.xcrossing.detail == NotifyInferior); + } + } + + if ((! leave || inferior) && ! isFocused()) { + bool success = setInputFocus(); + if (success) // if focus succeeded install the colormap + installColormap(True); // XXX: shouldnt we honour no install? + } + + if (screen->doAutoRaise()) + timer->start(); +} + + +void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) { + if (! (screen->isSloppyFocus() && screen->doAutoRaise())) + return; + + installColormap(False); + + if (timer->isTiming()) + timer->stop(); +} + + +#ifdef SHAPE +void BlackboxWindow::shapeEvent(XShapeEvent *) { + if (blackbox->hasShapeExtensions() && flags.shaped) { + configureShape(); + } +} +#endif // SHAPE + + +void BlackboxWindow::windowsWMControllerEvent(XWindowsWMNotifyEvent *windows_wm_event) { +#ifdef DEBUG + printf (stderr, "\tBlackboxWindow::windowsWMControllerEvent %d %d\n", + blackbox->hasWindowsWMExtensions(), windows_wm_event->arg); +#endif + if (blackbox->hasWindowsWMExtensions()) { + switch (windows_wm_event->arg) { + case WindowsWMMaximizeWindow: + //printf ("maximize 0x%08x\n", (int)c); + //raise_win(c); +#ifdef DEBUG + fprintf (stderr, "\tWindowsWMMaximizeWindow\n"); +#endif + break; + case WindowsWMMinimizeWindow: + //printf ("minimize 0x%08x\n", (int)c); + //iconify();//FIXME: This window become hide. Configure this behavior by ext? +#ifdef DEBUG + fprintf (stderr,"\tWindowsWMMinimizeWindow\n"); +#endif + //FIXME: + screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); + break; + case WindowsWMCloseWindow: +#ifdef DEBUG + fprintf (stderr, "\tWindowsWMCloseWindow\n"); +#endif + close(); + break; +#if 0 + case WindowsWMMoveWindow: +#ifdef DEBUG + fprintf (stderr, "move 0x%08x\n", (int)c); +#endif + c->x = e->x; + c->y = e->y; + XMoveWindow(dpy, c->frame, c->x, c->y - theight(c)); + send_config(c); + break; + case WindowsWMResizeWindow: +#ifdef DEBUG + fprintf (stderr, "resize 0x%08x\n", (int)c); +#endif + c->width = e->w; + c->height = e->h; + XMoveResizeWindow(dpy, c->frame, + c->x, c->y - theight(c), c->width, c->height + theight(c)); + XMoveResizeWindow(dpy, c->window, + 0, theight(c), c->width, c->height); + send_config(c); + break; +#endif + case WindowsWMActivateWindow: +#ifdef DEBUG + fprintf (stderr, "\tWindowsWMActivateWindow\n"); +#endif + if (! flags.focused) + setInputFocus(); + break; + default: + break; + } + } +} + + +bool BlackboxWindow::validateClient(void) const { + XSync(blackbox->getXDisplay(), False); + + XEvent e; + if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window, + DestroyNotify, &e) || + XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window, + UnmapNotify, &e)) { + XPutBackEvent(blackbox->getXDisplay(), &e); + + return False; + } + + return True; +} + + +void BlackboxWindow::restore(bool remap) { + XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete); + XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask); + + // do not leave a shaded window as an icon unless it was an icon + if (flags.shaded && ! flags.iconic) + current_state = NormalState; + + setState(current_state); + + restoreGravity(client.rect); + + XUnmapWindow(blackbox->getXDisplay(), frame.window); + XUnmapWindow(blackbox->getXDisplay(), client.window); + + XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw); + + XEvent ev; + if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window, + ReparentNotify, &ev)) { + remap = True; + } else { + // according to the ICCCM - if the client doesn't reparent to + // root, then we have to do it for them + XReparentWindow(blackbox->getXDisplay(), client.window, + screen->getRootWindow(), + client.rect.x(), client.rect.y()); + } + + if (remap) XMapWindow(blackbox->getXDisplay(), client.window); +} + + +// timer for autoraise +void BlackboxWindow::timeout(void) { + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); +} + + +void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) { + if ((net->flags & AttribShaded) && + ((blackbox_attrib.attrib & AttribShaded) != + (net->attrib & AttribShaded))) + shade(); + + if (flags.visible && // watch out for requests when we can not be seen + (net->flags & (AttribMaxVert | AttribMaxHoriz)) && + ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) != + (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) { + if (flags.maximized) { + maximize(0); + } else { + int button = 0; + + if (net->flags & AttribMaxHoriz && net->flags & AttribMaxVert && + net->attrib & (AttribMaxHoriz | AttribMaxVert)) + button = 1; + else if (net->flags & AttribMaxVert && net->attrib & AttribMaxVert) + button = 2; + else if (net->flags & AttribMaxHoriz && net->attrib & AttribMaxHoriz) + button = 3; + + maximize(button); + } + } + + if ((net->flags & AttribOmnipresent) && + ((blackbox_attrib.attrib & AttribOmnipresent) != + (net->attrib & AttribOmnipresent))) + stick(); + + if ((net->flags & AttribWorkspace) && + (blackbox_attrib.workspace != net->workspace)) { + screen->reassociateWindow(this, net->workspace, True); + + if (screen->getCurrentWorkspaceID() != net->workspace) { + withdraw(); + } else { + show(); + screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); + } + } + + if (net->flags & AttribDecoration) { + switch (net->decoration) { + case DecorNone: + decorations = 0; + + break; + + default: + case DecorNormal: + decorations |= Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize; + + break; + + case DecorTiny: + decorations |= Decor_Titlebar | Decor_Iconify; + decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize); + + break; + + case DecorTool: + decorations |= Decor_Titlebar; + decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle); + + break; + } + + // sanity check the new decor + if (! (functions & Func_Resize) || isTransient()) + decorations &= ~(Decor_Maximize | Decor_Handle); + if (! (functions & Func_Maximize)) + decorations &= ~Decor_Maximize; + if (! (functions & Func_Iconify)) + decorations &= ~Decor_Iconify; + if (decorations & Decor_Titlebar) { + if (functions & Func_Close) // close button is controlled by function + decorations |= Decor_Close; // not decor type + } else { + if (flags.shaded) // we can not be shaded if we lack a titlebar + shade(); + } + + if (flags.visible && frame.window) { + XMapSubwindows(blackbox->getXDisplay(), frame.window); + XMapWindow(blackbox->getXDisplay(), frame.window); + } + + reconfigure(); + setState(current_state); + } +} + + +/* + * Set the sizes of all components of the window frame + * (the window decorations). + * These values are based upon the current style settings and the client + * window's dimensions. + */ +void BlackboxWindow::upsize(void) { + unsigned int + height = client.rect.height(), + width = client.rect.width(); + + frame.rect.setSize(width, height); +} + + +/* + * Calculate the size of the client window and constrain it to the + * size specified by the size hints of the client window. + * + * The logical width and height are placed into pw and ph, if they + * are non-zero. Logical size refers to the users perception of + * the window size (for example an xterm resizes in cells, not in pixels). + * pw and ph are then used to display the geometry during window moves, resize, + * etc. + * + * The physical geometry is placed into frame.changing_{x,y,width,height}. + * Physical geometry refers to the geometry of the window in pixels. + */ +void BlackboxWindow::constrain(Corner anchor, + unsigned int *pw, unsigned int *ph) { + // frame.changing represents the requested frame size, we need to + // strip the frame margin off and constrain the client size + frame.changing.setCoords(frame.changing.left(), frame.changing.top(), + frame.changing.right(), frame.changing.bottom()); + + unsigned int dw = frame.changing.width(), dh = frame.changing.height(), + base_width = (client.base_width) ? client.base_width : client.min_width, + base_height = (client.base_height) ? client.base_height : + client.min_height; + + // constrain + if (dw < client.min_width) dw = client.min_width; + if (dh < client.min_height) dh = client.min_height; + if (dw > client.max_width) dw = client.max_width; + if (dh > client.max_height) dh = client.max_height; + + assert(dw >= base_width && dh >= base_height); + + if (client.width_inc > 1) { + dw -= base_width; + dw /= client.width_inc; + } + if (client.height_inc > 1) { + dh -= base_height; + dh /= client.height_inc; + } + + if (pw) + *pw = dw; + + if (ph) + *ph = dh; + + if (client.width_inc > 1) { + dw *= client.width_inc; + dw += base_width; + } + if (client.height_inc > 1) { + dh *= client.height_inc; + dh += base_height; + } + + frame.changing.setSize(dw, dh); + + // add the frame margin back onto frame.changing + frame.changing.setCoords(frame.changing.left(), frame.changing.top(), + frame.changing.right(), frame.changing.bottom()); + + // move frame.changing to the specified anchor + switch (anchor) { + case TopLeft: + // nothing to do + break; + + case TopRight: + int dx = frame.rect.right() - frame.changing.right(); + frame.changing.setPos(frame.changing.x() + dx, frame.changing.y()); + break; + } +} + + +BWindowGroup::BWindowGroup(Blackbox *b, Window _group) + : blackbox(b), group(_group) { + XWindowAttributes wattrib; + if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) { + // group window doesn't seem to exist anymore + delete this; + return; + } + + XSelectInput(blackbox->getXDisplay(), group, + PropertyChangeMask | FocusChangeMask | StructureNotifyMask); + + blackbox->saveGroupSearch(group, this); +} + + +BWindowGroup::~BWindowGroup(void) { + blackbox->removeGroupSearch(group); +} + + +BlackboxWindow * +BWindowGroup::find(BScreen *screen, bool allow_transients) const { + BlackboxWindow *ret = blackbox->getFocusedWindow(); + + // does the focus window match (or any transient_fors)? + for (; ret; ret = ret->getTransientFor()) { + if (ret->getScreen() == screen && ret->getGroupWindow() == group && + (! ret->isTransient() || allow_transients)) + break; + } + + if (ret) return ret; + + // the focus window didn't match, look in the group's window list + BlackboxWindowList::const_iterator it, end = windowList.end(); + for (it = windowList.begin(); it != end; ++it) { + ret = *it; + if (ret->getScreen() == screen && ret->getGroupWindow() == group && + (! ret->isTransient() || allow_transients)) + break; + } + + return ret; +} |