libGizmod/X11FocusWatcher.cpp

Go to the documentation of this file.
00001 
00012 /*
00013   
00014   Copyright (c) 2007, Tim Burrell
00015   Licensed under the Apache License, Version 2.0 (the "License");
00016   you may not use this file except in compliance with the License.
00017   You may obtain a copy of the License at 
00018 
00019         http://www.apache.org/licenses/LICENSE-2.0
00020 
00021   Unless required by applicable law or agreed to in writing, software
00022   distributed under the License is distributed on an "AS IS" BASIS,
00023   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00024   See the License for the specific language governing permissions and 
00025   limitations under the License. 
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 // Typedef's, structs
00049 
00054 #define WINDOW_UNKNOWN          "Unknown"
00055 
00060 #define TITLE_UNKNOWN           "(No Name)"
00061 
00063 // Callbacks
00065 
00069 int X11FocusWatcher::x11ErrorHandler(Display * display, XErrorEvent * error) {
00070         // do nothing we don't care
00071         return 0;
00072 }
00073 
00077 int X11FocusWatcher::x11IOErrorHandler(Display * display) {
00078         // do nothing we don't care
00079         return 0;
00080 }
00081 
00083 // Construction
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 // Class Body
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)) { /* Get window name if any**/
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)) { // Get window name if any
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         // override me
00362         cdbg << "X11FocusWatcher -- Current Focus: " << Event.WindowName << " [" << Event.WindowNameFormal << "] <" << Event.WindowClass << ">" << endl;
00363 }
00364 
00369 void X11FocusWatcher::onFocusOut(X11FocusEvent const & Event) {
00370         // override me
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         // set the error handlers
00394         // special thanks to ajax from #xorg for this tip
00395         // you have no idea how annoying this was to track down
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         // send a fake event in the queue to force the thread to quit
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         // wait until thread exits
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         // open the display
00515         if ( (!openDisplay(mDisplayName)) || (mWatching) )
00516                 return;
00517                 
00518         // init
00519         mWatching = true;
00520         Window window;
00521         int revert_to_return;
00522         string WindowName;
00523                 
00524         // Watch for focus changes      
00525         mWatching = true;
00526         while (mWatching) {
00527                 cdbg5 << "Processing X11 Focus Events..." << endl;
00528 
00529                 // reset focus event masks
00530                 setFocusEventMasks();
00531                 
00532                 // get the next event
00533                 // manually check for focus changes since brand new windows 
00534                 // might not have the proper event focus mask yet
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                 // handle the event
00549                 switch (event.type) {
00550                 case FocusIn: {
00551                         // avoid duplicate events
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         // we're shuttind down! close the display
00575         closeDisplay();
00576 }

Generated on Wed Nov 7 10:04:16 2007 for gizmod by  doxygen 1.5.3