00001 /*!@file AppPsycho/psycho-web.C displays a text message, and then 00002 opens a web page where the subjects eyes are tracked., afterwords, 00003 displays a question? The web browser must be opened and 00004 appropriately sized first. */ 00005 00006 // //////////////////////////////////////////////////////////////////// // 00007 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00008 // University of Southern California (USC) and the iLab at USC. // 00009 // See http://iLab.usc.edu for information about this project. // 00010 // //////////////////////////////////////////////////////////////////// // 00011 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00012 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00013 // in Visual Environments, and Applications'' by Christof Koch and // 00014 // Laurent Itti, California Institute of Technology, 2001 (patent // 00015 // pending; application number 09/912,225 filed July 23, 2001; see // 00016 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00017 // //////////////////////////////////////////////////////////////////// // 00018 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00019 // // 00020 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00021 // redistribute it and/or modify it under the terms of the GNU General // 00022 // Public License as published by the Free Software Foundation; either // 00023 // version 2 of the License, or (at your option) any later version. // 00024 // // 00025 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00026 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00027 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00028 // PURPOSE. See the GNU General Public License for more details. // 00029 // // 00030 // You should have received a copy of the GNU General Public License // 00031 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00032 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00033 // Boston, MA 02111-1307 USA. // 00034 // //////////////////////////////////////////////////////////////////// // 00035 // 00036 // Primary maintainer for this file: David J. Berg <dberg@usc.edu> 00037 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-web.C $ 00038 00039 #include "Component/ModelManager.H" 00040 #include "Image/Image.H" 00041 #include "Image/DrawOps.H" 00042 #include "Image/SimpleFont.H" 00043 #include "Raster/Raster.H" 00044 #include "Psycho/PsychoDisplay.H" 00045 #include "Psycho/EyeTrackerConfigurator.H" 00046 #include "Psycho/EyeTracker.H" 00047 #include "Psycho/PsychoOpts.H" 00048 #include "Component/EventLog.H" 00049 #include "Component/ComponentOpts.H" 00050 #include "Util/Types.H" 00051 #include "Util/StringConversions.H" 00052 #include "Util/StringUtil.H" 00053 #include "rutz/pipe.h" 00054 00055 #include <fstream> 00056 #include <X11/X.h> 00057 #include <X11/Xlib.h> 00058 #include <X11/Intrinsic.h> 00059 #include <X11/StringDefs.h> 00060 #include <X11/Xutil.h> 00061 #include <X11/Shell.h> 00062 00063 #define HDEG 54.9 00064 #define KEY_BUFF_SIZE 256 00065 00066 00067 // ##################################################################### 00068 // some code to collect clicks from all xwindows 00069 // ###################################################################### 00070 void TranslateKeyCode(XEvent *ev, char* key_buff) 00071 { 00072 int count; 00073 KeySym ks; 00074 00075 if (ev) 00076 { 00077 count = XLookupString((XKeyEvent *)ev, key_buff, KEY_BUFF_SIZE, &ks,NULL); 00078 key_buff[count] = '\0'; 00079 00080 if (count == 0) 00081 { 00082 char *tmp = key_buff = XKeysymToString(ks); 00083 if (tmp) 00084 strcpy(key_buff, tmp); 00085 else 00086 key_buff[0] = '\0'; 00087 } 00088 } 00089 else 00090 key_buff[0] = '\0'; 00091 } 00092 00093 // ###################################################################### 00094 void snoop_all_windows(Window root, unsigned long type, Display* d) 00095 { 00096 static int level = 0; 00097 Window parent, *children; 00098 unsigned int nchildren; 00099 int stat; 00100 00101 level++; 00102 00103 stat = XQueryTree(d, root, &root, &parent, &children, &nchildren); 00104 if (stat == FALSE) 00105 { 00106 LINFO("Can't query window tree...\n"); 00107 return; 00108 } 00109 00110 if (nchildren == 0) 00111 return; 00112 00113 /* For a more drastic inidication of the problem being exploited 00114 * here, you can change these calls to XSelectInput() to something 00115 * like XClearWindow(d, children[i]) or if you want to be real 00116 * nasty, do XKillWindow(d, children[i]). Of course if you do that, 00117 * then you'll want to remove the loop in main(). 00118 * 00119 * The whole point of this exercise being that I shouldn't be 00120 * allowed to manipulate resources which do not belong to me. 00121 */ 00122 XSelectInput(d, root, type); 00123 00124 for(uint i=0; i < nchildren; i++) 00125 { 00126 XSelectInput(d, children[i], type); 00127 snoop_all_windows(children[i], type, d); 00128 } 00129 00130 XFree((char *)children); 00131 } 00132 00133 // ###################################################################### 00134 void waitXKeyPress(char *hostname) 00135 { 00136 XEvent xev; 00137 char key_buff[KEY_BUFF_SIZE]; 00138 key_buff[0] = '\0'; 00139 00140 Display *d = NULL; 00141 d = XOpenDisplay(hostname); 00142 if (d == NULL) 00143 { 00144 LFATAL("Blah, can't open display: %s\n", hostname); 00145 } 00146 00147 snoop_all_windows(DefaultRootWindow(d), KeyPressMask, d); 00148 00149 while(1) 00150 { 00151 XNextEvent(d, &xev); 00152 TranslateKeyCode(&xev, key_buff); 00153 LINFO("polling"); 00154 if (strlen(key_buff)>0) 00155 break; 00156 } 00157 } 00158 00159 // ###################################################################### 00160 extern "C" int main(const int argc, char** argv) 00161 { 00162 MYLOGVERB = LOG_INFO; // suppress debug messages 00163 // Instantiate a ModelManager: 00164 ModelManager manager("Psycho Text"); 00165 00166 // Instantiate our various ModelComponents: 00167 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00168 manager.addSubComponent(d); 00169 00170 nub::soft_ref<EyeTrackerConfigurator> 00171 etc(new EyeTrackerConfigurator(manager)); 00172 manager.addSubComponent(etc); 00173 00174 nub::soft_ref<EventLog> el(new EventLog(manager)); 00175 manager.addSubComponent(el); 00176 00177 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00178 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00179 00180 // Parse command-line: 00181 if (manager.parseCommandLine(argc, argv, 00182 "<textfile> <webpage> " 00183 "<visual-angle-of-single-character>", 00184 3, 3)==false) 00185 return(1); 00186 00187 //get degrees 00188 double fontsize = fromStr<double>(manager.getExtraArg(2)); 00189 //get webpage 00190 std::string itsWeb = manager.getExtraArg(1); 00191 00192 // hook our various babies up and do post-command-line configs: 00193 nub::soft_ref<EyeTracker> et = etc->getET(); 00194 d->setEyeTracker(et); 00195 d->setEventLog(el); 00196 et->setEventLog(el); 00197 00198 // let's get all our ModelComponent instances started: 00199 manager.start(); 00200 00201 // let's do an eye tracker calibration: 00202 et->calibrate(d); 00203 00204 d->clearScreen(); 00205 d->displayText("<space> for random order, other key for ordered play."); 00206 int c = d->waitForKey(); 00207 d->clearScreen(); 00208 00209 // create an image frame for each sentence in our text file and store 00210 // it in a vector before we start the experiment, then we can just 00211 // present each frame like in psycho still 00212 // 00213 //First read the text file and all the sentences 00214 //load our file 00215 std::ifstream *itsFile; 00216 itsFile = new std::ifstream(manager.getExtraArg(0).c_str()); 00217 00218 //error if no file 00219 if (itsFile->is_open() == false) 00220 LFATAL("Cannot open '%s' for reading",manager.getExtraArg(0).c_str()); 00221 00222 //some storage variables 00223 std::string line; 00224 std::vector<std::vector<std::string> > lines; 00225 std::vector<uint> itsType; 00226 uint scount = 0; 00227 00228 //loop through lines of file 00229 while (!itsFile->eof()) 00230 { 00231 getline(*itsFile, line); 00232 00233 std::vector<std::string> temp; 00234 //store the sentence and type (question or statement) 00235 if (line[0] == '#')//question 00236 { 00237 line.erase(0,1); 00238 temp.push_back(line); 00239 lines.push_back(temp); 00240 itsType.push_back(1); 00241 scount++; 00242 } 00243 else if (line[0] =='!')//sentence 00244 { 00245 line.erase(0,1); 00246 temp.push_back(line); 00247 lines.push_back(temp); 00248 itsType.push_back(0); 00249 scount++; 00250 } 00251 else 00252 { 00253 if (line.size() > 1) 00254 { 00255 scount--; 00256 lines[scount].push_back(line); 00257 scount++; 00258 } 00259 } 00260 } 00261 itsFile->close(); 00262 00263 //now we have stored all of our sentences, lets create our images 00264 int w = d->getWidth();//width and height of SDL surface 00265 int h = d->getHeight(); 00266 uint fontwidth = uint(fontsize * w / HDEG); 00267 SimpleFont fnt = SimpleFont::fixedMaxWidth(fontwidth); //font 00268 std::vector<Image<PixRGB<byte> > > itsSImage; //store sentences 00269 std::vector<Image<PixRGB<byte> > > itsQImage; //store images 00270 00271 for (uint i = 0; i < lines.size(); i++) 00272 { 00273 int space = 0; 00274 int hanchor = int(h/2) - int(fnt.h()/2); 00275 Image<PixRGB<byte> > timage(w,h,ZEROS); 00276 timage += d->getGrey(); 00277 00278 for (uint j = 0; j < lines[i].size(); j++) 00279 { 00280 if (j < 1) 00281 space = int( double(w - fnt.w() * lines[i][j].size()) / 2.0 ); 00282 if (j > 0) 00283 hanchor = hanchor + fnt.h(); 00284 Point2D<int> tanchor(space, hanchor); 00285 writeText(timage,tanchor,lines[i][j].c_str(), 00286 PixRGB<byte>(0,0,0), 00287 d->getGrey(), 00288 fnt); 00289 } 00290 if (itsType[i] == 0) 00291 itsSImage.push_back(timage); 00292 else 00293 itsQImage.push_back(timage); 00294 } 00295 00296 uint count = scount/2; 00297 int index[count]; 00298 for (uint i = 0; i < count; i ++) 00299 { 00300 index[i] = i; 00301 } 00302 00303 if (c == ' ') 00304 { 00305 LINFO("Randomizing images..."); 00306 randShuffle(index, count); 00307 } 00308 00309 char* display = getenv((char*)"DISPLAY"); 00310 // main loop: 00311 for (uint i = 0; i < count; i ++) 00312 { 00313 // load up the frame and show a fixation cross on a blank screen: 00314 d->clearScreen(); 00315 00316 //seutp sdl surfaces 00317 Image< PixRGB<byte> > imageS = itsSImage[index[i]]; 00318 SDL_Surface *surf = d->makeBlittableSurface(imageS, true); 00319 00320 Image< PixRGB<byte> > imageQ = itsQImage[index[i]]; 00321 SDL_Surface *surfq = d->makeBlittableSurface(imageQ, true); 00322 00323 // ready to go whenever the user is ready: 00324 d->displayFixation(); 00325 d->waitForKey(); 00326 d->waitNextRequestedVsync(false, true); 00327 00328 d->pushEvent(std::string("===== Showing Sentence: ") + 00329 toStr<int>(index[i]) + " ====="); 00330 00331 // show the image: 00332 d->displaySurface(surf, -2); 00333 d->waitForKey(); 00334 // free the image: 00335 SDL_FreeSurface(surf); 00336 d->clearScreen(); 00337 d->waitNextRequestedVsync(false, true); 00338 00339 rutz::exec_pipe open("r","/usr/bin/mozilla-firefox", 00340 itsWeb.c_str(), NULL); 00341 00342 // start the eye tracker: 00343 et->track(true); 00344 00345 // blink the fixation: 00346 d->displayFixationBlink(); 00347 00348 //open webpage - close sdl 00349 d->closeDisplay(); 00350 d->pushEvent(std::string("===== Showing web page: ") + 00351 toStr<int>(index[i]) + " ====="); 00352 00353 //wait for a key in any window to be pressed 00354 waitXKeyPress(display); 00355 00356 // stop the eye tracker: 00357 usleep(50000); 00358 et->track(false); 00359 d->pushEvent(std::string("===== Destroying web page: ") + 00360 toStr<int>(index[i]) + " ====="); 00361 00362 //open sdl again 00363 d->openDisplay(); 00364 00365 //display the question 00366 d->pushEvent(std::string("===== Showing Question: ") + 00367 toStr<int>(index[i]) + " ====="); 00368 00369 // show the image: 00370 d->displaySurface(surfq, -2); 00371 d->waitForKey(); 00372 // free the image: 00373 SDL_FreeSurface(surfq); 00374 } 00375 00376 d->clearScreen(); 00377 d->displayText("Experiment complete. Thank you!"); 00378 d->waitForKey(); 00379 00380 // stop all our ModelComponents 00381 manager.stop(); 00382 00383 // all done! 00384 return 0; 00385 } 00386 00387 // ###################################################################### 00388 /* So things look consistent in everyone's emacs... */ 00389 /* Local Variables: */ 00390 /* indent-tabs-mode: nil */ 00391 /* End: */