Skip to content

Commit

Permalink
Add a new function for controlling Natlink's message window
Browse files Browse the repository at this point in the history
Re: dictation-toolbox#213.

The new function is setMessageWindow().  I have documented it in the
NatlinkSource/natlink.txt file.

It is now necessary to call this function with a Python callback to
enable the message window.  The default callback will soon reside in
the natlinkcore code and be set from appsupp.cpp.

Since it is related, this changeset includes modifications to the
message window's File>Reload logic, re: dictation-toolbox#28.  The default callback
will do a narrower reloading of user modules.
  • Loading branch information
drmfinlay committed Oct 20, 2024
1 parent 9eee5b9 commit c4b1997
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 40 deletions.
115 changes: 75 additions & 40 deletions NatlinkSource/DragonCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1203,37 +1203,45 @@ void CDragonCode::onTimer()

void CDragonCode::onMenuCommand( WPARAM wParam )
{
if( LOWORD(wParam) == IDD_RELOAD )
{

// currently we do not support this operation if we are using thread
// support because I have not worked through the issues about what
// to do about the thread state
if( m_pThreadState )
{
return;
}
WORD nMenuItem = LOWORD( wParam );
switch ( nMenuItem )
{
case IDD_RELOAD:
// note by DF:
// PyWin32, a library we all depend on, does not support
// interpreter reinitialization. Please see the following
// page for more information:
// https://mail.python.org/pipermail/python-win32/2013-January/012671.html
//
// With this limitation in mind, I have adjusted Natlink's
// reload mechanism for narrower code-reload.

// reload the Python subsystem
displayText( "Reloading Python subsystem...\r\n", FALSE, FALSE );

// Although we do not really care about the Python reference count
// we do want to reset the callbacks so we do not make a call into
// an obselete intrepreter.
// reset the callbacks set from Python
setChangeCallback( Py_None );
setBeginCallback( Py_None );
setTimerCallback( Py_None );
setTrayIcon( "", "", Py_None );

// We call this because the reinitialization will not free up the
// python objects. Note that we do free the COM objects but we do
// not free the Python objects. This means that we will have a
// minor memory leak but no object leaks (which would prevent
// shutdown of NatSpeak).
// release user Python-Natlink objects
releaseObjects();
break;

m_pAppClass->reloadPython();
case IDD_EXIT:
// nothing special to do here
break;

default:
return;
}

// invoke the message window callback for menu commands handled
// above
makeCallback(
m_pMessageWindowCallback,
Py_BuildValue( "(i)", nMenuItem ) );
}

//---------------------------------------------------------------------------
Expand Down Expand Up @@ -1536,16 +1544,10 @@ BOOL CDragonCode::initSecondWindow()
return FALSE;
}

// we store out class pointer in the window's extra data field so we
// we store our class pointer in the window's extra data field so we
// get called back when a menu message occurs
SetWindowLong( m_hMsgWnd, 0, (LONG)this );

// tell the thread about out message window
if( m_pSecdThrd )
{
m_pSecdThrd->setMsgWnd( m_hMsgWnd );
}

return TRUE;
}

Expand Down Expand Up @@ -1687,14 +1689,6 @@ BOOL CDragonCode::natConnect( IServiceProvider * pIDgnSite, BOOL bUseThreads )
}
#endif

// here we start the second thread for displaying messages; we only need
// this when we are called as a compatibility module

if( pIDgnSite != NULL )
{
m_pSecdThrd = new MessageWindow( 0 );
}

// Connect to NatSpeak

if( !initGetSiteObject( pIDgnSite ) )
Expand Down Expand Up @@ -1882,12 +1876,8 @@ BOOL CDragonCode::natDisconnect()
m_pIDgnSRTraining = NULL;
m_pISRCentral = NULL;

// shutdown the second thread
if( m_pSecdThrd )
{
delete m_pSecdThrd;
m_pSecdThrd = NULL;
}
// kill the message window
setMessageWindow( Py_None );

// destroy our hidden window
if( m_hMsgWnd )
Expand Down Expand Up @@ -3693,6 +3683,51 @@ BOOL CDragonCode::setTrayIcon(
return TRUE;
}

//---------------------------------------------------------------------------

BOOL CDragonCode::setMessageWindow(
PyObject * pCallback, DWORD dwFlags )
{
if( pCallback == Py_None )
{
Py_XDECREF( m_pMessageWindowCallback );
m_pMessageWindowCallback = NULL;

// shutdown the second thread
if( m_pSecdThrd )
{
delete m_pSecdThrd;
m_pSecdThrd = NULL;
}
}
else
{
NOTBEFORE_INIT( "setMessageWindow" );

Py_XINCREF( pCallback );
Py_XDECREF( m_pMessageWindowCallback );
m_pMessageWindowCallback = pCallback;

// start the second thread for displaying messages, if
// necessary. Otherwise, pass on the second parameter
if( !m_pSecdThrd )
{
m_pSecdThrd = new MessageWindow( dwFlags );

// tell the thread about our message window
m_pSecdThrd->setMsgWnd( m_hMsgWnd );
}
else
{
m_pSecdThrd->updateWindow( dwFlags );
}
}

return TRUE;
}

//---------------------------------------------------------------------------

void CDragonCode::logCookie( const char * pText, QWORD qCookie )
{
DWORD *pCookie = (DWORD*)&qCookie;
Expand Down
5 changes: 5 additions & 0 deletions NatlinkSource/DragonCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class CDragonCode
m_pszLogFile = NULL;
m_bHasTrayIcon = FALSE;
m_pTrayIconCallback = NULL;
m_pMessageWindowCallback = NULL;
m_pMessageStack = NULL;
m_pIDgnSSvcOutputEventA=0;
m_pIDgnSSvcOutputEvent=0;
Expand Down Expand Up @@ -89,6 +90,7 @@ class CDragonCode
BOOL deleteWord( char * wordName );
BOOL setWordInfo( char * wordName, DWORD wordInfo );
BOOL setTrayIcon( char * iconName, char * toolTip, PyObject * pCallback );
BOOL setMessageWindow( PyObject * pCallback, DWORD dwflags = 0 );

PyObject * getCurrentModule();
PyObject * getCurrentUser();
Expand Down Expand Up @@ -267,6 +269,9 @@ class CDragonCode
BOOL m_bHasTrayIcon;
PyObject *m_pTrayIconCallback;

// set when the message window is started
PyObject * m_pMessageWindowCallback;

// This is what we call when we are ready to recume recognition
void doPausedProcessing( QWORD dwCookie );

Expand Down
20 changes: 20 additions & 0 deletions NatlinkSource/natlink.txt
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,26 @@ waitForSpeech( timeout )
timeout except that the message box is not displayed (the function only
returns when the timeout elapses).

setMessageWindow( callback, flags )
This function enables, disables and updates Natlink's message window.

The function has one required parameter whose value should be a Python
function or None. If a function is passed for this parameter, it
should take one parameter which is the type of message window event:
idd_reload or idd_exit (each defined in natlinkutils)

Passing a callback function enables the message window, passing None
disables it.

The second parameter is optional. It is a combination of one or more
of the following flags:
0x01 # enable the window's File>Exit menu item

Note: The idd_reload event occurs when the File>Reload menu item is
activated. Before this function's callback is invoked, Natlink will
unload all loaded grammars, destroy all existing ResObjs and DictObjs,
and clear all callbacks except the message window callback -- this one.

Inside natlink, each grammar is maintained as a separate object. To expose
this structure, each grammar object will be exposed to Python as a class.
The GrammarBase class defined in natlinkutils encapusulates and extends the
Expand Down
32 changes: 32 additions & 0 deletions NatlinkSource/pythwrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,37 @@ natlink_setTrayIcon( PyObject *self, PyObject *args )
return Py_None;
}

//---------------------------------------------------------------------------
// natlink.setMessageWindow( callback, flags )
//
// See natlink.txt for documentation.

extern "C" static PyObject *
natlink_setMessageWindow( PyObject *self, PyObject *args )
{
PyObject * pFunc;
DWORD dwFlags = 0;

if( !PyArg_ParseTuple( args, "O|i:setMessageWindow", &pFunc, &dwFlags) )
{
return NULL;
}

if( pFunc != Py_None && !PyCallable_Check( pFunc ) )
{
PyErr_SetString( PyExc_TypeError, "first parameter must be callable" );
return NULL;
}

if( !cDragon.setMessageWindow( pFunc, dwFlags ) )
{
return NULL;
}

Py_INCREF( Py_None );
return Py_None;
}

/////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------------------------
Expand Down Expand Up @@ -2314,6 +2345,7 @@ static struct PyMethodDef natlink_methods[] = {
{ "setWordInfo", natlink_setWordInfo, METH_VARARGS },
{ "getWordProns", natlink_getWordProns, METH_VARARGS },
{ "setTrayIcon", natlink_setTrayIcon, METH_VARARGS },
{ "setMessageWindow", natlink_setMessageWindow, METH_VARARGS },
{ NULL }
};

Expand Down

0 comments on commit c4b1997

Please sign in to comment.