
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 <cstdio>
 #endif
+#ifndef _VCL_SM_HXX
+#include <sm.hxx>
+#endif
+#ifndef _OSL_SIGNAL_H_
+#include <osl/signal.h>
+#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 <cstdio>
 #endif
+#ifndef _VCL_SM_HXX
+#include <sm.hxx>
+#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<GtkSalFrame*>(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<char**>(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.


