From dc47491533e0ca7cf5a5386a10e68fbaf873e9db Mon Sep 17 00:00:00 2001 From: Mathias Gumz Date: Thu, 31 Jan 2013 09:13:45 +0100 Subject: [PATCH] Adds 'ClientPatternTest' command ClientPatterns might be tricky to get right. Instead of fiddling around in either the keys-file or the apps-file and restarting fluxbox to see if the changes had any effect / matched the right windows, 'ClientPatternTest' and the fluxbox-remote should make this easier: $> fluxbox-remote "clientpatterntest (title=.*vim*)" This causes fluxbox to store the list of matched windows in the _FLUXBOX_ACTION_RESULT property onto the rootwindow. This property might then be read by: $> xprop -root _FLUXBOX_ACTION_RESULT or $> fluxbox-remote result The format of the list is: win_id \t title_of_window \n win_id is '-1' when fluxbox wasn't able to parse the given ClientPattern. win_id is '0' when there are no windows matching the given ClientPattern. --- src/ClientPattern.hh | 1 + src/FbCommands.cc | 82 ++++++++++++++++++++++++++++++++++++++---- src/FbCommands.hh | 10 ++++++ src/FbTk/StringUtil.cc | 9 ++++- src/FbTk/StringUtil.hh | 1 + src/Screen.cc | 66 +++++++++++++++++++--------------- util/fluxbox-remote.cc | 61 ++++++++++++++++++++++--------- 7 files changed, 177 insertions(+), 53 deletions(-) diff --git a/src/ClientPattern.hh b/src/ClientPattern.hh index a5b72064..358977a7 100644 --- a/src/ClientPattern.hh +++ b/src/ClientPattern.hh @@ -89,6 +89,7 @@ public: * the column of the error is stored in m_matchlimit */ int error() const { return m_terms.empty() ? 1 : 0; } + int error_col() const { return m_matchlimit; } static FbTk::FbString getProperty(WinProperty prop, const Focusable &client); diff --git a/src/FbCommands.cc b/src/FbCommands.cc index b0289b8d..eb7fbfd3 100644 --- a/src/FbCommands.cc +++ b/src/FbCommands.cc @@ -445,13 +445,12 @@ REGISTER_UNTRUSTED_COMMAND_WITH_ARGS(bindkey, FbCommands::BindKeyCmd, void); BindKeyCmd::BindKeyCmd(const string &keybind):m_keybind(keybind) { } void BindKeyCmd::execute() { - if (Fluxbox::instance()->keys() != 0) { - if (Fluxbox::instance()->keys()->addBinding(m_keybind)) { - ofstream ofile(Fluxbox::instance()->keys()->filename().c_str(), ios::app); - if (!ofile) - return; - ofile<keys(); + if (keys && keys->addBinding(m_keybind)) { + ofstream ofile(keys->filename().c_str(), ios::app); + if (!ofile) + return; + ofile< matches; + std::string result; + std::string pat; + int opts; + ClientPattern* cp; + Display* dpy; + Atom atom_utf8; + Atom atom_fbcmd_result; + Fluxbox::ScreenList::const_iterator screen; + const Fluxbox::ScreenList screens(Fluxbox::instance()->screenList()); + + dpy = Fluxbox::instance()->display(); + atom_utf8 = XInternAtom(dpy, "UTF8_STRING", False); + atom_fbcmd_result = XInternAtom(dpy, "_FLUXBOX_ACTION_RESULT", False); + + FocusableList::parseArgs(m_args, opts, pat); + cp = new ClientPattern(pat.c_str()); + + if (!cp->error()) { + + const FocusableList* windows; + FocusControl::Focusables::const_iterator wit; + FocusControl::Focusables::const_iterator wit_end; + + for (screen = screens.begin(); screen != screens.end(); screen++) { + + windows = FocusableList::getListFromOptions(**screen, opts|FocusableList::LIST_GROUPS); + wit = windows->clientList().begin(); + wit_end = windows->clientList().end(); + + for ( ; wit != wit_end; wit++) { + if (typeid(**wit) == typeid(FluxboxWindow) && cp->match(**wit)) { + matches.push_back(static_cast(*wit)); + } + } + } + + if (!matches.empty()) { + std::vector< const FluxboxWindow* >::const_iterator win; + for (win = matches.begin(); win != matches.end(); win++) { + result += "0x"; + result += FbTk::StringUtil::number2HexString((*win)->clientWindow()); + result += "\t"; + result += (*win)->title().logical(); + result += "\n"; + } + } else { + result += "0\n"; + } + } else { + result = "-1\t"; + result += FbTk::StringUtil::number2String(cp->error_col()); + result += "\n"; + } + + + // write result to _FLUXBOX_ACTION_RESULT property + for (screen = screens.begin(); screen != screens.end(); screen++) { + (*screen)->rootWindow().changeProperty(atom_fbcmd_result, atom_utf8, 8, + PropModeReplace, (unsigned char*)result.c_str(), result.size()); + } +} + + } // end namespace FbCommands diff --git a/src/FbCommands.hh b/src/FbCommands.hh index b6b1f7f8..d9639adc 100644 --- a/src/FbCommands.hh +++ b/src/FbCommands.hh @@ -218,6 +218,16 @@ private: Destination m_dest; }; + +/// test client pattern +class ClientPatternTestCmd: public FbTk::Command { +public: + ClientPatternTestCmd(const std::string& args) : m_args(args) { }; + void execute(); +private: + std::string m_args; +}; + } // end namespace FbCommands #endif // FBCOMMANDS_HH diff --git a/src/FbTk/StringUtil.cc b/src/FbTk/StringUtil.cc index 0dcd9894..fa809bec 100644 --- a/src/FbTk/StringUtil.cc +++ b/src/FbTk/StringUtil.cc @@ -145,10 +145,17 @@ int extractNumber(const std::string& in, unsigned long long& out) { std::string number2String(long long num) { char s[128]; - sprintf(s, "%lld", num); + snprintf(s, sizeof(s), "%lld", num); return std::string(s); } +std::string number2HexString(long long num) { + char s[17]; + snprintf(s, sizeof(s), "%lx", num); + return std::string(s); +} + + /** Tries to find a string in another and ignoring the case of the characters diff --git a/src/FbTk/StringUtil.hh b/src/FbTk/StringUtil.hh index 538828bc..930e0b58 100644 --- a/src/FbTk/StringUtil.hh +++ b/src/FbTk/StringUtil.hh @@ -44,6 +44,7 @@ int extractNumber(const std::string& in, unsigned long long& out); /// creates a number to a string std::string number2String(long long num); +std::string number2HexString(long long num); /// Similar to `strstr' but this function ignores the case of both strings const char *strcasestr(const char *str, const char *ptn); diff --git a/src/Screen.cc b/src/Screen.cc index 637bd829..725d4b5f 100644 --- a/src/Screen.cc +++ b/src/Screen.cc @@ -231,6 +231,22 @@ const TabPlacementString placement_strings[] = { { FbWinFrame::RIGHTTOP, "RightTop" } }; +Atom atom_fbcmd = 0; +Atom atom_wm_check = 0; +Atom atom_net_desktop = 0; +Atom atom_utf8_string = 0; +Atom atom_kde_systray = 0; +Atom atom_kwm1 = 0; + +void initAtoms(Display* dpy) { + atom_wm_check = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + atom_net_desktop = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + atom_fbcmd = XInternAtom(dpy, "_FLUXBOX_ACTION", False); + atom_utf8_string = XInternAtom(dpy, "UTF8_STRING", False); + atom_kde_systray = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); + atom_kwm1 = XInternAtom(dpy, "KWM_DOCKWINDOW", False); +} + } // end anonymous namespace @@ -317,8 +333,7 @@ BScreen::BScreen(FbTk::ResourceManager &rm, m_geom_window(new OSDWindow(m_root_window, *this, *m_focused_windowtheme)), m_pos_window(new OSDWindow(m_root_window, *this, *m_focused_windowtheme)), m_tooltip_window(new TooltipWindow(m_root_window, *this, *m_focused_windowtheme)), - m_dummy_window(scrn, -1, -1, 1, 1, 0, true, false, CopyFromParent, - InputOnly), + m_dummy_window(scrn, -1, -1, 1, 1, 0, true, false, CopyFromParent, InputOnly), resource(rm, screenname, altscreenname), m_resource_manager(rm), m_name(screenname), @@ -331,8 +346,11 @@ BScreen::BScreen(FbTk::ResourceManager &rm, m_shutdown(false) { - Display *disp = m_root_window.display(); Fluxbox *fluxbox = Fluxbox::instance(); + Display *disp = fluxbox->display(); + + initAtoms(disp); + // TODO fluxgen: check if this is the right place (it was not -lis) // @@ -349,7 +367,7 @@ BScreen::BScreen(FbTk::ResourceManager &rm, SubstructureRedirectMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask| SubstructureNotifyMask); - FbTk::App::instance()->sync(false); + fluxbox->sync(false); XSetErrorHandler((XErrorHandler) old); @@ -370,12 +388,11 @@ BScreen::BScreen(FbTk::ResourceManager &rm, #endif // HAVE_GETPID // check if we're the first EWMH compliant window manager on this screen - Atom wm_check = XInternAtom(disp, "_NET_SUPPORTING_WM_CHECK", False); Atom xa_ret_type; int ret_format; unsigned long ret_nitems, ret_bytes_after; unsigned char *ret_prop; - if (rootWindow().property(wm_check, 0l, 1l, + if (rootWindow().property(atom_wm_check, 0l, 1l, False, XA_WINDOW, &xa_ret_type, &ret_format, &ret_nitems, &ret_bytes_after, &ret_prop) ) { m_restart = (ret_prop != NULL); @@ -415,7 +432,7 @@ BScreen::BScreen(FbTk::ResourceManager &rm, FbTk::EventManager *evm = FbTk::EventManager::instance(); evm->add(*this, rootWindow()); - Keys *keys = Fluxbox::instance()->keys(); + Keys *keys = fluxbox->keys(); if (keys) keys->registerWindow(rootWindow().window(), *this, Keys::GLOBAL|Keys::ON_DESKTOP); @@ -476,9 +493,8 @@ BScreen::BScreen(FbTk::ResourceManager &rm, // check which desktop we should start on unsigned int first_desktop = 0; if (m_restart) { - Atom net_desktop = XInternAtom(disp, "_NET_CURRENT_DESKTOP", False); bool exists; - unsigned int ret=static_cast(rootWindow().cardinalProperty(net_desktop, &exists)); + unsigned int ret=static_cast(rootWindow().cardinalProperty(atom_net_desktop, &exists)); if (exists) { if (ret < static_cast(nr_ws)) first_desktop = ret; @@ -764,29 +780,29 @@ void BScreen::focusedWinFrameThemeReconfigured() { } void BScreen::propertyNotify(Atom atom) { - static Atom fbcmd_atom = XInternAtom(FbTk::App::instance()->display(), - "_FLUXBOX_ACTION", False); - if (allowRemoteActions() && atom == fbcmd_atom) { + + if (allowRemoteActions() && atom == atom_fbcmd) { Atom xa_ret_type; int ret_format; unsigned long ret_nitems, ret_bytes_after; char *str; - if (rootWindow().property(fbcmd_atom, 0l, 64l, + if (rootWindow().property(atom_fbcmd, 0l, 64l, True, XA_STRING, &xa_ret_type, &ret_format, &ret_nitems, &ret_bytes_after, (unsigned char **)&str) && str) { if (ret_bytes_after) { XFree(str); long len = 64 + (ret_bytes_after + 3)/4; - rootWindow().property(fbcmd_atom, 0l, len, + rootWindow().property(atom_fbcmd, 0l, len, True, XA_STRING, &xa_ret_type, &ret_format, &ret_nitems, &ret_bytes_after, (unsigned char **)&str); } static std::auto_ptr > cmd(0); cmd.reset(FbTk::CommandParser::instance().parse(str, false)); - if (cmd.get()) + if (cmd.get()) { cmd->execute(); + } XFree(str); } @@ -852,9 +868,8 @@ void BScreen::cycleFocus(int options, const ClientPattern *pat, bool reverse) { } FbMenu *BScreen::createMenu(const string &label) { - FbMenu *menu = new FbMenu(menuTheme(), - imageControl(), - *layerManager().getLayer(ResourceLayer::MENU)); + FbTk::Layer* layer = layerManager().getLayer(ResourceLayer::MENU); + FbMenu *menu = new FbMenu(menuTheme(), imageControl(), *layer); if (!label.empty()) menu->setLabel(label); @@ -862,9 +877,8 @@ FbMenu *BScreen::createMenu(const string &label) { } FbMenu *BScreen::createToggleMenu(const string &label) { - FbMenu *menu = new ToggleMenu(menuTheme(), - imageControl(), - *layerManager().getLayer(ResourceLayer::MENU)); + FbTk::Layer* layer = layerManager().getLayer(ResourceLayer::MENU); + FbMenu *menu = new ToggleMenu(menuTheme(), imageControl(), *layer); if (!label.empty()) menu->setLabel(label); @@ -1177,9 +1191,7 @@ bool BScreen::isKdeDockapp(Window client) const { unsigned long *data = 0, uljunk; Display *disp = FbTk::App::instance()->display(); // Check if KDE v2.x dock applet - if (XGetWindowProperty(disp, client, - XInternAtom(FbTk::App::instance()->display(), - "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False), + if (XGetWindowProperty(disp, client, atom_kde_systray, 0l, 1l, False, XA_WINDOW, &ajunk, &ijunk, &uljunk, &uljunk, (unsigned char **) &data) == Success) { @@ -1192,11 +1204,9 @@ bool BScreen::isKdeDockapp(Window client) const { // Check if KDE v1.x dock applet if (!iskdedockapp) { - Atom kwm1 = XInternAtom(FbTk::App::instance()->display(), - "KWM_DOCKWINDOW", False); if (XGetWindowProperty(disp, client, - kwm1, 0l, 1l, False, - kwm1, &ajunk, &ijunk, &uljunk, + atom_kwm1, 0l, 1l, False, + atom_kwm1, &ajunk, &ijunk, &uljunk, &uljunk, (unsigned char **) &data) == Success && data) { iskdedockapp = (data && data[0] != 0); XFree((void *) data); diff --git a/util/fluxbox-remote.cc b/util/fluxbox-remote.cc index 5c665836..340ac5b5 100644 --- a/util/fluxbox-remote.cc +++ b/util/fluxbox-remote.cc @@ -21,50 +21,77 @@ #include #include +#include #include #include #include -bool g_gotError; + +bool g_gotError = false; static int HandleIPCError(Display *disp, XErrorEvent*ptr) { // ptr->error_code contains the actual error flags - g_gotError=true; + g_gotError = true; return( 0 ); } +typedef int (*xerror_cb_t)(Display*,XErrorEvent*); + + int main(int argc, char **argv) { + int rc; + Display* disp; + Window root; + Atom atom_utf8; + Atom atom_fbcmd; + Atom atom_result; + xerror_cb_t error_cb; + char* cmd; + if (argc <= 1) { printf("fluxbox-remote \n"); return EXIT_SUCCESS; } - Display *disp = XOpenDisplay(NULL); + disp = XOpenDisplay(NULL); if (!disp) { perror("error, can't open display."); - return EXIT_FAILURE; + return rc; } - Atom fbcmd_atom = XInternAtom(disp, "_FLUXBOX_ACTION", False); - Window root = DefaultRootWindow(disp); + cmd = argv[1]; + atom_utf8 = XInternAtom(disp, "UTF8_STRING", False); + atom_fbcmd = XInternAtom(disp, "_FLUXBOX_ACTION", False); + atom_result = XInternAtom(disp, "_FLUXBOX_ACTION_RESULT", False); + root = DefaultRootWindow(disp); - char *str = argv[1]; + // assign the custom handler, clear the flag, sync the data, + // then check it for success/failure + error_cb = XSetErrorHandler(HandleIPCError); - typedef int (*x_error_handler_t)(Display*,XErrorEvent*); - // assign the custom handler, clear the flag, sync the data, then check it for success/failure - x_error_handler_t handler = XSetErrorHandler( HandleIPCError ); - g_gotError=false; - XChangeProperty(disp, root, fbcmd_atom, + if (strcmp(cmd, "result") == 0) { + XTextProperty text_prop; + if (XGetTextProperty(disp, root, &text_prop, atom_result) != 0 + && text_prop.value > 0 + && text_prop.nitems > 0) { + + printf("%s", text_prop.value); + XFree(text_prop.value); + } + } else { + XChangeProperty(disp, root, atom_fbcmd, XA_STRING, 8, PropModeReplace, - (unsigned char *) str, strlen(str)); - XSync(disp,False); - int ret=(g_gotError?EXIT_FAILURE:EXIT_SUCCESS); - XSetErrorHandler(handler); + (unsigned char *)cmd, strlen(cmd)); + XSync(disp, false); + } + rc = (g_gotError ? EXIT_FAILURE : EXIT_SUCCESS); + + XSetErrorHandler(error_cb); XCloseDisplay(disp); - return ret; + return rc; }