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