#include "i18n.hh"
#include "blackbox.hh"
+#include "Font.hh"
#include "GCCache.hh"
#include "Iconmenu.hh"
#include "Image.hh"
#include "Workspace.hh"
#include "Slit.hh"
+using std::string;
/*
* Initializes the class with default values/the window's set initial values.
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...
+ /*
+ 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;
+ xatom = blackbox->getXAtom();
if (! validateClient()) {
delete this;
flags.moving = flags.resizing = flags.shaded = flags.visible =
flags.iconic = flags.focused = flags.stuck = flags.modal =
- flags.send_focus_message = flags.shaped = False;
+ flags.send_focus_message = flags.shaped = flags.skip_taskbar =
+ flags.skip_pager = flags.fullscreen = False;
flags.maximized = 0;
blackbox_attrib.workspace = window_number = BSENTINEL;
frame.fgrip_pixel = 0;
frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
- frame.pbutton = frame.ugrip = frame.fgrip = decorations;
+ frame.pbutton = frame.ugrip = frame.fgrip = None;
decorations = Decor_Titlebar | Decor_Border | Decor_Handle |
Decor_Iconify | Decor_Maximize;
functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
client.wm_hint_flags = client.normal_hint_flags = 0;
+ client.window_group = None;
client.transient_for = 0;
- // get 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.
+ /*
+ get 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;
timer = new BTimer(blackbox, this);
timer->setTimeout(blackbox->getAutoRaiseDelay());
- if (! getBlackboxHints())
+ if (! getBlackboxHints()) {
getMWMHints();
+ getNetWMHints();
+ }
// get size, aspect, minimum/maximum size and other hints set by the
// client
return;
}
+ if (isKDESystrayWindow()) {
+ screen->addSystrayWindow(client.window);
+ delete this;
+ return;
+ }
+
frame.window = createToplevelWindow();
frame.plate = createChildWindow(frame.window);
associateClientWindow();
// determine if this is a transient window
getTransientInfo();
- // adjust the window decorations based on transience and window sizes
- if (isTransient()) {
+ // determine the window's type, so we can decide its decorations and
+ // functionality, or if we should not manage it at all
+ getWindowType();
+
+ // adjust the window decorations/behavior based on the window type
+ switch (window_type) {
+ case Type_Desktop:
+ // desktop windows are not managed by us, we just make sure they stay on the
+ // bottom.
+ return;
+
+ case Type_Dock:
+ // docks (such as kicker) cannot be moved, and appear on all workspaces
+ functions &= ~(Func_Move);
+ flags.stuck = True;
+ case Type_Toolbar:
+ case Type_Menu:
+ case Type_Utility:
+ // these windows have minimal decorations, only a titlebar, and cannot
+ // be resized or iconified
+ decorations &= ~(Decor_Maximize | Decor_Handle | Decor_Border |
+ Decor_Iconify);
+ functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
+ break;
+
+ case Type_Splash:
+ // splash screens have no functionality or decorations, they are left up
+ // to the application which created them
+ decorations = 0;
+ functions = 0;
+ break;
+
+ case Type_Dialog:
+ // dialogs cannot be maximized, and don't display a handle
decorations &= ~(Decor_Maximize | Decor_Handle);
functions &= ~Func_Maximize;
+ break;
+
+ case Type_Normal:
+ // normal windows retain all of the possible decorations and functionality
+ break;
}
+ // further adjeust the window's decorations/behavior based on window sizes
if ((client.normal_hint_flags & PMinSize) &&
(client.normal_hint_flags & PMaxSize) &&
client.max_width <= client.min_width &&
}
upsize();
+ setAllowedActions();
+
bool place_window = True;
if (blackbox->isStartup() || isTransient() ||
client.normal_hint_flags & (PPosition|USPosition)) {
- setGravityOffsets();
-
+ applyGravity(frame.rect);
if (blackbox->isStartup() ||
client.rect.intersects(screen->availableArea()))
place_window = False;
}
+ // add the window's strut. note this is done *after* placing the window.
+ screen->addStrut(&client.strut);
+ updateStrut();
+
if (decorations & Decor_Titlebar)
createTitlebar();
}
#endif // SHAPE
- if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
- // grab button 1 for changing focus/raising
- blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
- GrabModeSync, GrabModeSync, frame.plate, None);
- }
-
- blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
- ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
- GrabModeAsync, frame.window, blackbox->getMoveCursor());
- blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
- ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
- frame.window, None);
- blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
- ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
- GrabModeAsync, frame.window,
- blackbox->getLowerRightAngleCursor());
-
- positionWindows();
- decorate();
-
- if (decorations & Decor_Titlebar)
- XMapSubwindows(blackbox->getXDisplay(), frame.title);
- XMapSubwindows(blackbox->getXDisplay(), frame.window);
-
windowmenu = new Windowmenu(this);
if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
frame.rect.width(), frame.rect.height());
}
+ // preserve the window's initial state on first map, and its current state
+ // across a restart
+ if (! getState()) {
+ if (client.wm_hint_flags & StateHint)
+ current_state = client.initial_state;
+ else
+ current_state = NormalState;
+ }
+
+ // get sticky state from our parent window if we've got one
+ if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
+ client.transient_for->isStuck() != flags.stuck)
+ stick();
+
if (flags.shaded) {
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 = NormalState;
+ }
+
+ if (flags.stuck) {
+ flags.stuck = False;
+ stick();
}
if (flags.maximized && (functions & Func_Maximize)) {
remaximize();
}
- setFocusFlag(False);
+ /*
+ When the window is mapped (and also when its attributes are restored), the
+ current_state that was set here will be used.
+ It is set to Normal if the window is to be mapped or it is set to Iconic
+ if the window is to be iconified.
+ *Note* that for sticky windows, the same rules apply here, they are in
+ fact never set to Iconic since there is no way for us to tell if a sticky
+ window was iconified previously.
+ */
+
+ positionWindows();
+ decorate();
+ grabButtons();
+
+ XMapSubwindows(blackbox->getXDisplay(), frame.window);
+
+ redrawWindowFrame();
}
BlackboxWindow::~BlackboxWindow(void) {
-
#ifdef DEBUG
fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
client.window);
if (! timer) // window not managed...
return;
- if (flags.moving || flags.resizing) {
- screen->hideGeometry();
- XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
- }
+ screen->removeStrut(&client.strut);
+ screen->updateAvailableArea();
+
+ // We don't need to worry about resizing because resizing always grabs the X
+ // server. This should only ever happen if using opaque moving.
+ if (flags.moving)
+ endMove();
delete timer;
void BlackboxWindow::positionButtons(bool redecorate_label) {
- unsigned int bw = frame.button_w + frame.bevel_w + 1,
- by = frame.bevel_w + 1, lx = by, lw = frame.inside_w - by;
-
- if (decorations & Decor_Iconify) {
- if (frame.iconify_button == None) createIconifyButton();
-
- XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, by, by,
- frame.button_w, frame.button_w);
- XMapWindow(blackbox->getXDisplay(), frame.iconify_button);
- XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
-
- lx += bw;
- lw -= bw;
- } else if (frame.iconify_button) {
- destroyIconifyButton();
+ string layout = blackbox->getTitlebarLayout();
+ string parsed;
+
+ bool hasclose, hasiconify, hasmaximize, haslabel;
+ hasclose = hasiconify = hasmaximize = haslabel = false;
+
+ string::const_iterator it, end;
+ for (it = layout.begin(), end = layout.end(); it != end; ++it) {
+ switch(*it) {
+ case 'C':
+ if (! hasclose && (decorations & Decor_Close)) {
+ hasclose = true;
+ parsed += *it;
+ }
+ break;
+ case 'I':
+ if (! hasiconify && (decorations & Decor_Iconify)) {
+ hasiconify = true;
+ parsed += *it;
+ }
+ break;
+ case 'M':
+ if (! hasmaximize && (decorations & Decor_Maximize)) {
+ hasmaximize = true;
+ parsed += *it;
+ }
+ break;
+ case 'L':
+ if (! haslabel) {
+ haslabel = true;
+ parsed += *it;
+ }
+ }
}
- int bx = frame.inside_w - bw;
-
- if (decorations & Decor_Close) {
- if (frame.close_button == None) createCloseButton();
-
- XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, bx, by,
- frame.button_w, frame.button_w);
- XMapWindow(blackbox->getXDisplay(), frame.close_button);
- XClearWindow(blackbox->getXDisplay(), frame.close_button);
-
- bx -= bw;
- lw -= bw;
- } else if (frame.close_button) {
+ if (! hasclose && frame.close_button)
destroyCloseButton();
- }
- if (decorations & Decor_Maximize) {
- if (frame.maximize_button == None) createMaximizeButton();
-
- XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, bx, by,
- frame.button_w, frame.button_w);
- XMapWindow(blackbox->getXDisplay(), frame.maximize_button);
- XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
-
- lw -= bw;
- } else if (frame.maximize_button) {
+ if (! hasiconify && frame.iconify_button)
+ destroyIconifyButton();
+ if (! hasmaximize && frame.maximize_button)
destroyMaximizeButton();
+ if (! haslabel)
+ parsed += 'L'; // require that the label be in the layout
+
+ const unsigned int bsep = frame.bevel_w + 1; // separation between elements
+ const unsigned int by = frame.bevel_w + 1;
+ const unsigned int ty = frame.bevel_w;
+
+ frame.label_w = frame.inside_w - bsep * 2 -
+ (frame.button_w + bsep) * (parsed.size() - 1);
+
+ unsigned int x = bsep;
+ for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
+ switch(*it) {
+ case 'C':
+ if (! frame.close_button) createCloseButton();
+ XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
+ frame.button_w, frame.button_w);
+ x += frame.button_w + bsep;
+ break;
+ case 'I':
+ if (! frame.iconify_button) createIconifyButton();
+ XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
+ frame.button_w, frame.button_w);
+ x += frame.button_w + bsep;
+ break;
+ case 'M':
+ if (! frame.maximize_button) createMaximizeButton();
+ XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
+ frame.button_w, frame.button_w);
+ x += frame.button_w + bsep;
+ break;
+ case 'L':
+ XMoveResizeWindow(blackbox->getXDisplay(), frame.label, x, ty,
+ frame.label_w, frame.label_h);
+ x += frame.label_w + bsep;
+ break;
+ }
}
- frame.label_w = lw - by;
- XMoveResizeWindow(blackbox->getXDisplay(), frame.label, lx, frame.bevel_w,
- frame.label_w, frame.label_h);
- if (redecorate_label) decorateLabel();
+ if (redecorate_label) decorateLabel();
redrawLabel();
redrawAllButtons();
}
void BlackboxWindow::reconfigure(void) {
upsize();
-
- client.rect.setPos(frame.rect.left() + frame.margin.left,
- frame.rect.top() + frame.margin.top);
-
positionWindows();
decorate();
+ redrawWindowFrame();
- XClearWindow(blackbox->getXDisplay(), frame.window);
- setFocusFlag(flags.focused);
-
- configure(frame.rect.x(), frame.rect.y(),
- frame.rect.width(), frame.rect.height());
+ ungrabButtons();
+ grabButtons();
if (windowmenu) {
windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
}
-void BlackboxWindow::updateFocusModel(void) {
- if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
+void BlackboxWindow::grabButtons(void) {
+ if ((! screen->isSloppyFocus()) || screen->doClickRaise())
// grab button 1 for changing focus/raising
blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
- GrabModeSync, GrabModeSync, None, None);
- } else {
+ GrabModeSync, GrabModeSync, frame.plate, None);
+
+ if (functions & Func_Move)
+ blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
+ ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+ GrabModeAsync, frame.window,
+ blackbox->getMoveCursor());
+ if (functions & Func_Resize)
+ blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
+ ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+ GrabModeAsync, frame.window,
+ blackbox->getLowerRightAngleCursor());
+ // alt+middle lowers the window
+ blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
+ ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
+ frame.window, None);
+}
+
+
+void BlackboxWindow::ungrabButtons(void) {
+ if ((! screen->isSloppyFocus()) || screen->doClickRaise())
blackbox->ungrabButton(Button1, 0, frame.plate);
- }
+
+ blackbox->ungrabButton(Button1, Mod1Mask, frame.window);
+ blackbox->ungrabButton(Button2, Mod1Mask, frame.window);
+ blackbox->ungrabButton(Button3, Mod1Mask, frame.window);
}
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.window,
+ frame.border_w);
XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
frame.mwm_border_w);
XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
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) {
if (frame.title == None) createTitlebar();
XSetWindowBorderWidth(blackbox->getXDisplay(), frame.right_grip,
frame.border_w);
+ // use client.rect here so the value is correct even if shaded
XMoveResizeWindow(blackbox->getXDisplay(), frame.handle,
-frame.border_w,
- frame.rect.height() - frame.margin.bottom +
+ client.rect.height() + frame.margin.top +
frame.mwm_border_w - frame.border_w,
frame.inside_w, frame.handle_h);
XMoveResizeWindow(blackbox->getXDisplay(), frame.left_grip,
XMoveResizeWindow(blackbox->getXDisplay(), frame.right_grip,
frame.inside_w - frame.grip_w - frame.border_w,
-frame.border_w, frame.grip_w, frame.handle_h);
+
XMapSubwindows(blackbox->getXDisplay(), frame.handle);
XMapWindow(blackbox->getXDisplay(), frame.handle);
} else if (frame.handle) {
destroyHandle();
}
+ XSync(blackbox->getXDisplay(), False);
}
-void BlackboxWindow::getWMName(void) {
- XTextProperty text_prop;
+void BlackboxWindow::updateStrut(void) {
+ unsigned long num = 4;
+ unsigned long *data;
+ if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
+ num, &data))
+ return;
+
+ if (num == 4) {
+ client.strut.left = data[0];
+ client.strut.right = data[1];
+ client.strut.top = data[2];
+ client.strut.bottom = data[3];
+
+ screen->updateAvailableArea();
+ }
+
+ delete [] data;
+}
+
+
+void BlackboxWindow::getWindowType(void) {
+ unsigned long val;
+ if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom,
+ val)) {
+ if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop))
+ window_type = Type_Desktop;
+ else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock))
+ window_type = Type_Dock;
+ else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar))
+ window_type = Type_Toolbar;
+ else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu))
+ window_type = Type_Menu;
+ else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility))
+ window_type = Type_Utility;
+ else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash))
+ window_type = Type_Splash;
+ else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog))
+ window_type = Type_Dialog;
+ else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal))
+ window_type = Type_Normal;
+ return;
+ }
- if (XGetWMName(blackbox->getXDisplay(), client.window, &text_prop)) {
- client.title = textPropertyToString(blackbox->getXDisplay(), text_prop);
- if (client.title.empty())
- client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
- XFree((char *) text_prop.value);
- } else {
- client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
+ /*
+ * the window type hint was not set, which means we either classify ourself
+ * as a normal window or a dialog, depending on if we are a transient.
+ */
+ if (isTransient())
+ window_type = Type_Dialog;
+
+ window_type = Type_Normal;
+}
+
+
+void BlackboxWindow::getWMName(void) {
+ if (xatom->getValue(client.window, XAtom::net_wm_name,
+ XAtom::utf8, client.title) &&
+ !client.title.empty()) {
+ xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
+ return;
+ }
+ //fall through to using WM_NAME
+ if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
+ && !client.title.empty()) {
+ xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
+ return;
}
+ // fall back to an internal default
+ client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
+ xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
+ client.title);
}
void BlackboxWindow::getWMIconName(void) {
- XTextProperty text_prop;
-
- if (XGetWMIconName(blackbox->getXDisplay(), client.window, &text_prop)) {
- client.icon_title =
- textPropertyToString(blackbox->getXDisplay(), text_prop);
- if (client.icon_title.empty())
- client.icon_title = client.title;
- XFree((char *) text_prop.value);
- } else {
- client.icon_title = client.title;
+ if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
+ XAtom::utf8, client.icon_title) &&
+ !client.icon_title.empty()) {
+ xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
+ return;
+ }
+ //fall through to using WM_ICON_NAME
+ if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
+ client.icon_title) &&
+ !client.icon_title.empty()) {
+ xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
+ return;
}
+ // fall back to using the main name
+ client.icon_title = client.title;
+ xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
+ client.icon_title);
}
if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
&proto, &num_return)) {
for (int i = 0; i < num_return; ++i) {
- if (proto[i] == blackbox->getWMDeleteAtom()) {
+ if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
decorations |= Decor_Close;
functions |= Func_Close;
- } else if (proto[i] == blackbox->getWMTakeFocusAtom())
+ } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
flags.send_focus_message = True;
- else if (proto[i] == blackbox->getBlackboxStructureMessagesAtom())
+ else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
screen->addNetizen(new Netizen(screen, client.window));
}
long icccm_mask;
XSizeHints sizehint;
- const Rect& screen_area = screen->availableArea();
-
client.min_width = client.min_height =
client.width_inc = client.height_inc = 1;
client.base_width = client.base_height = 0;
- client.max_width = screen_area.width();
- client.max_height = screen_area.height();
+ client.win_gravity = NorthWestGravity;
+#if 0
client.min_aspect_x = client.min_aspect_y =
client.max_aspect_x = client.max_aspect_y = 1;
- client.win_gravity = NorthWestGravity;
+#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))
client.normal_hint_flags = sizehint.flags;
if (sizehint.flags & PMinSize) {
- client.min_width = sizehint.min_width;
- client.min_height = sizehint.min_height;
+ 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) {
- client.max_width = sizehint.max_width;
- client.max_height = sizehint.max_height;
+ 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.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;
}
+/*
+ * Gets the NETWM hints for the class' contained window.
+ */
+void BlackboxWindow::getNetWMHints(void) {
+ unsigned long workspace;
+
+ if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+ workspace)) {
+ if (workspace == 0xffffffff)
+ flags.stuck = True;
+ else
+ blackbox_attrib.workspace = workspace;
+ }
+
+ unsigned long *state;
+ unsigned long num = (unsigned) -1;
+ if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
+ num, &state)) {
+ bool vert = False,
+ horz = False;
+ for (unsigned long i = 0; i < num; ++i) {
+ if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
+ flags.modal = True;
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
+ flags.shaded = True;
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
+ flags.skip_taskbar = True;
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
+ flags.skip_pager = True;
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
+ flags.fullscreen = True;
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
+ setState(IconicState);
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
+ vert = True;
+ else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
+ horz = True;
+ }
+ if (vert && horz)
+ flags.maximized = 1;
+ else if (vert)
+ flags.maximized = 2;
+ else if (horz)
+ flags.maximized = 3;
+
+ delete [] state;
+ }
+}
+
+
/*
* Gets the MWM hints for the class' contained window.
* This is used while initializing the window to its first state, and not
* 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)
+ unsigned long num;
+ MwmHints *mwm_hint;
+
+ num = PropMwmHintsElements;
+ if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
+ XAtom::motif_wm_hints, num,
+ (unsigned long **)&mwm_hint))
return;
+ if (num < PropMwmHintsElements) {
+ delete [] mwm_hint;
+ return;
+ }
if (mwm_hint->flags & MwmHintsDecorations) {
if (mwm_hint->decorations & MwmDecorAll) {
functions |= Func_Close;
}
}
- XFree(mwm_hint);
+ delete [] mwm_hint;
}
* 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)
+ unsigned long num;
+ BlackboxHints *blackbox_hint;
+
+ num = PropBlackboxHintsElements;
+ if (! xatom->getValue(client.window, XAtom::blackbox_hints,
+ XAtom::blackbox_hints, num,
+ (unsigned long **)&blackbox_hint))
+ return False;
+ if (num < PropBlackboxHintsElements) {
+ delete [] blackbox_hint;
return False;
+ }
if (blackbox_hint->flags & AttribShaded)
flags.shaded = (blackbox_hint->attrib & AttribShaded);
reconfigure();
}
- XFree(blackbox_hint);
+
+ delete [] blackbox_hint;
+
return True;
}
client.transient_for = 0;
Window trans_for;
- if (!XGetTransientForHint(blackbox->getXDisplay(), client.window,
- &trans_for)) {
+ if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
+ &trans_for)) {
// transient_for hint not set
return;
}
}
+bool BlackboxWindow::isKDESystrayWindow(void) {
+ Window systray;
+ if (xatom->getValue(client.window, XAtom::kde_net_wm_system_tray_window_for,
+ XAtom::window, systray) && systray)
+ return True;
+ return False;
+}
+
+
BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
if (client.transient_for &&
client.transient_for != (BlackboxWindow*) ~0ul)
}
+/*
+ * 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) {
- bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
+ bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
+ ! flags.moving);
- if ((dw != frame.rect.width()) || (dh != frame.rect.height())) {
+ 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);
positionWindows();
decorate();
- setFocusFlag(flags.focused);
- redrawAllButtons();
+ 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 && ! flags.moving) {
+ 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);
event.xconfigure.above = frame.window;
event.xconfigure.override_redirect = False;
- XSendEvent(blackbox->getXDisplay(), client.window, True,
- NoEventMask, &event);
-
+ XSendEvent(blackbox->getXDisplay(), client.window, False,
+ StructureNotifyMask, &event);
screen->updateNetizenConfigNotify(&event);
+ XFlush(blackbox->getXDisplay());
}
}
bool BlackboxWindow::setInputFocus(void) {
if (flags.focused) return True;
- if (! client.rect.intersects(screen->getRect())) {
+ assert(! flags.iconic &&
+ (flags.stuck || // window must be on the current workspace or sticky
+ blackbox_attrib.workspace == screen->getCurrentWorkspaceID()));
+#if 0
+ // if the window is not visible, mark the window as wanting focus rather
+ // than give it focus.
+ if (! flags.visible) {
+ Workspace *wkspc = screen->getWorkspace(blackbox_attrib.workspace);
+ wkspc->setLastFocusedWindow(this);
+ return True;
+ }
+#endif
+ 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,
if (flags.send_focus_message) {
XEvent ce;
ce.xclient.type = ClientMessage;
- ce.xclient.message_type = blackbox->getWMProtocolsAtom();
+ ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
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[0] = xatom->getAtom(XAtom::wm_take_focus);
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;
void BlackboxWindow::iconify(void) {
if (flags.iconic) return;
+ // We don't need to worry about resizing because resizing always grabs the X
+ // server. This should only ever happen if using opaque moving.
+ if (flags.moving)
+ endMove();
+
if (windowmenu) windowmenu->hide();
- setState(IconicState);
-
/*
* 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.
flags.visible = False;
flags.iconic = True;
+ setState(IconicState);
+
screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
if (isTransient()) {
void BlackboxWindow::show(void) {
- setState(NormalState);
+ flags.visible = True;
+ flags.iconic = False;
+
+ current_state = (flags.shaded) ? IconicState : NormalState;
+ setState(current_state);
XMapWindow(blackbox->getXDisplay(), client.window);
XMapSubwindows(blackbox->getXDisplay(), frame.window);
XMapWindow(blackbox->getXDisplay(), frame.window);
- flags.visible = True;
- flags.iconic = False;
+#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
}
void BlackboxWindow::deiconify(bool reassoc, bool raise) {
if (flags.iconic || reassoc)
screen->reassociateWindow(this, BSENTINEL, False);
- else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID())
+ else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
return;
show();
void BlackboxWindow::close(void) {
XEvent ce;
ce.xclient.type = ClientMessage;
- ce.xclient.message_type = blackbox->getWMProtocolsAtom();
+ ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
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[0] = xatom->getAtom(XAtom::wm_delete_window);
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);
-
+ // We don't need to worry about resizing because resizing always grabs the X
+ // server. This should only ever happen if using opaque moving.
+ if (flags.moving)
+ endMove();
+
flags.visible = False;
flags.iconic = False;
+ setState(current_state);
+
XUnmapWindow(blackbox->getXDisplay(), frame.window);
XGrabServer(blackbox->getXDisplay());
+
XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
XUnmapWindow(blackbox->getXDisplay(), client.window);
XSelectInput(blackbox->getXDisplay(), client.window,
PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
- XUngrabServer(blackbox->getXDisplay());
+
+ XUngrabServer(blackbox->getXDisplay());
if (windowmenu) windowmenu->hide();
}
void BlackboxWindow::maximize(unsigned int button) {
+ // We don't need to worry about resizing because resizing always grabs the X
+ // server. This should only ever happen if using opaque moving.
+ if (flags.moving)
+ endMove();
+
// handle case where menu is open then the max button is used instead
if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
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)
+ /*
+ when a resize finishes, 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();
+ 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();
- blackbox_attrib.premax_h = frame.rect.height();
+ // 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;
- constrain(TopLeft);
switch(button) {
case 1:
break;
}
+ constrain(TopLeft);
+
if (flags.shaded) {
blackbox_attrib.flags ^= AttribShaded;
blackbox_attrib.attrib ^= AttribShaded;
configure(frame.changing.x(), frame.changing.y(),
frame.changing.width(), frame.changing.height());
- screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
- redrawAllButtons();
+ if (flags.focused)
+ screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
+ redrawAllButtons(); // in case it is not called in configure()
setState(current_state);
}
void BlackboxWindow::setWorkspace(unsigned int n) {
blackbox_attrib.flags |= AttribWorkspace;
blackbox_attrib.workspace = n;
+ xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
}
void BlackboxWindow::shade(void) {
- if (! (decorations & Decor_Titlebar))
- return;
-
if (flags.shaded) {
XResizeWindow(blackbox->getXDisplay(), frame.window,
frame.inside_w, frame.inside_h);
frame.rect.setHeight(client.rect.height() + frame.margin.top +
frame.margin.bottom);
} 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;
}
+/*
+ * (Un)Sticks a window and its relatives.
+ */
void BlackboxWindow::stick(void) {
if (flags.stuck) {
blackbox_attrib.flags ^= AttribOmnipresent;
if (! flags.iconic)
screen->reassociateWindow(this, BSENTINEL, True);
+ else
+ // temporary fix since sticky windows suck. set the hint to what we
+ // actually hold in our data.
+ xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+ blackbox_attrib.workspace);
setState(current_state);
} else {
blackbox_attrib.flags |= AttribOmnipresent;
blackbox_attrib.attrib |= AttribOmnipresent;
+ // temporary fix since sticky windows suck. set the hint to a different
+ // value than that contained in the class' data.
+ xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+ 0xffffffff);
+
setState(current_state);
}
+ // go up the chain
+ if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
+ client.transient_for->isStuck() != flags.stuck)
+ client.transient_for->stick();
+ // go down the chain
+ BlackboxWindowList::iterator it;
+ const BlackboxWindowList::iterator end = client.transientList.end();
+ for (it = client.transientList.begin(); it != end; ++it)
+ if ((*it)->isStuck() != flags.stuck)
+ (*it)->stick();
}
-void BlackboxWindow::setFocusFlag(bool focus) {
- flags.focused = focus;
-
+void BlackboxWindow::redrawWindowFrame(void) const {
if (decorations & Decor_Titlebar) {
if (flags.focused) {
if (frame.ftitle)
XSetWindowBorder(blackbox->getXDisplay(),
frame.plate, frame.uborder_pixel);
}
+}
+
+
+void BlackboxWindow::setFocusFlag(bool focus) {
+ // only focus a window if it is visible
+ if (focus && !flags.visible)
+ return;
+
+ flags.focused = focus;
+
+ redrawWindowFrame();
if (screen->isSloppyFocus() && screen->doAutoRaise()) {
if (isFocused()) timer->start();
int i = 0, ncmap = 0;
Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
client.window, &ncmap);
- XWindowAttributes wattrib;
if (cmaps) {
+ XWindowAttributes wattrib;
if (XGetWindowAttributes(blackbox->getXDisplay(),
client.window, &wattrib)) {
if (install) {
}
+void BlackboxWindow::setAllowedActions(void) {
+ Atom actions[7];
+ int num = 0;
+
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
+
+ if (functions & Func_Move)
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
+ if (functions & Func_Resize)
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
+ if (functions & Func_Maximize) {
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
+ actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
+ }
+
+ xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
+ actions, num);
+}
+
+
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,
+ xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
+
+ xatom->setValue(client.window, XAtom::blackbox_attributes,
+ XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
PropBlackboxAttributesElements);
+
+ Atom netstate[8];
+ int num = 0;
+ if (flags.modal)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
+ if (flags.shaded)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
+ if (flags.iconic)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
+ if (flags.skip_taskbar)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
+ if (flags.skip_pager)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
+ if (flags.fullscreen)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
+ if (flags.maximized == 1 || flags.maximized == 2)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
+ if (flags.maximized == 1 || flags.maximized == 3)
+ netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
+ xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
+ netstate, num);
}
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);
-
+ bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
+ current_state);
+ if (! ret) current_state = 0;
return ret;
}
void BlackboxWindow::restoreAttributes(void) {
- if (! getState()) current_state = NormalState;
-
- Atom atom_return;
- int foo;
- unsigned long ulfoo, nitems;
-
+ unsigned long num = PropBlackboxAttributesElements;
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)
+ if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
+ XAtom::blackbox_attributes, num,
+ (unsigned long **)&net))
return;
+ if (num < PropBlackboxAttributesElements) {
+ delete [] net;
+ return;
+ }
- if (net->flags & AttribShaded &&
- net->attrib & AttribShaded) {
- int save_state =
- ((current_state == IconicState) ? NormalState : current_state);
-
+ if (net->flags & AttribShaded && net->attrib & AttribShaded) {
flags.shaded = False;
+ unsigned long orig_state = current_state;
shade();
- current_state = save_state;
- }
+ /*
+ 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())) {
+ if (net->workspace != screen->getCurrentWorkspaceID() &&
+ net->workspace < screen->getWorkspaceCount())
screen->reassociateWindow(this, net->workspace, True);
+ if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
+ (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
+ // 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) {
+ if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
flags.stuck = False;
stick();
- current_state = NormalState;
+ // 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)) {
+ if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
int x = net->premax_x, y = net->premax_y;
unsigned int w = net->premax_w, h = net->premax_h;
flags.maximized = 0;
blackbox_attrib.premax_h = h;
}
- 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);
+ delete [] net;
}
/*
- * Positions the frame according the the client window position and window
- * gravity.
+ * Positions the Rect r according the the client window position and
+ * window gravity.
*/
-void BlackboxWindow::setGravityOffsets(void) {
- // x coordinates for each gravity type
- const int x_west = client.rect.x();
- const int x_east = client.rect.right() - frame.inside_w + 1;
- const int x_center = client.rect.right() - (frame.rect.width()/2) + 1;
- // y coordinates for each gravity type
- const int y_north = client.rect.y();
- const int y_south = client.rect.bottom() - frame.inside_h + 1;
- const int y_center = client.rect.bottom() - (frame.rect.height()/2) + 1;
+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() - (frame.margin.left + frame.margin.right) / 2);
+ break;
+
+ case NorthEastGravity:
+ case SouthEastGravity:
+ case EastGravity:
+ r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
+ break;
+
+ case ForgetGravity:
+ case StaticGravity:
+ r.setX(client.rect.x() - frame.margin.left);
+ break;
+ }
+ // apply vertical window gravity
switch (client.win_gravity) {
default:
- case NorthWestGravity: frame.rect.setPos(x_west, y_north); break;
- case NorthGravity: frame.rect.setPos(x_center, y_north); break;
- case NorthEastGravity: frame.rect.setPos(x_east, y_north); break;
- case SouthWestGravity: frame.rect.setPos(x_west, y_south); break;
- case SouthGravity: frame.rect.setPos(x_center, y_south); break;
- case SouthEastGravity: frame.rect.setPos(x_east, y_south); break;
- case WestGravity: frame.rect.setPos(x_west, y_center); break;
- case CenterGravity: frame.rect.setPos(x_center, y_center); break;
- case EastGravity: frame.rect.setPos(x_east, y_center); break;
+ case NorthWestGravity:
+ case NorthEastGravity:
+ case NorthGravity:
+ r.setY(client.rect.y());
+ break;
+
+ case CenterGravity:
+ case EastGravity:
+ case WestGravity:
+ r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
+ break;
+
+ case SouthWestGravity:
+ case SouthEastGravity:
+ case SouthGravity:
+ r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
+ break;
case ForgetGravity:
case StaticGravity:
- frame.rect.setPos(client.rect.x() - frame.margin.left,
- client.rect.y() - frame.margin.top);
+ r.setY(client.rect.y() - frame.margin.top);
break;
}
}
/*
- * The reverse of the setGravityOffsets function. Uses the frame window's
- * position to find the window's reference point.
+ * The reverse of the applyGravity function.
+ *
+ * Positions the Rect r according to the frame window position and
+ * window gravity.
*/
-void BlackboxWindow::restoreGravity(void) {
- // x coordinates for each gravity type
- const int x_west = frame.rect.x();
- const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
- const int x_center = frame.rect.x() + (frame.rect.width()/2) -
- client.rect.width();
- // y coordinates for each gravity type
- const int y_north = frame.rect.y();
- const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
- const int y_center = frame.rect.y() + (frame.rect.height()/2) -
- client.rect.height();
-
- switch(client.win_gravity) {
+void BlackboxWindow::restoreGravity(Rect &r) {
+ // restore horizontal window gravity
+ switch (client.win_gravity) {
default:
- case NorthWestGravity: client.rect.setPos(x_west, y_north); break;
- case NorthGravity: client.rect.setPos(x_center, y_north); break;
- case NorthEastGravity: client.rect.setPos(x_east, y_north); break;
- case SouthWestGravity: client.rect.setPos(x_west, y_south); break;
- case SouthGravity: client.rect.setPos(x_center, y_south); break;
- case SouthEastGravity: client.rect.setPos(x_east, y_south); break;
- case WestGravity: client.rect.setPos(x_west, y_center); break;
- case CenterGravity: client.rect.setPos(x_center, y_center); break;
- case EastGravity: client.rect.setPos(x_east, y_center); break;
+ case NorthWestGravity:
+ case SouthWestGravity:
+ case WestGravity:
+ r.setX(frame.rect.x());
+ break;
+
+ case NorthGravity:
+ case SouthGravity:
+ case CenterGravity:
+ r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
+ break;
+
+ case NorthEastGravity:
+ case SouthEastGravity:
+ case EastGravity:
+ r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
+ break;
case ForgetGravity:
case StaticGravity:
- client.rect.setPos(frame.rect.left() + frame.margin.left,
- frame.rect.top() + frame.margin.top);
+ r.setX(frame.rect.x() + frame.margin.left);
+ 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() + (frame.margin.top + frame.margin.bottom) / 2);
+ break;
+
+ case SouthWestGravity:
+ case SouthEastGravity:
+ case SouthGravity:
+ r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
+ break;
+
+ case ForgetGravity:
+ case StaticGravity:
+ r.setY(frame.rect.y() + frame.margin.top);
break;
}
}
-void BlackboxWindow::redrawLabel(void) {
+void BlackboxWindow::redrawLabel(void) const {
if (flags.focused) {
if (frame.flabel)
XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
WindowStyle *style = screen->getWindowStyle();
- int pos = frame.bevel_w * 2,
- dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
- frame.bevel_w * 4, i18n.multibyte());
-
- BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
- style->font);
- if (i18n.multibyte())
- XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
- pen.gc(), pos,
- (1 - style->fontset_extents->max_ink_extent.y),
- client.title.c_str(), dlen);
- else
- XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
- (style->font->ascent + 1), client.title.c_str(), dlen);
+ int pos = frame.bevel_w * 2;
+ style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
+ style->font->drawString(frame.label, pos, 1,
+ (flags.focused ? style->l_text_focus :
+ style->l_text_unfocus),
+ client.title);
}
-void BlackboxWindow::redrawAllButtons(void) {
+void BlackboxWindow::redrawAllButtons(void) const {
if (frame.iconify_button) redrawIconifyButton(False);
if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
if (frame.close_button) redrawCloseButton(False);
}
-void BlackboxWindow::redrawIconifyButton(bool pressed) {
+void BlackboxWindow::redrawIconifyButton(bool pressed) const {
if (! pressed) {
if (flags.focused) {
if (frame.fbutton)
}
-void BlackboxWindow::redrawMaximizeButton(bool pressed) {
+void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
if (! pressed) {
if (flags.focused) {
if (frame.fbutton)
}
-void BlackboxWindow::redrawCloseButton(bool pressed) {
+void BlackboxWindow::redrawCloseButton(bool pressed) const {
if (! pressed) {
if (flags.focused) {
if (frame.fbutton)
}
-void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
+void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
if (re->window != client.window)
return;
client.window);
#endif // DEBUG
- bool get_state_ret = getState();
- if (! (get_state_ret && blackbox->isStartup())) {
- if ((client.wm_hint_flags & StateHint) &&
- (! (current_state == NormalState || current_state == IconicState)))
- current_state = client.initial_state;
- else
- current_state = NormalState;
- } else if (flags.iconic) {
- current_state = NormalState;
- }
-
switch (current_state) {
case IconicState:
iconify();
}
-void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
+void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
if (ue->window != client.window)
return;
}
-void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
+void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
if (de->window != client.window)
return;
}
-void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
+void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
if (re->window != client.window || re->parent == frame.plate)
return;
}
-void BlackboxWindow::propertyNotifyEvent(Atom atom) {
- switch(atom) {
+void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
+ if (pe->state == PropertyDelete)
+ return;
+
+#ifdef DEBUG
+ fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
+ client.window);
+#endif
+
+ switch(pe->atom) {
case XA_WM_CLASS:
case XA_WM_CLIENT_MACHINE:
case XA_WM_COMMAND:
if (isTransient()) {
decorations &= ~(Decor_Maximize | Decor_Handle);
functions &= ~Func_Maximize;
+ setAllowedActions();
}
reconfigure();
if (flags.iconic) screen->propagateWindowName(this);
break;
+ case XAtom::net_wm_name:
case XA_WM_NAME:
getWMName();
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 {
- decorations |= Decor_Maximize | Decor_Handle;
- functions |= Func_Resize | Func_Maximize;
+ if (! isTransient()) {
+ decorations |= Decor_Maximize | Decor_Handle;
+ functions |= Func_Maximize;
+ }
+ functions |= Func_Resize;
}
+ grabButtons();
+ setAllowedActions();
}
Rect old_rect = frame.rect;
}
default:
- if (atom == blackbox->getWMProtocolsAtom()) {
+ if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
getWMProtocols();
if ((decorations & Decor_Close) && (! frame.close_button)) {
}
if (windowmenu) windowmenu->reconfigure();
}
+ } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
+ updateStrut();
}
break;
}
-void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
+void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
+#ifdef DEBUG
+ fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
+#endif
+
if (frame.label == ee->window && (decorations & Decor_Titlebar))
redrawLabel();
else if (frame.close_button == ee->window)
}
-void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
+void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
if (cr->window != client.window || flags.iconic)
return;
- int cx = frame.rect.x(), cy = frame.rect.y();
- unsigned int cw = frame.rect.width(), ch = frame.rect.height();
-
if (cr->value_mask & CWBorderWidth)
client.old_bw = cr->border_width;
- if (cr->value_mask & CWX)
- cx = cr->x - frame.margin.left;
+ if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
+ Rect req = frame.rect;
- if (cr->value_mask & CWY)
- cy = cr->y - frame.margin.top;
+ 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);
- if (cr->value_mask & CWWidth)
- cw = cr->width + frame.margin.left + frame.margin.right;
+ applyGravity(req);
+ }
+
+ if (cr->value_mask & CWWidth)
+ req.setWidth(cr->width + frame.margin.left + frame.margin.right);
- if (cr->value_mask & CWHeight)
- ch = cr->height + frame.margin.top + frame.margin.bottom;
+ if (cr->value_mask & CWHeight)
+ req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
- if (frame.rect != Rect(cx, cy, cw, ch))
- configure(cx, cy, cw, ch);
+ configure(req.x(), req.y(), req.width(), req.height());
+ }
if (cr->value_mask & CWStackMode) {
switch (cr->detail) {
}
-void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
+void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
+#ifdef DEBUG
+ fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
+ client.window);
+#endif
+
if (frame.maximize_button == be->window) {
redrawMaximizeButton(True);
} else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
}
}
- frame.grab_x = be->x_root - frame.rect.x() - frame.border_w;
- frame.grab_y = be->y_root - frame.rect.y() - frame.border_w;
-
if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
} else if (windowmenu && be->button == 3 &&
(frame.title == be->window || frame.label == be->window ||
frame.handle == be->window || frame.window == be->window)) {
- int mx = 0, my = 0;
-
- if (frame.title == be->window || frame.label == be->window) {
- mx = be->x_root - (windowmenu->getWidth() / 2);
- my = frame.rect.y() + frame.title_h + frame.border_w;
- } else if (frame.handle == be->window) {
- mx = be->x_root - (windowmenu->getWidth() / 2);
- my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
- windowmenu->getHeight();
+ if (windowmenu->isVisible()) {
+ windowmenu->hide();
} else {
- mx = be->x_root - (windowmenu->getWidth() / 2);
+ int mx = be->x_root - windowmenu->getWidth() / 2,
+ my = be->y_root - windowmenu->getHeight() / 2;
- if (be->y <= static_cast<signed>(frame.bevel_w))
- my = frame.rect.y() + frame.title_h;
- else
- my = be->y_root - (windowmenu->getHeight() / 2);
- }
+ // snap the window menu into a corner/side if necessary
+ int left_edge, right_edge, top_edge, bottom_edge;
- // snap the window menu into a corner if necessary - we check the
- // position of the menu with the coordinates of the client to
- // make the comparisions easier.
- // ### this needs some work!
- if (mx > client.rect.right() -
- static_cast<signed>(windowmenu->getWidth()))
- mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
- if (mx < client.rect.left())
- mx = frame.rect.x();
-
- if (my > client.rect.bottom() -
- static_cast<signed>(windowmenu->getHeight()))
- my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
- if (my < client.rect.top())
- my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
- frame.title_h : 0);
-
- if (windowmenu) {
- if (! windowmenu->isVisible()) {
- windowmenu->move(mx, my);
- windowmenu->show();
- XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
- XRaiseWindow(blackbox->getXDisplay(),
- windowmenu->getSendToMenu()->getWindowID());
- } else {
- windowmenu->hide();
- }
+ /*
+ the " + (frame.border_w * 2) - 1" bits are to get the proper width
+ and height of the menu, as the sizes returned by it do not include
+ the borders.
+ */
+ left_edge = frame.rect.x();
+ right_edge = frame.rect.right() -
+ (windowmenu->getWidth() + (frame.border_w * 2) - 1);
+ top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
+ bottom_edge = client.rect.bottom() -
+ (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
+ (frame.border_w + frame.mwm_border_w);
+
+ if (mx < left_edge)
+ mx = left_edge;
+ if (mx > right_edge)
+ mx = right_edge;
+ if (my < top_edge)
+ my = top_edge;
+ if (my > bottom_edge)
+ my = bottom_edge;
+
+ windowmenu->move(mx, my);
+ windowmenu->show();
+ XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
+ XRaiseWindow(blackbox->getXDisplay(),
+ windowmenu->getSendToMenu()->getWindowID());
}
+ // mouse wheel up
+ } else if (be->button == 4) {
+ if ((be->window == frame.label ||
+ be->window == frame.title) &&
+ ! flags.shaded)
+ shade();
+ // mouse wheel down
+ } else if (be->button == 5) {
+ if ((be->window == frame.label ||
+ be->window == frame.title) &&
+ flags.shaded)
+ shade();
}
}
-void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
+void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
+#ifdef DEBUG
+ fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
+ client.window);
+#endif
+
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))) {
close();
redrawCloseButton(False);
} else if (flags.moving) {
- flags.moving = False;
+ endMove();
+ } else if (flags.resizing) {
+ endResize();
+ } else if (re->window == frame.window) {
+ if (re->button == 2 && re->state == Mod1Mask)
+ XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
+ }
+}
- 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());
+
+void BlackboxWindow::beginMove(int x_root, int y_root) {
+ assert(! (flags.resizing || flags.moving));
+
+ /*
+ Only one window can be moved/resized at a time. If another window is already
+ being moved or resized, then stop it before whating to work with this one.
+ */
+ BlackboxWindow *changing = blackbox->getChangingWindow();
+ if (changing && changing != this) {
+ if (changing->flags.moving)
+ changing->endMove();
+ else // if (changing->flags.resizing)
+ changing->endResize();
+ }
+
+ XGrabPointer(blackbox->getXDisplay(), frame.window, False,
+ PointerMotionMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync,
+ None, blackbox->getMoveCursor(), CurrentTime);
+
+ if (windowmenu && windowmenu->isVisible())
+ windowmenu->hide();
+
+ flags.moving = True;
+ blackbox->setChangingWindow(this);
+
+ 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);
+ }
+
+ frame.grab_x = x_root - frame.rect.x() - frame.border_w;
+ frame.grab_y = y_root - frame.rect.y() - frame.border_w;
+}
+
+
+void BlackboxWindow::doMove(int x_root, int y_root) {
+ assert(flags.moving);
+ assert(blackbox->getChangingWindow() == this);
+
+ int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
+ dx -= frame.border_w;
+ dy -= frame.border_w;
+
+ const int snap_distance = screen->getEdgeSnapThreshold();
+
+ if (snap_distance) {
+ // window corners
+ const int wleft = dx,
+ wright = dx + frame.rect.width() - 1,
+ wtop = dy,
+ wbottom = dy + frame.rect.height() - 1;
+
+ if (screen->getWindowToWindowSnap()) {
+ Workspace *w = screen->getWorkspace(getWorkspaceNumber());
+ assert(w);
+
+ // try snap to another window
+ for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
+ BlackboxWindow *snapwin = w->getWindow(i);
+ if (snapwin == this)
+ continue; // don't snap to self
+
+ bool snapped = False;
+
+ const Rect &winrect = snapwin->frameRect();
+ int dleft = std::abs(wright - winrect.left()),
+ dright = std::abs(wleft - winrect.right()),
+ dtop = std::abs(wbottom - winrect.top()),
+ dbottom = std::abs(wtop - winrect.bottom());
+
+ if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
+ wtop < (signed)(winrect.y() + winrect.height() - 1)) {
+
+ // snap left of other window?
+ if (dleft < snap_distance && dleft <= dright) {
+ dx = winrect.left() - frame.rect.width();
+ snapped = True;
+ }
+ // snap right of other window?
+ else if (dright < snap_distance) {
+ dx = winrect.right() + 1;
+ snapped = True;
+ }
+
+ if (snapped) {
+ if (screen->getWindowCornerSnap()) {
+ // try corner-snap to its other sides
+ dtop = std::abs(wtop - winrect.top());
+ dbottom = std::abs(wbottom - winrect.bottom());
+ if (dtop < snap_distance && dtop <= dbottom)
+ dy = winrect.top();
+ else if (dbottom < snap_distance)
+ dy = winrect.bottom() - frame.rect.height() + 1;
+ }
+
+ continue;
+ }
+ }
+
+ if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
+ wleft < (signed)(winrect.x() + winrect.width() - 1)) {
+
+ // snap top of other window?
+ if (dtop < snap_distance && dtop <= dbottom) {
+ dy = winrect.top() - frame.rect.height();
+ snapped = True;
+ }
+ // snap bottom of other window?
+ else if (dbottom < snap_distance) {
+ dy = winrect.bottom() + 1;
+ snapped = True;
+ }
+
+ if (snapped) {
+ if (screen->getWindowCornerSnap()) {
+ // try corner-snap to its other sides
+ dleft = std::abs(wleft - winrect.left());
+ dright = std::abs(wright - winrect.right());
+ if (dleft < snap_distance && dleft <= dright)
+ dx = winrect.left();
+ else if (dright < snap_distance)
+ dx = winrect.right() - frame.rect.width() + 1;
+ }
+
+ continue;
+ }
+ }
+ }
}
- screen->hideGeometry();
- XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
- } else if (flags.resizing) {
+
+ // try snap to the screen's available area
+ Rect srect = screen->availableArea();
+
+ int dleft = std::abs(wleft - srect.left()),
+ dright = std::abs(wright - srect.right()),
+ dtop = std::abs(wtop - srect.top()),
+ dbottom = std::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;
+
+ srect = screen->getRect(); // now get the full screen
+
+ dleft = std::abs(wleft - srect.left()),
+ dright = std::abs(wright - srect.right()),
+ dtop = std::abs(wtop - srect.top()),
+ dbottom = std::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);
+}
+
+
+void BlackboxWindow::endMove(void) {
+ assert(flags.moving);
+ assert(blackbox->getChangingWindow() == this);
+
+ flags.moving = False;
+ blackbox->setChangingWindow(0);
+
+ 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());
+ 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());
+ }
+ screen->hideGeometry();
- screen->hideGeometry();
+ XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
- constrain((re->window == frame.left_grip) ? TopRight : TopLeft);
+ // if there are any left over motions from the move, drop them now
+ XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
+ XEvent e;
+ while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
+ MotionNotify, &e));
+}
- // 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);
+void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
+ assert(! (flags.resizing || flags.moving));
+
+ /*
+ Only one window can be moved/resized at a time. If another window is already
+ being moved or resized, then stop it before whating to work with this one.
+ */
+ BlackboxWindow *changing = blackbox->getChangingWindow();
+ if (changing && changing != this) {
+ if (changing->flags.moving)
+ changing->endMove();
+ else // if (changing->flags.resizing)
+ changing->endResize();
+ }
+
+ resize_dir = dir;
+
+ Cursor cursor;
+ Corner anchor;
+
+ switch (resize_dir) {
+ case BottomLeft:
+ anchor = TopRight;
+ cursor = blackbox->getLowerLeftAngleCursor();
+ break;
+
+ case BottomRight:
+ anchor = TopLeft;
+ cursor = blackbox->getLowerRightAngleCursor();
+ break;
+
+ case TopLeft:
+ anchor = BottomRight;
+ cursor = blackbox->getUpperLeftAngleCursor();
+ break;
+
+ case TopRight:
+ anchor = BottomLeft;
+ cursor = blackbox->getUpperRightAngleCursor();
+ break;
+
+ default:
+ assert(false); // unhandled Corner
+ return; // unreachable, for the compiler
}
+
+ XGrabServer(blackbox->getXDisplay());
+ XGrabPointer(blackbox->getXDisplay(), frame.window, False,
+ PointerMotionMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
+
+ flags.resizing = True;
+ blackbox->setChangingWindow(this);
+
+ int gw, gh;
+ frame.changing = frame.rect;
+
+ 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);
+
+ screen->showGeometry(gw, gh);
+
+ frame.grab_x = x_root;
+ frame.grab_y = y_root;
}
-void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
- 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);
+void BlackboxWindow::doResize(int x_root, int y_root) {
+ assert(flags.resizing);
+ assert(blackbox->getChangingWindow() == this);
- if (windowmenu && windowmenu->isVisible())
- windowmenu->hide();
+ XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+ screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+ frame.changing.width() - 1, frame.changing.height() - 1);
- flags.moving = True;
+ int gw, gh;
+ Corner anchor;
- if (! screen->doOpaqueMove()) {
- XGrabServer(blackbox->getXDisplay());
+ switch (resize_dir) {
+ case BottomLeft:
+ anchor = TopRight;
+ frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
+ frame.rect.height() + (y_root - frame.grab_y));
+ break;
+ case BottomRight:
+ anchor = TopLeft;
+ frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
+ frame.rect.height() + (y_root - frame.grab_y));
+ break;
+ case TopLeft:
+ anchor = BottomRight;
+ frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
+ frame.rect.height() - (y_root - frame.grab_y));
+ break;
+ case TopRight:
+ anchor = BottomLeft;
+ frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
+ frame.rect.height() - (y_root - frame.grab_y));
+ break;
- frame.changing = frame.rect;
- screen->showPosition(frame.changing.x(), frame.changing.y());
+ default:
+ assert(false); // unhandled Corner
+ return; // unreachable, for the compiler
+ }
+
+ 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);
- }
- } 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 = std::abs(wleft - srect.left()),
- dright = std::abs(wright - srect.right()),
- dtop = std::abs(wtop - srect.top()),
- dbottom = std::abs(wbottom - srect.bottom());
-
- // snap left?
- if (dleft < snap_distance && dleft < dright)
- dx = srect.left();
- // snap right?
- else if (dright < snap_distance && dright < dleft)
- 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 && dbottom < dtop)
- dy = srect.bottom() - frame.rect.height() + 1;
-
- srect = screen->getRect(); // now get the full screen
-
- dleft = std::abs(wleft - srect.left()),
- dright = std::abs(wright - srect.right()),
- dtop = std::abs(wtop - srect.top()),
- dbottom = std::abs(wbottom - srect.bottom());
-
- // snap left?
- if (dleft < snap_distance && dleft < dright)
- dx = srect.left();
- // snap right?
- else if (dright < snap_distance && dright < dleft)
- 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 && dbottom < dtop)
- dy = srect.bottom() - frame.rect.height() + 1;
- }
+ XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+ screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+ frame.changing.width() - 1, frame.changing.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->showGeometry(gw, gh);
+}
- screen->showPosition(dx, dy);
- }
- } else if ((functions & Func_Resize) &&
- (((me->state & Button1Mask) &&
- (me->window == frame.right_grip ||
- me->window == frame.left_grip)) ||
- (me->state & (Mod1Mask | Button3Mask) &&
- 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;
-
- 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);
-
- screen->showGeometry(gw, gh);
- } else {
- XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
- screen->getOpGC(), frame.changing.x(), frame.changing.y(),
- frame.changing.width() - 1, frame.changing.height() - 1);
- int gw, gh;
+void BlackboxWindow::endResize(void) {
+ assert(flags.resizing);
+ assert(blackbox->getChangingWindow() == this);
+
+ XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+ screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+ frame.changing.width() - 1, frame.changing.height() - 1);
+ XUngrabServer(blackbox->getXDisplay());
- Corner anchor;
+ // unset maximized state after resized when fully maximized
+ if (flags.maximized == 1)
+ maximize(0);
+
+ flags.resizing = False;
+ blackbox->setChangingWindow(0);
- 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));
- }
+ configure(frame.changing.x(), frame.changing.y(),
+ frame.changing.width(), frame.changing.height());
+ screen->hideGeometry();
- constrain(anchor, &gw, &gh);
+ XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
+
+ // if there are any left over motions from the resize, drop them now
+ XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
+ XEvent e;
+ while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
+ MotionNotify, &e));
+}
- XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
- screen->getOpGC(), frame.changing.x(), frame.changing.y(),
- frame.changing.width() - 1, frame.changing.height() - 1);
- screen->showGeometry(gw, gh);
+void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
+#ifdef DEBUG
+ fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
+ client.window);
+#endif
+
+ if (flags.moving) {
+ doMove(me->x_root, me->y_root);
+ } else if (flags.resizing) {
+ doResize(me->x_root, me->y_root);
+ } else {
+ 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)) {
+ beginMove(me->x_root, me->y_root);
+ } else if ((functions & Func_Resize) &&
+ (((me->state & Button1Mask) &&
+ (me->window == frame.right_grip ||
+ me->window == frame.left_grip)) ||
+ (me->state & (Mod1Mask | Button3Mask) &&
+ me->window == frame.window))) {
+ beginResize(me->x_root, me->y_root,
+ (me->window == frame.left_grip) ? BottomLeft : BottomRight);
}
}
}
#endif // SHAPE
-bool BlackboxWindow::validateClient(void) {
+bool BlackboxWindow::validateClient(void) const {
XSync(blackbox->getXDisplay(), False);
XEvent e;
XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
- restoreGravity();
+ // do not leave a shaded window as an icon unless it was an icon
+ if (flags.shaded && ! flags.iconic) setState(NormalState);
+
+ 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)) {
+ 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,
default:
case DecorNormal:
- decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
- Decor_Iconify | Decor_Maximize;
+ decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
+
+ decorations = ((functions & Func_Resize) && !isTransient() ?
+ decorations | Decor_Handle :
+ decorations &= ~Decor_Handle);
+ decorations = (functions & Func_Maximize ?
+ decorations | Decor_Maximize :
+ decorations &= ~Decor_Maximize);
break;
case DecorTiny:
decorations |= Decor_Titlebar | Decor_Iconify;
- decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
+ decorations &= ~(Decor_Border | Decor_Handle);
+
+ decorations = (functions & Func_Maximize ?
+ decorations | Decor_Maximize :
+ decorations &= ~Decor_Maximize);
break;
case DecorTool:
decorations |= Decor_Titlebar;
- decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
- functions |= Func_Move;
+ decorations &= ~(Decor_Iconify | Decor_Border);
+
+ decorations = ((functions & Func_Resize) && !isTransient() ?
+ decorations | Decor_Handle :
+ decorations &= ~Decor_Handle);
+ decorations = (functions & Func_Maximize ?
+ decorations | Decor_Maximize :
+ decorations &= ~Decor_Maximize);
break;
}
- if (frame.window) {
+
+ // we can not be shaded if we lack a titlebar
+ if (flags.shaded && ! (decorations & Decor_Titlebar))
+ shade();
+
+ if (flags.visible && frame.window) {
XMapSubwindows(blackbox->getXDisplay(), frame.window);
XMapWindow(blackbox->getXDisplay(), frame.window);
}
if (decorations & Decor_Border) {
frame.border_w = screen->getBorderWidth();
- if (!isTransient())
+ if (! isTransient())
frame.mwm_border_w = screen->getFrameWidth();
else
frame.mwm_border_w = 0;
// the height of the titlebar is based upon the height of the font being
// used to display the window's title
WindowStyle *style = screen->getWindowStyle();
- if (i18n.multibyte())
- frame.title_h = (style->fontset_extents->max_ink_extent.height +
- (frame.bevel_w * 2) + 2);
- else
- frame.title_h = (style->font->ascent + style->font->descent +
- (frame.bevel_w * 2) + 2);
+ frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
frame.label_h = frame.title_h - (frame.bevel_w * 2);
frame.button_w = (frame.label_h - 2);
frame.margin.bottom = frame.border_w + frame.mwm_border_w;
}
- // set the frame rect
- frame.rect.setSize(client.rect.width() + frame.margin.left +
- frame.margin.right,
- client.rect.height() + frame.margin.top +
- frame.margin.bottom);
- frame.inside_w = frame.rect.width() - (frame.border_w * 2);
- frame.inside_h = frame.rect.height() - (frame.border_w * 2);
+ /*
+ We first get the normal dimensions and use this to define the inside_w/h
+ then we modify the height if shading is in effect.
+ If the shade state is not considered then frame.rect gets reset to the
+ normal window size on a reconfigure() call resulting in improper
+ dimensions appearing in move/resize and other events.
+ */
+ unsigned int
+ height = client.rect.height() + frame.margin.top + frame.margin.bottom,
+ width = client.rect.width() + frame.margin.left + frame.margin.right;
+
+ frame.inside_w = width - (frame.border_w * 2);
+ frame.inside_h = height - (frame.border_w * 2);
+
+ if (flags.shaded)
+ height = frame.title_h + (frame.border_w * 2);
+ frame.rect.setSize(width, height);
}
*
* 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 has resizes in cells, not in
- * pixels).
+ * the window size (for example an xterm resizes in cells, not in pixels).
*
* The physical geometry is placed into frame.changing_{x,y,width,height}.
* Physical geometry refers to the geometry of the window in pixels.
dh -= base_height;
dh /= client.height_inc;
- if (pw) *pw = dw;
- if (ph) *ph = dh;
+ if (pw) {
+ if (client.width_inc == 1)
+ *pw = dw + base_width;
+ else
+ *pw = dw;
+ }
+ if (ph) {
+ if (client.height_inc == 1)
+ *ph = dh + base_height;
+ else
+ *ph = dh;
+ }
dw *= client.width_inc;
dw += base_width;
frame.changing.bottom() + frame.margin.bottom);
// move frame.changing to the specified anchor
+ int dx = 0,
+ dy = 0;
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());
+ dx = frame.rect.right() - frame.changing.right();
+ break;
+
+ case BottomLeft:
+ dy = frame.rect.bottom() - frame.changing.bottom();
+ break;
+
+ case BottomRight:
+ dx = frame.rect.right() - frame.changing.right();
+ dy = frame.rect.bottom() - frame.changing.bottom();
break;
+
+ default:
+ assert(false); // unhandled corner
}
+ frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
}
-int WindowStyle::doJustify(const char *text, int &start_pos,
- unsigned int max_length, unsigned int modifier,
- bool multibyte) const {
- size_t text_len = strlen(text);
+void WindowStyle::doJustify(const std::string &text, int &start_pos,
+ unsigned int max_length,
+ unsigned int modifier) const {
+ size_t text_len = text.size();
unsigned int length;
do {
- if (multibyte) {
- XRectangle ink, logical;
- XmbTextExtents(fontset, text, text_len, &ink, &logical);
- length = logical.width;
- } else {
- length = XTextWidth(font, text, text_len);
- }
- length += modifier;
+ length = font->measureString(string(text, 0, text_len)) + modifier;
} while (length > max_length && text_len-- > 0);
switch (justify) {
default:
break;
}
-
- return text_len;
}
BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
: blackbox(b), group(_group) {
- // watch for destroy notify on the group window
- XSelectInput(blackbox->getXDisplay(), group, StructureNotifyMask);
+ XWindowAttributes wattrib;
+ if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
+ // group window doesn't seem to exist anymore
+ delete this;
+ return;
+ }
+
+ /*
+ watch for destroy notify on the group window (in addition to
+ any other events we are looking for)
+
+ since some managed windows can also be window group controllers,
+ we need to make sure that we don't clobber the event mask for the
+ managed window
+ */
+ XSelectInput(blackbox->getXDisplay(), group,
+ wattrib.your_event_mask | StructureNotifyMask);
+
blackbox->saveGroupSearch(group, this);
}