+ if (frame) { // this can be called while mapping, before frame exists
+ frame->adjustPosition();
+
+ // send synthetic configure notify (we don't need to if we aren't mapped
+ // yet)
+ XEvent event;
+ event.type = ConfigureNotify;
+ event.xconfigure.display = **otk::display;
+ event.xconfigure.event = _window;
+ event.xconfigure.window = _window;
+
+ // root window coords with border in mind
+ event.xconfigure.x = x - _border_width + frame->size().left;
+ event.xconfigure.y = y - _border_width + frame->size().top;
+
+ event.xconfigure.width = _area.width();
+ event.xconfigure.height = _area.height();
+ event.xconfigure.border_width = _border_width;
+ event.xconfigure.above = frame->plate();
+ event.xconfigure.override_redirect = False;
+ XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
+ StructureNotifyMask, &event);
+#if 0//def DEBUG
+ printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
+ event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
+ event.xconfigure.height, event.xconfigure.window);
+#endif
+ }
+}
+
+void Client::close()
+{
+ XEvent ce;
+
+ if (!(_functions & Func_Close)) return;
+
+ // XXX: itd be cool to do timeouts and shit here for killing the client's
+ // process off
+ // like... if the window is around after 5 seconds, then the close button
+ // turns a nice red, and if this function is called again, the client is
+ // explicitly killed.
+
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = otk::Property::atoms.wm_protocols;
+ ce.xclient.display = **otk::display;
+ ce.xclient.window = _window;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = otk::Property::atoms.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(**otk::display, _window, false, NoEventMask, &ce);
+}
+
+void Client::changeState()
+{
+ unsigned long state[2];
+ state[0] = _wmstate;
+ state[1] = None;
+ otk::Property::set(_window, otk::Property::atoms.wm_state,
+ otk::Property::atoms.wm_state, state, 2);
+
+ Atom netstate[10];
+ int num = 0;
+ if (_modal)
+ netstate[num++] = otk::Property::atoms.net_wm_state_modal;
+ if (_shaded)
+ netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
+ if (_iconic)
+ netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
+ if (_skip_taskbar)
+ netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
+ if (_skip_pager)
+ netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
+ if (_fullscreen)
+ netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
+ if (_max_vert)
+ netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
+ if (_max_horz)
+ netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
+ if (_above)
+ netstate[num++] = otk::Property::atoms.net_wm_state_above;
+ if (_below)
+ netstate[num++] = otk::Property::atoms.net_wm_state_below;
+ otk::Property::set(_window, otk::Property::atoms.net_wm_state,
+ otk::Property::atoms.atom, netstate, num);
+
+ calcLayer();
+
+ if (frame)
+ frame->adjustState();
+}
+
+void Client::changeAllowedActions(void)
+{
+ Atom actions[9];
+ int num = 0;
+
+ actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
+
+ if (_functions & Func_Shade)
+ actions[num++] = otk::Property::atoms.net_wm_action_shade;
+ if (_functions & Func_Close)
+ actions[num++] = otk::Property::atoms.net_wm_action_close;
+ if (_functions & Func_Move)
+ actions[num++] = otk::Property::atoms.net_wm_action_move;
+ if (_functions & Func_Iconify)
+ actions[num++] = otk::Property::atoms.net_wm_action_minimize;
+ if (_functions & Func_Resize)
+ actions[num++] = otk::Property::atoms.net_wm_action_resize;
+ if (_functions & Func_Fullscreen)
+ actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
+ if (_functions & Func_Maximize) {
+ actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
+ actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
+ }
+
+ otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
+ otk::Property::atoms.atom, actions, num);
+
+ // make sure the window isn't breaking any rules now
+
+ if (!(_functions & Func_Shade) && _shaded)
+ if (frame) shade(false);
+ else _shaded = false;
+ if (!(_functions & Func_Iconify) && _iconic)
+ if (frame) setDesktop(openbox->screen(_screen)->desktop());
+ else _iconic = false;
+ if (!(_functions & Func_Fullscreen) && _fullscreen)
+ if (frame) fullscreen(false);
+ else _fullscreen = false;
+ if (!(_functions & Func_Maximize) && (_max_horz || _max_vert))
+ if (frame) maximize(false, 0);
+ else _max_vert = _max_horz = false;
+}
+
+void Client::remaximize()
+{
+ int dir;
+ if (_max_horz && _max_vert)
+ dir = 0;
+ else if (_max_horz)
+ dir = 1;
+ else if (_max_vert)
+ dir = 2;
+ else
+ return; // not maximized
+ _max_horz = _max_vert = false;
+ maximize(true, dir, false);
+}
+
+void Client::applyStartupState()
+{
+ // these are in a carefully crafted order..
+
+ if (_iconic) {
+ _iconic = false;
+ iconify(true);
+ }
+ if (_fullscreen) {
+ _fullscreen = false;
+ fullscreen(true, false);
+ }
+ if (_shaded) {
+ _shaded = false;
+ shade(true);
+ }
+ if (_urgent)
+ fireUrgent();
+
+ if (_max_vert && _max_horz) {
+ _max_vert = _max_horz = false;
+ maximize(true, 0, false);
+ } else if (_max_vert) {
+ _max_vert = false;
+ maximize(true, 2, false);
+ } else if (_max_horz) {
+ _max_horz = false;
+ maximize(true, 1, false);
+ }
+
+ if (_skip_taskbar); // nothing to do for this
+ if (_skip_pager); // nothing to do for this
+ if (_modal); // nothing to do for this
+ if (_above); // nothing to do for this
+ if (_below); // nothing to do for this
+}
+
+void Client::fireUrgent()
+{
+ // call the python UrgentWindow callbacks
+ EventData data(_screen, this, EventAction::UrgentWindow, 0);
+ openbox->bindings()->fireEvent(&data);
+}
+
+void Client::shade(bool shade)
+{
+ if (!(_functions & Func_Shade) || // can't
+ _shaded == shade) return; // already done
+
+ // when we're iconic, don't change the wmstate
+ if (!_iconic)
+ _wmstate = shade ? IconicState : NormalState;
+ _shaded = shade;
+ changeState();
+ frame->adjustSize();
+}
+
+void Client::maximize(bool max, int dir, bool savearea)
+{
+ assert(dir == 0 || dir == 1 || dir == 2);
+ if (!(_functions & Func_Maximize)) return; // can't
+
+ // check if already done
+ if (max) {
+ if (dir == 0 && _max_horz && _max_vert) return;
+ if (dir == 1 && _max_horz) return;
+ if (dir == 2 && _max_vert) return;
+ } else {
+ if (dir == 0 && !_max_horz && !_max_vert) return;
+ if (dir == 1 && !_max_horz) return;
+ if (dir == 2 && !_max_vert) return;
+ }
+
+ const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
+ int x = frame->area().x(), y = frame->area().y(),
+ w = _area.width(), h = _area.height();
+
+ if (max) {
+ if (savearea) {
+ long dimensions[4];
+ long *readdim;
+ unsigned long n = 4;
+
+ dimensions[0] = x;
+ dimensions[1] = y;
+ dimensions[2] = w;
+ dimensions[3] = h;
+
+ // get the property off the window and use it for the dimentions we are
+ // already maxed on
+ if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
+ otk::Property::atoms.cardinal, &n,
+ (long unsigned**) &readdim)) {
+ if (n >= 4) {
+ if (_max_horz) {
+ dimensions[0] = readdim[0];
+ dimensions[2] = readdim[2];
+ }
+ if (_max_vert) {
+ dimensions[1] = readdim[1];
+ dimensions[3] = readdim[3];
+ }
+ }
+ delete readdim;
+ }
+
+ otk::Property::set(_window, otk::Property::atoms.openbox_premax,
+ otk::Property::atoms.cardinal,
+ (long unsigned*)dimensions, 4);
+ }
+ if (dir == 0 || dir == 1) { // horz
+ x = a.x();
+ w = a.width();
+ }
+ if (dir == 0 || dir == 2) { // vert
+ y = a.y();
+ h = a.height() - frame->size().top - frame->size().bottom;
+ }
+ } else {
+ long *dimensions;
+ long unsigned n = 4;
+
+ if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
+ otk::Property::atoms.cardinal, &n,
+ (long unsigned**) &dimensions)) {
+ if (n >= 4) {
+ if (dir == 0 || dir == 1) { // horz
+ x = (signed int)dimensions[0];
+ w = (signed int)dimensions[2];
+ }
+ if (dir == 0 || dir == 2) { // vert
+ y = (signed int)dimensions[1];
+ h = (signed int)dimensions[3];
+ }
+ }
+ delete dimensions;
+ } else {
+ // pick some fallbacks...
+ if (dir == 0 || dir == 1) { // horz
+ x = a.x() + a.width() / 4;
+ w = a.width() / 2;
+ }
+ if (dir == 0 || dir == 2) { // vert
+ y = a.y() + a.height() / 4;
+ h = a.height() / 2;
+ }
+ }
+ }
+
+ if (dir == 0 || dir == 1) // horz
+ _max_horz = max;
+ if (dir == 0 || dir == 2) // vert
+ _max_vert = max;
+
+ if (!_max_horz && !_max_vert)
+ otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
+
+ changeState(); // change the state hints on the client
+
+ frame->frameGravity(x, y); // figure out where the client should be going
+ internal_resize(TopLeft, w, h, true, x, y);
+}
+
+void Client::fullscreen(bool fs, bool savearea)
+{
+ static FunctionFlags saved_func;
+ static DecorationFlags saved_decor;
+
+ if (!(_functions & Func_Fullscreen) || // can't
+ _fullscreen == fs) return; // already done
+
+ _fullscreen = fs;
+ changeState(); // change the state hints on the client
+
+ int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
+
+ if (fs) {
+ // save the functions and remove them
+ saved_func = _functions;
+ _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
+ // save the decorations and remove them
+ saved_decor = _decorations;
+ _decorations = 0;
+ if (savearea) {
+ long dimensions[4];
+ dimensions[0] = _area.x();
+ dimensions[1] = _area.y();
+ dimensions[2] = _area.width();
+ dimensions[3] = _area.height();
+ otk::Property::set(_window, otk::Property::atoms.openbox_premax,
+ otk::Property::atoms.cardinal,
+ (long unsigned*)dimensions, 4);
+ }
+ const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
+ x = 0;
+ y = 0;
+ w = info->size().width();
+ h = info->size().height();
+ } else {
+ _functions = saved_func;
+ _decorations = saved_decor;
+
+ long *dimensions;
+ long unsigned n = 4;
+
+ if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
+ otk::Property::atoms.cardinal, &n,
+ (long unsigned**) &dimensions)) {
+ if (n >= 4) {
+ x = dimensions[0];
+ y = dimensions[1];
+ w = dimensions[2];
+ h = dimensions[3];
+ }
+ delete dimensions;
+ } else {
+ // pick some fallbacks...
+ const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
+ x = a.x() + a.width() / 4;
+ y = a.y() + a.height() / 4;
+ w = a.width() / 2;
+ h = a.height() / 2;
+ }
+ }
+
+ changeAllowedActions(); // based on the new _functions
+
+ // when fullscreening, don't obey things like increments, fill the screen
+ internal_resize(TopLeft, w, h, !fs, x, y);
+
+ // raise (back) into our stacking layer
+ openbox->screen(_screen)->raiseWindow(this);
+
+ // try focus us when we go into fullscreen mode
+ if (fs) focus();
+}
+
+void Client::iconify(bool iconic, bool curdesk)
+{
+ if (_iconic == iconic) return; // nothing to do
+
+#ifdef DEBUG
+ printf("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"), _window);
+#endif
+
+ _iconic = iconic;
+
+ if (_iconic) {
+ _wmstate = IconicState;
+ ignore_unmaps++;
+ // we unmap the client itself so that we can get MapRequest events, and
+ // because the ICCCM tells us to!
+ XUnmapWindow(**otk::display, _window);
+ } else {
+ if (curdesk)
+ setDesktop(openbox->screen(_screen)->desktop());
+ _wmstate = NormalState;
+ XMapWindow(**otk::display, _window);
+ }
+ changeState();
+ showhide();
+ openbox->screen(_screen)->updateStruts();
+}
+
+void Client::disableDecorations(DecorationFlags flags)
+{
+ _disabled_decorations = flags;
+ setupDecorAndFunctions();
+}
+
+void Client::installColormap(bool install) const
+{
+ XWindowAttributes wa;
+ if (XGetWindowAttributes(**otk::display, _window, &wa)) {
+ if (install)
+ XInstallColormap(**otk::display, wa.colormap);
+ else
+ XUninstallColormap(**otk::display, wa.colormap);
+ }
+}
+
+Client *Client::searchModalTree(Client *node, Client *skip)
+{
+ List::const_iterator it, end = node->_transients.end();
+ Client *ret;
+
+ for (it = node->_transients.begin(); it != end; ++it) {
+ if (*it == skip) continue; // circular?
+ if ((ret = searchModalTree(*it, skip))) return ret; // got one
+ if ((*it)->_modal) return *it; // got one
+ }
+ return 0;
+}
+
+Client *Client::findModalChild()
+{
+ return searchModalTree(this, this);
+}
+
+
+bool Client::focus()
+{
+ // if we have a modal child, then focus it, not us
+ Client *c = findModalChild();
+ if (c) return c->focus();
+
+ // won't try focus if the client doesn't want it, or if the window isn't
+ // visible on the screen
+ if (!(frame->visible() && (_can_focus || _focus_notify))) return false;
+
+ // do a check to see if the window has already been unmapped or destroyed
+ // do this intelligently while watching out for unmaps we've generated
+ // (ignore_unmaps > 0)
+ XEvent ev;
+ if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
+ XPutBackEvent(**otk::display, &ev);
+ return false;
+ }
+ while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
+ if (ignore_unmaps) {
+ unmapHandler(ev.xunmap);
+ } else {
+ XPutBackEvent(**otk::display, &ev);
+ return false;
+ }
+ }
+
+ if (_can_focus)
+ XSetInputFocus(**otk::display, _window,
+ RevertToNone, CurrentTime);
+
+ if (_focus_notify) {
+ XEvent ce;
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = otk::Property::atoms.wm_protocols;
+ ce.xclient.display = **otk::display;
+ ce.xclient.window = _window;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
+ ce.xclient.data.l[1] = openbox->lastTime();
+ ce.xclient.data.l[2] = 0l;
+ ce.xclient.data.l[3] = 0l;
+ ce.xclient.data.l[4] = 0l;
+ XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
+ }
+
+ XSync(**otk::display, False);
+ return true;
+}
+
+
+void Client::unfocus() const
+{
+ assert(openbox->focusedClient() == this);
+ openbox->setFocusedClient(0);
+}
+
+
+void Client::focusHandler(const XFocusChangeEvent &e)
+{
+#ifdef DEBUG
+// printf("FocusIn for 0x%lx\n", e.window);
+#endif // DEBUG
+
+ otk::EventHandler::focusHandler(e);
+
+ _focused = true;
+ frame->adjustFocus();
+
+ calcLayer(); // focus state can affect the stacking layer
+
+ openbox->setFocusedClient(this);