// WinClient.cc for Fluxbox - an X11 Window manager // Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org) // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #include "WinClient.hh" #include "Window.hh" #include "fluxbox.hh" #include "FocusControl.hh" #include "Remember.hh" #include "Screen.hh" #include "FbAtoms.hh" #include "Xutil.hh" #include "Debug.hh" #include "FbTk/EventManager.hh" #include "FbTk/MultLayers.hh" #include #include #include #include #include #ifdef HAVE_CASSERT #include #else #include #endif #ifdef HAVE_CSTRING #include #else #include #endif using std::string; using std::list; using std::mem_fun; using std::endl; using std::cerr; using std::hex; using std::dec; namespace { void sendMessage(const WinClient& win, Atom atom, Time time) { XEvent ce; ce.xclient.type = ClientMessage; ce.xclient.message_type = FbAtoms::instance()->getWMProtocolsAtom(); ce.xclient.display = win.display(); ce.xclient.window = win.window(); ce.xclient.format = 32; ce.xclient.data.l[0] = atom; ce.xclient.data.l[1] = time; ce.xclient.data.l[2] = 0l; ce.xclient.data.l[3] = 0l; ce.xclient.data.l[4] = 0l; XSendEvent(win.display(), win.window(), false, NoEventMask, &ce); } } // end of anonymous namespace WinClient::TransientWaitMap WinClient::s_transient_wait; WinClient::WinClient(Window win, BScreen &screen, FluxboxWindow *fbwin): Focusable(screen, fbwin), FbTk::FbWindow(win), transient_for(0), window_group(0), old_bw(0), initial_state(0), normal_hint_flags(0), wm_hint_flags(0), m_modal_count(0), m_modal(false), accepts_input(false), send_focus_message(false), send_close_message(false), m_title_override(false), m_icon_override(false), m_window_type(WindowState::TYPE_NORMAL), m_mwm_hint(0), m_strut(0) { old_bw = borderWidth(); updateWMProtocols(); updateMWMHints(); updateWMHints(); updateWMNormalHints(); updateWMClassHint(); updateTitle(); Fluxbox::instance()->saveWindowSearch(win, this); if (window_group != None) Fluxbox::instance()->saveGroupSearch(window_group, this); // search for this in transient waiting list if (s_transient_wait.find(win) != s_transient_wait.end()) { // Found transients that are waiting for this. // For each transient that waits call updateTransientInfo for_each(s_transient_wait[win].begin(), s_transient_wait[win].end(), mem_fun(&WinClient::updateTransientInfo)); // clear transient waiting list for this window s_transient_wait.erase(win); } m_title_update_timer.setTimeout(100 * FbTk::FbTime::IN_MILLISECONDS); m_title_update_timer.fireOnce(true); FbTk::RefCount > ets(new FbTk::SimpleCommand(*this, &WinClient::emitTitleSig)); m_title_update_timer.setCommand(ets); // also check if this window is a transient // this needs to be done before creating an fbwindow, so this doesn't get // tabbed using the apps file updateTransientInfo(); } WinClient::~WinClient() { fbdbg<<__FILE__<<"(~"<<__FUNCTION__<<")[this="<remove(window()); Fluxbox *fluxbox = Fluxbox::instance(); if (window()) fluxbox->removeWindowSearch(window()); clearStrut(); // // clear transients and transient_for // if (transient_for != 0) { assert(transient_for != this); transient_for->transientList().remove(this); if (m_modal) transient_for->removeModal(); } while (!transients.empty()) { transients.back()->transient_for = 0; transients.pop_back(); } accepts_input = send_focus_message = false; if (fbwindow() != 0) fbwindow()->removeClient(*this); // this takes care of any focus issues dieSig().emit(*this); // This fixes issue 1 (see WinClient.hh): // If transients die before the transient_for is created transient_for = 0; removeTransientFromWaitingList(); s_transient_wait.erase(window()); if (window_group != 0) { fluxbox->removeGroupSearch(window_group); window_group = 0; } if (m_mwm_hint != 0) XFree(m_mwm_hint); } bool WinClient::acceptsFocus() const { return ((accepts_input || send_focus_message) && // focusing fbpanel messes up quite a few things m_window_type != WindowState::TYPE_DOCK && m_window_type != WindowState::TYPE_SPLASH); } bool WinClient::sendFocus() { if (accepts_input) { setInputFocus(RevertToPointerRoot, CurrentTime); FocusControl::setExpectingFocus(this); return true; } if (!send_focus_message) return false; fbdbg<<"WinClient::"<<__FUNCTION__<<": this = "<getWMTakeFocusAtom(), Fluxbox::instance()->getLastTime()); FocusControl::setExpectingFocus(this); return true; } void WinClient::sendClose(bool forceful) { if (forceful || !send_close_message) XKillClient(display(), window()); else { // send WM_DELETE message sendMessage(*this, FbAtoms::instance()->getWMDeleteAtom(), CurrentTime); } } bool WinClient::getAttrib(XWindowAttributes &attr) const { return XGetWindowAttributes(display(), window(), &attr); } bool WinClient::getWMName(XTextProperty &textprop) const { return XGetWMName(display(), window(), &textprop); } bool WinClient::getWMIconName(XTextProperty &textprop) const { return XGetWMIconName(display(), window(), &textprop); } string WinClient::getWMRole() const { Atom wm_role = XInternAtom(FbTk::App::instance()->display(), "WM_WINDOW_ROLE", False); return textProperty(wm_role); } void WinClient::updateWMClassHint() { m_instance_name = Xutil::getWMClassName(window()); m_class_name = Xutil::getWMClassClass(window()); } void WinClient::updateTransientInfo() { // remove this from parent if (transientFor() != 0) { transientFor()->transientList().remove(this); if (m_modal) transientFor()->removeModal(); } transient_for = 0; // determine if this is a transient window Window win = 0; if (!XGetTransientForHint(display(), window(), &win)) { fbdbg<<__FUNCTION__<<": window() = 0x"<searchWindow(win); // if we did not find a transient WinClient but still // have a transient X window, then we have to put the // X transient_for window in a waiting list and update this clients transient // list later when the transient_for has a Winclient if (!transient_for) { // We might also already waiting for an old transient_for; // // this call fixes issue 2: // If transients changes to new transient_for before the old transient_for is created. // (see comment in WinClient.hh) // removeTransientFromWaitingList(); s_transient_wait[win].push_back(this); } fbdbg<<__FUNCTION__<<": transient_for window = 0x"<addModal(); else transient_for->removeModal(); } // TODO: we're not implementing the following part of EWMH spec: // "if WM_TRANSIENT_FOR is not set or set to the root window the dialog is // modal for its window group." } bool WinClient::validateClient() const { FbTk::App::instance()->sync(false); XEvent e; if (( XCheckTypedWindowEvent(display(), window(), DestroyNotify, &e) || XCheckTypedWindowEvent(display(), window(), UnmapNotify, &e)) && XPutBackEvent(display(), &e)) { Fluxbox::instance()->ungrab(); return false; } return true; } void WinClient::setStrut(Strut *strut) { clearStrut(); m_strut = strut; } void WinClient::clearStrut() { if (m_strut != 0) { screen().clearStrut(m_strut); screen().updateAvailableWorkspaceArea(); m_strut = 0; } } bool WinClient::focus() { if (fbwindow() == 0) return false; else return fbwindow()->setCurrentClient(*this, true); } bool WinClient::isFocused() const { return (fbwindow() ? fbwindow()->isFocused() && &fbwindow()->winClient() == this : false); } void WinClient::setAttentionState(bool value) { Focusable::setAttentionState(value); if (fbwindow() && !fbwindow()->isFocused()) fbwindow()->setAttentionState(value); } void WinClient::updateWMProtocols() { Atom *proto = 0; int num_return = 0; FbAtoms *fbatoms = FbAtoms::instance(); if (XGetWMProtocols(display(), window(), &proto, &num_return)) { // defaults send_focus_message = false; send_close_message = false; for (int i = 0; i < num_return; ++i) { if (proto[i] == fbatoms->getWMDeleteAtom()) send_close_message = true; else if (proto[i] == fbatoms->getWMTakeFocusAtom()) send_focus_message = true; } XFree(proto); if (fbwindow()) fbwindow()->updateFunctions(); } else { fbdbg<<"Warning: Failed to read WM Protocols. "< remove_list; // The worst case complexity is huge, but since we usually do not (virtualy never) // have a large transient waiting list the time spent here is neglectable TransientWaitMap::iterator t_it = s_transient_wait.begin(); TransientWaitMap::iterator t_it_end = s_transient_wait.end(); for (; t_it != t_it_end; ++t_it) { (*t_it).second.remove(this); // if the list is empty, add it to remove list // so we can erase it later if ((*t_it).second.empty()) remove_list.push_back((*t_it).first); } // erase empty waiting lists list::iterator it = remove_list.begin(); list::iterator it_end = remove_list.end(); for (; it != it_end; ++it) s_transient_wait.erase(*it); }