From 99166f48dd544d3fef849683b0b40175d7238547 Mon Sep 17 00:00:00 2001 From: dkf Date: Fri, 27 May 2005 23:14:26 +0000 Subject: Partial implementation of TIP#245; thanks Reinhard! --- ChangeLog | 13 +++++ doc/tk.n | 18 ++++++- generic/tk.decls | 9 +++- generic/tkCmds.c | 57 ++++++++++++++++++---- macosx/Wish.pbproj/project.pbxproj | 33 +++++++++++++ macosx/tkMacOSXXStubs.c | 97 +++++++++++++++++++++++++++++++++++++- unix/configure.in | 25 +++++++++- unix/tkUnix.c | 68 +++++++++++++++++++++++++- win/tkWinX.c | 96 ++++++++++++++++++++++++++++++++++++- 9 files changed, 401 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index f84142e..e8607e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2005-05-28 Donal K. Fellows + + TIP #245 IMPLEMENTATION from Reinhard Max + + * doc/tk.n: Documentation of [tk inactivity]. + * win/tkWinX.c (Tk_GetUserInactiveTime, Tk_ResetUserInactiveTime): + * unix/tkUnix.c (Tk_GetUserInactiveTime, Tk_ResetUserInactiveTime): + * macosx/tkMacOSXXStubs.c: Implementations of the core API for + (Tk_GetUserInactiveTime): determining how long as user's left + (Tk_ResetUserInactiveTime): her machine alone. + * unix/configure.in: Test for XScreenSaver support. + * generic/tkCmds.c (Tk_TkObjCmd): Implementation of [tk inactivity]. + 2005-05-27 Todd Helfter * library/menu.tcl: correct the sticky behavior of menus posted by diff --git a/doc/tk.n b/doc/tk.n index 587fbc2..d80f68c 100644 --- a/doc/tk.n +++ b/doc/tk.n @@ -5,7 +5,7 @@ '\" See the file "license.terms" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -'\" RCS: @(#) $Id: tk.n,v 1.9 2005/04/06 21:11:54 dkf Exp $ +'\" RCS: @(#) $Id: tk.n,v 1.10 2005/05/27 23:14:28 dkf Exp $ '\" .so man.macros .TH tk n 8.4 Tk "Tk Built-In Commands" @@ -79,6 +79,22 @@ is undefined whether existing widgets will resize themselves dynamically to accomodate the new scaling factor. .RE .TP +\fBtk inactive \fR?\fB\-displayof \fIwindow\fR? ?\fBreset\fR? +. +Returns a positive integer, the number of milliseconds since the last +time the user interacted with the system. If the \fB-displayof\fR +option is given then the return value refers to the display of +\fIwindow\fR; otherwise it refers to the display of the application's +main window. + +\fBtk inactive\fR will return -1, if querying the user inactive time +is not supported by the system, and in safe interpreters. + +If the literal string \fBreset\fR is given as an additional argument, +the timer is reset and an empty string is returned. Resetting the +inactivity time is forbidden in safe interpreters and will throw and +error if tried. +.TP \fBtk useinputmethods \fR?\fB\-displayof \fIwindow\fR? ?\fIboolean\fR? . Sets and queries the state of whether Tk should use XIM (X Input Methods) diff --git a/generic/tk.decls b/generic/tk.decls index 7465788..c1151bf 100644 --- a/generic/tk.decls +++ b/generic/tk.decls @@ -11,7 +11,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: tk.decls,v 1.26 2004/03/17 18:15:42 das Exp $ +# RCS: @(#) $Id: tk.decls,v 1.27 2005/05/27 23:14:28 dkf Exp $ library tk @@ -1262,6 +1262,13 @@ declare 268 generic { int Tk_PhotoSetSize(Tcl_Interp *interp, Tk_PhotoHandle handle, int width, int height) } +# TIP#245 +declare 269 generic { + long Tk_GetUserInactiveTime(Display *dpy) +} +declare 270 generic { + void Tk_ResetUserInactiveTime(Display *dpy) +} # Define the platform specific public Tk interface. These functions are # only available on the designated platform. diff --git a/generic/tkCmds.c b/generic/tkCmds.c index b51fd80..acc0ab0 100644 --- a/generic/tkCmds.c +++ b/generic/tkCmds.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkCmds.c,v 1.35 2004/11/12 23:53:12 hobbs Exp $ + * RCS: @(#) $Id: tkCmds.c,v 1.36 2005/05/27 23:14:29 dkf Exp $ */ #include "tkPort.h" @@ -619,11 +619,11 @@ Tk_TkObjCmd(clientData, interp, objc, objv) Tk_Window tkwin; static CONST char *optionStrings[] = { "appname", "caret", "scaling", "useinputmethods", - "windowingsystem", NULL + "windowingsystem", "inactive", NULL }; enum options { TK_APPNAME, TK_CARET, TK_SCALING, TK_USE_IM, - TK_WINDOWINGSYSTEM + TK_WINDOWINGSYSTEM, TK_INACTIVE }; tkwin = (Tk_Window) clientData; @@ -638,7 +638,7 @@ Tk_TkObjCmd(clientData, interp, objc, objv) } switch ((enum options) index) { - case TK_APPNAME: { + case TK_APPNAME: { TkWindow *winPtr; char *string; @@ -652,7 +652,7 @@ Tk_TkObjCmd(clientData, interp, objc, objv) winPtr = (TkWindow *) tkwin; if (objc > 3) { - Tcl_WrongNumArgs(interp, 2, objv, "?newName?"); + Tcl_WrongNumArgs(interp, 2, objv, "?newName?"); return TCL_ERROR; } if (objc == 3) { @@ -672,7 +672,7 @@ Tk_TkObjCmd(clientData, interp, objc, objv) { TK_CARET_X, TK_CARET_Y, TK_CARET_HEIGHT }; if ((objc < 3) || ((objc > 4) && !(objc & 1))) { - Tcl_WrongNumArgs(interp, 2, objv, + Tcl_WrongNumArgs(interp, 2, objv, "window ?-x x? ?-y y? ?-height height?"); return TCL_ERROR; } @@ -829,11 +829,11 @@ Tk_TkObjCmd(clientData, interp, objc, objv) (int) (dispPtr->flags & TK_DISPLAY_USE_IM)); break; } - case TK_WINDOWINGSYSTEM: { + case TK_WINDOWINGSYSTEM: { CONST char *windowingsystem; if (objc != 2) { - Tcl_WrongNumArgs(interp, 2, objv, NULL); + Tcl_WrongNumArgs(interp, 2, objv, NULL); return TCL_ERROR; } #if defined(WIN32) @@ -846,6 +846,45 @@ Tk_TkObjCmd(clientData, interp, objc, objv) Tcl_SetStringObj(Tcl_GetObjResult(interp), windowingsystem, -1); break; } + case TK_INACTIVE: { + int skip = TkGetDisplayOf(interp, objc - 2, objv + 2, &tkwin); + if (skip < 0) { + return TCL_ERROR; + } + if (objc - skip == 2) { + long inactive; + + inactive = (Tcl_IsSafe(interp) ? -1 : + Tk_GetUserInactiveTime(Tk_Display(tkwin))); + Tcl_SetObjResult(interp, Tcl_NewLongObj(inactive)); + + } else if (objc - skip == 3) { + char *string; + + string = Tcl_GetStringFromObj(objv[objc-1], NULL); + if (strcmp(string, "reset") != 0) { + Tcl_Obj *msg = Tcl_NewStringObj("bad option \"", -1); + Tcl_AppendStringsToObj(msg, string, + "\": must be reset", NULL); + Tcl_SetObjResult(interp, msg); + return TCL_ERROR; + } + if (Tcl_IsSafe(interp)) { + Tcl_SetResult(interp, + "resetting the user inactivity timer " + "is not allowed in a safe interpreter", + TCL_STATIC); + return TCL_ERROR; + } + Tk_ResetUserInactiveTime(Tk_Display(tkwin)); + Tcl_ResetResult(interp); + } else { + Tcl_WrongNumArgs(interp, 2, objv, + "?-displayof window? ?reset?"); + return TCL_ERROR; + } + break; + } } return TCL_OK; } @@ -1053,7 +1092,7 @@ Tk_UpdateObjCmd(clientData, interp, objc, objv) } flags = TCL_IDLE_EVENTS; } else { - Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?"); + Tcl_WrongNumArgs(interp, 1, objv, "?idletasks?"); return TCL_ERROR; } diff --git a/macosx/Wish.pbproj/project.pbxproj b/macosx/Wish.pbproj/project.pbxproj index 2a4def9..6f960ea 100644 --- a/macosx/Wish.pbproj/project.pbxproj +++ b/macosx/Wish.pbproj/project.pbxproj @@ -101,6 +101,36 @@ //4C2 //4C3 //4C4 +//950 +//951 +//952 +//953 +//954 + 95911CC7081532D8006F6BCB = { + isa = PBXFileReference; + lastKnownFileType = wrapper.framework; + name = IOKit.framework; + path = /System/Library/Frameworks/IOKit.framework; + refType = 0; + sourceTree = ""; + }; + 95911CC8081532D8006F6BCB = { + fileRef = 95911CC7081532D8006F6BCB; + isa = PBXBuildFile; + settings = { + }; + }; + 95911CC9081532D8006F6BCB = { + fileRef = 95911CC7081532D8006F6BCB; + isa = PBXBuildFile; + settings = { + }; + }; +//950 +//951 +//952 +//953 +//954 //F50 //F51 //F52 @@ -2167,6 +2197,7 @@ Initial MacOS X Port by Jim Ingham <jingham@apple.com> & Ian Reid, Cop files = ( F537567E016C3ADB01DC9062, F50D96130196176E01DC9062, + 95911CC9081532D8006F6BCB, ); isa = PBXFrameworksBuildPhase; runOnlyForDeploymentPostprocessing = 0; @@ -3075,6 +3106,7 @@ Initial MacOS X Port by Jim Ingham <jingham@apple.com> & Ian Reid, Cop }; F537567C016C3ADB01DC9062 = { children = ( + 95911CC7081532D8006F6BCB, F50D96120196176E01DC9062, F5875C7B016FEF1D01DC9062, F537567D016C3ADB01DC9062, @@ -3427,6 +3459,7 @@ Initial MacOS X Port by Jim Ingham <jingham@apple.com> & Ian Reid, Cop F53756A7016C4DD401DC9062, F50D96140196176E01DC9062, F5877FB9031F9F49016F146B, + 95911CC8081532D8006F6BCB, ); isa = PBXFrameworksBuildPhase; runOnlyForDeploymentPostprocessing = 0; diff --git a/macosx/tkMacOSXXStubs.c b/macosx/tkMacOSXXStubs.c index 892b4fc..d79cf12 100644 --- a/macosx/tkMacOSXXStubs.c +++ b/macosx/tkMacOSXXStubs.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMacOSXXStubs.c,v 1.9 2005/05/15 21:09:34 wolfsuit Exp $ + * RCS: @(#) $Id: tkMacOSXXStubs.c,v 1.10 2005/05/27 23:14:29 dkf Exp $ */ #include "tkInt.h" @@ -30,6 +30,8 @@ #include "tkPort.h" #include "tkMacOSXEvent.h" +#include + /* * Because this file is still under major development Debugger statements are * used through out this file. The define TCL_DEBUG will decide whether @@ -967,3 +969,96 @@ TkGetDefaultScreenName( #endif return macScreenName; } + +/* + *---------------------------------------------------------------------- + * + * Tk_GetUserInactiveTime -- + * + * Return the number of milliseconds the user was inactive. + * + * Results: + * The number of milliseconds the user has been inactive, + * or -1 if querying the inactive time is not supported. + * + * Side effects: + * None. + *---------------------------------------------------------------------- + */ + +long +Tk_GetUserInactiveTime(Display *dpy) +{ + io_registry_entry_t regEntry; + CFMutableDictionaryRef props = NULL; + CFTypeRef timeObj; + long ret = -1l; + uint64_t time; + + regEntry = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("IOHIDSystem")); + + if (regEntry == NULL) { + return -1l; + } + + IOReturn result = IORegistryEntryCreateCFProperties(regEntry, &props, + kCFAllocatorDefault, 0); + IOObjectRelease(regEntry); + + if (result != KERN_SUCCESS || props == NULL) { + return -1l; + } + + timeObj = CFDictionaryGetValue(props, CFSTR("HIDIdleTime")); + + if (timeObj) { + CFRetain(timeObj); + CFTypeID type = CFGetTypeID(timeObj); + + if (type == CFDataGetTypeID()) { /* Jaguar */ + CFDataGetBytes((CFDataRef) timeObj, + CFRangeMake(0, sizeof(time)), (UInt8 *) &time); + /* Convert nanoseconds to milliseconds. */ + /* ret /= kMillisecondScale; */ + ret = (long)(time/kMillisecondScale); + } else if (type == CFNumberGetTypeID()) { /* Panther+ */ + CFNumberGetValue((CFNumberRef)timeObj, + kCFNumberSInt64Type, &time); + /* Convert nanoseconds to milliseconds. */ + /* ret /= kMillisecondScale; */ + ret = (long)(time/kMillisecondScale); + } else { + ret = -1l; + } + + CFRelease(timeObj); + } + /* Cleanup */ + CFRelease(props); + + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_ResetUserInactiveTime -- + * + * Reset the user inactivity timer + * + * Results: + * none + * + * Side effects: + * The user inactivity timer of the underlaying windowing system + * is reset to zero. + * + *---------------------------------------------------------------------- + */ + +void +Tk_ResetUserInactiveTime(Display *dpy) +{ + UpdateSystemActivity(0); +} diff --git a/unix/configure.in b/unix/configure.in index 7dbf335..732b301 100644 --- a/unix/configure.in +++ b/unix/configure.in @@ -3,7 +3,7 @@ dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during Tk installation dnl to configure the system for the local environment. # -# RCS: @(#) $Id: configure.in,v 1.104 2005/05/26 11:19:04 das Exp $ +# RCS: @(#) $Id: configure.in,v 1.105 2005/05/27 23:14:29 dkf Exp $ AC_INIT([tk],[8.5]) AC_PREREQ(2.57) @@ -323,6 +323,29 @@ if test -d /usr/include/mit ; then fi #-------------------------------------------------------------------- +# Check whether the header and library for the XScreenSaver +# extension are available, and set HAVE_XSS if so. +# XScreenSaver is needed for Tk_GetUserInactiveTime(). +#-------------------------------------------------------------------- +AC_MSG_CHECKING([for XScreenSaver support]) +tk_oldCFlags=$CFLAGS +CFLAGS="$CFLAGS $XINCLUDES" +tk_oldLibs=$LIBS +LIBS="$LIBS $XLIBSW -lXss -lXext" +AC_TRY_LINK([ + #include + #include +] , [ + XScreenSaverAllocInfo(); +], [ + AC_MSG_RESULT(yes) + XLIBSW="$XLIBSW -lXss -lXext" + AC_DEFINE([HAVE_XSS]) +], AC_MSG_RESULT(no)) +CFLAGS=$tk_oldCFlags +LIBS=$tk_oldLibs + +#-------------------------------------------------------------------- # Check for freetype / fontconfig / Xft support. #-------------------------------------------------------------------- diff --git a/unix/tkUnix.c b/unix/tkUnix.c index 65f1a90..c0e454d 100644 --- a/unix/tkUnix.c +++ b/unix/tkUnix.c @@ -10,10 +10,13 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkUnix.c,v 1.6 2004/10/26 13:15:09 dkf Exp $ + * RCS: @(#) $Id: tkUnix.c,v 1.7 2005/05/27 23:14:29 dkf Exp $ */ #include +#ifdef HAVE_XSS +#include +#endif /* *---------------------------------------------------------------------- @@ -165,3 +168,66 @@ TkpBuildRegionFromAlphaData(region, x, y, width, height, dataPtr, dataPtr += lineStride; } } + +/* + *---------------------------------------------------------------------- + * + * Tk_GetUserInactiveTime -- + * + * Return the number of milliseconds the user was inactive. + * + * Results: + * The number of milliseconds since the user's latest interaction + * with the system on the given display, or -1 if the + * XScreenSaver extension is not supported by the client + * libraries or the X server implementation. + * + * Side effects: + * None. + *---------------------------------------------------------------------- + */ + +long +Tk_GetUserInactiveTime(dpy) + Display *dpy; /* The display for which to query the + * inactive time. */ +{ + long inactiveTime = -1; +#ifdef HAVE_XSS + int eventBase; + int errorBase; + + if (XScreenSaverQueryExtension(dpy, &eventBase, &errorBase)) { + XScreenSaverInfo *info = XScreenSaverAllocInfo(); + + XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info); + inactiveTime = info->idle; + XFree(info); + } +#endif /* HAVE_XSS */ + return inactiveTime; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_ResetUserInactiveTime -- + * + * Reset the user inactivity timer + * + * Results: + * none + * + * Side effects: + * The user inactivity timer of the underlaying windowing system + * is reset to zero. + * + *---------------------------------------------------------------------- + */ + +void +Tk_ResetUserInactiveTime(dpy) + Display *dpy; +{ + XResetScreenSaver(dpy); +} diff --git a/win/tkWinX.c b/win/tkWinX.c index ef99c24..bea8b9f 100644 --- a/win/tkWinX.c +++ b/win/tkWinX.c @@ -10,9 +10,17 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkWinX.c,v 1.45 2005/03/04 01:13:03 hobbs Exp $ + * RCS: @(#) $Id: tkWinX.c,v 1.46 2005/05/27 23:14:29 dkf Exp $ */ +/* + * Make sure the SendInput API is available: + */ +#if (_WIN32_WINNT <= 0x0400) +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0401 +#endif + #include "tkWinInt.h" /* @@ -1837,3 +1845,89 @@ Tk_SetCaretPos(Tk_Window tkwin, int x, int y, int height) } } } + +/* + *---------------------------------------------------------------------- + * + * Tk_GetUserInactiveTime -- + * + * Return the number of milliseconds the user was inactive. + * + * Results: + * Milliseconds of user inactive time or -1 if the user32.dll doesn't + * have the symbol GetLastInputInfo or GetLastInputInfo returns an + * error. + * + * Side effects: + * None. + *---------------------------------------------------------------------- + */ + +long +Tk_GetUserInactiveTime(dpy) + Display *dpy; /* Ignored on Windows */ +{ + struct tagLASTINPUTINFO { + UINT cbSize; + DWORD dwTime; + } li; + /* + * Multiple settings of either of these variables should be OK; + * any thread hazards should just cause inefficiency... + */ + static FARPROC pfnGetLastInputInfo = NULL; + static int initinfo = 0; + + if (!initinfo) { + HMODULE hMod = GetModuleHandleA("USER32.DLL"); + + initinfo = 1; + if (hMod){ + pfnGetLastInputInfo = + GetProcAddress(hMod, "GetLastInputInfo"); + } + } + if (pfnGetLastInputInfo == NULL) { + return -1; + } + li.cbSize = sizeof(li); + if (!(BOOL)(pfnGetLastInputInfo)(&li)) { + return -1; + } + /* last input info is in milliseconds, since restart time. */ + return (GetTickCount()-li.dwTime); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_ResetUserInactiveTime -- + * + * Reset the user inactivity timer + * + * Results: + * none + * + * Side effects: + * The user inactivity timer of the underlaying windowing system + * is reset to zero. + * + *---------------------------------------------------------------------- + */ + +void +Tk_ResetUserInactiveTime(dpy) + Display *dpy; +{ + INPUT inp; + + inp.type=INPUT_MOUSE; + inp.mi.dx=0; + inp.mi.dy=0; + inp.mi.mouseData=0; + inp.mi.dwFlags=MOUSEEVENTF_MOVE; + inp.mi.time=0; + inp.mi.dwExtraInfo=NULL; + + SendInput(1, &inp, sizeof(inp)); +} -- cgit v0.12