Had been trying to instrument sm.cxx to use osl_Conditions to wait till the save operation got completed, but came back to see that the code I was working was different from what was in CVS. Shifted to m90 and this has a behaviour similar to what gedit does, it does not prompt the user for a Save dialog. It directly writes the file to disk and exits. When it is re-launched, it restores itself to the state it was in when it was shut down. The gtk plugin crashes without doing that as well, so will just add in the SessionManagerClient::open calls. Surprise it's already there ? But the gtk plugin still crashes without a prompt. So need to check with the changes made in the cws vclshowstop02 and the changes I was trying out on this build, and need to verify the same thing. That's next. Then to see what changed in between the two editions that causes the change in behaviour. -- The end result of the exercise is the dialog prompting the user on logout in the gen plugin to save all open documents: vcl/source/window/winproc.cxx: case SALEVENT_SHUTDOWN: { static bool bInQueryExit = false; if( !bInQueryExit ) { bInQueryExit = true; if ( GetpApp()->QueryExit() ) { // Message-Schleife beenden Application::Quit(); return FALSE; } else { bInQueryExit = false; return TRUE; } } else return FALSE; } break; This happens when the SHUTDOWN event is launched by: [ GetpApp()->QueryExit() is the one in desktop/source/app/app.cxx which is inside the unlinked soffice.bin, should it be necessary to poke at it...] vcl/source/app/sm.cxx: IMPL_STATIC_LINK( SessionManagerClient, ShutDownHdl, void*, pDummy ) { const std::list< SalFrame* >& rFrames = GetSalData()->GetDisplay()->getFrames(); SMprintf( rFrames.begin() != rFrames.end() ? "shutdown on first frame\n" : "shutdown event but no frame\n" ); if( rFrames.begin() != rFrames.end() ) rFrames.front()->CallCallback( SALEVENT_SHUTDOWN, 0 ); return 0; } which in turn is launched by: vcl/source/app/sm.cxx: void SessionManagerClient::DieProc( SmcConn connection, SmPointer client_data ) { SMprintf( "Session: die\n" ); if( connection == aSmcConnection ) { Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownHdl ) ); SMprintf( "waiting for shutdown event to be processed\n" ); } } The initial problem was that the DieProc was not invoked at all from the gtk plugin. One thing that was not available was the initialization of the SessionManagerClient in gtkframe.cxx, that was done: --- vcl/unx/gtk/window/gtkframe.cxx 2005-03-16 14:56:46.000000000 +0530 +++ vcl/unx/gtk/window/gtkframe.cxx 2005-04-07 18:33:49.868673152 +0530 @@ -84,9 +84,17 @@ #if OSL_DEBUG_LEVEL > 1 #include #endif +#ifndef _VCL_SM_HXX +#include +#endif +#ifndef _OSL_SIGNAL_H_ +#include +#endif int GtkSalFrame::m_nFloats = 0; @@ -864,6 +911,8 @@ void GtkSalFrame::Show( BOOL bVisible, B if( m_pParent ) m_pParent->EndExtTextInput(0); } + + SessionManagerClient::open(); } else { --- vcl/unx/gtk/app/gtkinst.cxx 2005-01-13 23:38:47.000000000 +0530 +++ vcl/unx/gtk/app/gtkinst.cxx 2005-04-07 18:08:28.242914745 +0530 @@ -69,6 +69,9 @@ #if OSL_DEBUG_LEVEL > 1 #include #endif +#ifndef _VCL_SM_HXX +#include +#endif GtkHookedYieldMutex::GtkHookedYieldMutex() { @@ -195,6 +198,8 @@ extern "C" GtkInstance::~GtkInstance() { + // close session management + SessionManagerClient::close(); } SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, ULONG nStyle ) That did initialize the SessionManagerClient, but the Die procedure was still not invoked, so after fiddling with the passOnSaveYourself procedure in salframe.cxx, eventually made these changes: X11SalFrame* X11SalFrame::s_pSaveYourselfFrame = NULL; in salframe.cxx brought into gtkframe.cxx with this patch: --- vcl/unx/gtk/window/gtkframe.cxx 2005-03-16 14:56:46.000000000 +0530 +++ vcl/unx/gtk/window/gtkframe.cxx 2005-04-07 18:33:49.868673152 +0530 @@ -84,9 +84,17 @@ int GtkSalFrame::m_nFloats = 0; +GtkSalFrame* GtkSalFrame::s_pSaveYourselfFrame = NULL; + static USHORT GetModCode( guint state ) { USHORT nCode = 0; @@ -256,6 +264,36 @@ GtkSalFrame::GtkSalFrame( SystemParentDa Init( pSysData ); } +void GtkSalFrame::passOnSaveYourSelf() +{ + fprintf( stderr, "GtkSalFrame::passOnSaveYourSelf()\n" ); + using namespace vcl_sal; + if( this == s_pSaveYourselfFrame ) + { + // pass on SaveYourself + const GtkSalFrame* pFrame = NULL; + const std::list< SalFrame* >& rFrames = GetSalData()->GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it = rFrames.begin(); + while( it != rFrames.end() ) + { + pFrame = static_cast< const GtkSalFrame* >(*it); + if( ! (pFrame->m_nStyle & (SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_CHILD) || pFrame->m_pParent ) ) + break; + ++it; + } + + s_pSaveYourselfFrame = (it != rFrames.end() ) ? const_cast(pFrame) : NULL; + if( s_pSaveYourselfFrame ) + { + Atom a[4]; + int n = 0; + a[n++] = GetSalData()->GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::WM_DELETE_WINDOW ); + a[n++] = GetSalData()->GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::WM_SAVE_YOURSELF ); + XSetWMProtocols( getDisplay()->GetDisplay(), s_pSaveYourselfFrame->GetSystemData()->aShellWindow, a, n ); + } + } +} + GtkSalFrame::~GtkSalFrame() { getDisplay()->deregisterFrame( this ); @@ -277,6 +315,8 @@ GtkSalFrame::~GtkSalFrame() g_object_unref( G_OBJECT(m_pForeignParent) ); if( m_pForeignTopLevel ) g_object_unref(G_OBJECT( m_pForeignTopLevel) ); + + passOnSaveYourSelf(); } void GtkSalFrame::hardIMReset() @@ -504,6 +544,13 @@ void GtkSalFrame::Init( SalFrame* pParen m_aForeignTopLevelWindow = None; m_nStyle = nStyle; + if( ! s_pSaveYourselfFrame && ! m_pParent && ! (m_nStyle & SAL_FRAME_STYLE_CHILD) ) + { + // at all times have only one frame with SaveYourself +// a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_SAVE_YOURSELF ); + s_pSaveYourselfFrame = this; + } + if( m_pParent && m_pParent->m_pWindow && ! (m_pParent->m_nStyle & SAL_FRAME_STYLE_CHILD) ) gtk_window_set_screen( m_pWindow, gtk_window_get_screen( m_pParent->m_pWindow ) ); @@ -1517,6 +1566,8 @@ bool GtkSalFrame::SetPluginParent( Syste if( m_pForeignTopLevel ) g_object_unref( G_OBJECT(m_pForeignTopLevel) ); + passOnSaveYourSelf(); + // init new window if( pSysParent && pSysParent->aWindow != None ) Init( pSysParent ); --- vcl/unx/inc/plugins/gtk/gtkframe.hxx 2005-03-16 14:56:46.000000000 +0530 +++ vcl/unx/inc/plugins/gtk/gtkframe.hxx 2005-04-07 14:06:59.668222775 +0530 @@ -159,6 +159,8 @@ class GtkSalFrame : public SalFrame } }; + static GtkSalFrame* s_pSaveYourselfFrame; + GtkWindow* m_pWindow; GdkWindow* m_pForeignParent; GdkNativeWindow m_aForeignParentWindow; @@ -355,6 +357,7 @@ public: virtual bool SetPluginParent( SystemParentData* pNewParent ); virtual void SetBackgroundBitmap( SalBitmap* ); + void passOnSaveYourSelf(); }; With this, finally the die procedure seemed to be invoked or so it seems because now it seems to be invoked even if just the two lines of SessionManagerClient::open and close remain. Now the problem is that while the GtkSalFrame's Die Procedure is invoked and the dialog prompting the user does come up, the whole application is killed off by X instantly, whereas in the X11SalFrame, the prompt remains long enough to save all the open documents. The gtk plugin does get the Die Procedure and the save prompt up, but the X Session dies killing off all the applications which X11SalFrame is managing to hold on to till saving is complete. FWIW, I got the gen plugin to crash practically the same way the gtk one crashes commenting out the one line: --- vcl/unx/source/window/salframe.cxx 2005-03-16 14:56:46.000000000 +0530 +++ vcl/unx/source/window/salframe.cxx 2005-04-08 16:37:42.142357942 +0530 @@ -563,7 +563,7 @@ void X11SalFrame::Init( ULONG nSalFrameS { // at all times have only one frame with SaveYourself a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_SAVE_YOURSELF ); - s_pSaveYourselfFrame = this; +// s_pSaveYourselfFrame = this; } if( (nSalFrameStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_TAKE_FOCUS ); However, now commenting it out does not seem to be affecting it either now so ... :-O ------- These are the notes Raul should have taken: * Code reading in vcl/unx/source/app/sm.txt + follow 'Save Yourself' Callback is connected to void SessionManagerClient::SaveYourselfProc( posts event: Application::PostUserEvent( STATIC_LINK( (void*)(shutdown ? 0xffffffff : 0x0), SessionManagerClient, SaveYourselfHdl ) ); SMprintf( "waiting for save yourself event to be processed\n" ); * Query - does that really wait ? vcl/source/app/svapp.cxx: BOOL Application::PostUserEvent( ULONG& rEventId, ULONG nEvent, void* pEventData ) rEventId = (ULONG)pSVEvent; if ( ImplGetDefaultWindow()->ImplGetFrame()->PostEvent( pSVEvent ) ) return TRUE; app/svdata.cxx (ImplGetDefaultWindow): + returns a Window * - some random window. window/window2.cxx (ImplGetFrame) + returns a SalFrame * simple method accessor salframe.cxx: BOOL X11SalFrame::PostEvent( void *pData ) { GetDisplay()->SendInternalEvent( this, pData ); return TRUE; } gtkframe.cxx: BOOL GtkSalFrame::PostEvent( void* pData ) { getDisplay()->SendInternalEvent( this, pData ); return TRUE; } source/app/saldisp.cxx void SalDisplay::SendInternalEvent( SalFrame* pFrame, void* pData, USHORT nEvent ) { if( osl_acquireMutex( hEventGuard_ ) ) { m_aUserEvents.push_back( SalUserEvent( pFrame, pData, nEvent ) ); // Notify SalXLib::Yield() of a pending event. pXLib_->PostUserEvent(); osl_releaseMutex( hEventGuard_ ); } else DBG_ASSERT( 1, "SalDisplay::SendEvent !acquireMutex\n" ); } Bounces to the main thread and runs: IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, pDummy ) { SMprintf( "posting save documents event shutdown = %s\n", (pThis!=0) ? "true" : "false" ); if( pOneInstance ) { SalSessionSaveRequestEvent aEvent( pThis != 0, false ); pOneInstance->CallCallback( &aEvent ); } else saveDone(); return 0; } pOneInstance != NULL is the common case: -> 'CallCallback' is inherited from IceSalSession: vcl/unx/inc/sm.hxx: class IceSalSession : public SalSession instantiated from vcl/unx/inc/salinst.h (CreateSalSession) not overridden in GtkInstance; implemented in sm.cxx. Implemented in vcl/inc/salsession.hxx, that has: void SetCallback( SessionProc aCallback ) { m_aProc = aCallback; } void CallCallback( SalSessionEvent* pEvent ) { if( m_aProc ) m_aProc( pEvent ); } The callback is set - cf. vcl/source/app/session.cxx: m_pSession = ImplGetSVData()->mpDefInst->CreateSalSession(); if( m_pSession ) m_pSession->SetCallback( SalSessionEventProc ); With the 'gen' plugin, it _seems_ that OO.o is serializing the session on exit, and re-loading it on startup - regardless of the state of 'save session'. The session mgmt spec. here: http://netmirror.org/mirror/xfree86.org/4.1.0/doc/SMlib.TXT says: [snip] The session manager sends a ``Save Yourself'' message to a client either to checkpoint it or just before termination so that it can save its state. The client responds with zero or more calls to SmcSetProperties to update the properties indicating how to restart the client. When all the proper- ties have been set, the client calls SmcSaveYourselfDone. If interact_style is SmInteractStyleNone, the client must not interact with the user while saving state. If inter- act_style is SmInteractStyleErrors, the client may interact with the user only if an error condition arises. If inter- act_style is SmInteractStyleAny, then the client may inter- act with the user for any purpose. Because only one client can interact with the user at a time, the client must call SmcInteractRequest and wait for an ``Interact'' message from the session manager. When the client is done interacting with the user, it calls SmcInteractDone. The client may only call SmcInteractRequest after it receives a ``Save Yourself'' message and before it calls SmcSaveYourselfDone. [snip] We - however, discard our 'interact_style' immediately inside 'SaveYourselfProc'; indeed - everything except 'shutdown' Gnumeric's gnome-client.c usage does: static gboolean client_save_yourself_cb (GnomeClient *client, int phase, GnomeSaveStyle what_to_save, gboolean end, GnomeInteractStyle interaction, gboolean fast, gpointer data) { gboolean res = TRUE; if (!end) return TRUE; /* If we aren't shutting down, don't bother us any further. */ gnome_client_set_current_directory (client, current_dir); if (!(interaction == GNOME_INTERACT_ANY)) res = FALSE; else gnome_client_request_interaction (client, GNOME_DIALOG_NORMAL, interaction_function, NULL); set_clone_restart (client); return res; } static void interaction_function (GnomeClient *client, gint key, GnomeDialogType dialog_type, gpointer shutdown) { gboolean do_not_cancel = FALSE; ... gnome_interaction_key_return (key, do_not_cancel); } /** * gnome_client_request_interaction: ... * Description: Use the following functions, if you want to interact with the * user during a "save_yourself" handler without being restricted to using the * dialog based commands gnome_client_save_any_dialog() or * gnome_client_save_error_dialog(). Note, however, that overriding the session * manager specified preference in this way (by using arbitrary dialog boxes) * is not very nice. * * If and when the session manager decides that it's the app's turn to interact * then 'func' will be called with the specified arguments and a unique * 'GnomeInteractionKey'. The session manager will block other * clients from interacting until this key is returned with * gnome_interaction_key_return(). **/ * I also get crashers from incorrect resource setup, on forking 'soffice.bin' SalSessionSaveRequestEvent & SalSessionInteractionEvent are both trapped in: vcl/source/app/session.cxx (SalSessionEventProc) calls pOneInstance->callSaveRequested or pOneInstance->callInteractionGranted static VCLSession *pOneInstance ... This code fires off an UNO 'doSave' method to all it's XSessionManagerListener objects; or an 'approveInteraction' method. #define SESSION_SERVICE_IMPL_NAME "com.sun.star.frame.VCLSessionManagerClient" #define SESSION_SERVICE_SERVICE_NAME "com.sun.star.frame.SessionManagerClient" The XSessionManagerListener is implemented / used in: desktop/source/app/app.cxx - startup / reload code ? // session management try { Reference< XInitialization > aListener(::comphelper::getProcessServiceFactory()->createInstance( OUString::createFromAscii("com.sun.star.frame.SessionListener")), UNO_QUERY); if (aListener.is()) { aListener->initialize(Sequence< Any >(0)); Reference< XSessionManagerListener > r(aListener, UNO_QUERY); if (r.is() && !bLoaded) bLoaded = r->doRestore(); } } * Presumably calling directly into the Framework - avoiding vcl: and: framework/source/services/sessionlistener.cxx doSave() here ignores 'bCancelable' completely ... SessionListener::approveInteraction + fires a condition '_pcInteract' + Someone had to call _requestInteraction() first * Who calls _requestInteraction ? + seemingly no-one ... * Users of sessionlistener.hxx: + framework/source/register/registerservices.cxx ** Could the crash-on-login bug be related to getting an application name of 'soffice.bin' - when it needs to be soffice to have any chance of finding the right resources ? cf. String SessionManagerClient::getExecName() This turns 'soffice.bin' into 'soffice' vcl/unx/source/app/saldisp.cxx: // set client leader (session id gets set when session is started) if( hRefWindow_ ) { // client leader must have WM_CLIENT_LEADER pointing to itself XChangeProperty( pDisp_, hRefWindow_, XInternAtom( pDisp_, "WM_CLIENT_LEADER", False ), XA_WINDOW, 32, PropModeReplace, (unsigned char*)&hRefWindow_, 1); ByteString aExec( SessionManagerClient::getExecName(), osl_getThreadTextEncoding() ); const char* argv[2]; argv[0] = "/bin/sh"; argv[1] = aExec.GetBuffer(); XSetCommand( pDisp_, hRefWindow_, const_cast(argv), 2 ); ... Gnome doesn't ever do an XSetCommand though - quite possibly it relies on the SmRestartCommand functionality that gnome-client implements. We seem to create a correct SmRestartCommand property; but only send it on SaveYourself ( BuildSmPropertyList vs. saveDone ). ** If we run 'oowriter2' do we get 'soffice' as the correct save exec. name ? the gnome-client sets this property on startup. + Running with 'oowriter2' -> 'Save setup' I get: + Program: '/usr/lib/ooo-2.0/program/soffice.bin -writer' [ this will crash on startup ] + Running with PLUGIN=gen oowriter3 -> 'Save Setup' I get: + /usr/lib/ooo-2.0/program/soffice ** QED - it isn't communicating the correct program name to the session manager somehow.