Index: doc/wm.n ================================================================== --- doc/wm.n +++ doc/wm.n @@ -658,13 +658,20 @@ handler by one which responds differently. .RE .RS .PP The list of available window manager protocols depends on the window manager, -but all window managers supported by Tk provide \fBWM_DELETE_WINDOW\fR. On the -Windows platform, a \fBWM_SAVE_YOURSELF\fR message is sent on user logout -or system restart. +but all window managers supported by Tk provide \fBWM_DELETE_WINDOW\fR. +.RE +.RS +.PP +On the Windows platform, the following additional messages are implemented. +\fBWM_SAVE_YOURSELF\fR is sent on user logout or system restart. +\fBWTS_CONSOLE_CONNECT\fR and \fBWTS_CONSOLE_DISCONNECT\fR are sent on console connect/disconnect like a user change. +\fBWTS_REMOTE_CONNECT\fR and \fBWTS_REMOTE_DISCONNECT\fR are sent on remote connect/disconnect. +\fBWTS_SESSION_LOGON\fR and \fBWTS_SESSION_LOGOFF\fR are sent on session logon/logoff. +\fBWTS_SESSION_LOCK\fR and \fBWTS_SESSION_UNLOCK\fR are sent when the session is locked by the user or on any upper action (as session lock is a side effect). .RE .RS .PP If both \fIname\fR and \fIcommand\fR are specified, then \fIcommand\fR becomes the handler for the protocol specified by \fIname\fR. The atom for \fIname\fR Index: win/configure ================================================================== --- win/configure +++ win/configure @@ -4511,11 +4511,11 @@ if test "${GCC}" = "yes" ; then SHLIB_LD="" SHLIB_LD_LIBS='${LIBS}' LIBS="-lnetapi32 -lkernel32 -luser32 -ladvapi32 -luserenv -lws2_32" # mingw needs to link ole32 and oleaut32 for [send], but MSVC doesn't - LIBS_GUI="-lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32 -lwinspool" + LIBS_GUI="-lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32 -lwinspool -lwtsapi32" STLIB_LD='${AR} cr' RC_OUT=-o RC_TYPE= RC_INCLUDE=--include RC_DEFINE=--define @@ -4724,11 +4724,11 @@ CFLAGS_OPTIMIZE="-nologo -O2 ${runtime}" lflags="${lflags} -nologo" LINKBIN="link" fi - LIBS_GUI="gdi32.lib comdlg32.lib imm32.lib comctl32.lib shell32.lib uuid.lib winspool.lib" + LIBS_GUI="gdi32.lib comdlg32.lib imm32.lib comctl32.lib shell32.lib uuid.lib winspool.lib wtsapi32.lib" SHLIB_LD="${LINKBIN} -dll -incremental:no ${lflags}" SHLIB_LD_LIBS='${LIBS}' # link -lib only works when -lib is the first arg STLIB_LD="${LINKBIN} -lib ${lflags}" Index: win/makefile.vc ================================================================== --- win/makefile.vc +++ win/makefile.vc @@ -358,11 +358,11 @@ !endif PRJ_DEFINES = /DBUILD_ttk $(CONFIG_DEFS) /Dinline=__inline /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE # Additional Link libraries needed beyond those in rules.vc -PRJ_LIBS = netapi32.lib gdi32.lib user32.lib userenv.lib winspool.lib shell32.lib ole32.lib uuid.lib +PRJ_LIBS = netapi32.lib gdi32.lib user32.lib userenv.lib winspool.lib shell32.lib ole32.lib uuid.lib wtsapi32.lib #--------------------------------------------------------------------- # TkTest flags #--------------------------------------------------------------------- Index: win/tcl.m4 ================================================================== --- win/tcl.m4 +++ win/tcl.m4 @@ -652,11 +652,11 @@ if test "${GCC}" = "yes" ; then SHLIB_LD="" SHLIB_LD_LIBS='${LIBS}' LIBS="-lnetapi32 -lkernel32 -luser32 -ladvapi32 -luserenv -lws2_32" # mingw needs to link ole32 and oleaut32 for [send], but MSVC doesn't - LIBS_GUI="-lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32 -lwinspool" + LIBS_GUI="-lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32 -lwinspool -lwtsapi32" STLIB_LD='${AR} cr' RC_OUT=-o RC_TYPE= RC_INCLUDE=--include RC_DEFINE=--define @@ -841,11 +841,11 @@ CFLAGS_OPTIMIZE="-nologo -O2 ${runtime}" lflags="${lflags} -nologo" LINKBIN="link" fi - LIBS_GUI="gdi32.lib comdlg32.lib imm32.lib comctl32.lib shell32.lib uuid.lib winspool.lib" + LIBS_GUI="gdi32.lib comdlg32.lib imm32.lib comctl32.lib shell32.lib uuid.lib winspool.lib wtsapi32.lib" SHLIB_LD="${LINKBIN} -dll -incremental:no ${lflags}" SHLIB_LD_LIBS='${LIBS}' # link -lib only works when -lib is the first arg STLIB_LD="${LINKBIN} -lib ${lflags}" Index: win/tkWinWm.c ================================================================== --- win/tkWinWm.c +++ win/tkWinWm.c @@ -10,17 +10,26 @@ * Copyright © 1998-2000 Scriptics Corporation. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ - #include "tkWinInt.h" #include #include #include #include #include + +/* + * Macro CurrentTime is defined in X.h. + * CurrentTime is used as a variable name in the following include. + * Thus, undefine it and define it back afterwards + */ + +#undef CurrentTime +#include +#define CurrentTime 0L /* special Time */ #include "tkWinIco.h" /* * These next two defines are only valid on Win2K/XP+. */ @@ -4929,11 +4938,75 @@ break; } } cmd = Tcl_GetStringFromObj(objv[4], &cmdLength); if (cmdLength > 0) { - protPtr = (ProtocolHandler *)ckalloc(HANDLER_SIZE(cmdLength)); + const char *item; + TkSizeT itemLength; + + /* + * Activate notification by WM_WTSSESSION_CHANGE, for relevant + * messages. Note: this does not harm, if activated erroneously, + * so be relax on the protocol name and only check for prefix + * "WTS_". + */ + + item = Tcl_GetStringFromObj(objv[3], &itemLength); + if ((itemLength > 4) && (0 == memcmp(item,"WTS_", 4))) { + + /* + * Unregister callback on Windows level. + * This is required, to avoid multiple notifications due to + * multiple registrations. We first clear an eventual registration + * here and recreate it back below. If there was no prior + * registration, the call fails, which is ok and is ignored. + * + * Note: the Windows API requires to call + * WTSUnRegisterSessionNotification for each prior call to + * WTSRegisterSessionNotification. This would require to store + * the registration fact somewhere and to call unregister on + * windows destroy if registered. This is currently not implemented + * and has probably no negative impacts. + */ + + if (NULL != winPtr->wmInfoPtr + && NULL != winPtr->wmInfoPtr->wrapper) { + WTSUnRegisterSessionNotification( + winPtr->wmInfoPtr->wrapper); + } + + /* + * Be sure that the window exists. If not, try to make it exist. + * This is required, if no "update" was run after windows + * creation. + */ + + if (NULL == winPtr->wmInfoPtr + || NULL == winPtr->wmInfoPtr->wrapper) { + Tk_MakeWindowExist((Tk_Window)winPtr); + } + + /* + * Activate notification by message WM_WTSSESSION_CHANGE. + */ + + if (NULL == winPtr->wmInfoPtr + || NULL == winPtr->wmInfoPtr->wrapper + || ! WTSRegisterSessionNotification( + winPtr->wmInfoPtr->wrapper, + NOTIFY_FOR_THIS_SESSION) + ) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "registration failed for window \"%s\"", + Tk_PathName(winPtr))); + Tcl_SetErrorCode(interp, "TK", "WM", "PROTOCOL", "FAIL", + objv[3], NULL); + return TCL_ERROR; + } + } + + protPtr = (ProtocolHandler *)ckalloc(HANDLER_SIZE(cmdLength)); protPtr->protocol = protocol; protPtr->nextPtr = wmPtr->protPtr; wmPtr->protPtr = protPtr; protPtr->interp = interp; memcpy(protPtr->command, cmd, cmdLength + 1); @@ -7951,10 +8024,86 @@ event.xclient.data.l[0] = Tk_InternAtom((Tk_Window) winPtr, "WM_SAVE_YOURSELF"); TkWmProtocolEventProc(winPtr, &event); break; } + + case WM_WTSSESSION_CHANGE: { + DWORD SessionID; + XEvent event; + + /* + * Check, if it is my session. + * Normally, we get only notifications about our session. + * But, if WTSRegisterSessionNotification is called somewhere else + * with all sessions, filtering may be helpful. + */ + + if ( 0 == ProcessIdToSessionId(GetCurrentProcessId(),&SessionID) + || SessionID != lParam) { + break; + } + + /* + * Filter the notification items we take. + * Note, that the following notifications are not reported: + * WTS_SESSION_REMOTE_CONTROL, WTS_SESSION_CREATE, + * WTS_SESSION_TERMINATE. + */ + + switch (wParam) { + case WTS_CONSOLE_CONNECT: + case WTS_CONSOLE_DISCONNECT: + case WTS_REMOTE_CONNECT: + case WTS_REMOTE_DISCONNECT: + case WTS_SESSION_LOGON: + case WTS_SESSION_LOGOFF: + case WTS_SESSION_LOCK: + case WTS_SESSION_UNLOCK: + + winPtr = GetTopLevel(hwnd); + event.xclient.message_type = + Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"); + + switch (wParam) { + case WTS_CONSOLE_CONNECT: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_CONSOLE_CONNECT"); + break; + case WTS_CONSOLE_DISCONNECT: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_CONSOLE_DISCONNECT"); + break; + case WTS_REMOTE_CONNECT: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_REMOTE_CONNECT"); + break; + case WTS_REMOTE_DISCONNECT: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_REMOTE_DISCONNECT"); + break; + case WTS_SESSION_LOGON: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_SESSION_LOGON"); + break; + case WTS_SESSION_LOGOFF: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_SESSION_LOGOFF"); + break; + case WTS_SESSION_LOCK: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_SESSION_LOCK"); + break; + case WTS_SESSION_UNLOCK: + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WTS_SESSION_UNLOCK"); + break; + } + TkWmProtocolEventProc(winPtr, &event); + break; + } + } default: break; }