explorer: implement X session management
Damjan Jovanovic
damjan.jov at gmail.com
Thu Jul 29 08:18:34 CDT 2010
Changelog:
* explorer: implement X session management
Gives Wine applications a chance to save their documents and exit
gracefully on system shutdown. Closes #16188.
The patch needs autoconf and autoheader to be run.
Damjan Jovanovic
-------------- next part --------------
diff --git a/configure.ac b/configure.ac
index b7373b2..cf3f9e9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,6 +77,8 @@ AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]),
AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]),
[if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi])
AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
+AC_ARG_WITH(sm, AS_HELP_STRING([--without-sm],[do not use SM (X session management support)]),
+ [if test "x$withval" = "xno"; then ac_cv_header_X11_SM_SMlib_h=no; fi])
AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]),
[if test "x$withval" = "xno"; then ac_cv_header_tiffio_h=no; fi])
AC_ARG_WITH(v4l, AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
@@ -891,7 +893,8 @@ then
X11/extensions/Xrandr.h \
X11/extensions/Xrender.h \
X11/extensions/xf86vmode.h \
- X11/extensions/xf86vmproto.h],,,
+ X11/extensions/xf86vmproto.h \
+ X11/SM/SMlib.h],,,
[#ifdef HAVE_X11_XLIB_H
# include <X11/Xlib.h>
#endif
@@ -992,6 +995,14 @@ then
WINE_NOTICE_WITH(xcomposite,[test "x$ac_cv_lib_soname_Xcomposite" = "x"],
[libxcomposite ${notice_platform}development files not found, Xcomposite won't be supported.])
+ dnl *** Check for X SM
+ if test "$ac_cv_header_X11_SM_SMlib_h" = "yes"
+ then
+ WINE_CHECK_SONAME(SM,SmcOpenConnection,,,[$X_LIBS -lXext -lX11 $X_EXTRA_LIBS])
+ fi
+ WINE_NOTICE_WITH(SM,[test "x$ac_cv_lib_soname_SM" = "x"],
+ [libSM ${notice_platform}development files not found, X session management won't be supported.])
+
dnl *** Check for XICCallback struct
AC_CHECK_MEMBERS([XICCallback.callback],,,
[#ifdef HAVE_X11_XLIB_H
diff --git a/programs/explorer/Makefile.in b/programs/explorer/Makefile.in
index b61d32d..c199903 100644
--- a/programs/explorer/Makefile.in
+++ b/programs/explorer/Makefile.in
@@ -7,6 +7,8 @@ MODULE = explorer.exe
APPMODE = -mwindows -municode
IMPORTS = rpcrt4 user32 gdi32 advapi32
DELAYIMPORTS = comctl32
+EXTRAINCL = @X_CFLAGS@
+EXTRALIBS = @X_LIBS@ @X_PRE_LIBS@ @X_EXTRA_LIBS@
C_SRCS = \
appbar.c \
diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c
index 67ade26..fde3b3a 100644
--- a/programs/explorer/desktop.c
+++ b/programs/explorer/desktop.c
@@ -29,7 +29,14 @@
#include <wine/debug.h>
#include "explorer_private.h"
+#include <errno.h>
+#include <sys/poll.h>
+#ifdef HAVE_X11_SM_SMLIB_H
+#include <X11/SM/SMlib.h>
+#endif
+
WINE_DEFAULT_DEBUG_CHANNEL(explorer);
+WINE_DECLARE_DEBUG_CHANNEL(session);
#define DESKTOP_CLASS_ATOM ((LPCWSTR)MAKEINTATOM(32769))
#define DESKTOP_ALL_ACCESS 0x01ff
@@ -251,6 +258,207 @@ static void set_desktop_window_title( HWND hwnd, const WCHAR *name )
HeapFree( GetProcessHeap(), 0, window_titleW );
}
+#ifdef SONAME_LIBSM
+
+static BOOL CALLBACK broadcast_query_end_session(HWND hwnd, LPARAM lp)
+{
+ LRESULT lresult = SendMessageW(hwnd, WM_QUERYENDSESSION, 0, 0);
+ if (!lresult)
+ {
+ WINE_TRACE_(session)("window %p refusing to end session\n", hwnd);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL CALLBACK broadcast_end_session(HWND hwnd, LPARAM lp)
+{
+ SendMessageW(hwnd, WM_ENDSESSION, 0, 0);
+ return TRUE;
+}
+
+static void set_sm_properties(SmcConn smcConn)
+{
+ SmProp properties[5];
+ SmPropValue values[5];
+ SmProp *pProperties[5];
+
+ properties[0].name = SmCloneCommand;
+ properties[0].type = SmLISTofARRAY8;
+ properties[0].num_vals = 1;
+ properties[0].vals = &values[0];
+ values[0].value = "wine";
+ values[0].length = strlen(values[0].value);
+
+ properties[1].name = SmProgram;
+ properties[1].type = SmARRAY8;
+ properties[1].num_vals = 1;
+ properties[1].vals = &values[1];
+ values[1].value = "wine";
+ values[1].length = strlen(values[1].value);
+
+ properties[2].name = SmRestartCommand;
+ properties[2].type = SmLISTofARRAY8;
+ properties[2].num_vals = 1;
+ properties[2].vals = &values[2];
+ values[2].value = "wine";
+ values[2].length = strlen(values[2].value);
+
+ properties[3].name = SmUserID;
+ properties[3].type = SmARRAY8;
+ properties[3].num_vals = 1;
+ properties[3].vals = &values[3];
+ values[3].value = "wine";
+ values[3].length = strlen(values[3].value);
+
+ properties[4].name = SmRestartStyleHint;
+ properties[4].type = SmCARD8;
+ properties[4].num_vals = 1;
+ properties[4].vals = &values[4];
+ values[4].value = "\x03"; /* SmRestartNever */
+ values[4].length = 1;
+
+ pProperties[0] = &properties[0];
+ pProperties[1] = &properties[1];
+ pProperties[2] = &properties[2];
+ pProperties[3] = &properties[3];
+ pProperties[4] = &properties[4];
+
+ SmcSetProperties(smcConn, 4, pProperties);
+}
+
+static void sm_interact(SmcConn smcConn, SmPointer clientData)
+{
+ WINE_TRACE_(session)("(%p, %p)\n", smcConn, clientData);
+
+ if (EnumWindows(broadcast_query_end_session, (LONG_PTR)NULL) == FALSE)
+ {
+ WINE_TRACE_(session)("refusing shutdown\n");
+ SmcInteractDone(smcConn, True);
+ }
+ else
+ {
+ WINE_TRACE_(session)("allowing shutdown\n");
+ EnumWindows(broadcast_end_session, (LONG_PTR)NULL);
+ SmcInteractDone(smcConn, False);
+ }
+ SmcSaveYourselfDone(smcConn, True);
+}
+
+static void sm_save_yourself(SmcConn smcConn, SmPointer clientData, int saveType,
+ Bool shutdown, int interactStyle, Bool fast)
+{
+ WINE_TRACE_(session)("(%p, %p, %d, %d, %d, %d)\n", smcConn, clientData, saveType,
+ shutdown, interactStyle, fast);
+
+ set_sm_properties(smcConn);
+
+ if (!shutdown || interactStyle != SmInteractStyleAny)
+ {
+ SmcSaveYourselfDone(smcConn, True);
+ return;
+ }
+
+ WINE_TRACE_(session)("requesting interaction\n");
+ if (SmcInteractRequest(smcConn, SmDialogNormal, sm_interact, NULL) == 0)
+ {
+ WINE_TRACE_(session)("interact request failed\n");
+ SmcSaveYourselfDone(smcConn, False);
+ return;
+ }
+}
+
+static void sm_save_complete(SmcConn smcConn, SmPointer clientData)
+{
+ WINE_TRACE_(session)("(%p, %p)\n", smcConn, clientData);
+}
+
+static void sm_die(SmcConn smcConn, SmPointer clientData)
+{
+ DWORD recipients = BSM_APPLICATIONS;
+ WINE_TRACE_(session)("(%p, %p)\n", smcConn, clientData);
+ BroadcastSystemMessageW(0, &recipients, WM_ENDSESSION, TRUE, 0);
+}
+
+static void sm_shutdown_cancelled(SmcConn smcConn, SmPointer clientData)
+{
+ DWORD recipients = BSM_APPLICATIONS;
+ WINE_TRACE_(session)("(%p, %p)\n", smcConn, clientData);
+ BroadcastSystemMessageW(0, &recipients, WM_ENDSESSION, FALSE, 0);
+}
+
+static DWORD WINAPI manage_session(LPVOID arg)
+{
+ char *clientID;
+ char error_string[1024];
+ SmcConn smc_conn;
+ SmcCallbacks callbacks = {
+ { sm_save_yourself, NULL },
+ { sm_die, NULL },
+ { sm_save_complete, NULL },
+ { sm_shutdown_cancelled, NULL }
+ };
+
+ smc_conn = SmcOpenConnection(NULL, NULL, 1, 0,
+ SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask,
+ &callbacks, NULL, &clientID,
+ sizeof(error_string), error_string);
+ if (smc_conn == NULL)
+ {
+ WINE_ERR_(session)("opening SM connection failed: %s\n", error_string);
+ return 0;
+ }
+ else
+ WINE_TRACE_(session)("SM connected\n");
+
+ while (smc_conn != NULL)
+ {
+ IceConn ice_conn;
+ struct pollfd polldata;
+ char c;
+ int r;
+
+ ice_conn = SmcGetIceConnection(smc_conn);
+ polldata.fd = IceConnectionNumber(ice_conn);
+ polldata.events = POLLIN;
+ r = poll(&polldata, 1, -1);
+ if (r < 0)
+ {
+ WINE_ERR("poll failed, errno=%d\n", errno);
+ break;
+ }
+ else if (r > 0 && (polldata.revents & POLLIN))
+ {
+ r = recv(polldata.fd, &c, 1, MSG_PEEK);
+ if (r <= 0)
+ {
+ WINE_ERR("SM connection closed\n");
+ SmcCloseConnection(smc_conn, 0, NULL);
+ smc_conn = NULL;
+ }
+ else
+ {
+ if (IceProcessMessages(ice_conn, NULL, NULL) != IceProcessMessagesSuccess)
+ {
+ WINE_ERR("closing SM connection\n");
+ SmcCloseConnection(smc_conn, 0, NULL);
+ smc_conn = NULL;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+#endif /* SONAME_LIBSM */
+
+static void start_session_management(void)
+{
+#ifdef SONAME_LIBSM
+ CreateThread(NULL, 0, manage_session, NULL, 0, NULL);
+#endif /* SONAME_LIBSM */
+}
+
/* main desktop management function */
void manage_desktop( WCHAR *arg )
{
@@ -343,6 +551,8 @@ void manage_desktop( WCHAR *arg )
{
pShellDDEInit( TRUE );
}
+
+ start_session_management();
}
else
{
More information about the wine-patches
mailing list