2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
15 Neither the name of Loki software nor the names of its contributors may be used
16 to endorse or promote products derived from this software without specific prior
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 //-----------------------------------------------------------------------------
34 // monitoring window for running BSP processes (and possibly various other stuff)
39 #include <gtk/gtkmain.h>
43 #include "string/string.h"
44 #include "stream/stringstream.h"
46 #include "gtkutil/messagebox.h"
49 #include "preferences.h"
52 #include "mainframe.h"
55 void message_flush( message_info_t* self ){
56 Sys_Print( self->msg_level, self->m_buffer, self->m_length );
60 void message_print( message_info_t* self, const char* characters, std::size_t length ){
61 const char* end = characters + length;
62 while ( characters != end )
64 std::size_t space = message_info_t::bufsize - 1 - self->m_length;
66 message_flush( self );
70 std::size_t size = std::min( space, std::size_t( end - characters ) );
71 memcpy( self->m_buffer + self->m_length, characters, size );
72 self->m_length += size;
85 // a flag we have set to true when using an external BSP plugin
86 // the resulting code with that is a bit dirty, cleaner solution would be to seperate the succession of commands from the listening loop
87 // (in two seperate classes probably)
90 // EIdle: we are not listening
91 // DoMonitoringLoop will change state to EBeginStep
92 // EBeginStep: the socket is up for listening, we are expecting incoming connection
93 // incoming connection will change state to EWatching
94 // EWatching: we have a connection, monitor it
95 // connection closed will see if we start a new step (EBeginStep) or launch Quake3 and end (EIdle)
96 enum EWatchBSPState { EIdle, EBeginStep, EWatching } m_eState;
97 socket_t *m_pListenSocket;
98 socket_t *m_pInSocket;
101 // used to timeout EBeginStep
103 std::size_t m_iCurrentStep;
104 // name of the map so we can run the engine
106 // buffer we use in push mode to receive data directly from the network
107 xmlParserInputBufferPtr m_xmlInputBuffer;
108 xmlParserInputPtr m_xmlInput;
109 xmlParserCtxtPtr m_xmlParserCtxt;
110 // call this to switch the set listening mode
111 bool SetupListening();
112 // start a new EBeginStep
114 // the xml and sax parser state
115 char m_xmlBuf[MAX_NETMESSAGE];
116 bool m_bNeedCtxtInit;
117 message_info_t m_message_info;
122 m_bBSPPlugin = false;
123 m_pListenSocket = NULL;
126 m_pTimer = g_timer_new();
128 m_xmlInputBuffer = NULL;
129 m_bNeedCtxtInit = true;
131 virtual ~CWatchBSP(){
135 g_timer_destroy( m_pTimer );
138 bool HasBSPPlugin() const
139 { return m_bBSPPlugin; }
141 // called regularly to keep listening
142 void RoutineProcessing();
143 // start a monitoring loop with the following steps
144 void DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName );
145 void EndMonitoringLoop(){
148 string_release( m_sBSPName, string_length( m_sBSPName ) );
152 g_ptr_array_free( m_pCmd, TRUE );
156 // close everything - may be called from the outside to abort the process
158 // start a listening loop for an external process, possibly a BSP plugin
159 void ExternalListen();
162 CWatchBSP* g_pWatchBSP;
164 // watch the BSP process through network connections
165 // true: trigger the BSP steps one by one and monitor them through the network
166 // false: create a BAT / .sh file and execute it. don't bother monitoring it.
167 bool g_WatchBSP_Enabled = true;
168 // do we stop the compilation process if we come accross a leak?
169 bool g_WatchBSP_LeakStop = true;
170 bool g_WatchBSP_RunQuake = false;
171 // store prefs setting for automatic sleep mode activation
172 bool g_WatchBSP_DoSleep = true;
173 // timeout when beginning a step (in seconds)
174 // if we don't get a connection quick enough we assume something failed and go back to idling
175 int g_WatchBSP_Timeout = 10;
178 void Build_constructPreferences( PreferencesPage& page ){
179 GtkWidget* monitorbsp = page.appendCheckBox( "", "Enable Build Process Monitoring", g_WatchBSP_Enabled );
180 GtkWidget* leakstop = page.appendCheckBox( "", "Stop Compilation on Leak", g_WatchBSP_LeakStop );
181 GtkWidget* runengine = page.appendCheckBox( "", "Run Engine After Compile", g_WatchBSP_RunQuake );
182 GtkWidget* sleep = page.appendCheckBox ( "", "Sleep When Running the Engine", g_WatchBSP_DoSleep );
183 Widget_connectToggleDependency( leakstop, monitorbsp );
184 Widget_connectToggleDependency( runengine, monitorbsp );
185 Widget_connectToggleDependency( sleep, runengine );
187 void Build_constructPage( PreferenceGroup& group ){
188 PreferencesPage page( group.createPage( "Build", "Build Preferences" ) );
189 Build_constructPreferences( page );
191 void Build_registerPreferencesPage(){
192 PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, Build_constructPage>() );
195 #include "preferencesystem.h"
196 #include "stringio.h"
198 void BuildMonitor_Construct(){
199 g_pWatchBSP = new CWatchBSP();
201 g_WatchBSP_Enabled = !string_equal( g_pGameDescription->getKeyValue( "no_bsp_monitor" ), "1" );
203 GlobalPreferenceSystem().registerPreference( "WatchBSP", BoolImportStringCaller( g_WatchBSP_Enabled ), BoolExportStringCaller( g_WatchBSP_Enabled ) );
204 GlobalPreferenceSystem().registerPreference( "RunQuake2Run", BoolImportStringCaller( g_WatchBSP_RunQuake ), BoolExportStringCaller( g_WatchBSP_RunQuake ) );
205 GlobalPreferenceSystem().registerPreference( "LeakStop", BoolImportStringCaller( g_WatchBSP_LeakStop ), BoolExportStringCaller( g_WatchBSP_LeakStop ) );
206 GlobalPreferenceSystem().registerPreference( "SleepMode", BoolImportStringCaller( g_WatchBSP_DoSleep ), BoolExportStringCaller( g_WatchBSP_DoSleep ) );
208 Build_registerPreferencesPage();
211 void BuildMonitor_Destroy(){
213 g_pWatchBSP = nullptr;
216 CWatchBSP *GetWatchBSP(){
220 void BuildMonitor_Run( GPtrArray* commands, const char* mapName ){
221 GetWatchBSP()->DoMonitoringLoop( commands, mapName );
225 // Static functions for the SAX callbacks -------------------------------------------------------
227 // utility for saxStartElement below
228 static void abortStream( message_info_t *data ){
229 GetWatchBSP()->EndMonitoringLoop();
230 // tell there has been an error
232 if ( GetWatchBSP()->HasBSPPlugin() ) {
233 g_BSPFrontendTable.m_pfnEndListen( 2 );
236 // yeah this doesn't look good.. but it's needed so that everything will be ignored until the stream goes out
237 data->ignore_depth = -1;
241 #include "stream_version.h"
243 static void saxStartElement( message_info_t *data, const xmlChar *name, const xmlChar **attrs ){
245 globalOutputStream() << "<" << name;
247 for ( const xmlChar** p = attrs; *p != 0; p += 2 )
249 globalOutputStream() << " " << p[0] << "=" << makeQuoted( p[1] );
252 globalOutputStream() << ">\n";
255 if ( data->ignore_depth == 0 ) {
256 if ( data->pGeometry != 0 ) {
258 data->pGeometry->saxStartElement( data, name, attrs );
262 if ( strcmp( reinterpret_cast<const char*>( name ), "q3map_feedback" ) == 0 ) {
263 // check the correct version
264 // old q3map don't send a version attribute
265 // the ones we support .. send Q3MAP_STREAM_VERSION
266 if ( !attrs[0] || !attrs[1] || ( strcmp( reinterpret_cast<const char*>( attrs[0] ), "version" ) != 0 ) ) {
267 message_flush( data );
268 globalErrorStream() << "No stream version given in the feedback stream, this is an old q3map version.\n"
269 "Please turn off monitored compiling if you still wish to use this q3map executable\n";
273 else if ( q3map::stream_version() != reinterpret_cast<const char*>( attrs[1] ) ) {
274 message_flush( data );
275 globalErrorStream() << "This version of Radiant reads version "
276 << q3map::stream_version()
277 << " debug streams, I got an incoming connection with version "
278 << reinterpret_cast<const char*>( attrs[1] )
279 << "\nPlease make sure your versions of Radiant and q3map are matching.\n";
284 // we don't treat locally
285 else if ( strcmp( reinterpret_cast<const char*>( name ), "message" ) == 0 ) {
286 int msg_level = atoi( reinterpret_cast<const char*>( attrs[1] ) );
287 if ( msg_level != data->msg_level ) {
288 message_flush( data );
289 data->msg_level = msg_level;
292 else if ( strcmp( reinterpret_cast<const char*>( name ), "polyline" ) == 0 ) {
293 // polyline has a particular status .. right now we only use it for leakfile ..
294 data->geometry_depth = data->recurse;
295 data->pGeometry = &g_pointfile;
296 data->pGeometry->saxStartElement( data, name, attrs );
298 else if ( strcmp( reinterpret_cast<const char*>( name ), "select" ) == 0 ) {
299 CSelectMsg *pSelect = new CSelectMsg();
300 data->geometry_depth = data->recurse;
301 data->pGeometry = pSelect;
302 data->pGeometry->saxStartElement( data, name, attrs );
304 else if ( strcmp( reinterpret_cast<const char*>( name ), "pointmsg" ) == 0 ) {
305 CPointMsg *pPoint = new CPointMsg();
306 data->geometry_depth = data->recurse;
307 data->pGeometry = pPoint;
308 data->pGeometry->saxStartElement( data, name, attrs );
310 else if ( strcmp( reinterpret_cast<const char*>( name ), "windingmsg" ) == 0 ) {
311 CWindingMsg *pWinding = new CWindingMsg();
312 data->geometry_depth = data->recurse;
313 data->pGeometry = pWinding;
314 data->pGeometry->saxStartElement( data, name, attrs );
318 globalErrorStream() << "Warning: ignoring unrecognized node in XML stream (" << reinterpret_cast<const char*>( name ) << ")\n";
319 // we don't recognize this node, jump over it
320 // (NOTE: the ignore mechanism is a bit screwed, only works when starting an ignore at the highest level)
321 data->ignore_depth = data->recurse;
328 static void saxEndElement( message_info_t *data, const xmlChar *name ){
330 globalOutputStream() << "<" << name << "/>\n";
334 // we are out of an ignored chunk
335 if ( data->recurse == data->ignore_depth ) {
336 data->ignore_depth = 0;
339 if ( data->pGeometry != 0 ) {
340 data->pGeometry->saxEndElement( data, name );
341 // we add the object to the debug window
342 if ( data->geometry_depth == data->recurse ) {
343 g_DbgDlg.Push( data->pGeometry );
347 if ( data->recurse == data->stop_depth ) {
348 message_flush( data );
350 globalOutputStream() << "Received error msg .. shutting down..\n";
352 GetWatchBSP()->EndMonitoringLoop();
353 // tell there has been an error
355 if ( GetWatchBSP()->HasBSPPlugin() ) {
356 g_BSPFrontendTable.m_pfnEndListen( 2 );
363 class MessageOutputStream : public TextOutputStream
365 message_info_t* m_data;
367 MessageOutputStream( message_info_t* data ) : m_data( data ){
370 std::size_t write( const char* buffer, std::size_t length ){
371 if ( m_data->pGeometry != 0 ) {
372 m_data->pGeometry->saxCharacters( m_data, reinterpret_cast<const xmlChar*>( buffer ), int(length) );
376 if ( m_data->ignore_depth == 0 ) {
377 // output the message using the level
378 message_print( m_data, buffer, length );
379 // if this message has error level flag, we mark the depth to stop the compilation when we get out
380 // we don't set the msg level if we don't stop on leak
381 if ( m_data->msg_level == 3 ) {
382 m_data->stop_depth = m_data->recurse - 1;
392 inline MessageOutputStream& operator<<( MessageOutputStream& ostream, const T& t ){
393 return ostream_write( ostream, t );
396 static void saxCharacters( message_info_t *data, const xmlChar *ch, int len ){
397 MessageOutputStream ostream( data );
398 ostream << StringRange( reinterpret_cast<const char*>( ch ), reinterpret_cast<const char*>( ch + len ) );
401 static void saxComment( void *ctx, const xmlChar *msg ){
402 globalOutputStream() << "XML comment: " << reinterpret_cast<const char*>( msg ) << "\n";
405 static void saxWarning( void *ctx, const char *msg, ... ){
406 char saxMsgBuffer[4096];
409 va_start( args, msg );
410 vsprintf( saxMsgBuffer, msg, args );
412 globalOutputStream() << "XML warning: " << saxMsgBuffer << "\n";
415 static void saxError( void *ctx, const char *msg, ... ){
416 char saxMsgBuffer[4096];
419 va_start( args, msg );
420 vsprintf( saxMsgBuffer, msg, args );
422 globalErrorStream() << "XML error: " << saxMsgBuffer << "\n";
425 static void saxFatal( void *ctx, const char *msg, ... ){
430 va_start( args, msg );
431 vsprintf( buffer, msg, args );
433 globalErrorStream() << "XML fatal error: " << buffer << "\n";
436 static xmlSAXHandler saxParser = {
437 0, /* internalSubset */
438 0, /* isStandalone */
439 0, /* hasInternalSubset */
440 0, /* hasExternalSubset */
441 0, /* resolveEntity */
444 0, /* notationDecl */
445 0, /* attributeDecl */
447 0, /* unparsedEntityDecl */
448 0, /* setDocumentLocator */
449 0, /* startDocument */
451 (startElementSAXFunc)saxStartElement, /* startElement */
452 (endElementSAXFunc)saxEndElement, /* endElement */
454 (charactersSAXFunc)saxCharacters, /* characters */
455 0, /* ignorableWhitespace */
456 0, /* processingInstruction */
457 (commentSAXFunc)saxComment, /* comment */
458 (warningSAXFunc)saxWarning, /* warning */
459 (errorSAXFunc)saxError, /* error */
460 (fatalErrorSAXFunc)saxFatal, /* fatalError */
471 // ------------------------------------------------------------------------------------------------
475 static gint watchbsp_routine( gpointer data ){
476 reinterpret_cast<CWatchBSP*>( data )->RoutineProcessing();
480 void CWatchBSP::Reset(){
482 Net_Disconnect( m_pInSocket );
485 if ( m_pListenSocket ) {
486 Net_Disconnect( m_pListenSocket );
487 m_pListenSocket = NULL;
489 if ( m_xmlInputBuffer ) {
490 xmlFreeParserInputBuffer( m_xmlInputBuffer );
491 m_xmlInputBuffer = NULL;
494 if ( s_routine_id ) {
495 gtk_timeout_remove( s_routine_id );
500 bool CWatchBSP::SetupListening(){
502 if ( m_pListenSocket ) {
503 globalOutputStream() << "ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n";
507 globalOutputStream() << "Setting up\n";
509 m_pListenSocket = Net_ListenSocket( 39000 );
510 if ( m_pListenSocket == NULL ) {
513 globalOutputStream() << "Listening...\n";
517 void CWatchBSP::DoEBeginStep(){
519 if ( SetupListening() == false ) {
520 const char* msg = "Failed to get a listening socket on port 39000.\nTry running with Build monitoring disabled if you can't fix this.\n";
521 globalOutputStream() << msg;
522 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), msg, "Build monitoring", eMB_OK, eMB_ICONERROR );
525 // set the timer for timeouts and step cancellation
526 g_timer_reset( m_pTimer );
527 g_timer_start( m_pTimer );
529 if ( !m_bBSPPlugin ) {
530 globalOutputStream() << "=== running build command ===\n"
531 << static_cast<const char*>( g_ptr_array_index( m_pCmd, m_iCurrentStep ) ) << "\n";
533 if ( !Q_Exec( NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true, false ) ) {
534 StringOutputStream msg( 256 );
535 msg << "Failed to execute the following command: ";
536 msg << reinterpret_cast<const char*>( g_ptr_array_index( m_pCmd, m_iCurrentStep ) );
537 msg << "\nCheck that the file exists and that you don't run out of system resources.\n";
538 globalOutputStream() << msg.c_str();
539 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), msg.c_str(), "Build monitoring", eMB_OK, eMB_ICONERROR );
542 // re-initialise the debug window
543 if ( m_iCurrentStep == 0 ) {
547 m_eState = EBeginStep;
548 s_routine_id = gtk_timeout_add( 25, watchbsp_routine, this );
553 #define ENGINE_ATTRIBUTE "engine_win32"
554 #define MP_ENGINE_ATTRIBUTE "mp_engine_win32"
555 #elif defined( __linux__ ) || defined ( __FreeBSD__ )
556 #define ENGINE_ATTRIBUTE "engine_linux"
557 #define MP_ENGINE_ATTRIBUTE "mp_engine_linux"
558 #elif defined( __APPLE__ )
559 #define ENGINE_ATTRIBUTE "engine_macos"
560 #define MP_ENGINE_ATTRIBUTE "mp_engine_macos"
562 #error "unsupported platform"
565 class RunEngineConfiguration
568 const char* executable;
569 const char* mp_executable;
572 RunEngineConfiguration() :
573 executable( g_pGameDescription->getRequiredKeyValue( ENGINE_ATTRIBUTE ) ),
574 mp_executable( g_pGameDescription->getKeyValue( MP_ENGINE_ATTRIBUTE ) ){
575 do_sp_mp = !string_empty( mp_executable );
579 inline void GlobalGameDescription_string_write_mapparameter( StringOutputStream& string, const char* mapname ){
580 if ( g_pGameDescription->mGameType == "q2"
581 || g_pGameDescription->mGameType == "heretic2" ) {
582 string << ". +exec radiant.cfg +map " << mapname;
586 string << "+set sv_pure 0 ";
587 // TTimo: a check for vm_* but that's all fine
588 //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";
589 const char* fs_game = gamename_get();
590 if ( !string_equal( fs_game, basegame_get() ) ) {
591 string << "+set fs_game " << fs_game << " ";
593 if ( g_pGameDescription->mGameType == "wolf" ) {
594 //|| g_pGameDescription->mGameType == "et")
595 if ( string_equal( gamemode_get(), "mp" ) ) {
597 string << "+devmap " << mapname;
602 string << "+set nextmap \"spdevmap " << mapname << "\"";
607 string << "+devmap " << mapname;
613 void CWatchBSP::RoutineProcessing(){
617 // timeout: if we don't get an incoming connection fast enough, go back to idle
618 if ( g_timer_elapsed( m_pTimer, NULL ) > g_WatchBSP_Timeout ) {
619 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), "The connection timed out, assuming the build process failed\nMake sure you are using a networked version of Q3Map?\nOtherwise you need to disable BSP Monitoring in prefs.", "BSP process monitoring", eMB_OK );
622 if ( m_bBSPPlugin ) {
623 // status == 1 : didn't get the connection
624 g_BSPFrontendTable.m_pfnEndListen( 1 );
631 if ( !m_pListenSocket ) {
632 globalErrorStream() << "ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n";
636 // we are not connected yet, accept any incoming connection
637 m_pInSocket = Net_Accept( m_pListenSocket );
639 globalOutputStream() << "Connected.\n";
640 // prepare the message info struct for diving in
641 memset( &m_message_info, 0, sizeof( message_info_t ) );
642 // a dumb flag to make sure we init the push parser context when first getting a msg
643 m_bNeedCtxtInit = true;
644 m_eState = EWatching;
651 if ( !m_pInSocket ) {
652 globalErrorStream() << "ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n";
657 int ret = Net_Wait( m_pInSocket, 0, 0 );
659 globalOutputStream() << "WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n";
660 globalOutputStream() << "Terminating the connection.\n";
666 // the socket has been identified, there's something (message or disconnection)
667 // see if there's anything in input
668 ret = Net_Receive( m_pInSocket, &msg );
670 // unsigned int size = msg.size; //++timo just a check
671 strcpy( m_xmlBuf, NMSG_ReadString( &msg ) );
672 if ( m_bNeedCtxtInit ) {
673 m_xmlParserCtxt = NULL;
674 m_xmlParserCtxt = xmlCreatePushParserCtxt( &saxParser, &m_message_info, m_xmlBuf, static_cast<int>( strlen( m_xmlBuf ) ), NULL );
676 if ( m_xmlParserCtxt == NULL ) {
677 globalErrorStream() << "Failed to create the XML parser (incoming stream began with: " << m_xmlBuf << ")\n";
680 m_bNeedCtxtInit = false;
684 xmlParseChunk( m_xmlParserCtxt, m_xmlBuf, static_cast<int>( strlen( m_xmlBuf ) ), 0 );
689 message_flush( &m_message_info );
690 // error or connection closed/reset
691 // NOTE: if we get an error down the XML stream we don't reach here
692 Net_Disconnect( m_pInSocket );
694 globalOutputStream() << "Connection closed.\n";
696 if ( m_bBSPPlugin ) {
698 // let the BSP plugin know that the job is done
699 g_BSPFrontendTable.m_pfnEndListen( 0 );
703 // move to next step or finish
705 if ( m_iCurrentStep < m_pCmd->len ) {
710 // launch the engine .. OMG
711 if ( g_WatchBSP_RunQuake ) {
713 // do we enter sleep mode before?
714 if ( g_WatchBSP_DoSleep ) {
715 globalOutputStream() << "Going into sleep mode..\n";
716 g_pParentWnd->OnSleep();
719 globalOutputStream() << "Running engine...\n";
720 StringOutputStream cmd( 256 );
721 // build the command line
722 cmd << EnginePath_get();
723 // this is game dependant
725 RunEngineConfiguration engineConfig;
727 if ( engineConfig.do_sp_mp ) {
728 if ( string_equal( gamemode_get(), "mp" ) ) {
729 cmd << engineConfig.mp_executable;
733 cmd << engineConfig.executable;
738 cmd << engineConfig.executable;
741 StringOutputStream cmdline;
743 GlobalGameDescription_string_write_mapparameter( cmdline, m_sBSPName );
745 globalOutputStream() << cmd.c_str() << " " << cmdline.c_str() << "\n";
748 if ( !Q_Exec( cmd.c_str(), (char *)cmdline.c_str(), EnginePath_get(), false, false ) ) {
749 StringOutputStream msg;
750 msg << "Failed to execute the following command: " << cmd.c_str() << cmdline.c_str();
751 globalOutputStream() << msg.c_str();
752 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), msg.c_str(), "Build monitoring", eMB_OK, eMB_ICONERROR );
766 GPtrArray* str_ptr_array_clone( GPtrArray* array ){
767 GPtrArray* cloned = g_ptr_array_sized_new( array->len );
768 for ( guint i = 0; i < array->len; ++i )
770 g_ptr_array_add( cloned, g_strdup( (char*)g_ptr_array_index( array, i ) ) );
775 void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName ){
776 m_sBSPName = string_clone( sBSPName );
777 if ( m_eState != EIdle ) {
778 globalOutputStream() << "WatchBSP got a monitoring request while not idling...\n";
779 // prompt the user, should we cancel the current process and go ahead?
780 if ( gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), "I am already monitoring a Build process.\nDo you want me to override and start a new compilation?",
781 "Build process monitoring", eMB_YESNO ) == eIDYES ) {
782 // disconnect and set EIdle state
786 m_pCmd = str_ptr_array_clone( pCmd );
791 void CWatchBSP::ExternalListen(){
796 // the part of the watchbsp interface we export to plugins
797 // NOTE: in the long run, the whole watchbsp.cpp interface needs to go out and be handled at the BSP plugin level
798 // for now we provide something really basic and limited, the essential is to have something that works fine and fast (for 1.1 final)
799 void QERApp_Listen(){
800 // open the listening socket
801 GetWatchBSP()->ExternalListen();