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.
This commit is contained in:
Mathias Gumz 2013-01-31 09:13:45 +01:00
parent 716532dd47
commit dc47491533
7 changed files with 177 additions and 53 deletions

View file

@ -89,6 +89,7 @@ public:
* the column of the error is stored in m_matchlimit * the column of the error is stored in m_matchlimit
*/ */
int error() const { return m_terms.empty() ? 1 : 0; } 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); static FbTk::FbString getProperty(WinProperty prop, const Focusable &client);

View file

@ -445,13 +445,12 @@ REGISTER_UNTRUSTED_COMMAND_WITH_ARGS(bindkey, FbCommands::BindKeyCmd, void);
BindKeyCmd::BindKeyCmd(const string &keybind):m_keybind(keybind) { } BindKeyCmd::BindKeyCmd(const string &keybind):m_keybind(keybind) { }
void BindKeyCmd::execute() { void BindKeyCmd::execute() {
if (Fluxbox::instance()->keys() != 0) { Keys* keys = Fluxbox::instance()->keys();
if (Fluxbox::instance()->keys()->addBinding(m_keybind)) { if (keys && keys->addBinding(m_keybind)) {
ofstream ofile(Fluxbox::instance()->keys()->filename().c_str(), ios::app); ofstream ofile(keys->filename().c_str(), ios::app);
if (!ofile) if (!ofile)
return; return;
ofile<<m_keybind<<endl; ofile<<m_keybind<<endl;
}
} }
} }
@ -542,4 +541,73 @@ void DeiconifyCmd::execute() {
}; };
} }
REGISTER_COMMAND_WITH_ARGS(clientpatterntest, FbCommands::ClientPatternTestCmd, void);
void ClientPatternTestCmd::execute() {
std::vector< const FluxboxWindow* > 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<const FluxboxWindow*>(*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 } // end namespace FbCommands

View file

@ -218,6 +218,16 @@ private:
Destination m_dest; Destination m_dest;
}; };
/// test client pattern
class ClientPatternTestCmd: public FbTk::Command<void> {
public:
ClientPatternTestCmd(const std::string& args) : m_args(args) { };
void execute();
private:
std::string m_args;
};
} // end namespace FbCommands } // end namespace FbCommands
#endif // FBCOMMANDS_HH #endif // FBCOMMANDS_HH

View file

@ -145,10 +145,17 @@ int extractNumber(const std::string& in, unsigned long long& out) {
std::string number2String(long long num) { std::string number2String(long long num) {
char s[128]; char s[128];
sprintf(s, "%lld", num); snprintf(s, sizeof(s), "%lld", num);
return std::string(s); 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 Tries to find a string in another and
ignoring the case of the characters ignoring the case of the characters

View file

@ -44,6 +44,7 @@ int extractNumber(const std::string& in, unsigned long long& out);
/// creates a number to a string /// creates a number to a string
std::string number2String(long long num); std::string number2String(long long num);
std::string number2HexString(long long num);
/// Similar to `strstr' but this function ignores the case of both strings /// Similar to `strstr' but this function ignores the case of both strings
const char *strcasestr(const char *str, const char *ptn); const char *strcasestr(const char *str, const char *ptn);

View file

@ -231,6 +231,22 @@ const TabPlacementString placement_strings[] = {
{ FbWinFrame::RIGHTTOP, "RightTop" } { 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 } // 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_geom_window(new OSDWindow(m_root_window, *this, *m_focused_windowtheme)),
m_pos_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_tooltip_window(new TooltipWindow(m_root_window, *this, *m_focused_windowtheme)),
m_dummy_window(scrn, -1, -1, 1, 1, 0, true, false, CopyFromParent, m_dummy_window(scrn, -1, -1, 1, 1, 0, true, false, CopyFromParent, InputOnly),
InputOnly),
resource(rm, screenname, altscreenname), resource(rm, screenname, altscreenname),
m_resource_manager(rm), m_resource_manager(rm),
m_name(screenname), m_name(screenname),
@ -331,8 +346,11 @@ BScreen::BScreen(FbTk::ResourceManager &rm,
m_shutdown(false) { m_shutdown(false) {
Display *disp = m_root_window.display();
Fluxbox *fluxbox = Fluxbox::instance(); Fluxbox *fluxbox = Fluxbox::instance();
Display *disp = fluxbox->display();
initAtoms(disp);
// TODO fluxgen: check if this is the right place (it was not -lis) // 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 | SubstructureRedirectMask | KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask| SubstructureNotifyMask); ButtonPressMask | ButtonReleaseMask| SubstructureNotifyMask);
FbTk::App::instance()->sync(false); fluxbox->sync(false);
XSetErrorHandler((XErrorHandler) old); XSetErrorHandler((XErrorHandler) old);
@ -370,12 +388,11 @@ BScreen::BScreen(FbTk::ResourceManager &rm,
#endif // HAVE_GETPID #endif // HAVE_GETPID
// check if we're the first EWMH compliant window manager on this screen // 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; Atom xa_ret_type;
int ret_format; int ret_format;
unsigned long ret_nitems, ret_bytes_after; unsigned long ret_nitems, ret_bytes_after;
unsigned char *ret_prop; 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, False, XA_WINDOW, &xa_ret_type, &ret_format, &ret_nitems,
&ret_bytes_after, &ret_prop) ) { &ret_bytes_after, &ret_prop) ) {
m_restart = (ret_prop != NULL); m_restart = (ret_prop != NULL);
@ -415,7 +432,7 @@ BScreen::BScreen(FbTk::ResourceManager &rm,
FbTk::EventManager *evm = FbTk::EventManager::instance(); FbTk::EventManager *evm = FbTk::EventManager::instance();
evm->add(*this, rootWindow()); evm->add(*this, rootWindow());
Keys *keys = Fluxbox::instance()->keys(); Keys *keys = fluxbox->keys();
if (keys) if (keys)
keys->registerWindow(rootWindow().window(), *this, keys->registerWindow(rootWindow().window(), *this,
Keys::GLOBAL|Keys::ON_DESKTOP); Keys::GLOBAL|Keys::ON_DESKTOP);
@ -476,9 +493,8 @@ BScreen::BScreen(FbTk::ResourceManager &rm,
// check which desktop we should start on // check which desktop we should start on
unsigned int first_desktop = 0; unsigned int first_desktop = 0;
if (m_restart) { if (m_restart) {
Atom net_desktop = XInternAtom(disp, "_NET_CURRENT_DESKTOP", False);
bool exists; bool exists;
unsigned int ret=static_cast<unsigned int>(rootWindow().cardinalProperty(net_desktop, &exists)); unsigned int ret=static_cast<unsigned int>(rootWindow().cardinalProperty(atom_net_desktop, &exists));
if (exists) { if (exists) {
if (ret < static_cast<unsigned int>(nr_ws)) if (ret < static_cast<unsigned int>(nr_ws))
first_desktop = ret; first_desktop = ret;
@ -764,29 +780,29 @@ void BScreen::focusedWinFrameThemeReconfigured() {
} }
void BScreen::propertyNotify(Atom atom) { void BScreen::propertyNotify(Atom atom) {
static Atom fbcmd_atom = XInternAtom(FbTk::App::instance()->display(),
"_FLUXBOX_ACTION", False); if (allowRemoteActions() && atom == atom_fbcmd) {
if (allowRemoteActions() && atom == fbcmd_atom) {
Atom xa_ret_type; Atom xa_ret_type;
int ret_format; int ret_format;
unsigned long ret_nitems, ret_bytes_after; unsigned long ret_nitems, ret_bytes_after;
char *str; 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, True, XA_STRING, &xa_ret_type, &ret_format, &ret_nitems,
&ret_bytes_after, (unsigned char **)&str) && str) { &ret_bytes_after, (unsigned char **)&str) && str) {
if (ret_bytes_after) { if (ret_bytes_after) {
XFree(str); XFree(str);
long len = 64 + (ret_bytes_after + 3)/4; 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, True, XA_STRING, &xa_ret_type, &ret_format, &ret_nitems,
&ret_bytes_after, (unsigned char **)&str); &ret_bytes_after, (unsigned char **)&str);
} }
static std::auto_ptr<FbTk::Command<void> > cmd(0); static std::auto_ptr<FbTk::Command<void> > cmd(0);
cmd.reset(FbTk::CommandParser<void>::instance().parse(str, false)); cmd.reset(FbTk::CommandParser<void>::instance().parse(str, false));
if (cmd.get()) if (cmd.get()) {
cmd->execute(); cmd->execute();
}
XFree(str); XFree(str);
} }
@ -852,9 +868,8 @@ void BScreen::cycleFocus(int options, const ClientPattern *pat, bool reverse) {
} }
FbMenu *BScreen::createMenu(const string &label) { FbMenu *BScreen::createMenu(const string &label) {
FbMenu *menu = new FbMenu(menuTheme(), FbTk::Layer* layer = layerManager().getLayer(ResourceLayer::MENU);
imageControl(), FbMenu *menu = new FbMenu(menuTheme(), imageControl(), *layer);
*layerManager().getLayer(ResourceLayer::MENU));
if (!label.empty()) if (!label.empty())
menu->setLabel(label); menu->setLabel(label);
@ -862,9 +877,8 @@ FbMenu *BScreen::createMenu(const string &label) {
} }
FbMenu *BScreen::createToggleMenu(const string &label) { FbMenu *BScreen::createToggleMenu(const string &label) {
FbMenu *menu = new ToggleMenu(menuTheme(), FbTk::Layer* layer = layerManager().getLayer(ResourceLayer::MENU);
imageControl(), FbMenu *menu = new ToggleMenu(menuTheme(), imageControl(), *layer);
*layerManager().getLayer(ResourceLayer::MENU));
if (!label.empty()) if (!label.empty())
menu->setLabel(label); menu->setLabel(label);
@ -1177,9 +1191,7 @@ bool BScreen::isKdeDockapp(Window client) const {
unsigned long *data = 0, uljunk; unsigned long *data = 0, uljunk;
Display *disp = FbTk::App::instance()->display(); Display *disp = FbTk::App::instance()->display();
// Check if KDE v2.x dock applet // Check if KDE v2.x dock applet
if (XGetWindowProperty(disp, client, if (XGetWindowProperty(disp, client, atom_kde_systray,
XInternAtom(FbTk::App::instance()->display(),
"_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False),
0l, 1l, False, 0l, 1l, False,
XA_WINDOW, &ajunk, &ijunk, &uljunk, XA_WINDOW, &ajunk, &ijunk, &uljunk,
&uljunk, (unsigned char **) &data) == Success) { &uljunk, (unsigned char **) &data) == Success) {
@ -1192,11 +1204,9 @@ bool BScreen::isKdeDockapp(Window client) const {
// Check if KDE v1.x dock applet // Check if KDE v1.x dock applet
if (!iskdedockapp) { if (!iskdedockapp) {
Atom kwm1 = XInternAtom(FbTk::App::instance()->display(),
"KWM_DOCKWINDOW", False);
if (XGetWindowProperty(disp, client, if (XGetWindowProperty(disp, client,
kwm1, 0l, 1l, False, atom_kwm1, 0l, 1l, False,
kwm1, &ajunk, &ijunk, &uljunk, atom_kwm1, &ajunk, &ijunk, &uljunk,
&uljunk, (unsigned char **) &data) == Success && data) { &uljunk, (unsigned char **) &data) == Success && data) {
iskdedockapp = (data && data[0] != 0); iskdedockapp = (data && data[0] != 0);
XFree((void *) data); XFree((void *) data);

View file

@ -21,50 +21,77 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
bool g_gotError;
bool g_gotError = false;
static int HandleIPCError(Display *disp, XErrorEvent*ptr) static int HandleIPCError(Display *disp, XErrorEvent*ptr)
{ {
// ptr->error_code contains the actual error flags // ptr->error_code contains the actual error flags
g_gotError=true; g_gotError = true;
return( 0 ); return( 0 );
} }
typedef int (*xerror_cb_t)(Display*,XErrorEvent*);
int main(int argc, char **argv) { 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) { if (argc <= 1) {
printf("fluxbox-remote <fluxbox-command>\n"); printf("fluxbox-remote <fluxbox-command>\n");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
Display *disp = XOpenDisplay(NULL); disp = XOpenDisplay(NULL);
if (!disp) { if (!disp) {
perror("error, can't open display."); perror("error, can't open display.");
return EXIT_FAILURE; return rc;
} }
Atom fbcmd_atom = XInternAtom(disp, "_FLUXBOX_ACTION", False); cmd = argv[1];
Window root = DefaultRootWindow(disp); 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 if (strcmp(cmd, "result") == 0) {
x_error_handler_t handler = XSetErrorHandler( HandleIPCError ); XTextProperty text_prop;
g_gotError=false; if (XGetTextProperty(disp, root, &text_prop, atom_result) != 0
XChangeProperty(disp, root, fbcmd_atom, && 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, XA_STRING, 8, PropModeReplace,
(unsigned char *) str, strlen(str)); (unsigned char *)cmd, strlen(cmd));
XSync(disp,False); XSync(disp, false);
int ret=(g_gotError?EXIT_FAILURE:EXIT_SUCCESS); }
XSetErrorHandler(handler);
rc = (g_gotError ? EXIT_FAILURE : EXIT_SUCCESS);
XSetErrorHandler(error_cb);
XCloseDisplay(disp); XCloseDisplay(disp);
return ret; return rc;
} }