00001
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "X11FocusWatcher.hpp"
00030 #include "../libH/Debug.hpp"
00031 #include "../libH/Exception.hpp"
00032 #include "../libH/UtilTime.hpp"
00033 #include "../libH/stringconverter.hpp"
00034 #include <X11/Xlib.h>
00035 #include <X11/Xatom.h>
00036 #include <X11/Xutil.h>
00037 #include <X11/Xresource.h>
00038 #include <X11/keysym.h>
00039 #include <boost/thread/thread.hpp>
00040
00041 using namespace std;
00042 using namespace boost;
00043 using namespace H;
00044 using namespace Gizmod;
00045
00047
00049
00054 #define WINDOW_UNKNOWN "Unknown"
00055
00060 #define TITLE_UNKNOWN "(No Name)"
00061
00063
00065
00069 int X11FocusWatcher::x11ErrorHandler(Display * display, XErrorEvent * error) {
00070
00071 return 0;
00072 }
00073
00077 int X11FocusWatcher::x11IOErrorHandler(Display * display) {
00078
00079 return 0;
00080 }
00081
00083
00085
00089 X11FocusEvent::X11FocusEvent() {
00090 EventType = X11FOCUSEVENT_IN;
00091 WindowName = TITLE_UNKNOWN;
00092 WindowNameFormal = WINDOW_UNKNOWN;
00093 WindowClass = WINDOW_UNKNOWN;
00094 }
00095
00099 X11FocusEvent::X11FocusEvent(X11FocusEventType eventType, std::string windowName, std::string windowNameFormal, std::string windowClass) {
00100 EventType = eventType;
00101 WindowName = windowName;
00102 WindowNameFormal = windowNameFormal;
00103 WindowClass = windowClass;
00104 }
00105
00109 X11FocusEvent::X11FocusEvent(X11FocusEvent const & Event) {
00110 EventType = Event.EventType;
00111 WindowName = Event.WindowName;
00112 WindowNameFormal = Event.WindowNameFormal;
00113 WindowClass = Event.WindowClass;
00114 }
00115
00119 X11FocusWatcher::X11FocusWatcher() : mThreadProc(this) {
00120 mCurrentWindow = -1;
00121 mDisplay = NULL;
00122 mWatching = false;
00123 mThreading = false;
00124 }
00125
00129 X11FocusEvent::~X11FocusEvent() {
00130 }
00131
00135 X11FocusWatcher::~X11FocusWatcher() {
00136 shutdown();
00137 }
00138
00140
00142
00146 void X11FocusWatcher::closeDisplay() {
00147 if (!mDisplay)
00148 return;
00149
00150 XCloseDisplay(mDisplay);
00151 mDisplay = NULL;
00152 }
00153
00159 X11FocusEvent X11FocusWatcher::createFocusEvent(Window const & window, X11FocusEventType EventType) {
00160 tuple<string, string, string> WindowInfo = getWindowName(mDisplay, window);
00161 X11FocusEvent Event(EventType, WindowInfo.get<0>(), WindowInfo.get<1>(), WindowInfo.get<2>());
00162 return Event;
00163 }
00164
00178 boost::tuple<std::string, std::string, std::string> X11FocusWatcher::getWindowName(Display * dpy, Window const & window, bool recurse) {
00179 XTextProperty tp;
00180 string WindowName = TITLE_UNKNOWN;
00181 string WindowNameFormal = WINDOW_UNKNOWN;
00182 string WindowClass = WINDOW_UNKNOWN;
00183 if (!window) {
00184 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00185 } else {
00186 XLockDisplay(dpy);
00187 if (window == RootWindow(dpy, XDefaultScreen(dpy))) {
00188 XUnlockDisplay(dpy);
00189 return tuple<string, string, string>("(root window)", "(root window)", "(root window)");
00190 }
00191 #ifdef NO_I18N
00192 char * win_name;
00193 if (!XFetchName(dpy, window, &win_name)) {
00194 XFree(win_name);
00195 Window root_ret, parent_ret;
00196 unsigned int nChildren;
00197 Window * children = NULL;
00198 XQueryTree(dpy, window, &root_ret, &parent_ret, &children, &nChildren);
00199 if (children)
00200 XFree(children);
00201 if ((XGetWMName(dpy, parent_ret, &tp)) && (recurse)) {
00202 XFree((void*)tp.value);
00203 XUnlockDisplay(dpy);
00204 return getWindowName(parent_ret, false);
00205 } else {
00206 if (tp.value)
00207 XFree((void*)tp.value);
00208 XFree(win_name);
00209 XClassHint * pClassHint = XAllocClassHint();
00210 if (XGetClassHint(dpy, window, pClassHint) > 0) {
00211 WindowNameFormal = pClassHint->res_name;
00212 WindowClass = pClassHint->res_class;
00213 XFree(pClassHint->res_name);
00214 XFree(pClassHint->res_class);
00215 }
00216 XFree(pClassHint);
00217 XUnlockDisplay(dpy);
00218 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00219 }
00220 } else if (win_name) {
00221 const std::string retStr = win_name;
00222 XFree(win_name);
00223 XClassHint * pClassHint = XAllocClassHint();
00224 if (XGetClassHint(dpy, window, pClassHint) > 0) {
00225 WindowNameFormal = pClassHint->res_name;
00226 WindowClass = pClassHint->res_class;
00227 XFree(pClassHint->res_name);
00228 XFree(pClassHint->res_class);
00229 }
00230 XFree(pClassHint);
00231 WindowName = retStr;
00232 XUnlockDisplay(dpy);
00233 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00234 }
00235 #else
00236 if (!XGetWMName(dpy, window, &tp)) {
00237 if (tp.value)
00238 XFree((void*)tp.value);
00239 Window root_ret, parent_ret;
00240 unsigned int nChildren;
00241 Window * children = NULL;
00242 XQueryTree(dpy, window, &root_ret, &parent_ret, &children, &nChildren);
00243 if (children)
00244 XFree(children);
00245 if ((XGetWMName(dpy, parent_ret, &tp)) && (recurse)) {
00246 if (tp.value)
00247 XFree((void*)tp.value);
00248 XUnlockDisplay(dpy);
00249 return getWindowName(dpy, parent_ret, false);
00250 } else {
00251 if (tp.value)
00252 XFree((void*)tp.value);
00253 XClassHint * pClassHint = XAllocClassHint();
00254 if (XGetClassHint(dpy, window, pClassHint) > 0) {
00255 WindowNameFormal = pClassHint->res_name;
00256 WindowClass = pClassHint->res_class;
00257 XFree(pClassHint->res_name);
00258 XFree(pClassHint->res_class);
00259 }
00260 XFree(pClassHint);
00261 XUnlockDisplay(dpy);
00262 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00263 }
00264 } else if (tp.nitems > 0) {
00265 XClassHint * pClassHint = XAllocClassHint();
00266 if (XGetClassHint(dpy, window, pClassHint) > 0) {
00267 WindowNameFormal = pClassHint->res_name;
00268 WindowClass = pClassHint->res_class;
00269 XFree(pClassHint->res_name);
00270 XFree(pClassHint->res_class);
00271 }
00272 XFree(pClassHint);
00273
00274 std::string retStr;
00275 int count = 0, i, ret;
00276 char **list = NULL;
00277 ret = XmbTextPropertyToTextList(dpy, &tp, &list, &count);
00278 if((ret == Success || ret > 0) && list != NULL) {
00279 for(i=0; i<count; i++)
00280 retStr += (const char *) list[i];
00281 XFree((void*)tp.value);
00282 XFreeStringList(list);
00283 WindowName = retStr;
00284 XUnlockDisplay(dpy);
00285 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00286 } else {
00287 XFree((void*)tp.value);
00288 if (list)
00289 XFreeStringList(list);
00290 retStr = (const char *) tp.value;
00291 WindowName = retStr;
00292 XUnlockDisplay(dpy);
00293 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00294 }
00295 }
00296 #endif
00297 else {
00298 XUnlockDisplay(dpy);
00299 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00300 }
00301 }
00302
00303 XUnlockDisplay(dpy);
00304 return tuple<string, string, string>(WindowName, WindowNameFormal, WindowClass);
00305 }
00306
00310 void X11FocusWatcher::init() {
00311 if (mWatching)
00312 return;
00313 boost::thread thrd(mThreadProc);
00314 }
00315
00323 bool X11FocusWatcher::isApplicationRunning(std::string WindowTitle) {
00324 Display * Dsp;
00325 if ( (Dsp = XOpenDisplay(mDisplayName.c_str())) == NULL )
00326 return false;
00327
00328 Window RootRet, ParentRet;
00329 unsigned int nChildren;
00330 Window * Children = NULL;
00331 XQueryTree(Dsp, RootWindow(Dsp, DefaultScreen(Dsp)), &RootRet, &ParentRet, &Children, &nChildren);
00332 for (unsigned int lp = 0; lp < nChildren; lp ++) {
00333 tuple<string, string, string> WindowInfo = getWindowName(Dsp, Children[lp]);
00334 if (stringconverter::toLower(WindowInfo.get<0>()).find(stringconverter::toLower(WindowTitle)) != string::npos) {
00335 XFree(Children);
00336 XCloseDisplay(Dsp);
00337 return true;
00338 }
00339 }
00340 if (Children)
00341 XFree(Children);
00342 XCloseDisplay(Dsp);
00343 return false;
00344 }
00345
00350 bool X11FocusEvent::isNull() {
00351 if ( (WindowName == TITLE_UNKNOWN) && (WindowNameFormal == WINDOW_UNKNOWN) && (WindowClass == WINDOW_UNKNOWN) )
00352 return true;
00353 return false;
00354 }
00355
00360 void X11FocusWatcher::onFocusIn(X11FocusEvent const & Event) {
00361
00362 cdbg << "X11FocusWatcher -- Current Focus: " << Event.WindowName << " [" << Event.WindowNameFormal << "] <" << Event.WindowClass << ">" << endl;
00363 }
00364
00369 void X11FocusWatcher::onFocusOut(X11FocusEvent const & Event) {
00370
00371 cdbg << "X11FocusWatcher -- Leaving Focus: " << Event.WindowName << " [" << Event.WindowNameFormal << "] <" << Event.WindowClass << ">" << endl;
00372 }
00373
00378 bool X11FocusWatcher::openDisplay(std::string DisplayName) {
00379 if (mDisplay)
00380 closeDisplay();
00381
00382 if (DisplayName == "")
00383 mDisplayName = XDisplayName(NULL);
00384 else
00385 mDisplayName = DisplayName;
00386
00387 XInitThreads();
00388 if ( (mDisplay = XOpenDisplay(mDisplayName.c_str())) == NULL ) {
00389 cerr << "Unable to Open X11 Display [" << (mDisplayName == "" ? "Default" : mDisplayName) << "] -- Per application mappings will not work!" << endl;
00390 return false;
00391 }
00392
00393
00394
00395
00396 XLockDisplay(mDisplay);
00397 XSetErrorHandler(x11ErrorHandler);
00398 XSetIOErrorHandler(x11IOErrorHandler);
00399 mScreen = DefaultScreen(mDisplay);
00400 XUnlockDisplay(mDisplay);
00401
00402 return true;
00403 }
00404
00409 bool X11FocusEvent::operator != (X11FocusEvent const & Event) {
00410 if ( (EventType == Event.EventType) &&
00411 (WindowClass == Event.WindowClass) &&
00412 (WindowName == Event.WindowName) &&
00413 (WindowNameFormal == Event.WindowNameFormal) )
00414 return false;
00415 return true;
00416 }
00417
00422 bool X11FocusEvent::operator == (X11FocusEvent const & Event) {
00423 if ( (EventType == Event.EventType) &&
00424 (WindowClass == Event.WindowClass) &&
00425 (WindowName == Event.WindowName) &&
00426 (WindowNameFormal == Event.WindowNameFormal) )
00427 return true;
00428 return false;
00429 }
00430
00434 void X11FocusWatcher::setFocusEventMasks() {
00435 Window RootRet, ParentRet;
00436 unsigned int nChildren;
00437 Window * Children = NULL;
00438 XLockDisplay(mDisplay);
00439 XQueryTree(mDisplay, RootWindow(mDisplay, mScreen), &RootRet, &ParentRet, &Children, &nChildren);
00440 for (unsigned int lp = 0; lp < nChildren; lp ++)
00441 XSelectInput(mDisplay, Children[lp], FocusChangeMask);
00442 if (Children)
00443 XFree(Children);
00444 XUnlockDisplay(mDisplay);
00445 }
00446
00454 bool X11FocusWatcher::setInputFocus(std::string WindowTitle) {
00455 Display * Dsp;
00456 if ( (Dsp = XOpenDisplay(mDisplayName.c_str())) == NULL )
00457 return false;
00458
00459 Window RootRet, ParentRet;
00460 unsigned int nChildren;
00461 Window * Children = NULL;
00462 XQueryTree(Dsp, RootWindow(Dsp, DefaultScreen(Dsp)), &RootRet, &ParentRet, &Children, &nChildren);
00463 for (unsigned int lp = 0; lp < nChildren; lp ++) {
00464 tuple<string, string, string> WindowInfo = getWindowName(Dsp, Children[lp]);
00465 if (stringconverter::toLower(WindowInfo.get<0>()).find(stringconverter::toLower(WindowTitle)) != string::npos) {
00466 XSetInputFocus(Dsp, Children[lp], RevertToParent, CurrentTime);
00467 XFree(Children);
00468 XCloseDisplay(Dsp);
00469 return true;
00470 }
00471 }
00472 if (Children)
00473 XFree(Children);
00474 XCloseDisplay(Dsp);
00475 return false;
00476 }
00477
00481 void X11FocusWatcher::shutdown() {
00482 mWatching = false;
00483
00484
00485 if (mDisplay) {
00486 XFocusChangeEvent Event;
00487 Event.display = mDisplay;
00488 Event.type = 0;
00489 Event.window = mCurrentWindow;
00490 Event.mode = NotifyNormal;
00491 Event.detail = NotifyPointer;
00492
00493 XLockDisplay(mDisplay);
00494 XPutBackEvent(mDisplay, (XEvent*) &Event);
00495 XSync(mDisplay, True);
00496 XUnlockDisplay(mDisplay);
00497 }
00498
00499
00500 int count = 5;
00501 while (mThreading && count) {
00502 cdbg5 << "Waiting on X11FocusWatcher Thread to Finish..." << endl;
00503 UtilTime::sleep(0.1f);
00504 count --;
00505 }
00506 }
00507
00513 void X11FocusWatcher::threadProc() {
00514
00515 if ( (!openDisplay(mDisplayName)) || (mWatching) )
00516 return;
00517
00518
00519 mWatching = true;
00520 Window window;
00521 int revert_to_return;
00522 string WindowName;
00523
00524
00525 mWatching = true;
00526 while (mWatching) {
00527 cdbg5 << "Processing X11 Focus Events..." << endl;
00528
00529
00530 setFocusEventMasks();
00531
00532
00533
00534
00535 XEvent event;
00536 XLockDisplay(mDisplay);
00537 XGetInputFocus(mDisplay, &window, &revert_to_return);
00538 XUnlockDisplay(mDisplay);
00539 if (mCurrentWindow != window) {
00540 event.type = FocusIn;
00541 event.xany.window = window;
00542 } else {
00543 XNextEvent(mDisplay, &event);
00544 if (!mWatching)
00545 break;
00546 }
00547
00548
00549 switch (event.type) {
00550 case FocusIn: {
00551
00552 if (mCurrentWindow == event.xany.window)
00553 break;
00554 X11FocusEvent Event = createFocusEvent(event.xany.window, X11FOCUSEVENT_IN);
00555 mCurrentWindow = event.xany.window;
00556 if ( (!Event.isNull()) && (Event != mLastEventIn) ) {
00557 onFocusIn(Event);
00558 mLastEventIn = Event;
00559 }
00560 break; }
00561 case FocusOut: {
00562 X11FocusEvent Event = createFocusEvent(event.xany.window, X11FOCUSEVENT_OUT);
00563 if ( (!Event.isNull()) && (Event != mLastEventOut) ) {
00564 onFocusOut(Event);
00565 mLastEventOut = Event;
00566 }
00567 break; }
00568 default:
00569 cdbg << "Unkown Event Type: " << event.type << endl;
00570 break;
00571 }
00572 }
00573
00574
00575 closeDisplay();
00576 }