From: Damjan Jovanovic Subject: explorer: implement X session management Message-Id: Date: Thu, 29 Jul 2010 15:18:34 +0200 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 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 #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 #include "explorer_private.h" +#include +#include +#ifdef HAVE_X11_SM_SMLIB_H +#include +#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 {