X11-GUITest-0.27/0000755000076400007650000000000012104317102013517 5ustar pecastropecastroX11-GUITest-0.27/typemap0000644000076400007650000000022211564074460015135 0ustar pecastropecastro# X11::GUITest ($Id: typemap 209 2011-05-16 01:09:04Z ctrondlp $) BOOL T_IV Window T_UV Cursor T_UV UINT T_UV ULONG T_UV KeySym T_UV X11-GUITest-0.27/Changes0000644000076400007650000001523012104316203015014 0ustar pecastropecastroX11::GUITest ($Id: Changes 227 2013-02-05 23:57:55Z pecastro $) This is the list of module changes between revisions. -------------------------------------------------------------------- 0.27 Wed Feb 05 2013 00:01 - Move some files to the project root dir. - Fix RT #79751: Build on OpenBSD AMD64 failed - Add repository and license info 0.26 Sat Feb 02 2013 14:20 - Replace legacy XKeycodeToKeysym calls . Fix typo DOW to DOWN - Add supoprt fot AltGr and right Control and Shift - Enhanced GetWindowName() to check _NET_WM_NAME for text. 0.25 Sat May 21 2011 09:00 - Added IsWindowCursor() function to determine if a window has the specified cursor. - Various recorder enhancements. - Added -d (delaythreshold) option to recorder. Allows for inclusion of more/less granular event delays. - Added WaitSeconds() function, perhaps a little more intuitive then select. - Support for mouse wheel scrolling, using M_DOWN and M_UP constants. 0.24 Sun May 01 2011 16:50 - Reorganized source code and POD. - Updated x11guirecord build to use autotools. - Backend support added for internationalization. - Alternate document formats is now an optional build step for maintainer. 0.23 Sun Apr 24 2011 22:30 - Added support for recording using x11guirecord utility. - Updated behavior of QuoteStringForSendKeys function in handling zeros. - Added alias QSfSK() to QuoteStringForSendKeys() function. 0.22 Thu Jan 06 2011 20:00 - Example script eg/FindControlVisually.pl added to help demonstrate finding controls/widgets on screen using a baseline image. - Applied Thorsten H's AltGr keyboard modifier support. This applies to international (German, etc.) languages. - Minor compiler warning fix. - Skip building/testing if we're in AUTOMATED_TESTING (smoke testing) and DISPLAY isn't setup. Alleviate bogus test reports in this case. 0.21 Sat May 06 2006 10:24 - Implemented Nelson Caro's patch 1.1 dealing mostly with Makefile.PL enhancements concerning older versions of Perl. - Implemented Nelson Caro's patch 1.2 dealing with efficiency improvements, and fault handling. Also, GetWindowPos() now returns the border width of a window. - Implemented Nelson Caro's patch 1.3; which improves GetWindowFromPoint() in dealing with correct window border width. Also, FindWindowLike now handles invalid windows better. - Applied patches cpan #13682(XK_Meta_L fallback) and cpan #13684 (StartApp) from Alexey Tourbin. Compiler define X11_GUITEST_ALT_L_FALLBACK_META_L enabled by default. - Added special # modifier for Meta_Left. This modifier can be used like the existing ones (Alt=%, Control=^, etc.). - Applied Nelson's updates to test.t. Helps to eleviate X server jams when using Exceed, for MS Windows. Also testing GetMousePos() in list context. - Applied Nelson's patch to support multi-screen operation in various functions. Certain functions now include an additional parameter to specify the screen number. - Corrected incompatibility between Xinerama and the XTest extension. Compile with X11_GUITEST_USING_XINERAMA defined. See Makefile.PL for details. 0.20 Sat Feb 14 2004 09:43 - Now linking to Xext when building to support HP-UX 11 and possibly other OSs. - Added links into the documentation to the various project distribution sites. - test.t checks are now skipped if X Windows isn't running. An appropriate warning message accompanies this. 0.19 Sat Dec 13 2003 11:28 - Added FindWindowLike.pl example script. - Fixed a SendKeys bug spotted on RedHat 7.3 systems. - Added eg/templates/ScriptTemplate.pl for the purpose of constructing future example scripts. - Enhanced to help support Perl v5.5.3 (older; doesn't have newSVuv defined). - Various enhancements to the documentation. - Updated copyrights for new year. 0.18 Sun Sep 28 2003 17:45 - Added GetWindowFromPoint function. - Added GetParentWindow function. - Should now compile/link properly on Solaris systems. 0.17 Sat Sep 06 2003 09:23 - Corrected < and > key mappings. - Minor speed enhancements - Rearranged documentation sections. - Enabled optimizations and all warnings in Makefile.PL 0.16 Sun Aug 03 2003 15:00 - Added PressMouseButton, ReleaseMouseButton. - Added WebBrowser_1.pl example script. - Added more SendKeys examples, i.e. Ctrl-Shift-l - Added SetWindowName function. - Added documentation for DISPLAY environment variable alteration for the purpose of interacting with applications on remote X servers. - test.t modified to accommodate scenarios when there are no windows present that have a WM_NAME property set. - Other minor documentation enhancements. 0.15 Sat Jun 28 2003 17:45 - Quote string function will now handle data present after any embedded newline characters. - Enhanced TextEditor_1.pl example. - Added additional checks in test.t - Pristinized the typemap. - Misc. code/documentation enhancements. - Removed docs/Packaging file. Note: Now using `make tardist` to generate new module builds. - Removed MakeDocs.sh.. Now handled in Makefile.PL - Added a note that goes into man page concerning other documentation. - For those websites that grab only the POD from GUITest.pm, I've added a note into the documentation intended to relay that there is more then just that available for documentation. - Added LSH (Shift_L), RSH (Shift_R), LCT (Control_L), RCT (Control_R), LAL (Alt_L), RAL (Alt_R) abbreviated key names. - Added PressKey and ReleaseKey per suggestion. Also included PressReleaseKey. 0.14 Sun May 18 2003 11:53 - Documentation updates - Official support for FreeBSD 5.0 0.13 Sun Mar 30 2003 19:32 - Enhanced and reenabled man3 POD generation. - Added WaitWindowClose - Added TextEditor_1.pl example script. - Documentation updates 0.12 Fri Mar 21 2003 02:35 - Added IsKeyPressed - Added logical mouse button Ids to :CONST export tag: M_BTN1 - M_BTN5 - Added IsMouseButtonPressed - Updated documentation - Added QuoteStringForSendKeys - Disabled man3 POD generation in Makefile.PL until I find a pristine method of generating one manual page from the PM and XS file. Currently I'm using MakeDocs.sh to construct the core text/html documentation. 0.11 Sun Mar 09 2003 16:01 - Fixed aliased {PGU} and {PGD} keys. - Added GetMousePos, IsChild functions. - Now packaging as X11-GUITest-[VERSION].tar.gz versus x11guitest-[VERSION].tar.gz - Made enhancements to the documentation. 0.10 Tue Mar 05 2003 18:00 - Initial Release. X11-GUITest-0.27/META.json0000664000076400007650000000175512104317102015152 0ustar pecastropecastro{ "abstract" : "Collection of functions for X11 GUI testing/interaction.", "author" : [ "Dennis K. Paulsen " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.120921", "license" : [ "open_source" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "X11-GUITest", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : {} } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "https://x11guitest.svn.sourceforge.net/svnroot/x11guitest" } }, "version" : "0.27" } X11-GUITest-0.27/GUITest.h0000644000076400007650000000215311563632137015175 0ustar pecastropecastro/* X11::GUITest ($Id: GUITest.h 203 2011-05-15 02:03:11Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef GUITest_h #define GUITest_h #define DEF_EVENT_SEND_DELAY 10 /* Value < 10 not recommended */ #define DEF_KEY_SEND_DELAY 0 #define KEYMAP_VECTOR_SIZE 32 #define KEYMAP_BIT_COUNT 8 typedef struct WindowTable { Window *Ids; UINT NVals; UINT Max; } WindowTable; #endif /* #ifndef GUITest_h */ X11-GUITest-0.27/GUITest.xs0000644000076400007650000007200612103004557015372 0ustar pecastropecastro/* X11::GUITest ($Id: GUITest.xs 218 2013-02-01 18:29:03Z pecastro $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifdef __cplusplus extern "C" { #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus } #endif /* Added for pre-v5.6.x Perl */ #ifndef newSVuv #define newSVuv newSViv #endif #include #include #include #include #include #include #include #include #include #include #include #include "Common.h" #include "GUITest.h" #include "KeyUtil.h" /* File Level Variables */ static Display *TheXDisplay = NULL; static int TheScreen = 0; static WindowTable ChildWindows = {0}; static ULONG EventSendDelay = DEF_EVENT_SEND_DELAY; static ULONG KeySendDelay = DEF_KEY_SEND_DELAY; static int (*OldErrorHandler)(Display *, XErrorEvent *) = NULL; /* Non Exported Utility Functions: */ /* Function: IgnoreBadWindow * Description: User defined error handler callback for X event errors. */ static int IgnoreBadWindow(Display *display, XErrorEvent *error) { /* Ignore bad window errors here, handle elsewhere */ if (error->error_code != BadWindow) { assert(NULL != OldErrorHandler); (*OldErrorHandler)(display, error); } /* Note: Return is ignored */ return(0); } /* Function: SetupXDisplay * Description: Sets up our connection to the X server's display */ static void SetupXDisplay(void) { int eventnum = 0, errornum = 0, majornum = 0, minornum = 0; /* Get Display Pointer */ TheXDisplay = XOpenDisplay(NULL); if (TheXDisplay == NULL) { croak("X11::GUITest - This program is designed to run in X Windows!\n"); } /* Ensure the XTest extension is available */ if (!XTestQueryExtension(TheXDisplay, &eventnum, &errornum, &majornum, &minornum)) { croak("X11::GUITest - XServer %s doesn't support the XTest extensions!\n", DisplayString(TheXDisplay)); } TheScreen = DefaultScreen(TheXDisplay); /* Discard current events in queue. */ XSync(TheXDisplay, True); } /* Function: CloseXDisplay * Description: Closes our connection to the X server's display */ static void CloseXDisplay(void) { if (TheXDisplay) { XSync(TheXDisplay, False); XCloseDisplay(TheXDisplay); TheXDisplay = NULL; } } /* Function: IsNumber * Description: Determines if the specified string represents a * number. */ static BOOL IsNumber(const char *str) { size_t x = 0, len = 0; assert(str != NULL); len = strlen(str); for (x = 0; x < len; x++) { if (!isdigit(str[x])) { return(FALSE); } } return(TRUE); } /* Function: GetRegKeySym * Description: Given a regular key name as a single character(i.e., a), this * function obtains the appropriate keysym by calling GetKeySym(). * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. Also, * on success, sym gets set to the appropriate keysym. On failure, sym * will get set to NoSymbol. */ static BOOL GetRegKeySym(const char name, KeySym *sym) { #define MAX_REG_KEY 2 static char key[MAX_REG_KEY] = ""; key[0] = name; key[1] = NUL; return( GetKeySym(key, sym) ); } /* Function: GetKeycodeFromKeysym * Description: Wrapper around XKeysymToKeycode. Supports compile-time * workarounds, etc. */ static KeyCode GetKeycodeFromKeysym(Display *display, KeySym keysym) { KeyCode kc = XKeysymToKeycode(display, keysym); #ifdef X11_GUITEST_ALT_L_FALLBACK_META_L /* Xvfb lacks XK_Alt_L; fall back to XK_Meta_L */ if (kc == 0 && keysym == XK_Alt_L) { kc = XKeysymToKeycode(display, XK_Meta_L); } #endif return(kc); } /* Function: PressKeyImp * Description: Presses the key for the specified keysym. Lower-level * implementation. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. */ static BOOL PressKeyImp(KeySym sym) { KeyCode kc = 0; BOOL retval = 0; kc = GetKeycodeFromKeysym(TheXDisplay, sym); if (kc == 0) { return(FALSE); } retval = (BOOL)XTestFakeKeyEvent(TheXDisplay, kc, True, EventSendDelay); XFlush(TheXDisplay); return(retval); } /* Function: ReleaseKeyImp * Description: Releases the key for the specified keysym. Lower-level * implementation. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. */ static BOOL ReleaseKeyImp(KeySym sym) { KeyCode kc = 0; BOOL retval = 0; kc = GetKeycodeFromKeysym(TheXDisplay, sym); if (kc == 0) { return(FALSE); } retval = (BOOL)XTestFakeKeyEvent(TheXDisplay, kc, False, EventSendDelay); XFlush(TheXDisplay); return(retval); } /* Function: PressReleaseKeyImp * Description: Presses and releases the key for the specified keysym. * Also implements key send delay. Lower-level implementation. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. */ static BOOL PressReleaseKeyImp(KeySym sym) { if (!PressKeyImp(sym)) { return(FALSE); } if (!ReleaseKeyImp(sym)) { return(FALSE); } /* Possibly wait between(after) keystrokes */ if (KeySendDelay > 0) { /* usleep(500 * 1000) = ~500ms */ usleep(KeySendDelay * 1000); } return(TRUE); } /* Function: IsShiftNeeded * Description: Determines if the specified keysym needs the shift * modifier. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. */ static BOOL IsShiftNeeded(KeySym sym) { KeySym ksl = 0, ksu = 0, *kss = NULL; KeyCode kc = 0; int syms = 0; BOOL needed = FALSE; kc = GetKeycodeFromKeysym(TheXDisplay, sym); if (!kc) { return(FALSE); } /* kc(grave) = kss(grave, asciitilde, ) */ kss = XGetKeyboardMapping(TheXDisplay, kc, 1, &syms); XConvertCase(sym, &ksl, &ksu); if (sym == kss[0] && (sym == ksl && sym == ksu)) { /* Not subject to case conversion */ needed = FALSE; } else if (sym == ksl && sym != ksu) { /* Shift not needed */ needed = FALSE; } else { /* Shift needed */ needed = TRUE; } XFree(kss); return(needed); } /* Function: ProcessBraceSet * Description: Takes a brace set such as: {Tab}, {Tab 3}, * {Tab Tab a b c}, {PAUSE 500}, {PAUSE 500 Tab}, etc. * and breaks it into components, then proceeds to press * the appropriate keys or perform the special functionality * requested (i.e., PAUSE). Numeric elements are used in the * special functionality or simply to ensure the previous key * element gets pressed the specified number of times. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. */ static BOOL ProcessBraceSet(const char *braceset, size_t *len) { enum {NONE, PAUSE, KEY}; /* Various Functionalities */ int cmd = NONE, count = 0, i = 0; BOOL needshift = FALSE; char *buffer = NULL, *endbrc = NULL, *token = NULL; KeySym sym = 0; assert(braceset != NULL); assert(len != NULL); /* Fail if there isn't a valid brace set */ if (*braceset != '{' || !strchr(braceset, '}')) { return(FALSE); } /* Create backup buffer because we are using strtok */ buffer = (char *)safemalloc(strlen(braceset)); if (buffer == NULL) { return(FALSE); } /* Ignore beginning { char */ strcpy(buffer, &braceset[1]); /* Get brace end in buffer */ endbrc = strchr(buffer, '}'); if (endbrc == NULL) { safefree(buffer); return(FALSE); } /* If we have a quoted }, move over one character */ if (endbrc[1] == '}') { endbrc++; } /* Terminate brace set */ *endbrc = NUL; /* Store brace set length for calling function. Include * 2 for {} we ignored */ *len = strlen(buffer) + 2; /* Work on the space delimited items in the buffer. */ if ( !(token = strtok(buffer, " ")) ) { safefree(buffer); return(FALSE); } do { /* } while ( (token = strtok(NULL, " ")) ); */ count = 0; if (IsNumber(token)) { /* Yes, a number, so convert it for key depresses or for command specific use */ if ( (count = atoi(token)) <= 0 ) { safefree(buffer); return(FALSE); } } else { cmd = NONE; /* Special functionality? */ if (strcasecmp(token, "PAUSE") == 0) { /* Yes, PAUSE, so continue on to get the duration count */ cmd = PAUSE; continue; } else { /* No, just a key, so get symbol */ cmd = KEY; if (!GetKeySym(token, &sym)) { safefree(buffer); return(FALSE); } needshift = IsShiftNeeded(sym); if (needshift) { PressKeyImp(XK_Shift_L); } /* Press key */ if (!PressReleaseKeyImp(sym)) { if (needshift) { ReleaseKeyImp(XK_Shift_L); } safefree(buffer); return(FALSE); } if (needshift) { ReleaseKeyImp(XK_Shift_L); } } } /* Handle commands that can use a specified count */ if (count > 0) { switch (cmd) { case PAUSE: /* usleep(500 * 1000) = 500ms */ usleep(count * 1000); break; case KEY: /* Repeat the last key if needed. Start at iteration 2 * because we have already depressed key once up above */ /* Use shift if needed */ if (needshift) { PressKeyImp(XK_Shift_L); } for (i = 2; i <= count; i++) { /* Use sym that was already stored from above */ if (!PressReleaseKeyImp(sym)) { if (needshift) { ReleaseKeyImp(XK_Shift_L); } safefree(buffer); return(FALSE); } } if (needshift) { ReleaseKeyImp(XK_Shift_L); } break; default: /* Fail, we have a count, but an unknown command! */ safefree(buffer); return(FALSE); }; /* switch (cmd) { */ } /* if (count > 0) { */ } while ( (token = strtok(NULL, " ")) ); safefree(buffer); return(TRUE); } /* Function: SendKeysImp * Description: Underlying implementation of the SendKeys routine. Read * the SendKeys documentation below for some specifics. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. * Also, if you add special character handling below, also ensure * the QuoteStringForSendKeys function is accurate in GUITest.pm. */ static BOOL SendKeysImp(const char *keys) { KeySym sym = 0; size_t keyslen = 0, bracelen = 0; size_t x = 0; BOOL retval = FALSE, shift = FALSE, ctrl = FALSE, altgr = FALSE, alt = FALSE, meta = FALSE, modlock = FALSE, needshift = FALSE; assert(keys != NULL); keyslen = strlen(keys); for (x = 0; x < keyslen; x++) { switch (keys[x]) { /* Brace Set? of quoted/special characters (i.e. {{}, {TAB}, {F1 F2}, {PAUSE 200}) */ case '{': if (!ProcessBraceSet(&keys[x], &bracelen)) { return(FALSE); } /* Skip past the brace set, Note: - 1 because we are at { already */ x += (bracelen - 1); continue; /* Modifiers? */ case '~': retval = PressReleaseKeyImp(XK_Return); break; case '+': /* Shift */ retval = PressKeyImp(XK_Shift_L); shift = TRUE; break; case '^': /* Control */ retval = PressKeyImp(XK_Control_L); ctrl = TRUE; break; case '%': /* Alt */ retval = PressKeyImp(XK_Alt_L); alt = TRUE; break; case '#': /* Meta */ retval = PressKeyImp(XK_Meta_L); meta = TRUE; break; case '&': /* AltGr */ retval = PressKeyImp(XK_ISO_Level3_Shift); altgr = TRUE; break; case '(': modlock = TRUE; break; case ')': modlock = FALSE; break; /* Regular Key? (a, b, c, 1, 2, 3, _, *, %), etc. */ default: if (!GetRegKeySym(keys[x], &sym)) { return(FALSE); } /* Use shift if needed */ needshift = IsShiftNeeded(sym); if (!shift && needshift) { PressKeyImp(XK_Shift_L); } retval = PressReleaseKeyImp(sym); /* Release shift if needed */ if (!shift && needshift) { ReleaseKeyImp(XK_Shift_L); } break; }; /* switch (keys[x]) { */ /* If modlock coming up next, go on to process it */ if (keys[x + 1] == '(') { continue; } /* Ensure modifiers are clear when needed */ if (!modlock) { if (shift) { ReleaseKeyImp(XK_Shift_L); shift = FALSE; } if (ctrl) { ReleaseKeyImp(XK_Control_L); ctrl = FALSE; } if (alt) { ReleaseKeyImp(XK_Alt_L); alt = FALSE; } if (meta) { ReleaseKeyImp(XK_Meta_L); meta = FALSE; } if (altgr) { ReleaseKeyImp(XK_ISO_Level3_Shift); altgr = FALSE; } } if (!retval) { return(FALSE); } } /* for (x = 0; x < keyslen; x++) { */ return(TRUE); } /* Function: IsWindowImp * Description: Underlying implementation of the IsWindow routine. Read * the IsWindow documentation below for some specifics. * Note: Returns non-zero for true, zero for false. */ static BOOL IsWindowImp(Window win) { XWindowAttributes wattrs = {0}; BOOL retval; OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); retval = (BOOL)(XGetWindowAttributes(TheXDisplay, win, &wattrs) != 0); XSetErrorHandler(OldErrorHandler); return(retval); } /* Function: AddChildWindow * Description: Adds the specified window Id to the internally managed * table of available window Ids. Also handles the memory * allocation for this table. * Note: Returns TRUE (non-zero) on success, FALSE (zero) on failure. */ static BOOL AddChildWindow(Window win) { enum {INIT = 1, GROW = 2}; /* Memory Allocation */ if (!win) { return(FALSE); } if (ChildWindows.Ids == NULL) { /* Initialize */ ChildWindows.Ids = (Window *)safemalloc(INIT * sizeof(Window)); if (ChildWindows.Ids == NULL) { return(FALSE); } ChildWindows.Max = INIT; ChildWindows.NVals = 0; } else if (ChildWindows.NVals >= ChildWindows.Max) { /* Grow */ /* Note: Did not use [insert fancy algorythm name here] algorythm here on purpose */ Window *TempIds = NULL; TempIds = (Window *)saferealloc(ChildWindows.Ids, (GROW * ChildWindows.Max) * sizeof(Window)); if (TempIds == NULL) { return(FALSE); } ChildWindows.Max *= GROW; ChildWindows.Ids = TempIds; } /* Place the new window Id in */ ChildWindows.Ids[ChildWindows.NVals] = win; ChildWindows.NVals++; return(TRUE); } /* Function: ClearChildWindows * Description: Clears the table of window Ids. Memory allocated * in AddChildWindow is not freed here, because * we'll probably want to take advantage of it again. * Note: No return value. */ static void ClearChildWindows(void) { if (ChildWindows.Ids) { memset(ChildWindows.Ids, 0, ChildWindows.Max * sizeof(Window)); } ChildWindows.NVals = 0; } /* Function: FreeChildWindows * Description: Deallocates the memory of the window Id table * that was allocated through AddChildWindow. This * should be called on exit. * Note: No return value. */ static void FreeChildWindows(void) { if (ChildWindows.Ids) { safefree(ChildWindows.Ids); ChildWindows.Ids = NULL; } ChildWindows.NVals = 0; ChildWindows.Max = 0; } /* Function: EnumChildWindowsAux * Description: Obtains the list of window Ids * Note: Returns value indicating success of obtaining * windows. */ static BOOL EnumChildWindowsAux(Window win) { Window root = 0, parent = 0, *children = NULL; UINT childcount = 0; UINT i = 0; /* get list of child windows */ if (XQueryTree(TheXDisplay, win, &root, &parent, &children, &childcount)) { for (i = 0; i < childcount; i++) { /* Add Child */ AddChildWindow(children[i]); /* Look for its descendents */ if (!EnumChildWindowsAux(children[i])) { XFree(children); return FALSE; } } if (children) { XFree(children); } return TRUE; } else { return FALSE; } } /* Function: EnumChildWindows * Description: Calls utility function to obtain list of window * Ids. Helps handle window transitions. * Note: Returns nothing. */ static void EnumChildWindows(Window win) { BOOL success = 0; for (;;) { if (!IsWindowImp(win)) { return; } OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); success = EnumChildWindowsAux(win); XSetErrorHandler(OldErrorHandler); if (success) { return; } /* Failure: try again, in 1/2 second. */ ClearChildWindows(); usleep(500000); /* 500000 = 1/2 second */ } } MODULE = X11::GUITest PACKAGE = X11::GUITest PROTOTYPES: DISABLE void InitGUITest() PPCODE: /* Things to do on initialization */ SetupXDisplay(); XTestGrabControl(TheXDisplay, True); XSRETURN(0); void DeInitGUITest() PPCODE: /* Things to do on deinitialization */ CloseXDisplay(); FreeChildWindows(); XSRETURN(0); int DefaultScreen() CODE: RETVAL = TheScreen; OUTPUT: RETVAL int ScreenCount() CODE: RETVAL = ScreenCount(TheXDisplay); OUTPUT: RETVAL ULONG SetEventSendDelay(delay) ULONG delay CODE: /* Returning old delay amount */ RETVAL = EventSendDelay; EventSendDelay = delay; OUTPUT: RETVAL ULONG GetEventSendDelay() CODE: RETVAL = EventSendDelay; OUTPUT: RETVAL ULONG SetKeySendDelay(delay) ULONG delay CODE: /* Returning old delay amount */ RETVAL = KeySendDelay; KeySendDelay = delay; OUTPUT: RETVAL ULONG GetKeySendDelay() CODE: RETVAL = KeySendDelay; OUTPUT: RETVAL SV * GetWindowName(win) Window win PREINIT: char *name = NULL; XTextProperty wm_name = {0}; Atom wm_name_prop = None; CODE: RETVAL = &PL_sv_undef; if (IsWindowImp(win)) { if (XFetchName(TheXDisplay, win, &name)) { RETVAL = newSVpv(name, strlen(name)); XFree(name); } else { wm_name_prop = XInternAtom(TheXDisplay, "_NET_WM_NAME", False); if (wm_name_prop != None) { if (XGetTextProperty(TheXDisplay, win, &wm_name, wm_name_prop)) { RETVAL = newSVpv((char *)wm_name.value, strlen((char *)wm_name.value)); XFree(wm_name.value); } } } } OUTPUT: RETVAL BOOL SetWindowName(win, name) Window win char *name PREINIT: XTextProperty textprop = {0}; size_t namelen = 0; Atom utf8_string = 0; Atom net_wm_name = 0; Atom net_wm_icon_name = 0; CODE: RETVAL = FALSE; if (IsWindowImp(win)) { if (XStringListToTextProperty(&name, 1, &textprop)) { XSetWMName(TheXDisplay, win, &textprop); XSetWMIconName(TheXDisplay, win, &textprop); XFree(textprop.value); RETVAL = TRUE; } /* These UTF8 window name properties can be the properties * that get displayed, so we set them too. */ utf8_string = XInternAtom(TheXDisplay, "UTF8_STRING", True); if (utf8_string != None) { net_wm_name = XInternAtom(TheXDisplay, "_NET_WM_NAME", True); net_wm_icon_name = XInternAtom(TheXDisplay, "_NET_WM_ICON_NAME", True); if (net_wm_name != None && net_wm_icon_name != None) { namelen = strlen(name); XChangeProperty(TheXDisplay, win, net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)name, namelen); XChangeProperty(TheXDisplay, win, net_wm_icon_name, utf8_string, 8, PropModeReplace, (unsigned char *)name, namelen); } } } OUTPUT: RETVAL Window GetRootWindow(scr_num = NO_INIT) int scr_num CODE: if (0 == items) scr_num = TheScreen; if (scr_num >= 0 && scr_num < ScreenCount(TheXDisplay)) RETVAL = RootWindow(TheXDisplay, scr_num); else RETVAL = None; OUTPUT: RETVAL void GetChildWindows(win) Window win PREINIT: UINT i = 0; PPCODE: EnumChildWindows(win); EXTEND(SP, (int)ChildWindows.NVals); for (i = 0; i < ChildWindows.NVals; i++) { PUSHs(sv_2mortal(newSVuv((UV)ChildWindows.Ids[i]))); } ClearChildWindows(); XSRETURN(i); BOOL MoveMouseAbs(x, y, scr_num = NO_INIT) int x int y int scr_num CODE: if (items < 3) scr_num = TheScreen; if (scr_num >= 0 && scr_num < ScreenCount(TheXDisplay)) { #ifndef X11_GUITEST_USING_XINERAMA RETVAL = (BOOL)XTestFakeMotionEvent(TheXDisplay, scr_num, x, y, EventSendDelay); XFlush(TheXDisplay); #else ULONG tmp; /* I decided not to set our error handler, since the window must exist. */ XWarpPointer(TheXDisplay, None, RootWindow(TheXDisplay, scr_num), 0, 0, 0, 0, x, y); XSync(TheXDisplay, False); tmp = EventSendDelay / (ULONG) 1000; while ((ULONG) 0 != tmp) tmp = (ULONG) sleep((int) tmp); tmp = EventSendDelay % (ULONG) 1000; usleep(1000 * tmp); #endif RETVAL = (BOOL) 1; } else { RETVAL = (BOOL) 0; } OUTPUT: RETVAL void GetMousePos() PREINIT: Window root = 0, child = 0; int root_x = 0, root_y = 0; int win_x = 0, win_y = 0, scr_num = 0; UINT mask = 0; PPCODE: /* We do not bother to set our error handler because the window given has to exist. */ XQueryPointer(TheXDisplay, RootWindow(TheXDisplay, TheScreen), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask); EXTEND(SP, 3); PUSHs( sv_2mortal(newSViv((IV)root_x)) ); PUSHs( sv_2mortal(newSViv((IV)root_y)) ); for (scr_num = ScreenCount(TheXDisplay) - 1; scr_num >= 0 ; --scr_num) { if (root == RootWindow(TheXDisplay, scr_num)) { break; } assert(0 != scr_num); /* There is something really wrong with the Xlib data if this "assert" fails. */ } PUSHs( sv_2mortal(newSViv((IV)scr_num)) ); XSRETURN(3); BOOL PressMouseButton(button) int button CODE: RETVAL = (BOOL)XTestFakeButtonEvent(TheXDisplay, button, True, EventSendDelay); XFlush(TheXDisplay); OUTPUT: RETVAL BOOL ReleaseMouseButton(button) int button CODE: RETVAL = (BOOL)XTestFakeButtonEvent(TheXDisplay, button, False, EventSendDelay); XFlush(TheXDisplay); OUTPUT: RETVAL BOOL SendKeys(keys) char *keys CODE: RETVAL = SendKeysImp(keys); OUTPUT: RETVAL BOOL PressKey(key) char *key PREINIT: KeySym sym = 0; CODE: RETVAL = GetKeySym(key, &sym); if (RETVAL) { RETVAL = PressKeyImp(sym); } OUTPUT: RETVAL BOOL ReleaseKey(key) char *key PREINIT: KeySym sym = 0; CODE: RETVAL = GetKeySym(key, &sym); if (RETVAL) { RETVAL = ReleaseKeyImp(sym); } OUTPUT: RETVAL BOOL PressReleaseKey(key) char *key PREINIT: KeySym sym = 0; CODE: RETVAL = GetKeySym(key, &sym); if (RETVAL) { RETVAL = PressReleaseKeyImp(sym); } OUTPUT: RETVAL BOOL IsKeyPressed(key) char *key PREINIT: int pos = 0; KeySym sym = 0; KeyCode kc = 0, skc = 0; BOOL keyon = FALSE, shifton = FALSE; char keys_return[KEYMAP_VECTOR_SIZE] = ""; CODE: if (key && GetKeySym(key, &sym)) { kc = GetKeycodeFromKeysym(TheXDisplay, sym); skc = GetKeycodeFromKeysym(TheXDisplay, XK_Shift_L); XQueryKeymap(TheXDisplay, keys_return); for (pos = 0; pos < (KEYMAP_VECTOR_SIZE * KEYMAP_BIT_COUNT); pos++) { /* For the derived keycode, are we at the correct bit position for it? */ if (kc == pos) { /* Check the bit at this position to determine the state of the key */ if ( keys_return[pos / KEYMAP_BIT_COUNT] & (1 << (pos % KEYMAP_BIT_COUNT)) ) { /* Bit On, so key is pressed */ keyon = TRUE; } } /* For the shift keycode, ... */ if (skc == pos) { /* Check the bit at this position to determine the state of the shift key */ if ( keys_return[pos / KEYMAP_BIT_COUNT] & (1 << (pos % KEYMAP_BIT_COUNT)) ) { /* Bit On, so shift is pressed */ shifton = TRUE; } } } /* for (pos = 0; pos < (KEYMAP_VECTOR_SIZE * KEYMAP_BIT_COUNT); pos++) { */ } /* if (key && GetKeySym(key, &sym)) { */ /* Determine result */ if (keyon) { /* Key is on, so use its keysym to determine if shift modifier needs to be verified also */ if (IsShiftNeeded(sym)) { RETVAL = (shifton); } else { RETVAL = (!shifton); } } else { /* Key not on, so it is not pressed */ RETVAL = FALSE; } OUTPUT: RETVAL BOOL IsMouseButtonPressed(button) int button PREINIT: Window root = 0, child = 0; int root_x = 0, root_y = 0; int win_x = 0, win_y = 0; UINT mask = 0; CODE: XQueryPointer(TheXDisplay, RootWindow(TheXDisplay, TheScreen), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask); switch (button) { case Button1: RETVAL = (mask & Button1Mask); break; case Button2: RETVAL = (mask & Button2Mask); break; case Button3: RETVAL = (mask & Button3Mask); break; case Button4: RETVAL = (mask & Button4Mask); break; case Button5: RETVAL = (mask & Button5Mask); break; default: RETVAL = FALSE; break; }; OUTPUT: RETVAL BOOL IsWindowCursor(win, cursor) Window win Cursor cursor CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); RETVAL = XTestCompareCursorWithWindow(TheXDisplay, win, cursor); XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL IsWindow(win) Window win CODE: RETVAL = IsWindowImp(win); OUTPUT: RETVAL BOOL IsWindowViewable(win) Window win PREINIT: XWindowAttributes wattrs = {0}; CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); if (!XGetWindowAttributes(TheXDisplay, win, &wattrs)) { RETVAL = FALSE; } else { RETVAL = (wattrs.map_state == IsViewable); } XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL MoveWindow(win, x, y) Window win int x int y CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); RETVAL = XMoveWindow(TheXDisplay, win, x, y); XSync(TheXDisplay, False); XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL ResizeWindow(win, w, h) Window win int w int h CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); RETVAL = XResizeWindow(TheXDisplay, win, w, h); XSync(TheXDisplay, False); XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL IconifyWindow(win) Window win PREINIT: XWindowAttributes wattrs = {0}; int scr_num; CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); if (XGetWindowAttributes(TheXDisplay, win, &wattrs)) { for (scr_num = ScreenCount(TheXDisplay) - 1; scr_num >= 0 ; --scr_num) { if ( wattrs.screen == ScreenOfDisplay(TheXDisplay, scr_num)) { break; } /* There is something really wrong with the Xlib data if this "assert" fails. */ assert(0 != scr_num); } RETVAL = XIconifyWindow(TheXDisplay, win, scr_num); XSync(TheXDisplay, False); } else { RETVAL = (BOOL) 0; } XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL UnIconifyWindow(win) Window win CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); RETVAL = XMapWindow(TheXDisplay, win); XSync(TheXDisplay, False); XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL RaiseWindow(win) Window win CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); RETVAL = XRaiseWindow(TheXDisplay, win); XSync(TheXDisplay, False); XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL BOOL LowerWindow(win) Window win CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); RETVAL = XLowerWindow(TheXDisplay, win); XSync(TheXDisplay, False); XSetErrorHandler(OldErrorHandler); OUTPUT: RETVAL Window GetInputFocus() PREINIT: Window focus = 0; int revert = 0; CODE: XGetInputFocus(TheXDisplay, &focus, &revert); RETVAL = focus; OUTPUT: RETVAL BOOL SetInputFocus(win) Window win PREINIT: Window focus = 0; int revert = 0; CODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); /* Note: Per function man page, there is no effect if the time parameter * of this call isn't accurate. Will use CurrentTime. Also, it * appears that we can't trust its return value. */ XSetInputFocus(TheXDisplay, win, RevertToParent, CurrentTime); XSync(TheXDisplay, False); XSetErrorHandler(OldErrorHandler); /* Verify that the window now has focus. Used to determine return value */ XGetInputFocus(TheXDisplay, &focus, &revert); RETVAL = (focus == win); OUTPUT: RETVAL void GetWindowPos(win) Window win PREINIT: XWindowAttributes wattrs = {0}; Window child = 0; int num_ret = 0, x = 0, y = 0, scr_num; PPCODE: OldErrorHandler = XSetErrorHandler(IgnoreBadWindow); if (XGetWindowAttributes(TheXDisplay, win, &wattrs)) { XTranslateCoordinates(TheXDisplay, win, wattrs.root, 0 - wattrs.border_width, 0 - wattrs.border_width, &x, &y, &child); EXTEND(SP, 6); PUSHs( sv_2mortal(newSViv((IV)x)) ); PUSHs( sv_2mortal(newSViv((IV)y)) ); PUSHs( sv_2mortal(newSViv((IV)wattrs.width)) ); PUSHs( sv_2mortal(newSViv((IV)wattrs.height)) ); PUSHs( sv_2mortal(newSViv((IV)wattrs.border_width)) ); for (scr_num = ScreenCount(TheXDisplay) - 1; scr_num >= 0 ; --scr_num) { if ( wattrs.screen == ScreenOfDisplay(TheXDisplay, scr_num)) { break; } /* There is something really wrong with the Xlib data if this "assert" fails. */ assert(0 != scr_num); } PUSHs( sv_2mortal(newSViv((IV)scr_num)) ); num_ret = 6; } XSetErrorHandler(OldErrorHandler); XSRETURN(num_ret); Window GetParentWindow(win) Window win PREINIT: Window parent = 0, *children = NULL, root = 0; UINT childcount = 0; CODE: RETVAL = 0; if (XQueryTree(TheXDisplay, win, &root, &parent, &children, &childcount)) { XFree(children); RETVAL = parent; } OUTPUT: RETVAL void GetScreenRes(scr_num = NO_INIT) int scr_num PREINIT: int x = 0, y = 0, num_ret = 0; PPCODE: if (0 == items) scr_num = TheScreen; if (scr_num >= 0 && scr_num < ScreenCount(TheXDisplay)) { x = DisplayWidth(TheXDisplay, scr_num); y = DisplayHeight(TheXDisplay, scr_num); EXTEND(SP, 2); PUSHs( sv_2mortal(newSViv((IV)x)) ); PUSHs( sv_2mortal(newSViv((IV)y)) ); num_ret = 2; } XSRETURN(num_ret); int GetScreenDepth(scr_num = NO_INIT) int scr_num CODE: if (0 == items) scr_num = TheScreen; if (scr_num >= 0 && scr_num < ScreenCount(TheXDisplay)) { RETVAL = DefaultDepth(TheXDisplay, scr_num); } else { RETVAL = -1; } OUTPUT: RETVAL X11-GUITest-0.27/recorder/0000755000076400007650000000000012104317102015324 5ustar pecastropecastroX11-GUITest-0.27/recorder/autogen.sh0000755000076400007650000013443211563632637017361 0ustar pecastropecastro#!/bin/sh # a u t o g e n . s h # # Copyright (c) 2005-2009 United States Government as represented by # the U.S. Army Research Laboratory. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # 3. The name of the author may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ### # # Script for automatically preparing the sources for compilation by # performing the myriad of necessary steps. The script attempts to # detect proper version support, and outputs warnings about particular # systems that have autotool peculiarities. # # Basically, if everything is set up and installed correctly, the # script will validate that minimum versions of the GNU Build System # tools are installed, account for several common configuration # issues, and then simply run autoreconf for you. # # If autoreconf fails, which can happen for many valid configurations, # this script proceeds to run manual preparation steps effectively # providing a POSIX shell script (mostly complete) reimplementation of # autoreconf. # # The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER # environment variables and corresponding _OPTIONS variables (e.g. # AUTORECONF_OPTIONS) may be used to override the default automatic # detection behaviors. Similarly the _VERSION variables will override # the minimum required version numbers. # # Examples: # # To obtain help on usage: # ./autogen.sh --help # # To obtain verbose output: # ./autogen.sh --verbose # # To skip autoreconf and prepare manually: # AUTORECONF=false ./autogen.sh # # To verbosely try running with an older (unsupported) autoconf: # AUTOCONF_VERSION=2.50 ./autogen.sh --verbose # # Author: # Christopher Sean Morrison # # Patches: # Sebastian Pipping # ###################################################################### # set to minimum acceptable version of autoconf if [ "x$AUTOCONF_VERSION" = "x" ] ; then AUTOCONF_VERSION=2.52 fi # set to minimum acceptable version of automake if [ "x$AUTOMAKE_VERSION" = "x" ] ; then AUTOMAKE_VERSION=1.6.0 fi # set to minimum acceptable version of libtool if [ "x$LIBTOOL_VERSION" = "x" ] ; then LIBTOOL_VERSION=1.4.2 fi ################## # ident function # ################## ident ( ) { # extract copyright from header __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`" if [ "x$__copyright" = "x" ] ; then __copyright="`date +%Y`" fi # extract version from CVS Id string __id="$Id: autogen.sh 204 2011-05-15 02:08:31Z ctrondlp $" __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`" if [ "x$__version" = "x" ] ; then __version="" fi echo "autogen.sh build preparation script by Christopher Sean Morrison" echo " + config.guess download patch by Sebastian Pipping (2008-12-03)" echo "revised 3-clause BSD-style license, copyright (c) $__copyright" echo "script version $__version, ISO/IEC 9945 POSIX shell script" } ################## # USAGE FUNCTION # ################## usage ( ) { echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]" echo " --help Help on $NAME_OF_AUTOGEN usage" echo " --verbose Verbose progress output" echo " --quiet Quiet suppressed progress output" echo " --download Download the latest config.guess from gnulib" echo " --version Only perform GNU Build System version checks" echo echo "Description: This script will validate that minimum versions of the" echo "GNU Build System tools are installed and then run autoreconf for you." echo "Should autoreconf fail, manual preparation steps will be run" echo "potentially accounting for several common preparation issues. The" echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER," echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS" echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the" echo "default automatic detection behavior." echo ident return 0 } ########################## # VERSION_ERROR FUNCTION # ########################## version_error ( ) { if [ "x$1" = "x" ] ; then echo "INTERNAL ERROR: version_error was not provided a version" exit 1 fi if [ "x$2" = "x" ] ; then echo "INTERNAL ERROR: version_error was not provided an application name" exit 1 fi $ECHO $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch," $ECHO " at least version $1 of $2 must be installed." $ECHO $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will" $ECHO "run configure or make. Either the GNU Autotools will need to be installed" $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source" $ECHO "code on another system and then transferred to here. -- Cheers!" $ECHO } ########################## # VERSION_CHECK FUNCTION # ########################## version_check ( ) { if [ "x$1" = "x" ] ; then echo "INTERNAL ERROR: version_check was not provided a minimum version" exit 1 fi _min="$1" if [ "x$2" = "x" ] ; then echo "INTERNAL ERROR: version check was not provided a comparison version" exit 1 fi _cur="$2" # needed to handle versions like 1.10 and 1.4-p6 _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`" _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`" _min_major="`echo $_min | cut -d. -f1`" _min_minor="`echo $_min | cut -d. -f2`" _min_patch="`echo $_min | cut -d. -f3`" _cur_major="`echo $_cur | cut -d. -f1`" _cur_minor="`echo $_cur | cut -d. -f2`" _cur_patch="`echo $_cur | cut -d. -f3`" if [ "x$_min_major" = "x" ] ; then _min_major=0 fi if [ "x$_min_minor" = "x" ] ; then _min_minor=0 fi if [ "x$_min_patch" = "x" ] ; then _min_patch=0 fi if [ "x$_cur_minor" = "x" ] ; then _cur_major=0 fi if [ "x$_cur_minor" = "x" ] ; then _cur_minor=0 fi if [ "x$_cur_patch" = "x" ] ; then _cur_patch=0 fi $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}" if [ $_min_major -lt $_cur_major ] ; then return 0 elif [ $_min_major -eq $_cur_major ] ; then if [ $_min_minor -lt $_cur_minor ] ; then return 0 elif [ $_min_minor -eq $_cur_minor ] ; then if [ $_min_patch -lt $_cur_patch ] ; then return 0 elif [ $_min_patch -eq $_cur_patch ] ; then return 0 fi fi fi return 1 } ###################################### # LOCATE_CONFIGURE_TEMPLATE FUNCTION # ###################################### locate_configure_template ( ) { _pwd="`pwd`" if test -f "./configure.ac" ; then echo "./configure.ac" elif test -f "./configure.in" ; then echo "./configure.in" elif test -f "$_pwd/configure.ac" ; then echo "$_pwd/configure.ac" elif test -f "$_pwd/configure.in" ; then echo "$_pwd/configure.in" elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then echo "$PATH_TO_AUTOGEN/configure.ac" elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then echo "$PATH_TO_AUTOGEN/configure.in" fi } ################## # argument check # ################## ARGS="$*" PATH_TO_AUTOGEN="`dirname $0`" NAME_OF_AUTOGEN="`basename $0`" AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN" LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4" if [ "x$HELP" = "x" ] ; then HELP=no fi if [ "x$QUIET" = "x" ] ; then QUIET=no fi if [ "x$VERBOSE" = "x" ] ; then VERBOSE=no fi if [ "x$VERSION_ONLY" = "x" ] ; then VERSION_ONLY=no fi if [ "x$DOWNLOAD" = "x" ] ; then DOWNLOAD=no fi if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then AUTORECONF_OPTIONS="-i -f" fi if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then AUTOCONF_OPTIONS="-f" fi if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then AUTOMAKE_OPTIONS="-a -c -f" fi ALT_AUTOMAKE_OPTIONS="-a -c" if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then LIBTOOLIZE_OPTIONS="--automake -c -f" fi ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force" if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then ACLOCAL_OPTIONS="" fi if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then AUTOHEADER_OPTIONS="" fi if [ "x$CONFIG_GUESS_URL" = "x" ] ; then CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD" fi for arg in $ARGS ; do case "x$arg" in x--help) HELP=yes ;; x-[hH]) HELP=yes ;; x--quiet) QUIET=yes ;; x-[qQ]) QUIET=yes ;; x--verbose) VERBOSE=yes ;; x-[dD]) DOWNLOAD=yes ;; x--download) DOWNLOAD=yes ;; x-[vV]) VERBOSE=yes ;; x--version) VERSION_ONLY=yes ;; *) echo "Unknown option: $arg" echo usage exit 1 ;; esac done ##################### # environment check # ##################### # sanity check before recursions potentially begin if [ ! -f "$AUTOGEN_SH" ] ; then echo "INTERNAL ERROR: $AUTOGEN_SH does not exist" if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH" fi exit 1 fi # force locale setting to C so things like date output as expected LC_ALL=C # commands that this script expects for __cmd in echo head tail pwd ; do echo "test" | $__cmd > /dev/null 2>&1 if [ $? != 0 ] ; then echo "INTERNAL ERROR: '${__cmd}' command is required" exit 2 fi done echo "test" | grep "test" > /dev/null 2>&1 if test ! x$? = x0 ; then echo "INTERNAL ERROR: grep command is required" exit 1 fi echo "test" | sed "s/test/test/" > /dev/null 2>&1 if test ! x$? = x0 ; then echo "INTERNAL ERROR: sed command is required" exit 1 fi # determine the behavior of echo case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac # determine the behavior of head case "x`echo 'head' | head -n 1 2>&1`" in *xhead*) HEAD_N="n " ;; *) HEAD_N="" ;; esac # determine the behavior of tail case "x`echo 'tail' | tail -n 1 2>&1`" in *xtail*) TAIL_N="n " ;; *) TAIL_N="" ;; esac VERBOSE_ECHO=: ECHO=: if [ "x$QUIET" = "xyes" ] ; then if [ "x$VERBOSE" = "xyes" ] ; then echo "Verbose output quelled by quiet option. Further output disabled." fi else ECHO=echo if [ "x$VERBOSE" = "xyes" ] ; then echo "Verbose output enabled" VERBOSE_ECHO=echo fi fi # allow a recursive run to disable further recursions if [ "x$RUN_RECURSIVE" = "x" ] ; then RUN_RECURSIVE=yes fi ################################################ # check for help arg and bypass version checks # ################################################ if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then HELP=yes fi if [ "x$HELP" = "xyes" ] ; then usage $ECHO "---" $ECHO "Help was requested. No preparation or configuration will be performed." exit 0 fi ####################### # set up signal traps # ####################### untrap_abnormal ( ) { for sig in 1 2 13 15; do trap - $sig done } # do this cleanup whenever we exit. trap ' # start from the root if test -d "$START_PATH" ; then cd "$START_PATH" fi # restore/delete backup files if test "x$PFC_INIT" = "x1" ; then recursive_restore fi ' 0 # trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15) for sig in 1 2 13 15; do trap ' $ECHO "" $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'" # start from the root if test -d "$START_PATH" ; then cd "$START_PATH" fi # clean up on abnormal exit $VERBOSE_ECHO "rm -rf autom4te.cache" rm -rf autom4te.cache if test -f "acinclude.m4.$$.backup" ; then $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4" chmod u+w acinclude.m4 cat acinclude.m4.$$.backup > acinclude.m4 $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup" rm -f acinclude.m4.$$.backup fi { (exit 1); exit 1; } ' $sig done ############################# # look for a configure file # ############################# if [ "x$CONFIGURE" = "x" ] ; then CONFIGURE="`locate_configure_template`" if [ ! "x$CONFIGURE" = "x" ] ; then $VERBOSE_ECHO "Found a configure template: $CONFIGURE" fi else $ECHO "Using CONFIGURE environment variable override: $CONFIGURE" fi if [ "x$CONFIGURE" = "x" ] ; then if [ "x$VERSION_ONLY" = "xyes" ] ; then CONFIGURE=/dev/null else $ECHO $ECHO "A configure.ac or configure.in file could not be located implying" $ECHO "that the GNU Build System is at least not used in this directory. In" $ECHO "any case, there is nothing to do here without one of those files." $ECHO $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`" exit 1 fi fi #################### # get project name # #################### if [ "x$PROJECT" = "x" ] ; then PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" if [ "x$PROJECT" = "xAC_INIT" ] ; then # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" fi if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then PROJECT="project" fi if [ "x$PROJECT" = "x" ] ; then PROJECT="project" fi else $ECHO "Using PROJECT environment variable override: $PROJECT" fi $ECHO "Preparing the $PROJECT build system...please wait" $ECHO ######################## # check for autoreconf # ######################## HAVE_AUTORECONF=no if [ "x$AUTORECONF" = "x" ] ; then for AUTORECONF in autoreconf ; do $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version" $AUTORECONF --version > /dev/null 2>&1 if [ $? = 0 ] ; then HAVE_AUTORECONF=yes break fi done else HAVE_AUTORECONF=yes $ECHO "Using AUTORECONF environment variable override: $AUTORECONF" fi ########################## # autoconf version check # ########################## _acfound=no if [ "x$AUTOCONF" = "x" ] ; then for AUTOCONF in autoconf ; do $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version" $AUTOCONF --version > /dev/null 2>&1 if [ $? = 0 ] ; then _acfound=yes break fi done else _acfound=yes $ECHO "Using AUTOCONF environment variable override: $AUTOCONF" fi _report_error=no if [ ! "x$_acfound" = "xyes" ] ; then $ECHO "ERROR: Unable to locate GNU Autoconf." _report_error=yes else _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" if [ "x$_version" = "x" ] ; then _version="0.0.0" fi $ECHO "Found GNU Autoconf version $_version" version_check "$AUTOCONF_VERSION" "$_version" if [ $? -ne 0 ] ; then _report_error=yes fi fi if [ "x$_report_error" = "xyes" ] ; then version_error "$AUTOCONF_VERSION" "GNU Autoconf" exit 1 fi ########################## # automake version check # ########################## _amfound=no if [ "x$AUTOMAKE" = "x" ] ; then for AUTOMAKE in automake ; do $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version" $AUTOMAKE --version > /dev/null 2>&1 if [ $? = 0 ] ; then _amfound=yes break fi done else _amfound=yes $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE" fi _report_error=no if [ ! "x$_amfound" = "xyes" ] ; then $ECHO $ECHO "ERROR: Unable to locate GNU Automake." _report_error=yes else _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" if [ "x$_version" = "x" ] ; then _version="0.0.0" fi $ECHO "Found GNU Automake version $_version" version_check "$AUTOMAKE_VERSION" "$_version" if [ $? -ne 0 ] ; then _report_error=yes fi fi if [ "x$_report_error" = "xyes" ] ; then version_error "$AUTOMAKE_VERSION" "GNU Automake" exit 1 fi ######################## # check for libtoolize # ######################## HAVE_LIBTOOLIZE=yes HAVE_ALT_LIBTOOLIZE=no _ltfound=no if [ "x$LIBTOOLIZE" = "x" ] ; then LIBTOOLIZE=libtoolize $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version" $LIBTOOLIZE --version > /dev/null 2>&1 if [ ! $? = 0 ] ; then HAVE_LIBTOOLIZE=no $ECHO if [ "x$HAVE_AUTORECONF" = "xno" ] ; then $ECHO "Warning: libtoolize does not appear to be available." else $ECHO "Warning: libtoolize does not appear to be available. This means that" $ECHO "the automatic build preparation via autoreconf will probably not work." $ECHO "Preparing the build by running each step individually, however, should" $ECHO "work and will be done automatically for you if autoreconf fails." fi # look for some alternates for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version" _glibtoolize="`$tool --version > /dev/null 2>&1`" if [ $? = 0 ] ; then $VERBOSE_ECHO "Found $tool --version" _glti="`which $tool`" if [ "x$_glti" = "x" ] ; then $VERBOSE_ECHO "Cannot find $tool with which" continue; fi if test ! -f "$_glti" ; then $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file" continue; fi _gltidir="`dirname $_glti`" if [ "x$_gltidir" = "x" ] ; then $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti" continue; fi if test ! -d "$_gltidir" ; then $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory" continue; fi HAVE_ALT_LIBTOOLIZE=yes LIBTOOLIZE="$tool" $ECHO $ECHO "Fortunately, $tool was found which means that your system may simply" $ECHO "have a non-standard or incomplete GNU Autotools install. If you have" $ECHO "sufficient system access, it may be possible to quell this warning by" $ECHO "running:" $ECHO sudo -V > /dev/null 2>&1 if [ $? = 0 ] ; then $ECHO " sudo ln -s $_glti $_gltidir/libtoolize" $ECHO else $ECHO " ln -s $_glti $_gltidir/libtoolize" $ECHO $ECHO "Run that as root or with proper permissions to the $_gltidir directory" $ECHO fi _ltfound=yes break fi done else _ltfound=yes fi else _ltfound=yes $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE" fi ############################ # libtoolize version check # ############################ _report_error=no if [ ! "x$_ltfound" = "xyes" ] ; then $ECHO $ECHO "ERROR: Unable to locate GNU Libtool." _report_error=yes else _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`" if [ "x$_version" = "x" ] ; then _version="0.0.0" fi $ECHO "Found GNU Libtool version $_version" version_check "$LIBTOOL_VERSION" "$_version" if [ $? -ne 0 ] ; then _report_error=yes fi fi if [ "x$_report_error" = "xyes" ] ; then version_error "$LIBTOOL_VERSION" "GNU Libtool" exit 1 fi ##################### # check for aclocal # ##################### if [ "x$ACLOCAL" = "x" ] ; then for ACLOCAL in aclocal ; do $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version" $ACLOCAL --version > /dev/null 2>&1 if [ $? = 0 ] ; then break fi done else $ECHO "Using ACLOCAL environment variable override: $ACLOCAL" fi ######################## # check for autoheader # ######################## if [ "x$AUTOHEADER" = "x" ] ; then for AUTOHEADER in autoheader ; do $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version" $AUTOHEADER --version > /dev/null 2>&1 if [ $? = 0 ] ; then break fi done else $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER" fi ######################### # check if version only # ######################### $VERBOSE_ECHO "Checking whether to only output version information" if [ "x$VERSION_ONLY" = "xyes" ] ; then $ECHO ident $ECHO "---" $ECHO "Version requested. No preparation or configuration will be performed." exit 0 fi ################################# # PROTECT_FROM_CLOBBER FUNCTION # ################################# protect_from_clobber ( ) { PFC_INIT=1 # protect COPYING & INSTALL from overwrite by automake. the # automake force option will (inappropriately) ignore the existing # contents of a COPYING and/or INSTALL files (depending on the # version) instead of just forcing *missing* files like it does # for AUTHORS, NEWS, and README. this is broken but extremely # prevalent behavior, so we protect against it by keeping a backup # of the file that can later be restored. for file in COPYING INSTALL ; do if test -f ${file} ; then if test -f ${file}.$$.protect_from_automake.backup ; then $VERBOSE_ECHO "Already backed up ${file} in `pwd`" else $VERBOSE_ECHO "Backing up ${file} in `pwd`" $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup" cp -p ${file} ${file}.$$.protect_from_automake.backup fi fi done } ############################## # RECURSIVE_PROTECT FUNCTION # ############################## recursive_protect ( ) { # for projects using recursive configure, run the build # preparation steps for the subdirectories. this function assumes # START_PATH was set to pwd before recursion begins so that # relative paths work. # git 'r done, protect COPYING and INSTALL from being clobbered protect_from_clobber if test -d autom4te.cache ; then $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it" $VERBOSE_ECHO "rm -rf autom4te.cache" rm -rf autom4te.cache fi # find configure template _configure="`locate_configure_template`" if [ "x$_configure" = "x" ] ; then return fi # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure" # look for subdirs # $VERBOSE_ECHO "Looking for subdirs in `pwd`" _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" CHECK_DIRS="" for dir in $_det_config_subdirs ; do if test -d "`pwd`/$dir" ; then CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\"" fi done # process subdirs if [ ! "x$CHECK_DIRS" = "x" ] ; then $VERBOSE_ECHO "Recursively scanning the following directories:" $VERBOSE_ECHO " $CHECK_DIRS" for dir in $CHECK_DIRS ; do $VERBOSE_ECHO "Protecting files from automake in $dir" cd "$START_PATH" eval "cd $dir" # recursively git 'r done recursive_protect done fi } # end of recursive_protect ############################# # RESTORE_CLOBBERED FUNCION # ############################# restore_clobbered ( ) { # The automake (and autoreconf by extension) -f/--force-missing # option may overwrite COPYING and INSTALL even if they do exist. # Here we restore the files if necessary. spacer=no for file in COPYING INSTALL ; do if test -f ${file}.$$.protect_from_automake.backup ; then if test -f ${file} ; then # compare entire content, restore if needed if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then if test "x$spacer" = "xno" ; then $VERBOSE_ECHO spacer=yes fi # restore the backup $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)" $VERBOSE_ECHO "rm -f ${file}" rm -f ${file} $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}" mv ${file}.$$.protect_from_automake.backup ${file} fi # check contents elif test -f ${file}.$$.protect_from_automake.backup ; then $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}" mv ${file}.$$.protect_from_automake.backup ${file} fi # -f ${file} # just in case $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup" rm -f ${file}.$$.protect_from_automake.backup fi # -f ${file}.$$.protect_from_automake.backup done CONFIGURE="`locate_configure_template`" if [ "x$CONFIGURE" = "x" ] ; then return fi _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" if test ! -d "$_aux_dir" ; then _aux_dir=. fi for file in config.guess config.sub ltmain.sh ; do if test -f "${_aux_dir}/${file}" ; then $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\"" rm -f "${_aux_dir}/${file}.backup" fi done } # end of restore_clobbered ############################## # RECURSIVE_RESTORE FUNCTION # ############################## recursive_restore ( ) { # restore COPYING and INSTALL from backup if they were clobbered # for each directory recursively. # git 'r undone restore_clobbered # find configure template _configure="`locate_configure_template`" if [ "x$_configure" = "x" ] ; then return fi # look for subdirs _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" CHECK_DIRS="" for dir in $_det_config_subdirs ; do if test -d "`pwd`/$dir" ; then CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\"" fi done # process subdirs if [ ! "x$CHECK_DIRS" = "x" ] ; then $VERBOSE_ECHO "Recursively scanning the following directories:" $VERBOSE_ECHO " $CHECK_DIRS" for dir in $CHECK_DIRS ; do $VERBOSE_ECHO "Checking files for automake damage in $dir" cd "$START_PATH" eval "cd $dir" # recursively git 'r undone recursive_restore done fi } # end of recursive_restore ####################### # INITIALIZE FUNCTION # ####################### initialize ( ) { # this routine performs a variety of directory-specific # initializations. some are sanity checks, some are preventive, # and some are necessary setup detection. # # this function sets: # CONFIGURE # SEARCH_DIRS # CONFIG_SUBDIRS ################################## # check for a configure template # ################################## CONFIGURE="`locate_configure_template`" if [ "x$CONFIGURE" = "x" ] ; then $ECHO $ECHO "A configure.ac or configure.in file could not be located implying" $ECHO "that the GNU Build System is at least not used in this directory. In" $ECHO "any case, there is nothing to do here without one of those files." $ECHO $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`" exit 1 fi ##################### # detect an aux dir # ##################### _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" if test ! -d "$_aux_dir" ; then _aux_dir=. else $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir" fi ################################ # detect a recursive configure # ################################ CONFIG_SUBDIRS="" _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`" for dir in $_det_config_subdirs ; do if test -d "`pwd`/$dir" ; then $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir" CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir" fi done ########################################################### # make sure certain required files exist for GNU projects # ########################################################### _marker_found="" _marker_found_message_intro='Detected non-GNU marker "' _marker_found_message_mid='" in ' for marker in foreign cygnus ; do _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid} _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`" if [ ! "x$_marker_found" = "x" ] ; then $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`" break fi if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`" if [ ! "x$_marker_found" = "x" ] ; then $VERBOSE_ECHO "${_marker_found_message}Makefile.am" break fi fi done if [ "x${_marker_found}" = "x" ] ; then _suggest_foreign=no for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do if [ ! -f $file ] ; then $VERBOSE_ECHO "Touching ${file} since it does not exist" _suggest_foreign=yes touch $file fi done if [ "x${_suggest_foreign}" = "xyes" ] ; then $ECHO $ECHO "Warning: Several files expected of projects that conform to the GNU" $ECHO "coding standards were not found. The files were automatically added" $ECHO "for you since you do not have a 'foreign' declaration specified." $ECHO $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`" if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file." fi $ECHO fi fi ################################################## # make sure certain generated files do not exist # ################################################## for file in config.guess config.sub ltmain.sh ; do if test -f "${_aux_dir}/${file}" ; then $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\"" mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup" fi done ############################ # search alternate m4 dirs # ############################ SEARCH_DIRS="" for dir in m4 ; do if [ -d $dir ] ; then $VERBOSE_ECHO "Found extra aclocal search directory: $dir" SEARCH_DIRS="$SEARCH_DIRS -I $dir" fi done ###################################### # remove any previous build products # ###################################### if test -d autom4te.cache ; then $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it" $VERBOSE_ECHO "rm -rf autom4te.cache" rm -rf autom4te.cache fi # tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it # if test -f aclocal.m4 ; then # $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it" # $VERBOSE_ECHO "rm -f aclocal.m4" # rm -f aclocal.m4 # fi } # end of initialize() ############## # initialize # ############## # stash path START_PATH="`pwd`" # Before running autoreconf or manual steps, some prep detection work # is necessary or useful. Only needs to occur once per directory, but # does need to traverse the entire subconfigure hierarchy to protect # files from being clobbered even by autoreconf. recursive_protect # start from where we started cd "$START_PATH" # get ready to process initialize ######################################### # DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION # ######################################### # TODO - should make sure wget/curl exist and/or work before trying to # use them. download_gnulib_config_guess () { # abuse gitweb to download gnulib's latest config.guess via HTTP config_guess_temp="config.guess.$$.download" ret=1 for __cmd in wget curl fetch ; do $VERBOSE_ECHO "Checking for command ${__cmd}" ${__cmd} --version > /dev/null 2>&1 ret=$? if [ ! $ret = 0 ] ; then continue fi __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'` $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}" opts="" case ${__cmd} in wget) opts="-O" ;; curl) opts="-o" ;; fetch) opts="-t 5 -f" ;; esac $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1 if [ $? = 0 ] ; then mv -f "${config_guess_temp}" ${_aux_dir}/config.guess ret=0 break fi done if [ ! $ret = 0 ] ; then $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL" rm -f "${config_guess_temp}" fi } ############################## # LIBTOOLIZE_NEEDED FUNCTION # ############################## libtoolize_needed () { ret=1 # means no, don't need libtoolize for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do $VERBOSE_ECHO "Searching for $feature in $CONFIGURE" found="`grep \"^$feature.*\" $CONFIGURE`" if [ ! "x$found" = "x" ] ; then ret=0 # means yes, need to run libtoolize break fi done return ${ret} } ############################################ # prepare build via autoreconf or manually # ############################################ reconfigure_manually=no if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then $ECHO $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C" $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS" autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$autoreconf_output" if [ ! $ret = 0 ] ; then if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then $ECHO $ECHO "Warning: autoreconf failed but due to what is usually a common libtool" $ECHO "misconfiguration issue. This problem is encountered on systems that" $ECHO "have installed libtoolize under a different name without providing a" $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable." $ECHO $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE" export LIBTOOLIZE RUN_RECURSIVE=no export RUN_RECURSIVE untrap_abnormal $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" exit $? fi fi $ECHO "Warning: $AUTORECONF failed" if test -f ltmain.sh ; then $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should" fi $ECHO "Attempting to run the preparation steps individually" reconfigure_manually=yes else if [ "x$DOWNLOAD" = "xyes" ] ; then if libtoolize_needed ; then download_gnulib_config_guess fi fi fi else reconfigure_manually=yes fi ############################ # LIBTOOL_FAILURE FUNCTION # ############################ libtool_failure ( ) { # libtool is rather error-prone in comparison to the other # autotools and this routine attempts to compensate for some # common failures. the output after a libtoolize failure is # parsed for an error related to AC_PROG_LIBTOOL and if found, we # attempt to inject a project-provided libtool.m4 file. _autoconf_output="$1" if [ "x$RUN_RECURSIVE" = "xno" ] ; then # we already tried the libtool.m4, don't try again return 1 fi if test -f "$LIBTOOL_M4" ; then found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`" if test ! "x$found_libtool" = "x" ; then if test -f acinclude.m4 ; then rm -f acinclude.m4.$$.backup $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup" cat acinclude.m4 > acinclude.m4.$$.backup fi $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4" chmod u+w acinclude.m4 cat "$LIBTOOL_M4" >> acinclude.m4 # don't keep doing this RUN_RECURSIVE=no export RUN_RECURSIVE untrap_abnormal $ECHO $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4" $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" exit $? fi fi } ########################### # MANUAL_AUTOGEN FUNCTION # ########################### manual_autogen ( ) { ################################################## # Manual preparation steps taken are as follows: # # aclocal [-I m4] # # libtoolize --automake -c -f # # aclocal [-I m4] # # autoconf -f # # autoheader # # automake -a -c -f # ################################################## ########### # aclocal # ########### $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS" aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$aclocal_output" if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi ############## # libtoolize # ############## if libtoolize_needed ; then if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS" libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$libtoolize_output" if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi else if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS" libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$libtoolize_output" if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi fi fi ########### # aclocal # ########### # re-run again as instructed by libtoolize $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS" aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$aclocal_output" # libtoolize might put ltmain.sh in the wrong place if test -f ltmain.sh ; then if test ! -f "${_aux_dir}/ltmain.sh" ; then $ECHO $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory" $ECHO $ECHO "Fortunately, the problem can be worked around by simply copying the" $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you." $ECHO $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\"" cp -p ltmain.sh "${_aux_dir}/ltmain.sh" $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C" fi fi # ltmain.sh if [ "x$DOWNLOAD" = "xyes" ] ; then download_gnulib_config_guess fi fi # libtoolize_needed ############ # autoconf # ############ $VERBOSE_ECHO $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS" autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`" ret=$? $VERBOSE_ECHO "$autoconf_output" if [ ! $ret = 0 ] ; then # retry without the -f and check for usage of macros that are too new ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE" ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE" ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T" macros_to_search="" ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`" ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`" if [ $ac_major -lt 2 ] ; then macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros" else if [ $ac_minor -lt 54 ] ; then macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros" elif [ $ac_minor -lt 55 ] ; then macros_to_search="$ac2_59_macros $ac2_55_macros" elif [ $ac_minor -lt 59 ] ; then macros_to_search="$ac2_59_macros" fi fi configure_ac_macros=__none__ for feature in $macros_to_search ; do $VERBOSE_ECHO "Searching for $feature in $CONFIGURE" found="`grep \"^$feature.*\" $CONFIGURE`" if [ ! "x$found" = "x" ] ; then if [ "x$configure_ac_macros" = "x__none__" ] ; then configure_ac_macros="$feature" else configure_ac_macros="$feature $configure_ac_macros" fi fi done if [ ! "x$configure_ac_macros" = "x__none__" ] ; then $ECHO $ECHO "Warning: Unsupported macros were found in $CONFIGURE" $ECHO $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any" $ECHO "unsupported macros are used that exceed the minimum version" $ECHO "settings specified within this file. As such, the following macros" $ECHO "should be removed from configure.ac or the version numbers in this" $ECHO "file should be increased:" $ECHO $ECHO "$configure_ac_macros" $ECHO $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C" fi ################### # autoconf, retry # ################### $VERBOSE_ECHO $VERBOSE_ECHO "$AUTOCONF" autoconf_output="`$AUTOCONF 2>&1`" ret=$? $VERBOSE_ECHO "$autoconf_output" if [ ! $ret = 0 ] ; then # test if libtool is busted libtool_failure "$autoconf_output" # let the user know what went wrong cat < .br .br X11-GUITest-0.27/recorder/man/Makefile.am0000644000076400007650000000003311563632637020154 0ustar pecastropecastroman_MANS = x11guirecord.1 X11-GUITest-0.27/recorder/configure.ac0000644000076400007650000000117212104316203017614 0ustar pecastropecastro# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.66]) AC_INIT(x11guirecord, 0.27, ctrondlp@cpan.org) AM_INIT_AUTOMAKE(x11guirecord, 0.27) AC_CONFIG_SRCDIR([src/script_file.c]) AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CC # Checks for libraries. # Checks for header files. AC_PATH_X AC_CHECK_HEADERS([stdlib.h string.h sys/time.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CHECK_FUNCS([gettimeofday]) AC_OUTPUT(Makefile src/Makefile man/Makefile) X11-GUITest-0.27/recorder/src/0000755000076400007650000000000012104317102016113 5ustar pecastropecastroX11-GUITest-0.27/recorder/src/record_event.h0000644000076400007650000000221211563632137020760 0ustar pecastropecastro/* X11::GUITest ($Id: record_event.h 203 2011-05-15 02:03:11Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef RECORD_EVENT_H #define RECORD_EVENT_H typedef enum {NOTYPE, MOUSEBUTTON, KEY, MOUSEMOVE} EventType; typedef enum {NOSTATE, UP, DOWN } EventState; struct record_event { EventType type; EventState state; unsigned long delay; const char *dataname; int data; int posX; int posY; }; #endif /* #ifndef RECORD_EVENT_H */ X11-GUITest-0.27/recorder/src/main.h0000644000076400007650000000312411667407514017234 0ustar pecastropecastro/* X11::GUITest ($Id: main.h 215 2011-12-06 12:49:16Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef MAIN_H #define MAIN_H #define APP_NAME "x11guirecord" #define DEFAULT_WAIT_SECS 1 #define DEFAULT_DELAY_MS 50 #define MIN_DELAY_MS 0 #define MAX_DELAY_MS 1000 #define MAX_KEY_NAME 35 #define MAX_KEYDELAY_BEFOREFLUSH_MS 1000 #define MOUSE_DBLCLICK_THRESHOLD 300 #define MAX_MBUTTON_NAME 25 #define MAX_KEY_BUFFER 128 #define KEY_BUFFER_THRESHOLD 60 #define MIN_WAIT_SECONDS 1 #define MAX_WAIT_SECONDS 240 #define MIN_GRANULARITY 1 #define MAX_GRANULARITY 10 static void PrintAppInfo(void); static BOOL GetMouseButtonFromIndex(int index, char *button); static void HandleDelay(unsigned long delay); static void HandleKeyBuffer(BOOL forceKeyFlush); static void ProcessEvent(struct record_event ev); static BOOL IsMouseMoveTooGranular(struct record_event ev); #endif /* #ifndef MAIN_H */ X11-GUITest-0.27/recorder/src/script_file.h0000644000076400007650000000201711563632137020607 0ustar pecastropecastro/* X11::GUITest ($Id: script_file.h 203 2011-05-15 02:03:11Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef SCRIPT_FILE_H #define SCRIPT_FILE_H #define MAX_SCRIPT_BUFFER 2048 BOOL OpenScript(char *scriptFile); void WriteScript(char *format, ...); void CloseScript(void); #endif /* #ifndef SCRIPT_FILE_H */ X11-GUITest-0.27/recorder/src/script_file.c0000644000076400007650000000314411563632137020604 0ustar pecastropecastro/* X11::GUITest ($Id: script_file.c 203 2011-05-15 02:03:11Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include #include #include #include #include #include #include "Common.h" #include "script_file.h" static FILE *sfp = NULL; // script output BOOL OpenScript(char *scriptFile) { sfp = fopen(scriptFile, "wt"); if (sfp == NULL) { fprintf(stderr, _("Unable to open script file '%s'!\n"), scriptFile); return FALSE; } return TRUE; } void WriteScript(char *format, ...) { if (sfp == NULL) { fprintf(stderr, _("Unable to write to script file!\n")); return; } char buffer[MAX_SCRIPT_BUFFER] = "\0"; va_list args; va_start(args, format); vsprintf(buffer, format, args); fwrite(buffer, sizeof(char), strlen(buffer), sfp); va_end(args); } void CloseScript(void) { if (sfp != NULL) { fflush(sfp); fclose(sfp); } } X11-GUITest-0.27/recorder/src/record.c0000644000076400007650000001164412103005024017537 0ustar pecastropecastro/* X11::GUITest ($Id: record.c 221 2013-02-01 18:31:48Z pecastro $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "record_event.h" #include "record.h" #include "Common.h" static BOOL shouldExit = FALSE; static struct timeval lastTime = {0}; static struct timeval currentTime = {0}; static void (*HandleEvent)(struct record_event ev); static XRecordContext rcon; static Display *otherDisp = NULL; static Display *disp = NULL; int RecordEvents(void (*handleEvent)(struct record_event ev)) { XRecordClientSpec rcspec = {0}; XRecordRange *xrr = NULL; int major = 0, minor = 0; HandleEvent = handleEvent; // Register it signal(SIGINT, sigint_handler); SetLastTime(); disp = XOpenDisplay(NULL); if (disp == NULL) { fprintf(stderr, _("Unable to open display connection.\n")); return 1; } XSynchronize(disp, False); // Ensure extension available if (!XRecordQueryVersion(disp, &major, &minor)) { fprintf(stderr, _("The record extension is unavailable.\n")); return 1; } xrr = XRecordAllocRange(); if (xrr == NULL) { fprintf(stderr, _("Range allocation failed.\n")); return 1; } xrr->device_events.first = KeyPress; xrr->device_events.last = MotionNotify; rcspec = XRecordAllClients; rcon = XRecordCreateContext(disp, 0, &rcspec, 1, &xrr, 1); if (!rcon) { fprintf(stderr, _("Unable to create context.\n")); return 1; } otherDisp = XOpenDisplay(NULL); if (otherDisp == NULL) { fprintf(stderr, _("Unable to open other display connection.\n")); return 1; } // Clean out X events in progress XFlush(disp); XFlush(otherDisp); // Record... if (!XRecordEnableContext(otherDisp, rcon, EventCallback, (XPointer)disp)) { fprintf(stderr, _("Enable context failed\n")); return 1; } // ...until StopRecording() is called. XFree(xrr); return 0; } void StopRecording(void) { shouldExit = TRUE; XRecordDisableContext(disp, rcon); XRecordFreeContext(disp, rcon); //XCloseDisplay(otherDisp); // Note: N/A, blocks indefinitely XCloseDisplay(disp); } void SetLastTime(void) { if (gettimeofday(&lastTime, NULL) != 0) { fprintf(stderr, _("unable to get time\n")); } } void SetCurrentTime(void) { if (gettimeofday(¤tTime, NULL) != 0) { fprintf(stderr, _("unable to get time\n")); } } long GetDelay(void) { long secDiff = 0; long usecDiff = 0; long final = 0; SetCurrentTime(); /* Get delay between the previous event and this one */ secDiff = (currentTime.tv_sec - lastTime.tv_sec); usecDiff = ((currentTime.tv_usec - lastTime.tv_usec) / 1000); final = ((secDiff * 1000) + usecDiff); SetLastTime(); return final; } void EventCallback(XPointer p, XRecordInterceptData *idata) { if (shouldExit) { return; } if (XRecordFromServer == idata->category) { Display *disp = (Display *)p; xEvent *xev = (xEvent *)idata->data; int type = xev->u.u.type; int keyPress = 0; struct record_event re = {0}; re.delay = GetDelay(); re.type = NOTYPE; re.state = NOSTATE; re.dataname = NULL; re.data = 0; switch (type) { case ButtonPress: re.type = MOUSEBUTTON; re.state = DOWN; re.data = xev->u.u.detail; break; case ButtonRelease: re.type = MOUSEBUTTON; re.state = UP; re.data = xev->u.u.detail; break; case KeyPress: keyPress = 1; case KeyRelease: { //printf("key code: %d\n", xev->u.u.detail); KeyCode kc = xev->u.u.detail; KeySym ks = XkbKeycodeToKeysym(disp, kc, 0, 0); re.dataname = XKeysymToString(ks); re.data = ks; re.type = KEY; re.state = (keyPress == 1) ? DOWN : UP; } break; case MotionNotify: { re.type = MOUSEMOVE; re.posX = xev->u.keyButtonPointer.rootX; re.posY = xev->u.keyButtonPointer.rootY; } break; case EnterNotify: case LeaveNotify: default: break; } //// HandleEvent(re); //// } if (idata != NULL) { XRecordFreeData(idata); } } void sigint_handler(int sig) { StopRecording(); } X11-GUITest-0.27/recorder/src/main.c0000644000076400007650000001634111667407514017234 0ustar pecastropecastro/* X11::GUITest ($Id: main.c 215 2011-12-06 12:49:16Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include #include #include #include #include #include #include #include #include "record.h" #include "record_event.h" #include "KeyUtil.h" #include "script_file.h" #include "Common.h" #include "main.h" static char *scriptFile = NULL; static char *exitKey = "ESC"; static KeySym exitKeySym = 0; static BOOL excludeDelays = FALSE; static int waitSeconds = DEFAULT_WAIT_SECS; static int delayThresholdMs = DEFAULT_DELAY_MS; static int granularity = MAX_GRANULARITY; static struct record_event lastEvent = {0}; static char buttonName[MAX_MBUTTON_NAME] = "\0"; static char keyBuffer[MAX_KEY_BUFFER] = "\0"; int main (int argc, char *argv[]) { poptContext optCon = {0}; // International support setlocale(LC_MESSAGES, ""); bindtextdomain(APP_NAME, APP_TEXTDOMAIN); textdomain(APP_NAME); // Parse arguments struct poptOption optTbl[] = { {"script", 's', POPT_ARG_STRING, &scriptFile, 0, _("Script file to create"), NULL}, {"wait", 'w', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &waitSeconds, 0, _("Seconds to wait before recording"), NULL}, {"delaythreshold", 'd', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &delayThresholdMs, 0, _("Event delay (ms) threshold to account for / record"), NULL}, {"exitkey", 'e', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &exitKey, 0, _("Exit key to stop recording"), NULL}, {"nodelay", 'n', POPT_ARG_NONE, &excludeDelays, 0, _("Don't include user delays"), NULL}, {"granularity", 'g', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &granularity, 0, _("Level of granularity, mouse move frequency, 1-10"), NULL}, POPT_AUTOHELP POPT_TABLEEND }; optCon = poptGetContext(NULL, argc, (const char **)argv, optTbl, 0); if (argc <= 1) { PrintAppInfo(); poptPrintHelp(optCon, stderr, 0); exit(1); } while (poptGetNextOpt(optCon) >= 0) {} poptFreeContext(optCon); // Check arguments if (scriptFile == NULL || !*scriptFile) { fprintf(stderr, _("No script file specified.\n")); exit(1); } if (!GetKeySym(exitKey, &exitKeySym)) { fprintf(stderr, _("Invalid exit key defined.\n")); exit(1); } if (waitSeconds < MIN_WAIT_SECONDS || waitSeconds > MAX_WAIT_SECONDS) { fprintf(stderr, _("Invalid wait defined (supplied %d, but needs 1-%d).\n"), waitSeconds, MAX_WAIT_SECONDS); exit(1); } if (granularity < MIN_GRANULARITY || granularity > MAX_GRANULARITY) { fprintf(stderr, _("Invalid granularity defined (supplied %d, but needs %d-%d).\n"), granularity, MIN_GRANULARITY, MAX_GRANULARITY); exit(1); } if (delayThresholdMs < MIN_DELAY_MS || delayThresholdMs > MAX_DELAY_MS) { fprintf(stderr, _("Invalid delay theshold defined (supplied %d, but needs %d-%d).\n"), delayThresholdMs, MIN_DELAY_MS, MAX_DELAY_MS); exit(1); } if (!OpenScript(scriptFile)) { exit(1); } // Starting up... usleep(waitSeconds * 1000000); printf(_("Recording Started, press %s to exit.\n"), exitKey); WriteScript("#!/usr/bin/perl\n\n"); WriteScript("use X11::GUITest qw/:ALL/;\n"); WriteScript("use strict;\n"); WriteScript("use warnings;\n\n\n"); WriteScript(_("# Begin (Recorder Version %s).\n"), APP_VERSION); //// RecordEvents(ProcessEvent); //// WriteScript(_("\n\n# End.\n")); CloseScript(); printf(_("\nRecording Finished.\n")); exit(0); } static void PrintAppInfo(void) { printf("%s (%s: %s)\n\n", APP_NAME, _("Version"), APP_VERSION); } static BOOL GetMouseButtonFromIndex(int index, char *button) { if (button == NULL) { return FALSE; } *button = NUL; if (index == 1) { strcpy(button, "M_LEFT"); } else if (index == 2) { strcpy(button, "M_MIDDLE"); } else if (index == 3) { strcpy(button, "M_RIGHT"); } else if (index == 4) { strcpy(button, "M_UP"); } else if (index == 5) { strcpy(button, "M_DOWN"); } else { return FALSE; } return TRUE; } static void HandleDelay(unsigned long delay) { if (excludeDelays == FALSE) { if (delay > delayThresholdMs) { float secs = ((float)delay / 1000); // ms to secs WriteScript("WaitSeconds(%0.3f);\n", secs); } } } static void HandleKeyBuffer(BOOL forceKeyFlush) { int len = strlen(keyBuffer); if (forceKeyFlush || len >= KEY_BUFFER_THRESHOLD) { if (len > 0) { WriteScript("SendKeys('%s');\n", keyBuffer); *keyBuffer = '\0'; // clear } } } static void ProcessEvent(struct record_event ev) { if (ev.type == KEY) { // TODO: Granular delay between buffered key events BOOL forceKeyFlush = (ev.delay > MAX_KEYDELAY_BEFOREFLUSH_MS); HandleKeyBuffer(forceKeyFlush); HandleDelay(ev.delay); // Are we exiting? if (ev.data == exitKeySym) { HandleKeyBuffer(TRUE); StopRecording(); return; } const char *nam = GetKeyName(ev.data); if (nam != NULL) { const char *mod = GetModifierCode(ev.data); if (mod != NULL) { //// handle modifiers if (ev.state == DOWN) { strcat(keyBuffer, mod); strcat(keyBuffer, "("); } else { strcat(keyBuffer, ")"); } } else { //// handle other keys if (ev.state == UP) { //printf("Key: %s (%s)\n", nam, mod); if (strlen(nam) > 1) { // special key strcat(keyBuffer, "{"); strcat(keyBuffer, nam); strcat(keyBuffer, "}"); } else { if (nam[0] == '\'') { // escape this strcat(keyBuffer, "\\"); } strcat(keyBuffer, nam); } } } } else { WriteScript(_("# [Unhandled Key %d/%d]\n"), ev.data, ev.state); } } else { // Mouse, etc. HandleKeyBuffer(TRUE); // Flush out others events... HandleDelay(ev.delay); if (ev.type == MOUSEMOVE) { if (!IsMouseMoveTooGranular(ev)) { WriteScript("MoveMouseAbs(%d, %d);\n", ev.posX, ev.posY); } } else if (ev.type == MOUSEBUTTON) { GetMouseButtonFromIndex(ev.data, buttonName); if (!*buttonName) { WriteScript(_("# [Unhandled Mouse Button %d/%d]\n"), ev.data, ev.state); } else { // TODO: Simplify to 'ClickMouseButton' where possible... if (ev.state == UP) { WriteScript("ReleaseMouseButton(%s);\n", buttonName); } else { WriteScript("PressMouseButton(%s);\n", buttonName); } } } else { //printf("Unhandled event type: %d\n", ev.type); } } memcpy(&lastEvent, &ev, sizeof(struct record_event)); } static BOOL IsMouseMoveTooGranular(struct record_event ev) { if (lastEvent.type != MOUSEMOVE) { return(FALSE); // must be mousemove -> mousemove to count } else { // TODO: Adjust int threshold = (int)MAX_GRANULARITY / granularity - 1; if (ev.delay < threshold) { return(TRUE); } } return(FALSE); } X11-GUITest-0.27/recorder/src/record.h0000644000076400007650000000226211563632137017564 0ustar pecastropecastro/* X11::GUITest ($Id: record.h 203 2011-05-15 02:03:11Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef RECORD_H #define RECORD_H #include #include "record_event.h" int RecordEvents(void (*handleEvent)(struct record_event)); void StopRecording(void); void sigint_handler(int sig); void SetLastTime(void); void SetCurrentTime(void); void EventCallback(XPointer p, XRecordInterceptData *idata); long GetDelay(void); #endif /* #ifndef RECORD_H */ X11-GUITest-0.27/recorder/src/Makefile.am0000644000076400007650000000067011563632637020177 0ustar pecastropecastroDEBUG = -DNOPERL AM_CFLAGS = -Wall -Werror -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) -I/usr/include -I../ -I../../ -I/usr/X11R6/include -I/usr/X/include AM_LDFLAGS = -O2 -s -L/usr/X11R6/lib64 -L/usr/X/lib64 -L/usr/lib -L/usr/X11R6/lib -L/usr/X/lib -lXtst -lXext -lX11 -lpopt # -lefence bindir = $(prefix)/bin bin_PROGRAMS = x11guirecord x11guirecord_SOURCES = ../../KeyUtil.c record.c script_file.c main.c X11-GUITest-0.27/recorder/Makefile.am0000644000076400007650000000005611563632637017406 0ustar pecastropecastroAUTOMAKE_OPTIONS = foreign SUBDIRS = src man X11-GUITest-0.27/KeyUtil.c0000644000076400007650000001052412103004727015260 0ustar pecastropecastro/* X11::GUITest ($Id: KeyUtil.c 220 2013-02-01 18:30:47Z pecastro $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifdef __cplusplus extern "C" { #endif #ifndef NOPERL #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #else #include #endif #ifdef __cplusplus } #endif #include #include #include #include #include #include #include #include #include #include "KeyUtil.h" static const KeyNameSymTable kns_table[] = { /* {Name, Sym}, */ {"BAC", XK_BackSpace}, {"BS", XK_BackSpace}, {"BKS", XK_BackSpace}, {"BRE", XK_Break}, {"CAN", XK_Cancel}, {"CAP", XK_Caps_Lock}, {"DEL", XK_Delete}, {"DOWN", XK_Down}, {"END", XK_End}, {"ENT", XK_Return}, {"ESC", XK_Escape}, {"HEL", XK_Help}, {"HOM", XK_Home}, {"INS", XK_Insert}, {"LEF", XK_Left}, {"NUM", XK_Num_Lock}, {"PGD", XK_Next}, {"PGU", XK_Prior}, {"PRT", XK_Print}, {"RIG", XK_Right}, {"SCR", XK_Scroll_Lock}, {"TAB", XK_Tab}, {"UP", XK_Up}, {"F1", XK_F1}, {"F2", XK_F2}, {"F3", XK_F3}, {"F4", XK_F4}, {"F5", XK_F5}, {"F6", XK_F6}, {"F7", XK_F7}, {"F8", XK_F8}, {"F9", XK_F9}, {"F10", XK_F10}, {"F11", XK_F11}, {"F12", XK_F12}, {"SPC", XK_space}, {"SPA", XK_space}, {"LSK", XK_Super_L}, {"RSK", XK_Super_R}, {"MNU", XK_Menu}, {"~", XK_asciitilde}, {"_", XK_underscore}, {"[", XK_bracketleft}, {"]", XK_bracketright}, {"!", XK_exclam}, {"\"", XK_quotedbl}, {"#", XK_numbersign}, {"$", XK_dollar}, {"%", XK_percent}, {"&", XK_ampersand}, {"'", XK_quoteright}, {"*", XK_asterisk}, {"+", XK_plus}, {",", XK_comma}, {"-", XK_minus}, {".", XK_period}, {"?", XK_question}, {"<", XK_less}, {">", XK_greater}, {"=", XK_equal}, {"@", XK_at}, {":", XK_colon}, {";", XK_semicolon}, {"\\", XK_backslash}, {"`", XK_grave}, {"{", XK_braceleft}, {"}", XK_braceright}, {"|", XK_bar}, {"^", XK_asciicircum}, {"(", XK_parenleft}, {")", XK_parenright}, {" ", XK_space}, {"/", XK_slash}, {"\t", XK_Tab}, {"\n", XK_Return}, {"LSH", XK_Shift_L}, {"RSH", XK_Shift_R}, {"LCT", XK_Control_L}, {"RCT", XK_Control_R}, {"LAL", XK_Alt_L}, {"RAL", XK_Alt_R}, {"LMA", XK_Meta_L}, {"RMA", XK_Meta_R}, }; static const KeyNameSymTable kns_modcode_table[] = { /* {ModCodeName, Sym}, */ {"^", XK_Control_L}, {"%", XK_Alt_L}, {"+", XK_Shift_L}, {"#", XK_Meta_L}, {"^", XK_Control_R}, {"&",XK_ISO_Level3_Shift}, {"+", XK_Shift_R}, }; BOOL GetKeySym(const char *name, KeySym *sym) { size_t x = 0; assert(name != NULL); assert(sym != NULL); /* See if we can obtain the KeySym without looking at table. * Note: XStringToKeysym("space") would return KeySym * XK_space... Case sensitive. */ *sym = XStringToKeysym(name); if (*sym != NoSymbol) { /* Got It */ return(TRUE); } /* Do case insensitive search for specified name to obtain the KeySym from table */ for (x = 0; x < (sizeof(kns_table) / sizeof(KeyNameSymTable)); x++) { if (strcasecmp(kns_table[x].Name, name) == 0) { /* Found It */ *sym = kns_table[x].Sym; return(TRUE); } } /* Not Found */ *sym = NoSymbol; return(FALSE); } const char *GetKeyName(KeySym sym) { size_t x = 0; /* Look for KeySym in order to obtain name */ for (x = 0; x < (sizeof(kns_table) / sizeof(KeyNameSymTable)); x++) { if (sym == kns_table[x].Sym) { /* Found It */ return kns_table[x].Name; } } return XKeysymToString(sym); } const char *GetModifierCode(KeySym sym) { size_t x = 0; for (x = 0; x < (sizeof(kns_modcode_table) / sizeof(KeyNameSymTable)); x++) { if (sym == kns_modcode_table[x].Sym) { return kns_modcode_table[x].Name; } } return NULL; } X11-GUITest-0.27/eg/0000755000076400007650000000000012104317102014112 5ustar pecastropecastroX11-GUITest-0.27/eg/templates/0000755000076400007650000000000012104317102016110 5ustar pecastropecastroX11-GUITest-0.27/eg/templates/ScriptTemplate.pl0000644000076400007650000000121411563632137021423 0ustar pecastropecastro#!/usr/bin/perl #-----------------------------------------------------------------------------# # X11::GUITest ($Id: ScriptTemplate.pl 203 2011-05-15 02:03:11Z ctrondlp $) # Notes: #-----------------------------------------------------------------------------# ## Pragmas/Directives/Diagnostics ## use strict; use warnings; ## Imports (use [MODULE] qw/[IMPORTLIST]/;) ## use X11::GUITest qw/ /; ## Constants (sub [CONSTANT]() { [VALUE]; }) ## ## Variables (my [SIGIL][VARIABLE] = [INITIALVALUE];) ## ## Core ## print "$0 : Script Start\n"; print "This script serves as a template.\n"; print "$0 : Script End (Success)\n"; ## Subroutines ## X11-GUITest-0.27/eg/FindControlVisually.pl0000755000076400007650000000347211563632137020452 0ustar pecastropecastro#!/usr/bin/perl #-----------------------------------------------------------------------------# # X11::GUITest ($Id: FindControlVisually.pl 203 2011-05-15 02:03:11Z ctrondlp $) # Notes: Example of script to locate a widget/control visually using an # image baseline. #-----------------------------------------------------------------------------# ## Pragmas/Directives/Diagnostics ## use strict; use warnings; ## Imports (use [MODULE] qw/[IMPORTLIST]/;) ## use File::Temp qw/:POSIX/; use Image::SubImageFind qw/FindSubImage/; use X11::GUITest qw/ MoveMouseAbs ClickMouseButton :CONST /; ## Constants (sub [CONSTANT]() { [VALUE]; }) ## ## Variables (my [SIGIL][VARIABLE] = [INITIALVALUE];) ## ## Core ## print "$0 : Script Start\n"; print "Locating the control...\n"; # Find the control on-screen using baseline image to compare to my ($x, $y) = FindScreenObject('/test/but_superscript.png'); if ($x > 0 || $y > 0) { print "Found at $x X $y\n"; #MoveMouseAbs $x, $y; #ClickMouseButton M_LEFT; } else { print "Not found\n"; } print "$0 : Script End (Success)\n"; ## Subroutines ## sub FindScreenObject { my $baseline = shift; # baseline sub-image/clip to find on screen my $maxwait = shift || 30; # seconds to wait for discovery if (!-e $baseline) { die("Baseline $baseline image does not exist"); } my $scrfile = GetScreenshot(); for (my $i = 1; $i <= $maxwait; $i++) { # Results may vary, depending on baseline quality and detail, etc. # In general, a larger (50x50 pixels) baseline with good detail will fair good. my ($x, $y) = FindSubImage($scrfile, $baseline); if ($x > 0 || $y > 0) { unlink $scrfile; return ($x, $y); } sleep(1); } unlink $scrfile; return (-1,-1); } sub GetScreenshot { my $file = tmpnam(); my $cmd = `xwd -root | convert xwd:- $file`; return $file; } X11-GUITest-0.27/eg/TextEditor_1.pl0000755000076400007650000000441411563632137017010 0ustar pecastropecastro#!/usr/bin/perl #----------------------------------------------------------------------# # X11::GUITest ($Id: TextEditor_1.pl 203 2011-05-15 02:03:11Z ctrondlp $) # Notes: Example of interaction with gedit (Text Editor). Tested with # version 2.2.0 of the editor application using the English # language. #----------------------------------------------------------------------# ## Pragmas/Directives/Diagnostics ## use strict; use warnings; ## Imports (use [MODULE] qw/[IMPORTLIST]/;) ## use X11::GUITest qw/ StartApp WaitWindowClose WaitWindowViewable SendKeys ClickWindow GetWindowName /; ## Constants (sub [CONSTANT]() { [VALUE]; }) ## ## Variables (my [SIGIL][VARIABLE] = [INITIALVALUE];) ## my $GEMainWin = 0; my $GEAboutWin = 0; ## Core ## print "$0 : Script Start\n"; # Start the text editor StartApp('gedit'); # Wait for it to appear within 120 seconds. RegEx: .* = zero or more of any character. ( ($GEMainWin) = WaitWindowViewable('Un.*gedit', undef, 120) ) or die('Unable to find editor window!'); # Send some text to the editor (TEXT x NUM TIMES) SendKeys("Hello, how are you today?\n" x 2) or die('Unable to send text to editor!'); # Ensure the window changes its name to include the # 'modified' word since we sent it text above. (GetWindowName($GEMainWin) =~ /(modified|\*Unsaved)/i) or die('Editor did not switch its title as expected!'); # Using shortcuts (Alt-h, a), open about box and wait for it SendKeys('%(h)a'); ( ($GEAboutWin) = WaitWindowViewable('About gedit') ) or die('Unable to find about box!'); # Close about box (Alt-o (old) and Alt-c (new)) using shortcut for OK button. SendKeys('%(o)%(c)'); # To be safe, ensure about box is closed before we continue WaitWindowClose($GEAboutWin); # Now close the editor using menu short-cuts SendKeys('%(f)q'); # Wait for confirmation window to appear # New gedit may not have this text. May need to implement screen capture sooner then later. WaitWindowViewable('Question'); # or die('Unable to find confirmation (Question) window!'); # Select DoN't Save or Close without Saving (new) SendKeys('%(n)%(w)') or die('Unable to select Don\'t Save button!'); # Ensure main window gets closed WaitWindowClose($GEMainWin) or die('The editor window did not close!'); print "$0 : Script End (Success)\n"; ## Subroutines ## X11-GUITest-0.27/eg/WebBrowser_1.pl0000755000076400007650000000450711563751773017011 0ustar pecastropecastro#!/usr/bin/perl #----------------------------------------------------------------------# # X11::GUITest ($Id: WebBrowser_1.pl 206 2011-05-15 13:24:11Z ctrondlp $) # Notes: Basic interaction with Mozilla (Web Browser). Tested using # v1.2.1 of the application under the English language. #----------------------------------------------------------------------# ## Pragmas/Directives/Diagnostics ## use strict; use warnings; ## Imports (use [MODULE] qw/[IMPORTLIST]/;) ## use X11::GUITest qw/ StartApp FindWindowLike WaitWindowClose WaitWindowViewable SendKeys SetEventSendDelay /; ## Constants (sub [CONSTANT]() { [VALUE]; }) ## ## Variables (my [SIGIL][VARIABLE] = [INITIALVALUE];) ## my $MainWin = 0; my $AlertWin = 0; my $AboutWin = 0; ## Core ## print "$0 : Script Start\n"; # Slow event sending down a little for when X server # is busy with this and other bigger applications SetEventSendDelay(20); # Make sure Mozilla isn't already running # even though we could find a way around # other instances of it. if (FindWindowLike('Mozilla Firefox')) { die('Mozilla Firefox window is already open!'); } # Start the application StartApp('firefox'); # Wait at most 20 seconds for it to come up ($MainWin) = WaitWindowViewable('Mozilla Firefox', undef, 20) or die('Could not find browser window!'); # If an alert window presents itself within 5 seconds, close it if ( (($AlertWin) = WaitWindowViewable('Alert', undef, 5)) ) { SendKeys('{SPC}'); WaitWindowClose($AlertWin) or die('Could not close Alert window!'); } # Select web address bar and go to a website SendKeys('^(l)'); SendKeys("http://sourceforge.net/projects/x11guitest\n"); # Wait for website page to start coming up WaitWindowViewable('X11::GUITest') or die('Could not find website page!'); # Give page time to finish loading, so we can interact with the shortcut keys # again. Hopefully we can find a better way in the future rather then hard waits. sleep(15); # Open About Mozilla Window SendKeys('%(h)a'); # Alt-h, a ($AboutWin) = WaitWindowViewable('About.*Mozilla') or die('Could not find About window!'); # Now close it SendKeys('%(c)'); WaitWindowClose($AboutWin) or die('Could not close About window!'); # Close main Mozilla window SendKeys('^(w)'); WaitWindowClose($MainWin) or die('Could not close Mozilla window!'); print "$0 : Script End (Success)\n"; ## Subroutines ## X11-GUITest-0.27/eg/FindWindowLike.pl0000755000076400007650000000246411563632137017355 0ustar pecastropecastro#!/usr/bin/perl #-----------------------------------------------------------------------------# # X11::GUITest ($Id: FindWindowLike.pl 203 2011-05-15 02:03:11Z ctrondlp $) # Notes: Example usage of FindWindowLike #-----------------------------------------------------------------------------# ## Pragmas/Directives/Diagnostics ## use strict; use warnings; ## Imports (use [MODULE] qw/[IMPORTLIST]/;) ## use X11::GUITest qw/ FindWindowLike GetWindowName /; ## Constants (sub [CONSTANT]() { [VALUE]; }) ## ## Variables (my [SIGIL][VARIABLE] = [INITIALVALUE];) ## my @wins = (); ## Core ## print "$0 : Script Start\n"; # Get list of Ids for all windows # (RegExp: .* = zero or more characters, so it will pick up on nameless windows also) @wins = FindWindowLike('.*') or die("Didn't find any windows!"); # Alternatively, one may use the following syntax in a script if there is # interest in a specific window: # my ($win) = FindWindowLike('My Window'); print "FindWindowLike found " . @wins . " window(s).\n"; print "Press to display a list of these window(s)."; readline(*STDIN); # Output a little information for each window foreach my $win (@wins) { my $name = GetWindowName($win) || '[NO NAME]'; print "\t" . sprintf("0x%X", $win) . " ($name)\n"; } print "$0 : Script End (Success)\n"; ## Subroutines ## X11-GUITest-0.27/GUITest.pm0000644000076400007650000010001012104316714015341 0ustar pecastropecastro# X11::GUITest ($Id: GUITest.pm 228 2013-02-06 00:03:24Z pecastro $) # # Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. # Email: ctrondlp@cpan.org # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # =head1 NAME B - Provides GUI testing/interaction routines. Developed by Dennis K. Paulsen =head1 VERSION 0.27 Updates are made available at the following sites: http://sourceforge.net/projects/x11guitest http://www.cpan.org Please consult 'docs/Changes' for the list of changes between module revisions. =head1 DESCRIPTION This Perl package is intended to facilitate the testing of GUI applications by means of user emulation. It can be used to test/interact with GUI applications; which have been built upon the X library or toolkits (i.e., GTK+, Xt, Qt, Motif, etc.) that "wrap" the X library's functionality. A basic recorder (x11guirecord) is also available, and can be found in the source code respository. =head1 DEPENDENCIES An X server with the XTest extensions enabled. This seems to be the norm. If it is not enabled, it usually can be by modifying the X server configuration (i.e., XF86Config). The standard DISPLAY environment variable is utilized to determine the host, display, and screen to work with. By default it is usually set to ":0.0" for the localhost. However, by altering this variable one can interact with applications under a remote host's X server. To change this from a terminal window, one can utilize the following basic syntax: export DISPLAY=:. Please note that under most circumstances, xhost will need to be executed properly on the remote host as well. There is a known incompatibility between the XTest and Xinerama extensions, which causes the XTestFakeMotionEvent() function to misbehave. When the Xinerama (X server) extension is turned on, this (Perl) extension has been modified to allow one to invoke an alternative function. See Makefile.PL for details. =head1 INSTALLATION perl Makefile.PL make make test make install # If you'd like to install the recorder, use these steps: cd recorder ./autogen.sh ./configure make make install x11guirecord --help =head1 SYNOPSIS For additional examples, please look under the 'eg/' sub-directory from the installation folder. use X11::GUITest qw/ StartApp WaitWindowViewable SendKeys /; # Start gedit application StartApp('gedit'); # Wait for application window to come up and become viewable. my ($GEditWinId) = WaitWindowViewable('gedit'); if (!$GEditWinId) { die("Couldn't find gedit window in time!"); } # Send text to it SendKeys("Hello, how are you?\n"); # Close Application (Alt-f, q). SendKeys('%(f)q'); # Handle gedit's Question window if it comes up when closing. Wait # at most 5 seconds for it. if (WaitWindowViewable('Question', undef, 5)) { # DoN't Save (Alt-n) SendKeys('%(n)'); } =cut package X11::GUITest; use strict; use warnings; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); require Exporter; require DynaLoader; #require AutoLoader; @ISA = qw(Exporter DynaLoader); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. @EXPORT = qw( ); @EXPORT_OK = qw( ClickMouseButton ClickWindow DefaultScreen FindWindowLike GetChildWindows GetEventSendDelay GetInputFocus GetKeySendDelay GetMousePos GetParentWindow GetRootWindow GetScreenDepth GetScreenRes GetWindowFromPoint GetWindowName GetWindowPos IconifyWindow IsChild IsKeyPressed IsMouseButtonPressed IsWindow IsWindowCursor IsWindowViewable LowerWindow MoveMouseAbs MoveWindow PressKey PressMouseButton PressReleaseKey QSfSK QuoteStringForSendKeys RaiseWindow ReleaseKey ReleaseMouseButton ResizeWindow RunApp ScreenCount SendKeys SetEventSendDelay SetInputFocus SetKeySendDelay SetWindowName StartApp UnIconifyWindow WaitSeconds WaitWindowClose WaitWindowLike WaitWindowViewable ); # Tags (:ALL, etc.) %EXPORT_TAGS = ( 'ALL' => \@EXPORT_OK, 'CONST' => [qw(DEF_WAIT M_LEFT M_MIDDLE M_RIGHT M_UP M_DOWN M_BTN1 M_BTN2 M_BTN3 M_BTN4 M_BTN5 XC_X_CURSOR XC_ARROW XC_BASED_ARROW_DOWN XC_BASED_ARROW_UP XC_BOAT XC_BOGOSITY XC_BOTTOM_LEFT_CORNER XC_BOTTOM_RIGHT_CORNER XC_BOTTOM_SIDE XC_BOTTOM_TEE XC_BOX_SPIRAL XC_CENTER_PTR XC_CIRCLE XC_CLOCK XC_COFFEE_MUG XC_CROSS XC_CROSS_REVERSE XC_CROSSHAIR XC_DIAMOND_CROSS XC_DOT XC_DOTBOX XC_DOUBLE_ARROW XC_DRAFT_LARGE XC_DRAFT_SMALL XC_DRAPED_BOX XC_EXCHANGE XC_FLEUR XC_GOBBLER XC_GUMBY XC_HAND1 XC_HAND2 XC_HEART XC_ICON XC_IRON_CROSS XC_LEFT_PTR XC_LEFT_SIDE XC_LEFT_TEE XC_LEFTBUTTON XC_LL_ANGLE XC_LR_ANGLE XC_MAN XC_MIDDLEBUTTON XC_MOUSE XC_PENCIL XC_PIRATE XC_PLUS XC_QUESTION_ARROW XC_RIGHT_PTR XC_RIGHT_SIDE XC_RIGHT_TEE XC_RIGHTBUTTON XC_RTL_LOGO XC_SAILBOAT XC_SB_DOWN_ARROW XC_SB_H_DOUBLE_ARROW XC_SB_LEFT_ARROW XC_SB_RIGHT_ARROW XC_SB_UP_ARROW XC_SB_V_DOUBLE_ARROW XC_SHUTTLE XC_SIZING XC_SPIDER XC_SPRAYCAN XC_STAR XC_TARGET XC_TCROSS XC_TOP_LEFT_ARROW XC_TOP_LEFT_CORNER XC_TOP_RIGHT_CORNER XC_TOP_SIDE XC_TOP_TEE XC_TREK XC_UL_ANGLE XC_UMBRELLA XC_UR_ANGLE XC_WATCH XC_XTERM)], ); Exporter::export_ok_tags(keys %EXPORT_TAGS); $VERSION = '0.27'; # Module Constants sub DEF_WAIT() { 10; } # Mouse Buttons sub M_BTN1() { 1; } sub M_BTN2() { 2; } sub M_BTN3() { 3; } sub M_BTN4() { 4; } sub M_BTN5() { 5; } sub M_LEFT() { M_BTN1; } sub M_MIDDLE() { M_BTN2; } sub M_RIGHT() { M_BTN3; } sub M_UP() { M_BTN4; } sub M_DOWN() { M_BTN5; } # Cursors sub XC_X_CURSOR() { 0 }; sub XC_ARROW() { 2 }; sub XC_BASED_ARROW_DOWN() { 4 }; sub XC_BASED_ARROW_UP() { 6 }; sub XC_BOAT() { 8 }; sub XC_BOGOSITY() { 10 }; sub XC_BOTTOM_LEFT_CORNER() { 12 }; sub XC_BOTTOM_RIGHT_CORNER() { 14 }; sub XC_BOTTOM_SIDE() { 16 }; sub XC_BOTTOM_TEE() { 18 }; sub XC_BOX_SPIRAL() { 20 }; sub XC_CENTER_PTR() { 22 }; sub XC_CIRCLE() { 24 }; sub XC_CLOCK() { 26 }; sub XC_COFFEE_MUG() { 28 }; sub XC_CROSS() { 30 }; sub XC_CROSS_REVERSE() { 32 }; sub XC_CROSSHAIR() { 34 }; sub XC_DIAMOND_CROSS() { 36 }; sub XC_DOT() { 38 }; sub XC_DOTBOX() { 40 }; sub XC_DOUBLE_ARROW() { 42 }; sub XC_DRAFT_LARGE() { 44 }; sub XC_DRAFT_SMALL() { 46 }; sub XC_DRAPED_BOX() { 48 }; sub XC_EXCHANGE() { 50 }; sub XC_FLEUR() { 52 }; sub XC_GOBBLER() { 54 }; sub XC_GUMBY() { 56 }; sub XC_HAND1() { 58 }; sub XC_HAND2() { 60 }; sub XC_HEART() { 62 }; sub XC_ICON() { 64 }; sub XC_IRON_CROSS() { 66 }; sub XC_LEFT_PTR() { 68 }; sub XC_LEFT_SIDE() { 70 }; sub XC_LEFT_TEE() { 72 }; sub XC_LEFTBUTTON() { 74 }; sub XC_LL_ANGLE() { 76 }; sub XC_LR_ANGLE() { 78 }; sub XC_MAN() { 80 }; sub XC_MIDDLEBUTTON() { 82 }; sub XC_MOUSE() { 84 }; sub XC_PENCIL() { 86 }; sub XC_PIRATE() { 88 }; sub XC_PLUS() { 90 }; sub XC_QUESTION_ARROW() { 92 }; sub XC_RIGHT_PTR() { 94 }; sub XC_RIGHT_SIDE() { 96 }; sub XC_RIGHT_TEE() { 98 }; sub XC_RIGHTBUTTON() { 100 }; sub XC_RTL_LOGO() { 102 }; sub XC_SAILBOAT() { 104 }; sub XC_SB_DOWN_ARROW() { 106 }; sub XC_SB_H_DOUBLE_ARROW() { 108 }; sub XC_SB_LEFT_ARROW() { 110 }; sub XC_SB_RIGHT_ARROW() { 112 }; sub XC_SB_UP_ARROW() { 114 }; sub XC_SB_V_DOUBLE_ARROW() { 116 }; sub XC_SHUTTLE() { 118 }; sub XC_SIZING() { 120 }; sub XC_SPIDER() { 122 }; sub XC_SPRAYCAN() { 124 }; sub XC_STAR() { 126 }; sub XC_TARGET() { 128 }; sub XC_TCROSS() { 130 }; sub XC_TOP_LEFT_ARROW() { 132 }; sub XC_TOP_LEFT_CORNER() { 134 }; sub XC_TOP_RIGHT_CORNER() { 136 }; sub XC_TOP_SIDE() { 138 }; sub XC_TOP_TEE() { 140 }; sub XC_TREK() { 142 }; sub XC_UL_ANGLE() { 144 }; sub XC_UMBRELLA() { 146 }; sub XC_UR_ANGLE() { 148 }; sub XC_WATCH() { 150 }; sub XC_XTERM() { 152 }; # Module Variables bootstrap X11::GUITest $VERSION; =head1 FUNCTIONS Parameters enclosed within [] are optional. If there are multiple optional parameters available for a function and you would like to specify the last one, for example, you can utilize undef for those parameters you don't specify. REGEX in the documentation below denotes an item that is treated as a regular expression. For example, the regex "^OK$" would look for an exact match for the word OK. =over 8 =item FindWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] Finds the window Ids of the windows matching the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window. An array of window Ids is returned for the matches found. An empty array is returned if no matches were found. my @WindowIds = FindWindowLike('gedit'); # Only worry about first window found my ($WindowId) = FindWindowLike('gedit'); =back =cut my $FindWindowLikeAux = sub { my $titlerx = shift; my $start = shift; my $winname = ''; my @wins = (); # Match the starting window??? $winname = GetWindowName($start); if (defined $winname && $winname =~ /$titlerx/i) { push @wins, $start; } # Match a child window? foreach my $child (GetChildWindows($start)) { $winname = GetWindowName($child); if (defined $winname && $winname =~ /$titlerx/i) { push @wins, $child; } } return(@wins); }; sub FindWindowLike { my $titlerx = shift; my $start = shift; if (defined $start) { return &$FindWindowLikeAux($titlerx, $start); } else { my @wins = (); for (my $i = ScreenCount() - 1; $i >= 0 ; --$i) { push @wins, &$FindWindowLikeAux($titlerx, GetRootWindow($i)); } return(@wins); } } =over 8 =item WaitWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS] Waits for a window to come up that matches the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window. One can optionally specify an alternative wait amount in seconds. A window will keep being looked for that matches the specified title regex until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag. If a window is going to be manipulated by input, WaitWindowViewable is the more robust solution to utilize. An array of window Ids is returned for the matches found. An empty array is returned if no matches were found. my @WindowIds = WaitWindowLike('gedit'); # Only worry about first window found my ($WindowId) = WaitWindowLike('gedit'); WaitWindowLike('gedit') or die("gedit window not found!"); =back =cut sub WaitWindowLike { my $titlerx = shift; my $start = shift; my $wait = shift || DEF_WAIT; my @wins = (); # For each second we $wait, look for window title once. for (my $i = 0; $i < $wait; $i++) { my @wins = FindWindowLike($titlerx, $start); if (@wins) { return(@wins); } # Wait 1 sec in order not to bog down the system select(undef, undef, undef, 1); } # Nothing return(@wins); } =over 8 =item WaitWindowViewable TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS] Similar to WaitWindow, but only recognizes windows that are viewable. When GUI applications are started, their window isn't necessarily viewable yet, let alone available for input, so this function is very useful. Likewise, this function will only return an array of the matching window Ids for those windows that are viewable. An empty array is returned if no matches were found. =back =cut sub WaitWindowViewable { my $titlerx = shift; my $start = shift; my $wait = shift || DEF_WAIT; my @wins = (); # For each second we $wait, look for window title once. for (my $i = 0; $i < $wait; $i++) { # Find windows, but recognize only those that are viewable foreach my $win (FindWindowLike($titlerx, $start)) { if (IsWindowViewable($win)) { push @wins, $win; } } if (@wins) { return(@wins); } # Wait 1 sec in order not to bog down the system. select(undef, undef, undef, 1); } # Nothing return(@wins); } =over 8 =item WaitWindowClose WINDOWID [, MAXWAITINSECONDS] Waits for the specified window to close. One can optionally specify an alternative wait amount in seconds. The window will keep being checked to see if it has closed until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag. zero is returned if window is not gone, non-zero if it is gone. =back =cut sub WaitWindowClose { my $win = shift; my $wait = shift || DEF_WAIT; # For each second we $wait, check window Id # twice (2 lookups * 500ms = ~1 second). for (my $i = 0; $i < ($wait * 2); $i++) { if (not IsWindow($win)) { # Success, window isn't recognized return(1); } # Wait 500 ms in order not to bog down the system. If one # changes this, the ($wait * 2) above will want to be changed # in order to represent seconds correctly. select(undef, undef, undef, 0.50); } # Failure return(0); } =over 8 =item WaitSeconds SECONDS Pauses execution for the specified amount of seconds. WaitSeconds(0.5); # Wait 1/2 second WaitSeconds(3); # Wait 3 seconds =back =cut sub WaitSeconds { select(undef, undef, undef, shift); } =over 8 =item ClickWindow WINDOWID [, X Offset] [, Y Offset] [, Button] Clicks on the specified window with the mouse. Optionally one can specify the X offset and Y offset. By default, the top left corner of the window is clicked on, with these two parameters one can specify a different position to be clicked on. One can also specify an alternative button. The default button is M_LEFT, but M_MIDDLE and M_RIGHT may be specified too. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success =back =cut sub ClickWindow { my $win = shift; my $x_offset = shift || 0; my $y_offset = shift || 0; my $button = shift || M_LEFT; my ($x, $y, $scr); ($x, $y, undef, undef, undef, $scr) = GetWindowPos($win); if (!defined($x) or !defined($y)) { return(0); } if (!MoveMouseAbs($x + $x_offset, $y + $y_offset, $scr)) { return(0); } if (!ClickMouseButton($button)) { return(0); } return(1); } =over 8 =item GetWindowFromPoint X, Y [, SCREEN] Returns the window that is at the specified point. If no screen is given, it is taken from the value given when opening the X display. zero is returned if there are no matches (i.e., off screen). =back =cut sub GetWindowFromPoint { my $x = shift; my $y = shift; my $scr = shift; my $lastmatch = 0; if ( ! defined $scr) { $scr = DefaultScreen(); } # Note: Windows are returned in current stacking order, therefore # the last match should be the top-most window. foreach my $win ( GetChildWindows(GetRootWindow($scr)) ) { my ($w_x1, $w_y1, $w_w, $w_h, $w_b) = GetWindowPos($win); # Is window position invalid? if (!defined $w_x1) { next; } my $w_x2 = ($w_x1 + $w_w + ($w_b << 1)); my $w_y2 = ($w_y1 + $w_h + ($w_b << 1)); # Does window match our point? if ($x >= $w_x1 && $x < $w_x2 && $y >= $w_y1 && $y < $w_y2) { $lastmatch = $win; } } return($lastmatch); } =over 8 =item IsChild PARENTWINDOWID, WINDOWID Determines if the specified window is a child of the specified parent. zero is returned for false, non-zero for true. =back =cut sub IsChild { my $parent = shift; my $win = shift; foreach my $child ( GetChildWindows($parent) ) { if ($child == $win && $child != $parent) { return(1); } } return(0); } =over 8 =item QuoteStringForSendKeys STRING Quotes {} characters in the specified string that would be interpreted as having special meaning if sent to SendKeys directly. This function would be useful if you had a text file in which you wanted to use each line of the file as input to the SendKeys function, but didn't want any special interpretation of the characters in the file. Returns the quoted string, undef is returned on error. # Quote ~, %, etc. as {~}, {%}, etc for literal use in SendKeys. SendKeys( QuoteStringForSendKeys('Hello: ~%^(){}+#') ); SendKeys( QSfSK('#+#') ); =back =cut sub QuoteStringForSendKeys { my $str = shift; if (!defined($str)) { return(undef); } # Quote {} special characters (^, %, (, {, etc.) $str =~ s/(\^|\%|\+|\~|\(|\)|\{|\})/\{$1\}/gm; return($str); } sub QSfSK { return QuoteStringForSendKeys(shift); } =over 8 =item StartApp COMMANDLINE Uses the shell to execute a program. This function returns as soon as the program is called. Useful for starting GUI applications and then going on to work with them. zero is returned on failure, non-zero for success StartApp('gedit'); =back =cut sub StartApp { my @cmd = @_; my $pid = fork; if ($pid) { use POSIX qw(WNOHANG); sleep 1; waitpid($pid, WNOHANG) != $pid and kill(0, $pid) == 1 and return $pid; } elsif (defined $pid) { use POSIX qw(_exit); exec @cmd or _exit(1); } return; } =over 8 =item RunApp COMMANDLINE Uses the shell to execute a program until its completion. Return value will be application specific, however -1 is returned to indicate a failure in starting the program. RunApp('/work/myapp'); =back =cut sub RunApp { my $cmdline = shift; return( system($cmdline) ); } =over 8 =item ClickMouseButton BUTTON Clicks the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. =back =cut sub ClickMouseButton { my $button = shift; if (!PressMouseButton($button) || !ReleaseMouseButton($button)) { return(0); } return(1); } # Subroutine: INIT # Description: Used to initialize the underlying mechanisms # that this package utilizes. # Note: Perl idiom not to return values for this subroutine. sub INIT { if (!defined($ENV{'AUTOMATED_TESTING'}) || $ENV{'AUTOMATED_TESTING'} ne 1) { InitGUITest(); } } # Subroutine: END # Description: Used to deinitialize the underlying mechanisms # that this package utilizes. # Note: Perl idiom not to return values for this subroutine. sub END { DeInitGUITest(); } =over 8 =item DefaultScreen Returns the screen number specified in the X display value used to open the display. Leverages the Xlib macro of the same name. =back =cut =over 8 =item ScreenCount Returns the number of screens in the X display specified when opening it. Leverages the Xlib macro of the same name. =back =cut =over 8 =item SetEventSendDelay DELAYINMILLISECONDS Sets the milliseconds of delay between events being sent to the X display. It is usually not a good idea to set this to 0. Please note that this delay will also affect SendKeys. Returns the old delay amount in milliseconds. =back =cut =over 8 =item GetEventSendDelay Returns the current event sending delay amount in milliseconds. =back =cut =over 8 =item SetKeySendDelay DELAYINMILLISECONDS Sets the milliseconds of delay between keystrokes. Returns the old delay amount in milliseconds. =back =cut =over 8 =item GetKeySendDelay Returns the current keystroke sending delay amount in milliseconds. =back =cut =over 8 =item GetWindowName WINDOWID Returns the window name for the specified window Id. undef is returned if name could not be obtained. # Return the name of the window that has the input focus. my $WinName = GetWindowName(GetInputFocus()); =back =cut =over 8 =item SetWindowName WINDOWID, NAME Sets the window name for the specified window Id. zero is returned on failure, non-zero for success. =back =cut =over 8 =item GetRootWindow [SCREEN] Returns the Id of the root window of the screen. This is the top/root level window that all other windows are under. If no screen is given, it is taken from the value given when opening the X display. =back =cut =over 8 =item GetChildWindows WINDOWID Returns an array of the child windows for the specified window Id. If it detects that the window hierarchy is in transition, it will wait half a second and try again. =back =cut =over 8 =item MoveMouseAbs X, Y [, SCREEN] Moves the mouse cursor to the specified absolute position in the optionally given screen. If no screen is given, it is taken from the value given when opening the X display. Zero is returned on failure, non-zero for success. =back =cut =over 8 =item GetMousePos Returns an array containing the position and the screen (number) of the mouse cursor. my ($x, $y, $scr_num) = GetMousePos(); =back =cut =over 8 =item PressMouseButton BUTTON Presses the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. =back =cut =over 8 =item ReleaseMouseButton BUTTON Releases the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. =back =cut =over 8 =item SendKeys KEYS Sends keystrokes to the window that has the input focus. The keystrokes to send are those specified in KEYS parameter. Some characters have special meaning, they are: Modifier Keys: ^ CTRL % ALT + SHIFT # META & ALTGR Other Keys: ~ ENTER \n ENTER \t TAB ( and ) MODIFIER GROUPING { and } QUOTE / ESCAPE CHARACTERS Simply, one can send a text string like so: SendKeys('Hello, how are you today?'); Parenthesis allow a modifier to work on one or more characters. For example: SendKeys('%(f)q'); # Alt-f, then press q SendKeys('%(fa)^(m)'); # Alt-f, Alt-a, Ctrl-m SendKeys('+(abc)'); # Uppercase ABC using shift modifier SendKeys('^(+(l))'); # Ctrl-Shift-l SendKeys('+'); # Press shift Braces are used to quote special characters, for utilizing aliased key names, or for special functionality. Multiple characters can be specified in a brace by space delimiting the entries. Characters can be repeated using a number that is space delimited after the preceeding key. Quote Special Characters SendKeys('{{}'); # { SendKeys('{+}'); # + SendKeys('{#}'); # # You can also use QuoteStringForSendKeys (QSfSK) to perform quoting. Aliased Key Names SendKeys('{BAC}'); # Backspace SendKeys('{F1 F2 F3}'); # F1, F2, F3 SendKeys('{TAB 3}'); # Press TAB 3 times SendKeys('{SPC 3 a b c}'); # Space 3 times, a, b, c Special Functionality # Pause execution for 500 milliseconds SendKeys('{PAUSE 500}'); Combinations SendKeys('abc+(abc){TAB PAUSE 500}'); # a, b, c, A, B, C, Tab, Pause 500 SendKeys('+({a b c})'); # A, B, C The following abbreviated key names are currently recognized within a brace set. If you don't see the desired key, you can still use the unabbreviated name for the key. If you are unsure of this name, utilize the xev (X event view) tool, press the key you want and look at the tools output for the name of that key. Names that are in the list below can be utilized regardless of case. Ones that aren't in this list are going to be case sensitive and also not abbreviated. For example, using 'xev' you will find that the name of the backspace key is BackSpace, so you could use {BackSpace} in place of {bac} if you really wanted to. Name Action ------------------- BAC BackSpace BS BackSpace BKS BackSpace BRE Break CAN Cancel CAP Caps_Lock DEL Delete DOWN Down END End ENT Return ESC Escape F1 F1 ... ... F12 F12 HEL Help HOM Home INS Insert LAL Alt_L LMA Meta_L LCT Control_L LEF Left LSH Shift_L LSK Super_L MNU Menu NUM Num_Lock PGD Page_Down PGU Page_Up PRT Print RAL Alt_R RMA Meta_R RCT Control_R RIG Right RSH Shift_R RSK Super_R SCR Scroll_Lock SPA Space SPC Space TAB Tab UP Up zero is returned on failure, non-zero for success. For configurations (Xvfb) that don't support Alt_Left, Meta_Left is automatically used in its place. =back =cut =over 8 =item PressKey KEY Presses the specified key. One can utilize the abbreviated key names from the table listed above as outlined in the following example: # Alt-n PressKey('LAL'); # Left Alt PressKey('n'); ReleaseKey('n'); ReleaseKey('LAL'); # Uppercase a PressKey('LSH'); # Left Shift PressKey('a'); ReleaseKey('a'); ReleaseKey('LSH'); The ReleaseKey calls in the above example are there to set both key states back. zero is returned on failure, non-zero for success. =back =cut =over 8 =item ReleaseKey KEY Releases the specified key. Normally follows a PressKey call. One can utilize the abbreviated key names from the table listed above. ReleaseKey('n'); zero is returned on failure, non-zero for success. =back =cut =over 8 =item PressReleaseKey KEY Presses and releases the specified key. One can utilize the abbreviated key names from the table listed above. PressReleaseKey('n'); This function is affected by the key send delay. zero is returned on failure, non-zero for success. =back =cut =over 8 =item IsKeyPressed KEY Determines if the specified key is currently being pressed. You can specify such things as 'bac' or the unabbreviated form 'BackSpace' as covered in the SendKeys information. Brace forms such as '{bac}' are unsupported. A '{' is taken literally and letters are case sensitive. if (IsKeyPressed('esc')) { # Is Escape pressed? if (IsKeyPressed('a')) { # Is a pressed? if (IsKeyPressed('A')) { # Is A pressed? Returns non-zero for true, zero for false. =back =cut =over 8 =item IsMouseButtonPressed BUTTON Determines if the specified mouse button is currently being pressed. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. if (IsMouseButtonPressed(M_LEFT)) { # Is left button pressed? Returns non-zero for true, zero for false. =back =cut =over 8 =item IsWindow WINDOWID zero is returned if the specified window Id is not for something that can be recognized as a window. non-zero is returned if it looks like a window. =back =cut =over 8 =item IsWindowViewable WINDOWID zero is returned if the specified window Id is for a window that isn't viewable. non-zero is returned if the window is viewable. =back =cut =over 8 =item IsWindowCursor WINDOWID CURSOR Determines if the specified window has the specified cursor. zero is returned for false, non-zero for true. The following cursors are available through the :CONST export tag. Name ------------------- XC_NUM_GLYPHS XC_X_CURSOR XC_ARROW XC_BASED_ARROW_DOWN XC_BASED_ARROW_UP XC_BOAT XC_BOGOSITY XC_BOTTOM_LEFT_CORNER XC_BOTTOM_RIGHT_CORNER XC_BOTTOM_SIDE XC_BOTTOM_TEE XC_BOX_SPIRAL XC_CENTER_PTR XC_CIRCLE XC_CLOCK XC_COFFEE_MUG XC_CROSS XC_CROSS_REVERSE XC_CROSSHAIR XC_DIAMOND_CROSS XC_DOT XC_DOTBOX XC_DOUBLE_ARROW XC_DRAFT_LARGE XC_DRAFT_SMALL XC_DRAPED_BOX XC_EXCHANGE XC_FLEUR XC_GOBBLER XC_GUMBY XC_HAND1 XC_HAND2 XC_HEART XC_ICON XC_IRON_CROSS XC_LEFT_PTR XC_LEFT_SIDE XC_LEFT_TEE XC_LEFTBUTTON XC_LL_ANGLE XC_LR_ANGLE XC_MAN XC_MIDDLEBUTTON XC_MOUSE XC_PENCIL XC_PIRATE XC_PLUS XC_QUESTION_ARROW XC_RIGHT_PTR XC_RIGHT_SIDE XC_RIGHT_TEE XC_RIGHTBUTTON XC_RTL_LOGO XC_SAILBOAT XC_SB_DOWN_ARROW XC_SB_H_DOUBLE_ARROW XC_SB_LEFT_ARROW XC_SB_RIGHT_ARROW XC_SB_UP_ARROW XC_SB_V_DOUBLE_ARROW XC_SHUTTLE XC_SIZING XC_SPIDER XC_SPRAYCAN XC_STAR XC_TARGET XC_TCROSS XC_TOP_LEFT_ARROW XC_TOP_LEFT_CORNER XC_TOP_RIGHT_CORNER XC_TOP_SIDE XC_TOP_TEE XC_TREK XC_UL_ANGLE XC_UMBRELLA XC_UR_ANGLE XC_WATCH XC_XTERM =back =cut =over 8 =item MoveWindow WINDOWID, X, Y Moves the window to the specified location. zero is returned on failure, non-zero for success. =back =cut =over 8 =item ResizeWindow WINDOWID, WIDTH, HEIGHT Resizes the window to the specified size. zero is returned on failure, non-zero for success. =back =cut =over 8 =item IconifyWindow WINDOWID Minimizes (Iconifies) the specified window. zero is returned on failure, non-zero for success. =back =cut =over 8 =item UnIconifyWindow WINDOWID Unminimizes (UnIconifies) the specified window. zero is returned on failure, non-zero for success. =back =cut =over 8 =item RaiseWindow WINDOWID Raises the specified window to the top of the stack, so that no other windows cover it. zero is returned on failure, non-zero for success. =back =cut =over 8 =item LowerWindow WINDOWID Lowers the specified window to the bottom of the stack, so other existing windows will cover it. zero is returned on failure, non-zero for success. =back =cut =over 8 =item GetInputFocus Returns the window that currently has the input focus. =back =cut =over 8 =item SetInputFocus WINDOWID Sets the specified window to be the one that has the input focus. zero is returned on failure, non-zero for success. =back =cut =over 8 =item GetWindowPos WINDOWID Returns an array containing the position information for the specified window. It also returns size information (including border width) and the number of the screen where the window resides. my ($x, $y, $width, $height, $borderWidth, $screen) = GetWindowPos(GetRootWindow()); =back =cut =over 8 =item GetParentWindow WINDOWID Returns the parent of the specified window. zero is returned if parent couldn't be determined (i.e., root window). =back =cut =over 8 =item GetScreenDepth [SCREEN] Returns the color depth for the screen. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, -1 will be returned. Value is represented as bits, i.e. 16. my $depth = GetScreenDepth(); =back =cut =over 8 =item GetScreenRes [SCREEN] Returns the screen resolution. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, the returned list will be empty. my ($x, $y) = GetScreenRes(); =back =cut =head1 OTHER DOCUMENTATION =begin html Module Changes
Coding-Style Guidelines
ToDo List
Copy of the GPL License
=end html =begin text Available under the docs sub-directory. CodingStyle (Coding-Style Guidelines) Copying (Copy of the GPL License) =end text =begin man Not installed. =end man =head1 COPYRIGHT Copyright(c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. =head1 AUTHOR Dennis K. Paulsen (Des Moines, Iowa USA) =head1 CONTRIBUTORS Paulo E. Castro =head1 CREDITS Thanks to everyone; including those specifically mentioned below for patches, suggestions, etc.: Alexey Tourbin Richard Clamp Gustav Larsson Nelson D. Caro =cut # Autoload methods go after __END__, and are processed by the autosplit program. # Return success 1; __END__ X11-GUITest-0.27/README0000644000076400007650000006324612104317043014416 0ustar pecastropecastroNAME X11::GUITest - Provides GUI testing/interaction routines. Developed by Dennis K. Paulsen VERSION 0.27 Updates are made available at the following sites: http://sourceforge.net/projects/x11guitest http://www.cpan.org Please consult 'docs/Changes' for the list of changes between module revisions. DESCRIPTION This Perl package is intended to facilitate the testing of GUI applications by means of user emulation. It can be used to test/interact with GUI applications; which have been built upon the X library or toolkits (i.e., GTK+, Xt, Qt, Motif, etc.) that "wrap" the X library's functionality. A basic recorder (x11guirecord) is also available, and can be found in the source code respository. DEPENDENCIES An X server with the XTest extensions enabled. This seems to be the norm. If it is not enabled, it usually can be by modifying the X server configuration (i.e., XF86Config). The standard DISPLAY environment variable is utilized to determine the host, display, and screen to work with. By default it is usually set to ":0.0" for the localhost. However, by altering this variable one can interact with applications under a remote host's X server. To change this from a terminal window, one can utilize the following basic syntax: export DISPLAY=:. Please note that under most circumstances, xhost will need to be executed properly on the remote host as well. There is a known incompatibility between the XTest and Xinerama extensions, which causes the XTestFakeMotionEvent() function to misbehave. When the Xinerama (X server) extension is turned on, this (Perl) extension has been modified to allow one to invoke an alternative function. See Makefile.PL for details. INSTALLATION perl Makefile.PL make make test make install # If you'd like to install the recorder, use these steps: cd recorder ./autogen.sh ./configure make make install x11guirecord --help SYNOPSIS For additional examples, please look under the 'eg/' sub-directory from the installation folder. use X11::GUITest qw/ StartApp WaitWindowViewable SendKeys /; # Start gedit application StartApp('gedit'); # Wait for application window to come up and become viewable. my ($GEditWinId) = WaitWindowViewable('gedit'); if (!$GEditWinId) { die("Couldn't find gedit window in time!"); } # Send text to it SendKeys("Hello, how are you?\n"); # Close Application (Alt-f, q). SendKeys('%(f)q'); # Handle gedit's Question window if it comes up when closing. Wait # at most 5 seconds for it. if (WaitWindowViewable('Question', undef, 5)) { # DoN't Save (Alt-n) SendKeys('%(n)'); } FUNCTIONS Parameters enclosed within [] are optional. If there are multiple optional parameters available for a function and you would like to specify the last one, for example, you can utilize undef for those parameters you don't specify. REGEX in the documentation below denotes an item that is treated as a regular expression. For example, the regex "^OK$" would look for an exact match for the word OK. FindWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] Finds the window Ids of the windows matching the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window. An array of window Ids is returned for the matches found. An empty array is returned if no matches were found. my @WindowIds = FindWindowLike('gedit'); # Only worry about first window found my ($WindowId) = FindWindowLike('gedit'); WaitWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS] Waits for a window to come up that matches the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window. One can optionally specify an alternative wait amount in seconds. A window will keep being looked for that matches the specified title regex until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag. If a window is going to be manipulated by input, WaitWindowViewable is the more robust solution to utilize. An array of window Ids is returned for the matches found. An empty array is returned if no matches were found. my @WindowIds = WaitWindowLike('gedit'); # Only worry about first window found my ($WindowId) = WaitWindowLike('gedit'); WaitWindowLike('gedit') or die("gedit window not found!"); WaitWindowViewable TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS] Similar to WaitWindow, but only recognizes windows that are viewable. When GUI applications are started, their window isn't necessarily viewable yet, let alone available for input, so this function is very useful. Likewise, this function will only return an array of the matching window Ids for those windows that are viewable. An empty array is returned if no matches were found. WaitWindowClose WINDOWID [, MAXWAITINSECONDS] Waits for the specified window to close. One can optionally specify an alternative wait amount in seconds. The window will keep being checked to see if it has closed until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag. zero is returned if window is not gone, non-zero if it is gone. WaitSeconds SECONDS Pauses execution for the specified amount of seconds. WaitSeconds(0.5); # Wait 1/2 second WaitSeconds(3); # Wait 3 seconds ClickWindow WINDOWID [, X Offset] [, Y Offset] [, Button] Clicks on the specified window with the mouse. Optionally one can specify the X offset and Y offset. By default, the top left corner of the window is clicked on, with these two parameters one can specify a different position to be clicked on. One can also specify an alternative button. The default button is M_LEFT, but M_MIDDLE and M_RIGHT may be specified too. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success GetWindowFromPoint X, Y [, SCREEN] Returns the window that is at the specified point. If no screen is given, it is taken from the value given when opening the X display. zero is returned if there are no matches (i.e., off screen). IsChild PARENTWINDOWID, WINDOWID Determines if the specified window is a child of the specified parent. zero is returned for false, non-zero for true. QuoteStringForSendKeys STRING Quotes {} characters in the specified string that would be interpreted as having special meaning if sent to SendKeys directly. This function would be useful if you had a text file in which you wanted to use each line of the file as input to the SendKeys function, but didn't want any special interpretation of the characters in the file. Returns the quoted string, undef is returned on error. # Quote ~, %, etc. as {~}, {%}, etc for literal use in SendKeys. SendKeys( QuoteStringForSendKeys('Hello: ~%^(){}+#') ); SendKeys( QSfSK('#+#') ); StartApp COMMANDLINE Uses the shell to execute a program. This function returns as soon as the program is called. Useful for starting GUI applications and then going on to work with them. zero is returned on failure, non-zero for success StartApp('gedit'); RunApp COMMANDLINE Uses the shell to execute a program until its completion. Return value will be application specific, however -1 is returned to indicate a failure in starting the program. RunApp('/work/myapp'); ClickMouseButton BUTTON Clicks the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. DefaultScreen Returns the screen number specified in the X display value used to open the display. Leverages the Xlib macro of the same name. ScreenCount Returns the number of screens in the X display specified when opening it. Leverages the Xlib macro of the same name. SetEventSendDelay DELAYINMILLISECONDS Sets the milliseconds of delay between events being sent to the X display. It is usually not a good idea to set this to 0. Please note that this delay will also affect SendKeys. Returns the old delay amount in milliseconds. GetEventSendDelay Returns the current event sending delay amount in milliseconds. SetKeySendDelay DELAYINMILLISECONDS Sets the milliseconds of delay between keystrokes. Returns the old delay amount in milliseconds. GetKeySendDelay Returns the current keystroke sending delay amount in milliseconds. GetWindowName WINDOWID Returns the window name for the specified window Id. undef is returned if name could not be obtained. # Return the name of the window that has the input focus. my $WinName = GetWindowName(GetInputFocus()); SetWindowName WINDOWID, NAME Sets the window name for the specified window Id. zero is returned on failure, non-zero for success. GetRootWindow [SCREEN] Returns the Id of the root window of the screen. This is the top/root level window that all other windows are under. If no screen is given, it is taken from the value given when opening the X display. GetChildWindows WINDOWID Returns an array of the child windows for the specified window Id. If it detects that the window hierarchy is in transition, it will wait half a second and try again. MoveMouseAbs X, Y [, SCREEN] Moves the mouse cursor to the specified absolute position in the optionally given screen. If no screen is given, it is taken from the value given when opening the X display. Zero is returned on failure, non-zero for success. GetMousePos Returns an array containing the position and the screen (number) of the mouse cursor. my ($x, $y, $scr_num) = GetMousePos(); PressMouseButton BUTTON Presses the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. ReleaseMouseButton BUTTON Releases the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. SendKeys KEYS Sends keystrokes to the window that has the input focus. The keystrokes to send are those specified in KEYS parameter. Some characters have special meaning, they are: Modifier Keys: ^ CTRL % ALT + SHIFT # META & ALTGR Other Keys: ~ ENTER \n ENTER \t TAB ( and ) MODIFIER GROUPING { and } QUOTE / ESCAPE CHARACTERS Simply, one can send a text string like so: SendKeys('Hello, how are you today?'); Parenthesis allow a modifier to work on one or more characters. For example: SendKeys('%(f)q'); # Alt-f, then press q SendKeys('%(fa)^(m)'); # Alt-f, Alt-a, Ctrl-m SendKeys('+(abc)'); # Uppercase ABC using shift modifier SendKeys('^(+(l))'); # Ctrl-Shift-l SendKeys('+'); # Press shift Braces are used to quote special characters, for utilizing aliased key names, or for special functionality. Multiple characters can be specified in a brace by space delimiting the entries. Characters can be repeated using a number that is space delimited after the preceeding key. Quote Special Characters SendKeys('{{}'); # { SendKeys('{+}'); # + SendKeys('{#}'); # # You can also use QuoteStringForSendKeys (QSfSK) to perform quoting. Aliased Key Names SendKeys('{BAC}'); # Backspace SendKeys('{F1 F2 F3}'); # F1, F2, F3 SendKeys('{TAB 3}'); # Press TAB 3 times SendKeys('{SPC 3 a b c}'); # Space 3 times, a, b, c Special Functionality # Pause execution for 500 milliseconds SendKeys('{PAUSE 500}'); Combinations SendKeys('abc+(abc){TAB PAUSE 500}'); # a, b, c, A, B, C, Tab, Pause 500 SendKeys('+({a b c})'); # A, B, C The following abbreviated key names are currently recognized within a brace set. If you don't see the desired key, you can still use the unabbreviated name for the key. If you are unsure of this name, utilize the xev (X event view) tool, press the key you want and look at the tools output for the name of that key. Names that are in the list below can be utilized regardless of case. Ones that aren't in this list are going to be case sensitive and also not abbreviated. For example, using 'xev' you will find that the name of the backspace key is BackSpace, so you could use {BackSpace} in place of {bac} if you really wanted to. Name Action ------------------- BAC BackSpace BS BackSpace BKS BackSpace BRE Break CAN Cancel CAP Caps_Lock DEL Delete DOWN Down END End ENT Return ESC Escape F1 F1 ... ... F12 F12 HEL Help HOM Home INS Insert LAL Alt_L LMA Meta_L LCT Control_L LEF Left LSH Shift_L LSK Super_L MNU Menu NUM Num_Lock PGD Page_Down PGU Page_Up PRT Print RAL Alt_R RMA Meta_R RCT Control_R RIG Right RSH Shift_R RSK Super_R SCR Scroll_Lock SPA Space SPC Space TAB Tab UP Up zero is returned on failure, non-zero for success. For configurations (Xvfb) that don't support Alt_Left, Meta_Left is automatically used in its place. PressKey KEY Presses the specified key. One can utilize the abbreviated key names from the table listed above as outlined in the following example: # Alt-n PressKey('LAL'); # Left Alt PressKey('n'); ReleaseKey('n'); ReleaseKey('LAL'); # Uppercase a PressKey('LSH'); # Left Shift PressKey('a'); ReleaseKey('a'); ReleaseKey('LSH'); The ReleaseKey calls in the above example are there to set both key states back. zero is returned on failure, non-zero for success. ReleaseKey KEY Releases the specified key. Normally follows a PressKey call. One can utilize the abbreviated key names from the table listed above. ReleaseKey('n'); zero is returned on failure, non-zero for success. PressReleaseKey KEY Presses and releases the specified key. One can utilize the abbreviated key names from the table listed above. PressReleaseKey('n'); This function is affected by the key send delay. zero is returned on failure, non-zero for success. IsKeyPressed KEY Determines if the specified key is currently being pressed. You can specify such things as 'bac' or the unabbreviated form 'BackSpace' as covered in the SendKeys information. Brace forms such as '{bac}' are unsupported. A '{' is taken literally and letters are case sensitive. if (IsKeyPressed('esc')) { # Is Escape pressed? if (IsKeyPressed('a')) { # Is a pressed? if (IsKeyPressed('A')) { # Is A pressed? Returns non-zero for true, zero for false. IsMouseButtonPressed BUTTON Determines if the specified mouse button is currently being pressed. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. if (IsMouseButtonPressed(M_LEFT)) { # Is left button pressed? Returns non-zero for true, zero for false. IsWindow WINDOWID zero is returned if the specified window Id is not for something that can be recognized as a window. non-zero is returned if it looks like a window. IsWindowViewable WINDOWID zero is returned if the specified window Id is for a window that isn't viewable. non-zero is returned if the window is viewable. IsWindowCursor WINDOWID CURSOR Determines if the specified window has the specified cursor. zero is returned for false, non-zero for true. The following cursors are available through the :CONST export tag. Name ------------------- XC_NUM_GLYPHS XC_X_CURSOR XC_ARROW XC_BASED_ARROW_DOWN XC_BASED_ARROW_UP XC_BOAT XC_BOGOSITY XC_BOTTOM_LEFT_CORNER XC_BOTTOM_RIGHT_CORNER XC_BOTTOM_SIDE XC_BOTTOM_TEE XC_BOX_SPIRAL XC_CENTER_PTR XC_CIRCLE XC_CLOCK XC_COFFEE_MUG XC_CROSS XC_CROSS_REVERSE XC_CROSSHAIR XC_DIAMOND_CROSS XC_DOT XC_DOTBOX XC_DOUBLE_ARROW XC_DRAFT_LARGE XC_DRAFT_SMALL XC_DRAPED_BOX XC_EXCHANGE XC_FLEUR XC_GOBBLER XC_GUMBY XC_HAND1 XC_HAND2 XC_HEART XC_ICON XC_IRON_CROSS XC_LEFT_PTR XC_LEFT_SIDE XC_LEFT_TEE XC_LEFTBUTTON XC_LL_ANGLE XC_LR_ANGLE XC_MAN XC_MIDDLEBUTTON XC_MOUSE XC_PENCIL XC_PIRATE XC_PLUS XC_QUESTION_ARROW XC_RIGHT_PTR XC_RIGHT_SIDE XC_RIGHT_TEE XC_RIGHTBUTTON XC_RTL_LOGO XC_SAILBOAT XC_SB_DOWN_ARROW XC_SB_H_DOUBLE_ARROW XC_SB_LEFT_ARROW XC_SB_RIGHT_ARROW XC_SB_UP_ARROW XC_SB_V_DOUBLE_ARROW XC_SHUTTLE XC_SIZING XC_SPIDER XC_SPRAYCAN XC_STAR XC_TARGET XC_TCROSS XC_TOP_LEFT_ARROW XC_TOP_LEFT_CORNER XC_TOP_RIGHT_CORNER XC_TOP_SIDE XC_TOP_TEE XC_TREK XC_UL_ANGLE XC_UMBRELLA XC_UR_ANGLE XC_WATCH XC_XTERM MoveWindow WINDOWID, X, Y Moves the window to the specified location. zero is returned on failure, non-zero for success. ResizeWindow WINDOWID, WIDTH, HEIGHT Resizes the window to the specified size. zero is returned on failure, non-zero for success. IconifyWindow WINDOWID Minimizes (Iconifies) the specified window. zero is returned on failure, non-zero for success. UnIconifyWindow WINDOWID Unminimizes (UnIconifies) the specified window. zero is returned on failure, non-zero for success. RaiseWindow WINDOWID Raises the specified window to the top of the stack, so that no other windows cover it. zero is returned on failure, non-zero for success. LowerWindow WINDOWID Lowers the specified window to the bottom of the stack, so other existing windows will cover it. zero is returned on failure, non-zero for success. GetInputFocus Returns the window that currently has the input focus. SetInputFocus WINDOWID Sets the specified window to be the one that has the input focus. zero is returned on failure, non-zero for success. GetWindowPos WINDOWID Returns an array containing the position information for the specified window. It also returns size information (including border width) and the number of the screen where the window resides. my ($x, $y, $width, $height, $borderWidth, $screen) = GetWindowPos(GetRootWindow()); GetParentWindow WINDOWID Returns the parent of the specified window. zero is returned if parent couldn't be determined (i.e., root window). GetScreenDepth [SCREEN] Returns the color depth for the screen. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, -1 will be returned. Value is represented as bits, i.e. 16. my $depth = GetScreenDepth(); GetScreenRes [SCREEN] Returns the screen resolution. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, the returned list will be empty. my ($x, $y) = GetScreenRes(); OTHER DOCUMENTATION Available under the docs sub-directory. CodingStyle (Coding-Style Guidelines) Copying (Copy of the GPL License) COPYRIGHT Copyright(c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. AUTHOR Dennis K. Paulsen (Des Moines, Iowa USA) CONTRIBUTORS Paulo E. Castro CREDITS Thanks to everyone; including those specifically mentioned below for patches, suggestions, etc.: Alexey Tourbin Richard Clamp Gustav Larsson Nelson D. Caro X11-GUITest-0.27/KeyUtil.h0000644000076400007650000000216211563632137015277 0ustar pecastropecastro/* X11::GUITest ($Id: KeyUtil.h 203 2011-05-15 02:03:11Z ctrondlp $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef KEYUTIL_H #define KEYUTIL_H #include "Common.h" #include "GUITest.h" BOOL GetKeySym(const char *name, KeySym *sym); const char *GetKeyName(KeySym sym); const char *GetModifierCode(KeySym sym); typedef struct KeyNameSymTable { char *Name; KeySym Sym; } KeyNameSymTable; #endif /* #ifndef KEYUTIL_H */ X11-GUITest-0.27/ToDo0000644000076400007650000000144712104316141014317 0ustar pecastropecastroX11::GUITest ($Id: ToDo 226 2013-02-05 23:57:21Z pecastro $) This is a list of Todo items. -------------------------------------------------------------------- - Be able to obtain a widget's text. For example, if we could obtain the label text of a button, we could then locate its position and use ClickWindow to click on it rather then using ClickWindow with pre-known offsets or even SendKeys. I've looked at such things as XtWindowToWidget, XFetchName, XtGetValues. The EditRes protocol is not generic enough and requires the support of the client application. Could look at spying on the X server to find this information or use AT-SPI. - Ensure alternatively mapped mouse buttons are handled appropriately. -------------------------------------------------------------------- X11-GUITest-0.27/META.yml0000664000076400007650000000112512104317102014771 0ustar pecastropecastro--- abstract: 'Collection of functions for X11 GUI testing/interaction.' author: - 'Dennis K. Paulsen ' build_requires: ExtUtils::MakeMaker: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.120921' license: open_source meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: X11-GUITest no_index: directory: - t - inc requires: {} resources: repository: https://x11guitest.svn.sourceforge.net/svnroot/x11guitest version: 0.27 X11-GUITest-0.27/Makefile.PL0000644000076400007650000000457612104316076015516 0ustar pecastropecastro#!/usr/bin/perl # X11::GUITest ($Id: Makefile.PL 225 2013-02-05 23:56:46Z pecastro $) use strict; use warnings; use ExtUtils::MakeMaker; # Optional building of additional doc formats. if (defined($ARGV[0]) && $ARGV[0] eq 'docs') { BuildDocumentation(); } # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( 'NAME' => 'X11::GUITest', ($] ge '5.005') ? ( 'AUTHOR' => 'Dennis K. Paulsen ', 'ABSTRACT' => 'Collection of functions for X11 GUI testing/interaction.', ) : (), 'VERSION_FROM' => 'GUITest.pm', # Finds $VERSION ($] lt '5.008') ? ( 'INST_MAN3DIR' => './blib/man3', 'MAN3EXT' => '3pm' ) : (), 'MAN3PODS' => {'GUITest.pm' => '$(INST_MAN3DIR)/X11::GUITest.$(MAN3EXT)'}, 'LIBS' => GetLibs(), # e.g., '-lm' # To work around an incompatibility between the XTest and the Xinerama # (X server) extensions, use "-DX11_GUITEST_USING_XINERAMA". 'DEFINE' => '-DNDEBUG -DX11_GUITEST_ALT_L_FALLBACK_META_L', # e.g., '-DHAVE_SOMETHING' 'INC' => '-I/usr/X11R6/include -I/usr/X/include', # e.g., '-I/usr/include/other' 'CCFLAGS' => '-Wall', 'OBJECT' => 'GUITest$(OBJ_EXT) KeyUtil$(OBJ_EXT)', 'OPTIMIZE' => '-O2', 'META_MERGE' => { resources => { repository => 'https://x11guitest.svn.sourceforge.net/svnroot/x11guitest', }, }, 'LICENSE' => 'gpl', ); sub GetLibs { my $un = `uname -a 2>&1` || ""; if ($un =~ /x86_64|amd64/i && $un !~ /OpenBSD/) { # In case of x64, lets make sure we use x64 libs, system might have both. return ['-L/usr/X11R6/lib64 -L/usr/X/lib64 -lXtst -lXext -lX11']; } else { return ['-L/usr/X11R6/lib -L/usr/X/lib -lXtst -lXext -lX11']; } } # Subroutine: BuildDocumentation # Description: This function is implemented to generate the # documentation in HTML/plain-text formats. sub BuildDocumentation { my $podfile = 'GUITest.pm'; # Check POD if ( system("podchecker $podfile &>/dev/null") != 0 ) { print "POD validation failed! Documentation will not be written.\n"; return(0); } # Generate Text and HTML documents print "Writing documentation for X11::GUITest\n"; system("pod2text $podfile docs/X11-GUITest.txt"); system("pod2html --infile=$podfile --outfile=docs/X11-GUITest.html"); system("cp -f docs/X11-GUITest.txt README"); # Cleanup unlink ; unlink ; return(1); } X11-GUITest-0.27/docs/0000755000076400007650000000000012104317102014447 5ustar pecastropecastroX11-GUITest-0.27/docs/Copying0000644000076400007650000004311011563632637016026 0ustar pecastropecastro GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. X11-GUITest-0.27/docs/X11-GUITest.txt0000644000076400007650000006324612104317043017062 0ustar pecastropecastroNAME X11::GUITest - Provides GUI testing/interaction routines. Developed by Dennis K. Paulsen VERSION 0.27 Updates are made available at the following sites: http://sourceforge.net/projects/x11guitest http://www.cpan.org Please consult 'docs/Changes' for the list of changes between module revisions. DESCRIPTION This Perl package is intended to facilitate the testing of GUI applications by means of user emulation. It can be used to test/interact with GUI applications; which have been built upon the X library or toolkits (i.e., GTK+, Xt, Qt, Motif, etc.) that "wrap" the X library's functionality. A basic recorder (x11guirecord) is also available, and can be found in the source code respository. DEPENDENCIES An X server with the XTest extensions enabled. This seems to be the norm. If it is not enabled, it usually can be by modifying the X server configuration (i.e., XF86Config). The standard DISPLAY environment variable is utilized to determine the host, display, and screen to work with. By default it is usually set to ":0.0" for the localhost. However, by altering this variable one can interact with applications under a remote host's X server. To change this from a terminal window, one can utilize the following basic syntax: export DISPLAY=:. Please note that under most circumstances, xhost will need to be executed properly on the remote host as well. There is a known incompatibility between the XTest and Xinerama extensions, which causes the XTestFakeMotionEvent() function to misbehave. When the Xinerama (X server) extension is turned on, this (Perl) extension has been modified to allow one to invoke an alternative function. See Makefile.PL for details. INSTALLATION perl Makefile.PL make make test make install # If you'd like to install the recorder, use these steps: cd recorder ./autogen.sh ./configure make make install x11guirecord --help SYNOPSIS For additional examples, please look under the 'eg/' sub-directory from the installation folder. use X11::GUITest qw/ StartApp WaitWindowViewable SendKeys /; # Start gedit application StartApp('gedit'); # Wait for application window to come up and become viewable. my ($GEditWinId) = WaitWindowViewable('gedit'); if (!$GEditWinId) { die("Couldn't find gedit window in time!"); } # Send text to it SendKeys("Hello, how are you?\n"); # Close Application (Alt-f, q). SendKeys('%(f)q'); # Handle gedit's Question window if it comes up when closing. Wait # at most 5 seconds for it. if (WaitWindowViewable('Question', undef, 5)) { # DoN't Save (Alt-n) SendKeys('%(n)'); } FUNCTIONS Parameters enclosed within [] are optional. If there are multiple optional parameters available for a function and you would like to specify the last one, for example, you can utilize undef for those parameters you don't specify. REGEX in the documentation below denotes an item that is treated as a regular expression. For example, the regex "^OK$" would look for an exact match for the word OK. FindWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] Finds the window Ids of the windows matching the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window. An array of window Ids is returned for the matches found. An empty array is returned if no matches were found. my @WindowIds = FindWindowLike('gedit'); # Only worry about first window found my ($WindowId) = FindWindowLike('gedit'); WaitWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS] Waits for a window to come up that matches the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window. One can optionally specify an alternative wait amount in seconds. A window will keep being looked for that matches the specified title regex until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag. If a window is going to be manipulated by input, WaitWindowViewable is the more robust solution to utilize. An array of window Ids is returned for the matches found. An empty array is returned if no matches were found. my @WindowIds = WaitWindowLike('gedit'); # Only worry about first window found my ($WindowId) = WaitWindowLike('gedit'); WaitWindowLike('gedit') or die("gedit window not found!"); WaitWindowViewable TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS] Similar to WaitWindow, but only recognizes windows that are viewable. When GUI applications are started, their window isn't necessarily viewable yet, let alone available for input, so this function is very useful. Likewise, this function will only return an array of the matching window Ids for those windows that are viewable. An empty array is returned if no matches were found. WaitWindowClose WINDOWID [, MAXWAITINSECONDS] Waits for the specified window to close. One can optionally specify an alternative wait amount in seconds. The window will keep being checked to see if it has closed until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag. zero is returned if window is not gone, non-zero if it is gone. WaitSeconds SECONDS Pauses execution for the specified amount of seconds. WaitSeconds(0.5); # Wait 1/2 second WaitSeconds(3); # Wait 3 seconds ClickWindow WINDOWID [, X Offset] [, Y Offset] [, Button] Clicks on the specified window with the mouse. Optionally one can specify the X offset and Y offset. By default, the top left corner of the window is clicked on, with these two parameters one can specify a different position to be clicked on. One can also specify an alternative button. The default button is M_LEFT, but M_MIDDLE and M_RIGHT may be specified too. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success GetWindowFromPoint X, Y [, SCREEN] Returns the window that is at the specified point. If no screen is given, it is taken from the value given when opening the X display. zero is returned if there are no matches (i.e., off screen). IsChild PARENTWINDOWID, WINDOWID Determines if the specified window is a child of the specified parent. zero is returned for false, non-zero for true. QuoteStringForSendKeys STRING Quotes {} characters in the specified string that would be interpreted as having special meaning if sent to SendKeys directly. This function would be useful if you had a text file in which you wanted to use each line of the file as input to the SendKeys function, but didn't want any special interpretation of the characters in the file. Returns the quoted string, undef is returned on error. # Quote ~, %, etc. as {~}, {%}, etc for literal use in SendKeys. SendKeys( QuoteStringForSendKeys('Hello: ~%^(){}+#') ); SendKeys( QSfSK('#+#') ); StartApp COMMANDLINE Uses the shell to execute a program. This function returns as soon as the program is called. Useful for starting GUI applications and then going on to work with them. zero is returned on failure, non-zero for success StartApp('gedit'); RunApp COMMANDLINE Uses the shell to execute a program until its completion. Return value will be application specific, however -1 is returned to indicate a failure in starting the program. RunApp('/work/myapp'); ClickMouseButton BUTTON Clicks the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. DefaultScreen Returns the screen number specified in the X display value used to open the display. Leverages the Xlib macro of the same name. ScreenCount Returns the number of screens in the X display specified when opening it. Leverages the Xlib macro of the same name. SetEventSendDelay DELAYINMILLISECONDS Sets the milliseconds of delay between events being sent to the X display. It is usually not a good idea to set this to 0. Please note that this delay will also affect SendKeys. Returns the old delay amount in milliseconds. GetEventSendDelay Returns the current event sending delay amount in milliseconds. SetKeySendDelay DELAYINMILLISECONDS Sets the milliseconds of delay between keystrokes. Returns the old delay amount in milliseconds. GetKeySendDelay Returns the current keystroke sending delay amount in milliseconds. GetWindowName WINDOWID Returns the window name for the specified window Id. undef is returned if name could not be obtained. # Return the name of the window that has the input focus. my $WinName = GetWindowName(GetInputFocus()); SetWindowName WINDOWID, NAME Sets the window name for the specified window Id. zero is returned on failure, non-zero for success. GetRootWindow [SCREEN] Returns the Id of the root window of the screen. This is the top/root level window that all other windows are under. If no screen is given, it is taken from the value given when opening the X display. GetChildWindows WINDOWID Returns an array of the child windows for the specified window Id. If it detects that the window hierarchy is in transition, it will wait half a second and try again. MoveMouseAbs X, Y [, SCREEN] Moves the mouse cursor to the specified absolute position in the optionally given screen. If no screen is given, it is taken from the value given when opening the X display. Zero is returned on failure, non-zero for success. GetMousePos Returns an array containing the position and the screen (number) of the mouse cursor. my ($x, $y, $scr_num) = GetMousePos(); PressMouseButton BUTTON Presses the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. ReleaseMouseButton BUTTON Releases the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. zero is returned on failure, non-zero for success. SendKeys KEYS Sends keystrokes to the window that has the input focus. The keystrokes to send are those specified in KEYS parameter. Some characters have special meaning, they are: Modifier Keys: ^ CTRL % ALT + SHIFT # META & ALTGR Other Keys: ~ ENTER \n ENTER \t TAB ( and ) MODIFIER GROUPING { and } QUOTE / ESCAPE CHARACTERS Simply, one can send a text string like so: SendKeys('Hello, how are you today?'); Parenthesis allow a modifier to work on one or more characters. For example: SendKeys('%(f)q'); # Alt-f, then press q SendKeys('%(fa)^(m)'); # Alt-f, Alt-a, Ctrl-m SendKeys('+(abc)'); # Uppercase ABC using shift modifier SendKeys('^(+(l))'); # Ctrl-Shift-l SendKeys('+'); # Press shift Braces are used to quote special characters, for utilizing aliased key names, or for special functionality. Multiple characters can be specified in a brace by space delimiting the entries. Characters can be repeated using a number that is space delimited after the preceeding key. Quote Special Characters SendKeys('{{}'); # { SendKeys('{+}'); # + SendKeys('{#}'); # # You can also use QuoteStringForSendKeys (QSfSK) to perform quoting. Aliased Key Names SendKeys('{BAC}'); # Backspace SendKeys('{F1 F2 F3}'); # F1, F2, F3 SendKeys('{TAB 3}'); # Press TAB 3 times SendKeys('{SPC 3 a b c}'); # Space 3 times, a, b, c Special Functionality # Pause execution for 500 milliseconds SendKeys('{PAUSE 500}'); Combinations SendKeys('abc+(abc){TAB PAUSE 500}'); # a, b, c, A, B, C, Tab, Pause 500 SendKeys('+({a b c})'); # A, B, C The following abbreviated key names are currently recognized within a brace set. If you don't see the desired key, you can still use the unabbreviated name for the key. If you are unsure of this name, utilize the xev (X event view) tool, press the key you want and look at the tools output for the name of that key. Names that are in the list below can be utilized regardless of case. Ones that aren't in this list are going to be case sensitive and also not abbreviated. For example, using 'xev' you will find that the name of the backspace key is BackSpace, so you could use {BackSpace} in place of {bac} if you really wanted to. Name Action ------------------- BAC BackSpace BS BackSpace BKS BackSpace BRE Break CAN Cancel CAP Caps_Lock DEL Delete DOWN Down END End ENT Return ESC Escape F1 F1 ... ... F12 F12 HEL Help HOM Home INS Insert LAL Alt_L LMA Meta_L LCT Control_L LEF Left LSH Shift_L LSK Super_L MNU Menu NUM Num_Lock PGD Page_Down PGU Page_Up PRT Print RAL Alt_R RMA Meta_R RCT Control_R RIG Right RSH Shift_R RSK Super_R SCR Scroll_Lock SPA Space SPC Space TAB Tab UP Up zero is returned on failure, non-zero for success. For configurations (Xvfb) that don't support Alt_Left, Meta_Left is automatically used in its place. PressKey KEY Presses the specified key. One can utilize the abbreviated key names from the table listed above as outlined in the following example: # Alt-n PressKey('LAL'); # Left Alt PressKey('n'); ReleaseKey('n'); ReleaseKey('LAL'); # Uppercase a PressKey('LSH'); # Left Shift PressKey('a'); ReleaseKey('a'); ReleaseKey('LSH'); The ReleaseKey calls in the above example are there to set both key states back. zero is returned on failure, non-zero for success. ReleaseKey KEY Releases the specified key. Normally follows a PressKey call. One can utilize the abbreviated key names from the table listed above. ReleaseKey('n'); zero is returned on failure, non-zero for success. PressReleaseKey KEY Presses and releases the specified key. One can utilize the abbreviated key names from the table listed above. PressReleaseKey('n'); This function is affected by the key send delay. zero is returned on failure, non-zero for success. IsKeyPressed KEY Determines if the specified key is currently being pressed. You can specify such things as 'bac' or the unabbreviated form 'BackSpace' as covered in the SendKeys information. Brace forms such as '{bac}' are unsupported. A '{' is taken literally and letters are case sensitive. if (IsKeyPressed('esc')) { # Is Escape pressed? if (IsKeyPressed('a')) { # Is a pressed? if (IsKeyPressed('A')) { # Is A pressed? Returns non-zero for true, zero for false. IsMouseButtonPressed BUTTON Determines if the specified mouse button is currently being pressed. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag. if (IsMouseButtonPressed(M_LEFT)) { # Is left button pressed? Returns non-zero for true, zero for false. IsWindow WINDOWID zero is returned if the specified window Id is not for something that can be recognized as a window. non-zero is returned if it looks like a window. IsWindowViewable WINDOWID zero is returned if the specified window Id is for a window that isn't viewable. non-zero is returned if the window is viewable. IsWindowCursor WINDOWID CURSOR Determines if the specified window has the specified cursor. zero is returned for false, non-zero for true. The following cursors are available through the :CONST export tag. Name ------------------- XC_NUM_GLYPHS XC_X_CURSOR XC_ARROW XC_BASED_ARROW_DOWN XC_BASED_ARROW_UP XC_BOAT XC_BOGOSITY XC_BOTTOM_LEFT_CORNER XC_BOTTOM_RIGHT_CORNER XC_BOTTOM_SIDE XC_BOTTOM_TEE XC_BOX_SPIRAL XC_CENTER_PTR XC_CIRCLE XC_CLOCK XC_COFFEE_MUG XC_CROSS XC_CROSS_REVERSE XC_CROSSHAIR XC_DIAMOND_CROSS XC_DOT XC_DOTBOX XC_DOUBLE_ARROW XC_DRAFT_LARGE XC_DRAFT_SMALL XC_DRAPED_BOX XC_EXCHANGE XC_FLEUR XC_GOBBLER XC_GUMBY XC_HAND1 XC_HAND2 XC_HEART XC_ICON XC_IRON_CROSS XC_LEFT_PTR XC_LEFT_SIDE XC_LEFT_TEE XC_LEFTBUTTON XC_LL_ANGLE XC_LR_ANGLE XC_MAN XC_MIDDLEBUTTON XC_MOUSE XC_PENCIL XC_PIRATE XC_PLUS XC_QUESTION_ARROW XC_RIGHT_PTR XC_RIGHT_SIDE XC_RIGHT_TEE XC_RIGHTBUTTON XC_RTL_LOGO XC_SAILBOAT XC_SB_DOWN_ARROW XC_SB_H_DOUBLE_ARROW XC_SB_LEFT_ARROW XC_SB_RIGHT_ARROW XC_SB_UP_ARROW XC_SB_V_DOUBLE_ARROW XC_SHUTTLE XC_SIZING XC_SPIDER XC_SPRAYCAN XC_STAR XC_TARGET XC_TCROSS XC_TOP_LEFT_ARROW XC_TOP_LEFT_CORNER XC_TOP_RIGHT_CORNER XC_TOP_SIDE XC_TOP_TEE XC_TREK XC_UL_ANGLE XC_UMBRELLA XC_UR_ANGLE XC_WATCH XC_XTERM MoveWindow WINDOWID, X, Y Moves the window to the specified location. zero is returned on failure, non-zero for success. ResizeWindow WINDOWID, WIDTH, HEIGHT Resizes the window to the specified size. zero is returned on failure, non-zero for success. IconifyWindow WINDOWID Minimizes (Iconifies) the specified window. zero is returned on failure, non-zero for success. UnIconifyWindow WINDOWID Unminimizes (UnIconifies) the specified window. zero is returned on failure, non-zero for success. RaiseWindow WINDOWID Raises the specified window to the top of the stack, so that no other windows cover it. zero is returned on failure, non-zero for success. LowerWindow WINDOWID Lowers the specified window to the bottom of the stack, so other existing windows will cover it. zero is returned on failure, non-zero for success. GetInputFocus Returns the window that currently has the input focus. SetInputFocus WINDOWID Sets the specified window to be the one that has the input focus. zero is returned on failure, non-zero for success. GetWindowPos WINDOWID Returns an array containing the position information for the specified window. It also returns size information (including border width) and the number of the screen where the window resides. my ($x, $y, $width, $height, $borderWidth, $screen) = GetWindowPos(GetRootWindow()); GetParentWindow WINDOWID Returns the parent of the specified window. zero is returned if parent couldn't be determined (i.e., root window). GetScreenDepth [SCREEN] Returns the color depth for the screen. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, -1 will be returned. Value is represented as bits, i.e. 16. my $depth = GetScreenDepth(); GetScreenRes [SCREEN] Returns the screen resolution. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, the returned list will be empty. my ($x, $y) = GetScreenRes(); OTHER DOCUMENTATION Available under the docs sub-directory. CodingStyle (Coding-Style Guidelines) Copying (Copy of the GPL License) COPYRIGHT Copyright(c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License. AUTHOR Dennis K. Paulsen (Des Moines, Iowa USA) CONTRIBUTORS Paulo E. Castro CREDITS Thanks to everyone; including those specifically mentioned below for patches, suggestions, etc.: Alexey Tourbin Richard Clamp Gustav Larsson Nelson D. Caro X11-GUITest-0.27/docs/CodingStyle0000644000076400007650000000252211563632137016637 0ustar pecastropecastroX11::GUITest ($Id: CodingStyle 203 2011-05-15 02:03:11Z ctrondlp $) This is a short list of coding style guidelines to be used when developing for this project. -------------------------------------------------------------------- General: - Utilize language idioms for aspects not covered below. - Be consistent. - Write the code for people first, computers second. - If you are just adding enhancements to an existing module and not rewriting it, you should maintain the existing coding styles that are in place. Indentation: - 4 character indents Braces/Spacing: int func(void) { } if (var) { printf("Hello\n"); } while (true) { } Variables: - Descriptive names for globals or ones that are file scoped. Mixed case is also prefered for these. - Shorter names can be used for locals. - Use static and const when appropriate. - Perl variables should be scoped appropriately. Functions/Subroutines: - Utilize mixed case, first character of each word capitalized - Noun, Verb, etc. ordering of function names should be consistent (i.e., GetThis, SetThat) Commenting: - Don't try to explain HOW your code works, rather explain WHY it is doing what it does. - And if you have to explain a lot about your code, consider rewriting your code to be easier to follow. -------------------------------------------------------------------- X11-GUITest-0.27/docs/X11-GUITest.html0000644000076400007650000007052012104317043017200 0ustar pecastropecastro B<X11::GUITest> - Provides GUI testing/interaction routines.

NAME

X11::GUITest - Provides GUI testing/interaction routines.

Developed by Dennis K. Paulsen


VERSION

0.27

Updates are made available at the following sites:

  http://sourceforge.net/projects/x11guitest
  http://www.cpan.org

Please consult 'docs/Changes' for the list of changes between module revisions.


DESCRIPTION

This Perl package is intended to facilitate the testing of GUI applications by means of user emulation. It can be used to test/interact with GUI applications; which have been built upon the X library or toolkits (i.e., GTK+, Xt, Qt, Motif, etc.) that "wrap" the X library's functionality.

A basic recorder (x11guirecord) is also available, and can be found in the source code respository.


DEPENDENCIES

An X server with the XTest extensions enabled. This seems to be the norm. If it is not enabled, it usually can be by modifying the X server configuration (i.e., XF86Config).

The standard DISPLAY environment variable is utilized to determine the host, display, and screen to work with. By default it is usually set to ":0.0" for the localhost. However, by altering this variable one can interact with applications under a remote host's X server. To change this from a terminal window, one can utilize the following basic syntax: export DISPLAY=<hostname-or-ipaddress>:<display>.<screen> Please note that under most circumstances, xhost will need to be executed properly on the remote host as well.

There is a known incompatibility between the XTest and Xinerama extensions, which causes the XTestFakeMotionEvent() function to misbehave. When the Xinerama (X server) extension is turned on, this (Perl) extension has been modified to allow one to invoke an alternative function. See Makefile.PL for details.


INSTALLATION

  perl Makefile.PL
  make
  make test
  make install
  # If you'd like to install the recorder, use these steps:
  cd recorder
  ./autogen.sh
  ./configure
  make
  make install
  x11guirecord --help


SYNOPSIS

For additional examples, please look under the 'eg/' sub-directory from the installation folder.

  use X11::GUITest qw/
    StartApp
    WaitWindowViewable
    SendKeys
  /;
  # Start gedit application
  StartApp('gedit');
  # Wait for application window to come up and become viewable.
  my ($GEditWinId) = WaitWindowViewable('gedit');
  if (!$GEditWinId) {
    die("Couldn't find gedit window in time!");
  }
  # Send text to it
  SendKeys("Hello, how are you?\n");
  # Close Application (Alt-f, q).
  SendKeys('%(f)q');
  # Handle gedit's Question window if it comes up when closing.  Wait
  # at most 5 seconds for it.
  if (WaitWindowViewable('Question', undef, 5)) {
    # DoN't Save (Alt-n)
    SendKeys('%(n)');
  }


FUNCTIONS

Parameters enclosed within [] are optional.

If there are multiple optional parameters available for a function and you would like to specify the last one, for example, you can utilize undef for those parameters you don't specify.

REGEX in the documentation below denotes an item that is treated as a regular expression. For example, the regex "^OK$" would look for an exact match for the word OK.

FindWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER]

Finds the window Ids of the windows matching the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window.

An array of window Ids is returned for the matches found. An empty array is returned if no matches were found.

  my @WindowIds = FindWindowLike('gedit');
  # Only worry about first window found
  my ($WindowId) = FindWindowLike('gedit');
WaitWindowLike TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS]

Waits for a window to come up that matches the specified title regex. Optionally one can specify the window to start under; which would allow one to constrain the search to child windows of that window.

One can optionally specify an alternative wait amount in seconds. A window will keep being looked for that matches the specified title regex until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag.

If a window is going to be manipulated by input, WaitWindowViewable is the more robust solution to utilize.

An array of window Ids is returned for the matches found. An empty array is returned if no matches were found.

  my @WindowIds = WaitWindowLike('gedit');
  # Only worry about first window found
  my ($WindowId) = WaitWindowLike('gedit');
  WaitWindowLike('gedit') or die("gedit window not found!");
WaitWindowViewable TITLEREGEX [, WINDOWIDSTARTUNDER] [, MAXWAITINSECONDS]

Similar to WaitWindow, but only recognizes windows that are viewable. When GUI applications are started, their window isn't necessarily viewable yet, let alone available for input, so this function is very useful.

Likewise, this function will only return an array of the matching window Ids for those windows that are viewable. An empty array is returned if no matches were found.

WaitWindowClose WINDOWID [, MAXWAITINSECONDS]

Waits for the specified window to close.

One can optionally specify an alternative wait amount in seconds. The window will keep being checked to see if it has closed until this amount of time has been reached. The default amount is defined in the DEF_WAIT constant available through the :CONST export tag.

zero is returned if window is not gone, non-zero if it is gone.

WaitSeconds SECONDS

Pauses execution for the specified amount of seconds.

  WaitSeconds(0.5); # Wait 1/2 second
  WaitSeconds(3); # Wait 3 seconds
ClickWindow WINDOWID [, X Offset] [, Y Offset] [, Button]

Clicks on the specified window with the mouse.

Optionally one can specify the X offset and Y offset. By default, the top left corner of the window is clicked on, with these two parameters one can specify a different position to be clicked on.

One can also specify an alternative button. The default button is M_LEFT, but M_MIDDLE and M_RIGHT may be specified too. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag.

zero is returned on failure, non-zero for success

GetWindowFromPoint X, Y [, SCREEN]

Returns the window that is at the specified point. If no screen is given, it is taken from the value given when opening the X display.

zero is returned if there are no matches (i.e., off screen).

IsChild PARENTWINDOWID, WINDOWID

Determines if the specified window is a child of the specified parent.

zero is returned for false, non-zero for true.

QuoteStringForSendKeys STRING

Quotes {} characters in the specified string that would be interpreted as having special meaning if sent to SendKeys directly. This function would be useful if you had a text file in which you wanted to use each line of the file as input to the SendKeys function, but didn't want any special interpretation of the characters in the file.

Returns the quoted string, undef is returned on error.

  # Quote  ~, %, etc.  as  {~}, {%}, etc for literal use in SendKeys.
  SendKeys( QuoteStringForSendKeys('Hello: ~%^(){}+#') );
  SendKeys( QSfSK('#+#') );
StartApp COMMANDLINE

Uses the shell to execute a program. This function returns as soon as the program is called. Useful for starting GUI applications and then going on to work with them.

zero is returned on failure, non-zero for success

  StartApp('gedit');
RunApp COMMANDLINE

Uses the shell to execute a program until its completion.

Return value will be application specific, however -1 is returned to indicate a failure in starting the program.

  RunApp('/work/myapp');
ClickMouseButton BUTTON

Clicks the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag.

zero is returned on failure, non-zero for success.

DefaultScreen

Returns the screen number specified in the X display value used to open the display.

Leverages the Xlib macro of the same name.

ScreenCount

Returns the number of screens in the X display specified when opening it.

Leverages the Xlib macro of the same name.

SetEventSendDelay DELAYINMILLISECONDS

Sets the milliseconds of delay between events being sent to the X display. It is usually not a good idea to set this to 0.

Please note that this delay will also affect SendKeys.

Returns the old delay amount in milliseconds.

GetEventSendDelay

Returns the current event sending delay amount in milliseconds.

SetKeySendDelay DELAYINMILLISECONDS

Sets the milliseconds of delay between keystrokes.

Returns the old delay amount in milliseconds.

GetKeySendDelay

Returns the current keystroke sending delay amount in milliseconds.

GetWindowName WINDOWID

Returns the window name for the specified window Id. undef is returned if name could not be obtained.

  # Return the name of the window that has the input focus.
  my $WinName = GetWindowName(GetInputFocus());
SetWindowName WINDOWID, NAME

Sets the window name for the specified window Id.

zero is returned on failure, non-zero for success.

GetRootWindow [SCREEN]

Returns the Id of the root window of the screen. This is the top/root level window that all other windows are under. If no screen is given, it is taken from the value given when opening the X display.

GetChildWindows WINDOWID

Returns an array of the child windows for the specified window Id. If it detects that the window hierarchy is in transition, it will wait half a second and try again.

MoveMouseAbs X, Y [, SCREEN]

Moves the mouse cursor to the specified absolute position in the optionally given screen. If no screen is given, it is taken from the value given when opening the X display.

Zero is returned on failure, non-zero for success.

GetMousePos

Returns an array containing the position and the screen (number) of the mouse cursor.

  my ($x, $y, $scr_num) = GetMousePos();
PressMouseButton BUTTON

Presses the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag.

zero is returned on failure, non-zero for success.

ReleaseMouseButton BUTTON

Releases the specified mouse button. Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT, M_DOWN, M_UP. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag.

zero is returned on failure, non-zero for success.

SendKeys KEYS

Sends keystrokes to the window that has the input focus.

The keystrokes to send are those specified in KEYS parameter. Some characters have special meaning, they are:

        Modifier Keys:
        ^       CTRL
        %       ALT
        +       SHIFT
        #       META
        &       ALTGR
        Other Keys:
        ~       ENTER
        \n      ENTER
        \t      TAB
        ( and ) MODIFIER GROUPING
        { and } QUOTE / ESCAPE CHARACTERS

Simply, one can send a text string like so:

        SendKeys('Hello, how are you today?');

Parenthesis allow a modifier to work on one or more characters. For example:

        SendKeys('%(f)q'); # Alt-f, then press q
        SendKeys('%(fa)^(m)'); # Alt-f, Alt-a, Ctrl-m
        SendKeys('+(abc)'); # Uppercase ABC using shift modifier
        SendKeys('^(+(l))'); # Ctrl-Shift-l
        SendKeys('+'); # Press shift

Braces are used to quote special characters, for utilizing aliased key names, or for special functionality. Multiple characters can be specified in a brace by space delimiting the entries. Characters can be repeated using a number that is space delimited after the preceeding key.

Quote Special Characters

        SendKeys('{{}'); # {
        SendKeys('{+}'); # +
        SendKeys('{#}'); # #
        You can also use QuoteStringForSendKeys (QSfSK) to perform quoting.

Aliased Key Names

        SendKeys('{BAC}'); # Backspace
        SendKeys('{F1 F2 F3}'); # F1, F2, F3
        SendKeys('{TAB 3}'); # Press TAB 3 times
        SendKeys('{SPC 3 a b c}'); # Space 3 times, a, b, c

Special Functionality

        # Pause execution for 500 milliseconds
        SendKeys('{PAUSE 500}');

Combinations

        SendKeys('abc+(abc){TAB PAUSE 500}'); # a, b, c, A, B, C, Tab, Pause 500
        SendKeys('+({a b c})'); # A, B, C

The following abbreviated key names are currently recognized within a brace set. If you don't see the desired key, you can still use the unabbreviated name for the key. If you are unsure of this name, utilize the xev (X event view) tool, press the key you want and look at the tools output for the name of that key. Names that are in the list below can be utilized regardless of case. Ones that aren't in this list are going to be case sensitive and also not abbreviated. For example, using 'xev' you will find that the name of the backspace key is BackSpace, so you could use {BackSpace} in place of {bac} if you really wanted to.

        Name    Action
        -------------------
        BAC     BackSpace
        BS      BackSpace
        BKS     BackSpace
        BRE     Break
        CAN     Cancel
        CAP     Caps_Lock
        DEL     Delete
        DOWN    Down
        END     End
        ENT     Return
        ESC     Escape
        F1      F1
        ...     ...
        F12     F12
        HEL     Help
        HOM     Home
        INS     Insert
        LAL     Alt_L
        LMA     Meta_L
        LCT     Control_L
        LEF     Left
        LSH     Shift_L
        LSK     Super_L
        MNU     Menu
        NUM     Num_Lock
        PGD     Page_Down
        PGU     Page_Up
        PRT     Print
        RAL     Alt_R
        RMA     Meta_R
        RCT     Control_R
        RIG     Right
        RSH     Shift_R
        RSK     Super_R
        SCR     Scroll_Lock
        SPA     Space
        SPC     Space
        TAB     Tab
        UP      Up

zero is returned on failure, non-zero for success. For configurations (Xvfb) that don't support Alt_Left, Meta_Left is automatically used in its place.

PressKey KEY

Presses the specified key.

One can utilize the abbreviated key names from the table listed above as outlined in the following example:

  # Alt-n
  PressKey('LAL'); # Left Alt
  PressKey('n');
  ReleaseKey('n');
  ReleaseKey('LAL');
  # Uppercase a
  PressKey('LSH'); # Left Shift
  PressKey('a');
  ReleaseKey('a');
  ReleaseKey('LSH');

The ReleaseKey calls in the above example are there to set both key states back.

zero is returned on failure, non-zero for success.

ReleaseKey KEY

Releases the specified key. Normally follows a PressKey call.

One can utilize the abbreviated key names from the table listed above.

  ReleaseKey('n');

zero is returned on failure, non-zero for success.

PressReleaseKey KEY

Presses and releases the specified key.

One can utilize the abbreviated key names from the table listed above.

  PressReleaseKey('n');

This function is affected by the key send delay.

zero is returned on failure, non-zero for success.

IsKeyPressed KEY

Determines if the specified key is currently being pressed.

You can specify such things as 'bac' or the unabbreviated form 'BackSpace' as covered in the SendKeys information. Brace forms such as '{bac}' are unsupported. A '{' is taken literally and letters are case sensitive.

  if (IsKeyPressed('esc')) {  # Is Escape pressed?
  if (IsKeyPressed('a')) { # Is a pressed?
  if (IsKeyPressed('A')) { # Is A pressed?

Returns non-zero for true, zero for false.

IsMouseButtonPressed BUTTON

Determines if the specified mouse button is currently being pressed.

Available mouse buttons are: M_LEFT, M_MIDDLE, M_RIGHT. Also, you could use the logical Id for the button: M_BTN1, M_BTN2, M_BTN3, M_BTN4, M_BTN5. These are all available through the :CONST export tag.

  if (IsMouseButtonPressed(M_LEFT)) { # Is left button pressed?

Returns non-zero for true, zero for false.

IsWindow WINDOWID

zero is returned if the specified window Id is not for something that can be recognized as a window. non-zero is returned if it looks like a window.

IsWindowViewable WINDOWID

zero is returned if the specified window Id is for a window that isn't viewable. non-zero is returned if the window is viewable.

IsWindowCursor WINDOWID CURSOR

Determines if the specified window has the specified cursor.

zero is returned for false, non-zero for true.

The following cursors are available through the :CONST export tag.

    Name
    -------------------
        XC_NUM_GLYPHS
        XC_X_CURSOR
        XC_ARROW
        XC_BASED_ARROW_DOWN
        XC_BASED_ARROW_UP
        XC_BOAT
        XC_BOGOSITY
        XC_BOTTOM_LEFT_CORNER
        XC_BOTTOM_RIGHT_CORNER
        XC_BOTTOM_SIDE
        XC_BOTTOM_TEE
        XC_BOX_SPIRAL
        XC_CENTER_PTR
        XC_CIRCLE
        XC_CLOCK
        XC_COFFEE_MUG
        XC_CROSS
        XC_CROSS_REVERSE
        XC_CROSSHAIR
        XC_DIAMOND_CROSS
        XC_DOT
        XC_DOTBOX
        XC_DOUBLE_ARROW
        XC_DRAFT_LARGE
        XC_DRAFT_SMALL
        XC_DRAPED_BOX
        XC_EXCHANGE
        XC_FLEUR
        XC_GOBBLER
        XC_GUMBY
        XC_HAND1
        XC_HAND2
        XC_HEART
        XC_ICON
        XC_IRON_CROSS
        XC_LEFT_PTR
        XC_LEFT_SIDE
        XC_LEFT_TEE
        XC_LEFTBUTTON
        XC_LL_ANGLE
        XC_LR_ANGLE
        XC_MAN
        XC_MIDDLEBUTTON
        XC_MOUSE
        XC_PENCIL
        XC_PIRATE
        XC_PLUS
        XC_QUESTION_ARROW
        XC_RIGHT_PTR
        XC_RIGHT_SIDE
        XC_RIGHT_TEE
        XC_RIGHTBUTTON
        XC_RTL_LOGO
        XC_SAILBOAT
        XC_SB_DOWN_ARROW
        XC_SB_H_DOUBLE_ARROW
        XC_SB_LEFT_ARROW
        XC_SB_RIGHT_ARROW
        XC_SB_UP_ARROW
        XC_SB_V_DOUBLE_ARROW
        XC_SHUTTLE
        XC_SIZING
        XC_SPIDER
        XC_SPRAYCAN
        XC_STAR
        XC_TARGET
        XC_TCROSS
        XC_TOP_LEFT_ARROW
        XC_TOP_LEFT_CORNER
        XC_TOP_RIGHT_CORNER
        XC_TOP_SIDE
        XC_TOP_TEE
        XC_TREK
        XC_UL_ANGLE
        XC_UMBRELLA
        XC_UR_ANGLE
        XC_WATCH
        XC_XTERM
MoveWindow WINDOWID, X, Y

Moves the window to the specified location.

zero is returned on failure, non-zero for success.

ResizeWindow WINDOWID, WIDTH, HEIGHT

Resizes the window to the specified size.

zero is returned on failure, non-zero for success.

IconifyWindow WINDOWID

Minimizes (Iconifies) the specified window.

zero is returned on failure, non-zero for success.

UnIconifyWindow WINDOWID

Unminimizes (UnIconifies) the specified window.

zero is returned on failure, non-zero for success.

RaiseWindow WINDOWID

Raises the specified window to the top of the stack, so that no other windows cover it.

zero is returned on failure, non-zero for success.

LowerWindow WINDOWID

Lowers the specified window to the bottom of the stack, so other existing windows will cover it.

zero is returned on failure, non-zero for success.

GetInputFocus

Returns the window that currently has the input focus.

SetInputFocus WINDOWID

Sets the specified window to be the one that has the input focus.

zero is returned on failure, non-zero for success.

GetWindowPos WINDOWID

Returns an array containing the position information for the specified window. It also returns size information (including border width) and the number of the screen where the window resides.

  my ($x, $y, $width, $height, $borderWidth, $screen) =
        GetWindowPos(GetRootWindow());
GetParentWindow WINDOWID

Returns the parent of the specified window.

zero is returned if parent couldn't be determined (i.e., root window).

GetScreenDepth [SCREEN]

Returns the color depth for the screen. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, -1 will be returned.

Value is represented as bits, i.e. 16.

  my $depth = GetScreenDepth();
GetScreenRes [SCREEN]

Returns the screen resolution. If no screen is specified, it is taken from the value given when opening the X display. If the screen (number) is invalid, the returned list will be empty.

  my ($x, $y) = GetScreenRes();


OTHER DOCUMENTATION

Module Changes
Coding-Style Guidelines
ToDo List
Copy of the GPL License


COPYRIGHT

Copyright(c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License.


AUTHOR

Dennis K. Paulsen <ctrondlp@cpan.org> (Des Moines, Iowa USA)


CONTRIBUTORS

Paulo E. Castro <pauloedgarcastro tata gmail.com>


CREDITS

Thanks to everyone; including those specifically mentioned below for patches, suggestions, etc.:

  Alexey Tourbin
  Richard Clamp
  Gustav Larsson
  Nelson D. Caro
X11-GUITest-0.27/MANIFEST0000644000076400007650000000144412104317102014653 0ustar pecastropecastroChanges Common.h docs/CodingStyle docs/Copying docs/X11-GUITest.html docs/X11-GUITest.txt eg/FindControlVisually.pl eg/FindWindowLike.pl eg/templates/ScriptTemplate.pl eg/TextEditor_1.pl eg/WebBrowser_1.pl GUITest.h GUITest.pm GUITest.xs KeyUtil.c KeyUtil.h Makefile.PL MANIFEST This list of files README recorder/autogen.sh recorder/configure.ac recorder/Makefile.am recorder/man/Makefile.am recorder/man/x11guirecord.1 recorder/src/main.c recorder/src/main.h recorder/src/Makefile.am recorder/src/record.c recorder/src/record.h recorder/src/record_event.h recorder/src/script_file.c recorder/src/script_file.h t/test.t ToDo typemap META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) X11-GUITest-0.27/Common.h0000644000076400007650000000241512104316203015123 0ustar pecastropecastro/* X11::GUITest ($Id: Common.h 227 2013-02-05 23:57:55Z pecastro $) * * Copyright (c) 2003-2011 Dennis K. Paulsen, All Rights Reserved. * Email: ctrondlp@cpan.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef COMMON_H #define COMMON_H #define APP_VERSION "0.27" #define APP_TEXTDOMAIN "/usr/share/locale" #ifndef _ #define _(str) gettext(str) #endif #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #ifndef BOOL #define BOOL int #endif #ifndef UINT #define UINT unsigned int #endif #ifndef ULONG #define ULONG unsigned long #endif #ifndef NUL #define NUL '\0' #endif #ifndef MAX_PATH #define MAX_PATH 255 #endif #endif /* #ifndef COMMON_H */ X11-GUITest-0.27/t/0000755000076400007650000000000012104317102013762 5ustar pecastropecastroX11-GUITest-0.27/t/test.t0000644000076400007650000001023111563632137015143 0ustar pecastropecastro#!/usr/bin/perl # X11::GUITest ($Id: test.t 203 2011-05-15 02:03:11Z ctrondlp $) # Note: Functions that might be too intrusive are not checked BEGIN { $| = 1; # Is X info not available? if (!defined($ENV{'DISPLAY'}) || length $ENV{'DISPLAY'} <= 0) { warn "X11::GUITest - X Windows not running or DISPLAY not set.\n"; print "1..1\n"; print "ok 1\n"; exit(0); } # Testing too ambiguous for varied environments if (defined($ENV{'AUTOMATED_TESTING'}) && $ENV{'AUTOMATED_TESTING'}) { warn "X11::GUITest - Not performing extensive tests.\n"; print "1..1\n"; print "ok 1\n"; exit(0); } # All seems ok, so plan on running the tests. print "1..23\n"; } END { print "not ok 1\n" unless $loaded; } use X11::GUITest qw/ :ALL /; $loaded = 1; print "ok 1\n"; use strict; use warnings; # Used for testing below my $BadWinTitle = 'BadWindowNameNotToBeFound'; my $BadWinId = '898989899'; my @Windows = (); # FindWindowLike print "not " unless FindWindowLike(".*"); print "not " unless not FindWindowLike($BadWinTitle); print "ok 2\n"; # WaitWindowClose print "not " unless WaitWindowClose($BadWinId); print "ok 3\n"; # WaitWindowLike print "not " unless WaitWindowLike(".*"); print "not " unless not WaitWindowLike($BadWinTitle, undef, 1); print "ok 4\n"; # WaitWindowViewable print "not " unless WaitWindowViewable(".*"); print "not " unless not WaitWindowViewable($BadWinTitle, undef, 1); print "ok 5\n"; # ClickWindow # StartApp # RunApp # SetEventSendDelay # GetEventSendDelay # SetKeySendDelay # GetKeySendDelay # GetWindowName my $WinName = ''; # Note: Only worry about windows that have a name # RegExp: ".+" = one or more characters foreach my $win (FindWindowLike(".+")) { # If call fails, WinName will be set to undef $WinName = GetWindowName($win); if (not defined($WinName)) { last; } } print "not " unless defined($WinName); print "ok 6\n"; # SetWindowName # GetRootWindow print "not " unless GetRootWindow(); print "ok 7\n"; # GetChildWindows print "not " unless GetChildWindows(GetRootWindow()); print "ok 8\n"; # MoveMouseAbs print "not " unless MoveMouseAbs(2, 2); print "ok 9\n"; # Give some respite to the X server sleep 1; # ClickMouseButton # SendKeys # IsWindow print "not " unless IsWindow(GetRootWindow()); print "not " unless not IsWindow($BadWinId); print "ok 10\n"; # IsWindowViewable @Windows = WaitWindowViewable(".*"); if (not IsWindow($Windows[0])) { # First window not viewable $Windows[0] = GetRootWindow(); # Fall-back to root } print "not " unless IsWindowViewable($Windows[0]); print "not " unless not IsWindowViewable($BadWinId); print "ok 11\n"; # MoveWindow # ResizeWindow # IconifyWindow # UnIconifyWindow # Raise Window # LowerWindow # SetInputFocus # GetInputFocus print "not " unless GetInputFocus(); print "ok 12\n"; # GetWindowPos my ($x, $y, $width, $height) = GetWindowPos(GetRootWindow()); print "not " unless (defined($x) and defined($y) and defined($width) and defined($height)); print "ok 13\n"; # GetScreenRes print "not " unless GetScreenRes(); print "ok 14\n"; # GetScreenDepth print "not " unless GetScreenDepth(); print "ok 15\n"; # GetMousePos my @coords = (); print "not " unless ( @coords = GetMousePos() ); print "ok 16\n"; # IsChild print "not " unless ( @Windows = GetChildWindows(GetRootWindow()) ); # Note: Limiting check to a certain number of windows (10) foreach my $win ( @Windows[0..(9 < $#Windows ? 9 : $#Windows)] ) { if (!IsChild(GetRootWindow(), $win)) { print "not "; last; } } print "ok 17\n"; # IsKeyPressed # IsMouseButtonPressed # QuoteStringForSendKeys print "not " unless defined( QuoteStringForSendKeys('~!@#$%^&*()_+') ); print "ok 18\n"; print "not " unless not defined ( QuoteStringForSendKeys(undef) ); print "ok 19\n"; # GetParentWindow print "not " unless not GetParentWindow(GetRootWindow()); print "ok 20\n"; print "not " unless GetParentWindow($Windows[0]); print "ok 21\n"; # GetWindowFromPoint # Note: Using invalid window position of (-1500 x -1500) for testing. print "not " unless not GetWindowFromPoint(-1500, -1500); print "ok 22\n"; print "not " unless GetWindowFromPoint(0, 0); print "ok 23\n"; # PressKey # ReleaseKey # PressReleaseKey # PressMouseButton # ReleaseMouseButton