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;
80 #include <uilib/uilib.h>
86 // a flag we have set to true when using an external BSP plugin
87 // the resulting code with that is a bit dirty, cleaner solution would be to seperate the succession of commands from the listening loop
88 // (in two seperate classes probably)
91 // EIdle: we are not listening
92 // DoMonitoringLoop will change state to EBeginStep
93 // EBeginStep: the socket is up for listening, we are expecting incoming connection
94 // incoming connection will change state to EWatching
95 // EWatching: we have a connection, monitor it
96 // connection closed will see if we start a new step (EBeginStep) or launch Quake3 and end (EIdle)
97 enum EWatchBSPState { EIdle, EBeginStep, EWatching } m_eState;
98 socket_t *m_pListenSocket;
99 socket_t *m_pInSocket;
102 // used to timeout EBeginStep
104 std::size_t m_iCurrentStep;
105 // name of the map so we can run the engine
107 // buffer we use in push mode to receive data directly from the network
108 xmlParserInputBufferPtr m_xmlInputBuffer;
109 xmlParserInputPtr m_xmlInput;
110 xmlParserCtxtPtr m_xmlParserCtxt;
111 // call this to switch the set listening mode
112 bool SetupListening();
113 // start a new EBeginStep
115 // the xml and sax parser state
116 char m_xmlBuf[MAX_NETMESSAGE];
117 bool m_bNeedCtxtInit;
118 message_info_t m_message_info;
123 m_bBSPPlugin = false;
124 m_pListenSocket = NULL;
127 m_pTimer = g_timer_new();
129 m_xmlInputBuffer = NULL;
130 m_bNeedCtxtInit = true;
132 virtual ~CWatchBSP(){
136 g_timer_destroy( m_pTimer );
139 bool HasBSPPlugin() const
140 { return m_bBSPPlugin; }
142 // called regularly to keep listening
143 void RoutineProcessing();
144 // start a monitoring loop with the following steps
145 void DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName );
146 void EndMonitoringLoop(){
149 string_release( m_sBSPName, string_length( m_sBSPName ) );
153 g_ptr_array_free( m_pCmd, TRUE );
157 // close everything - may be called from the outside to abort the process
159 // start a listening loop for an external process, possibly a BSP plugin
160 void ExternalListen();
163 CWatchBSP* g_pWatchBSP;
165 // watch the BSP process through network connections
166 // true: trigger the BSP steps one by one and monitor them through the network
167 // false: create a BAT / .sh file and execute it. don't bother monitoring it.
168 bool g_WatchBSP_Enabled = true;
169 // do we stop the compilation process if we come accross a leak?
170 bool g_WatchBSP_LeakStop = true;
171 bool g_WatchBSP_RunQuake = false;
172 // store prefs setting for automatic sleep mode activation
173 bool g_WatchBSP_DoSleep = true;
174 // timeout when beginning a step (in seconds)
175 // if we don't get a connection quick enough we assume something failed and go back to idling
176 int g_WatchBSP_Timeout = 10;
179 void Build_constructPreferences( PreferencesPage& page ){
180 ui::CheckButton monitorbsp = page.appendCheckBox( "", "Enable Build Process Monitoring", g_WatchBSP_Enabled );
181 ui::CheckButton leakstop = page.appendCheckBox( "", "Stop Compilation on Leak", g_WatchBSP_LeakStop );
182 ui::CheckButton runengine = page.appendCheckBox( "", "Run Engine After Compile", g_WatchBSP_RunQuake );
183 ui::CheckButton sleep = page.appendCheckBox ( "", "Sleep When Running the Engine", g_WatchBSP_DoSleep );
184 Widget_connectToggleDependency( leakstop, monitorbsp );
185 Widget_connectToggleDependency( runengine, monitorbsp );
186 Widget_connectToggleDependency( sleep, runengine );
188 void Build_constructPage( PreferenceGroup& group ){
189 PreferencesPage page( group.createPage( "Build", "Build Preferences" ) );
190 Build_constructPreferences( page );
192 void Build_registerPreferencesPage(){
193 PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, Build_constructPage>() );
196 #include "preferencesystem.h"
197 #include "stringio.h"
199 void BuildMonitor_Construct(){
200 g_pWatchBSP = new CWatchBSP();
202 g_WatchBSP_Enabled = !string_equal( g_pGameDescription->getKeyValue( "no_bsp_monitor" ), "1" );
204 GlobalPreferenceSystem().registerPreference( "WatchBSP", BoolImportStringCaller( g_WatchBSP_Enabled ), BoolExportStringCaller( g_WatchBSP_Enabled ) );
205 GlobalPreferenceSystem().registerPreference( "RunQuake2Run", BoolImportStringCaller( g_WatchBSP_RunQuake ), BoolExportStringCaller( g_WatchBSP_RunQuake ) );
206 GlobalPreferenceSystem().registerPreference( "LeakStop", BoolImportStringCaller( g_WatchBSP_LeakStop ), BoolExportStringCaller( g_WatchBSP_LeakStop ) );
207 GlobalPreferenceSystem().registerPreference( "SleepMode", BoolImportStringCaller( g_WatchBSP_DoSleep ), BoolExportStringCaller( g_WatchBSP_DoSleep ) );
209 Build_registerPreferencesPage();
212 void BuildMonitor_Destroy(){
214 g_pWatchBSP = nullptr;
217 CWatchBSP *GetWatchBSP(){
221 void BuildMonitor_Run( GPtrArray* commands, const char* mapName ){
222 GetWatchBSP()->DoMonitoringLoop( commands, mapName );
226 // Static functions for the SAX callbacks -------------------------------------------------------
228 // utility for saxStartElement below
229 static void abortStream( message_info_t *data ){
230 GetWatchBSP()->EndMonitoringLoop();
231 // tell there has been an error
233 if ( GetWatchBSP()->HasBSPPlugin() ) {
234 g_BSPFrontendTable.m_pfnEndListen( 2 );
237 // yeah this doesn't look good.. but it's needed so that everything will be ignored until the stream goes out
238 data->ignore_depth = -1;
242 #include "stream_version.h"
244 static void saxStartElement( message_info_t *data, const xmlChar *name, const xmlChar **attrs ){
246 globalOutputStream() << "<" << name;
248 for ( const xmlChar** p = attrs; *p != 0; p += 2 )
250 globalOutputStream() << " " << p[0] << "=" << makeQuoted( p[1] );
253 globalOutputStream() << ">\n";
256 if ( data->ignore_depth == 0 ) {
257 if ( data->pGeometry != 0 ) {
259 data->pGeometry->saxStartElement( data, name, attrs );
263 if ( strcmp( reinterpret_cast<const char*>( name ), "q3map_feedback" ) == 0 ) {
264 // check the correct version
265 // old q3map don't send a version attribute
266 // the ones we support .. send Q3MAP_STREAM_VERSION
267 if ( !attrs[0] || !attrs[1] || ( strcmp( reinterpret_cast<const char*>( attrs[0] ), "version" ) != 0 ) ) {
268 message_flush( data );
269 globalErrorStream() << "No stream version given in the feedback stream, this is an old q3map version.\n"
270 "Please turn off monitored compiling if you still wish to use this q3map executable\n";
274 else if ( q3map::stream_version() != reinterpret_cast<const char*>( attrs[1] ) ) {
275 message_flush( data );
276 globalErrorStream() << "This version of Radiant reads version "
277 << q3map::stream_version()
278 << " debug streams, I got an incoming connection with version "
279 << reinterpret_cast<const char*>( attrs[1] )
280 << "\nPlease make sure your versions of Radiant and q3map are matching.\n";
285 // we don't treat locally
286 else if ( strcmp( reinterpret_cast<const char*>( name ), "message" ) == 0 ) {
287 int msg_level = atoi( reinterpret_cast<const char*>( attrs[1] ) );
288 if ( msg_level != data->msg_level ) {
289 message_flush( data );
290 data->msg_level = msg_level;
293 else if ( strcmp( reinterpret_cast<const char*>( name ), "polyline" ) == 0 ) {
294 // polyline has a particular status .. right now we only use it for leakfile ..
295 data->geometry_depth = data->recurse;
296 data->pGeometry = &g_pointfile;
297 data->pGeometry->saxStartElement( data, name, attrs );
299 else if ( strcmp( reinterpret_cast<const char*>( name ), "select" ) == 0 ) {
300 CSelectMsg *pSelect = new CSelectMsg();
301 data->geometry_depth = data->recurse;
302 data->pGeometry = pSelect;
303 data->pGeometry->saxStartElement( data, name, attrs );
305 else if ( strcmp( reinterpret_cast<const char*>( name ), "pointmsg" ) == 0 ) {
306 CPointMsg *pPoint = new CPointMsg();
307 data->geometry_depth = data->recurse;
308 data->pGeometry = pPoint;
309 data->pGeometry->saxStartElement( data, name, attrs );
311 else if ( strcmp( reinterpret_cast<const char*>( name ), "windingmsg" ) == 0 ) {
312 CWindingMsg *pWinding = new CWindingMsg();
313 data->geometry_depth = data->recurse;
314 data->pGeometry = pWinding;
315 data->pGeometry->saxStartElement( data, name, attrs );
319 globalErrorStream() << "Warning: ignoring unrecognized node in XML stream (" << reinterpret_cast<const char*>( name ) << ")\n";
320 // we don't recognize this node, jump over it
321 // (NOTE: the ignore mechanism is a bit screwed, only works when starting an ignore at the highest level)
322 data->ignore_depth = data->recurse;
329 static void saxEndElement( message_info_t *data, const xmlChar *name ){
331 globalOutputStream() << "<" << name << "/>\n";
335 // we are out of an ignored chunk
336 if ( data->recurse == data->ignore_depth ) {
337 data->ignore_depth = 0;
340 if ( data->pGeometry != 0 ) {
341 data->pGeometry->saxEndElement( data, name );
342 // we add the object to the debug window
343 if ( data->geometry_depth == data->recurse ) {
344 g_DbgDlg.Push( data->pGeometry );
348 if ( data->recurse == data->stop_depth ) {
349 message_flush( data );
351 globalOutputStream() << "Received error msg .. shutting down..\n";
353 GetWatchBSP()->EndMonitoringLoop();
354 // tell there has been an error
356 if ( GetWatchBSP()->HasBSPPlugin() ) {
357 g_BSPFrontendTable.m_pfnEndListen( 2 );
364 class MessageOutputStream : public TextOutputStream
366 message_info_t* m_data;
368 MessageOutputStream( message_info_t* data ) : m_data( data ){
371 std::size_t write( const char* buffer, std::size_t length ){
372 if ( m_data->pGeometry != 0 ) {
373 m_data->pGeometry->saxCharacters( m_data, reinterpret_cast<const xmlChar*>( buffer ), int(length) );
377 if ( m_data->ignore_depth == 0 ) {
378 // output the message using the level
379 message_print( m_data, buffer, length );
380 // if this message has error level flag, we mark the depth to stop the compilation when we get out
381 // we don't set the msg level if we don't stop on leak
382 if ( m_data->msg_level == 3 ) {
383 m_data->stop_depth = m_data->recurse - 1;
393 inline MessageOutputStream& operator<<( MessageOutputStream& ostream, const T& t ){
394 return ostream_write( ostream, t );
397 static void saxCharacters( message_info_t *data, const xmlChar *ch, int len ){
398 MessageOutputStream ostream( data );
399 ostream << StringRange( reinterpret_cast<const char*>( ch ), reinterpret_cast<const char*>( ch + len ) );
402 static void saxComment( void *ctx, const xmlChar *msg ){
403 globalOutputStream() << "XML comment: " << reinterpret_cast<const char*>( msg ) << "\n";
406 static void saxWarning( void *ctx, const char *msg, ... ){
407 char saxMsgBuffer[4096];
410 va_start( args, msg );
411 vsprintf( saxMsgBuffer, msg, args );
413 globalOutputStream() << "XML warning: " << saxMsgBuffer << "\n";
416 static void saxError( void *ctx, const char *msg, ... ){
417 char saxMsgBuffer[4096];
420 va_start( args, msg );
421 vsprintf( saxMsgBuffer, msg, args );
423 globalErrorStream() << "XML error: " << saxMsgBuffer << "\n";
426 static void saxFatal( void *ctx, const char *msg, ... ){
431 va_start( args, msg );
432 vsprintf( buffer, msg, args );
434 globalErrorStream() << "XML fatal error: " << buffer << "\n";
437 static xmlSAXHandler saxParser = {
438 0, /* internalSubset */
439 0, /* isStandalone */
440 0, /* hasInternalSubset */
441 0, /* hasExternalSubset */
442 0, /* resolveEntity */
445 0, /* notationDecl */
446 0, /* attributeDecl */
448 0, /* unparsedEntityDecl */
449 0, /* setDocumentLocator */
450 0, /* startDocument */
452 (startElementSAXFunc)saxStartElement, /* startElement */
453 (endElementSAXFunc)saxEndElement, /* endElement */
455 (charactersSAXFunc)saxCharacters, /* characters */
456 0, /* ignorableWhitespace */
457 0, /* processingInstruction */
458 (commentSAXFunc)saxComment, /* comment */
459 (warningSAXFunc)saxWarning, /* warning */
460 (errorSAXFunc)saxError, /* error */
461 (fatalErrorSAXFunc)saxFatal, /* fatalError */
472 // ------------------------------------------------------------------------------------------------
476 static gint watchbsp_routine( gpointer data ){
477 reinterpret_cast<CWatchBSP*>( data )->RoutineProcessing();
481 void CWatchBSP::Reset(){
483 Net_Disconnect( m_pInSocket );
486 if ( m_pListenSocket ) {
487 Net_Disconnect( m_pListenSocket );
488 m_pListenSocket = NULL;
490 if ( m_xmlInputBuffer ) {
491 xmlFreeParserInputBuffer( m_xmlInputBuffer );
492 m_xmlInputBuffer = NULL;
495 if ( s_routine_id ) {
496 gtk_timeout_remove( s_routine_id );
501 bool CWatchBSP::SetupListening(){
503 if ( m_pListenSocket ) {
504 globalOutputStream() << "ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n";
508 globalOutputStream() << "Setting up\n";
510 m_pListenSocket = Net_ListenSocket( 39000 );
511 if ( m_pListenSocket == NULL ) {
514 globalOutputStream() << "Listening...\n";
518 void CWatchBSP::DoEBeginStep(){
520 if ( SetupListening() == false ) {
521 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";
522 globalOutputStream() << msg;
523 MainFrame_getWindow().alert( msg, "Build monitoring", ui::alert_type::OK, ui::alert_icon::ERROR );
526 // set the timer for timeouts and step cancellation
527 g_timer_reset( m_pTimer );
528 g_timer_start( m_pTimer );
530 if ( !m_bBSPPlugin ) {
531 globalOutputStream() << "=== running build command ===\n"
532 << static_cast<const char*>( g_ptr_array_index( m_pCmd, m_iCurrentStep ) ) << "\n";
534 if ( !Q_Exec( NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true, false ) ) {
535 StringOutputStream msg( 256 );
536 msg << "Failed to execute the following command: ";
537 msg << reinterpret_cast<const char*>( g_ptr_array_index( m_pCmd, m_iCurrentStep ) );
538 msg << "\nCheck that the file exists and that you don't run out of system resources.\n";
539 globalOutputStream() << msg.c_str();
540 MainFrame_getWindow().alert( msg.c_str(), "Build monitoring", ui::alert_type::OK, ui::alert_icon::ERROR );
543 // re-initialise the debug window
544 if ( m_iCurrentStep == 0 ) {
548 m_eState = EBeginStep;
549 s_routine_id = gtk_timeout_add( 25, watchbsp_routine, this );
554 #define ENGINE_ATTRIBUTE "engine_win32"
555 #define MP_ENGINE_ATTRIBUTE "mp_engine_win32"
556 #elif defined( __linux__ ) || defined ( __FreeBSD__ )
557 #define ENGINE_ATTRIBUTE "engine_linux"
558 #define MP_ENGINE_ATTRIBUTE "mp_engine_linux"
559 #elif defined( __APPLE__ )
560 #define ENGINE_ATTRIBUTE "engine_macos"
561 #define MP_ENGINE_ATTRIBUTE "mp_engine_macos"
563 #error "unsupported platform"
566 class RunEngineConfiguration
569 const char* executable;
570 const char* mp_executable;
573 RunEngineConfiguration() :
574 executable( g_pGameDescription->getRequiredKeyValue( ENGINE_ATTRIBUTE ) ),
575 mp_executable( g_pGameDescription->getKeyValue( MP_ENGINE_ATTRIBUTE ) ){
576 do_sp_mp = !string_empty( mp_executable );
580 inline void GlobalGameDescription_string_write_mapparameter( StringOutputStream& string, const char* mapname ){
581 if ( g_pGameDescription->mGameType == "q2"
582 || g_pGameDescription->mGameType == "heretic2" ) {
583 string << ". +exec radiant.cfg +map " << mapname;
587 string << "+set sv_pure 0 ";
588 // TTimo: a check for vm_* but that's all fine
589 //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";
590 const char* fs_game = gamename_get();
591 if ( !string_equal( fs_game, basegame_get() ) ) {
592 string << "+set fs_game " << fs_game << " ";
594 if ( g_pGameDescription->mGameType == "wolf" ) {
595 //|| g_pGameDescription->mGameType == "et")
596 if ( string_equal( gamemode_get(), "mp" ) ) {
598 string << "+devmap " << mapname;
603 string << "+set nextmap \"spdevmap " << mapname << "\"";
608 string << "+devmap " << mapname;
614 void CWatchBSP::RoutineProcessing(){
618 // timeout: if we don't get an incoming connection fast enough, go back to idle
619 if ( g_timer_elapsed( m_pTimer, NULL ) > g_WatchBSP_Timeout ) {
620 MainFrame_getWindow().alert( "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", ui::alert_type::OK );
623 if ( m_bBSPPlugin ) {
624 // status == 1 : didn't get the connection
625 g_BSPFrontendTable.m_pfnEndListen( 1 );
632 if ( !m_pListenSocket ) {
633 globalErrorStream() << "ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n";
637 // we are not connected yet, accept any incoming connection
638 m_pInSocket = Net_Accept( m_pListenSocket );
640 globalOutputStream() << "Connected.\n";
641 // prepare the message info struct for diving in
642 memset( &m_message_info, 0, sizeof( message_info_t ) );
643 // a dumb flag to make sure we init the push parser context when first getting a msg
644 m_bNeedCtxtInit = true;
645 m_eState = EWatching;
652 if ( !m_pInSocket ) {
653 globalErrorStream() << "ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n";
658 int ret = Net_Wait( m_pInSocket, 0, 0 );
660 globalOutputStream() << "WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n";
661 globalOutputStream() << "Terminating the connection.\n";
667 // the socket has been identified, there's something (message or disconnection)
668 // see if there's anything in input
669 ret = Net_Receive( m_pInSocket, &msg );
671 // unsigned int size = msg.size; //++timo just a check
672 strcpy( m_xmlBuf, NMSG_ReadString( &msg ) );
673 if ( m_bNeedCtxtInit ) {
674 m_xmlParserCtxt = NULL;
675 m_xmlParserCtxt = xmlCreatePushParserCtxt( &saxParser, &m_message_info, m_xmlBuf, static_cast<int>( strlen( m_xmlBuf ) ), NULL );
677 if ( m_xmlParserCtxt == NULL ) {
678 globalErrorStream() << "Failed to create the XML parser (incoming stream began with: " << m_xmlBuf << ")\n";
681 m_bNeedCtxtInit = false;
685 xmlParseChunk( m_xmlParserCtxt, m_xmlBuf, static_cast<int>( strlen( m_xmlBuf ) ), 0 );
690 message_flush( &m_message_info );
691 // error or connection closed/reset
692 // NOTE: if we get an error down the XML stream we don't reach here
693 Net_Disconnect( m_pInSocket );
695 globalOutputStream() << "Connection closed.\n";
697 if ( m_bBSPPlugin ) {
699 // let the BSP plugin know that the job is done
700 g_BSPFrontendTable.m_pfnEndListen( 0 );
704 // move to next step or finish
706 if ( m_iCurrentStep < m_pCmd->len ) {
711 // launch the engine .. OMG
712 if ( g_WatchBSP_RunQuake ) {
714 // do we enter sleep mode before?
715 if ( g_WatchBSP_DoSleep ) {
716 globalOutputStream() << "Going into sleep mode..\n";
717 g_pParentWnd->OnSleep();
720 globalOutputStream() << "Running engine...\n";
721 StringOutputStream cmd( 256 );
722 // build the command line
723 cmd << EnginePath_get();
724 // this is game dependant
726 RunEngineConfiguration engineConfig;
728 if ( engineConfig.do_sp_mp ) {
729 if ( string_equal( gamemode_get(), "mp" ) ) {
730 cmd << engineConfig.mp_executable;
734 cmd << engineConfig.executable;
739 cmd << engineConfig.executable;
742 StringOutputStream cmdline;
744 GlobalGameDescription_string_write_mapparameter( cmdline, m_sBSPName );
746 globalOutputStream() << cmd.c_str() << " " << cmdline.c_str() << "\n";
749 if ( !Q_Exec( cmd.c_str(), (char *)cmdline.c_str(), EnginePath_get(), false, false ) ) {
750 StringOutputStream msg;
751 msg << "Failed to execute the following command: " << cmd.c_str() << cmdline.c_str();
752 globalOutputStream() << msg.c_str();
753 MainFrame_getWindow().alert( msg.c_str(), "Build monitoring", ui::alert_type::OK, ui::alert_icon::ERROR );
767 GPtrArray* str_ptr_array_clone( GPtrArray* array ){
768 GPtrArray* cloned = g_ptr_array_sized_new( array->len );
769 for ( guint i = 0; i < array->len; ++i )
771 g_ptr_array_add( cloned, g_strdup( (char*)g_ptr_array_index( array, i ) ) );
776 void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName ){
777 m_sBSPName = string_clone( sBSPName );
778 if ( m_eState != EIdle ) {
779 globalOutputStream() << "WatchBSP got a monitoring request while not idling...\n";
780 // prompt the user, should we cancel the current process and go ahead?
781 if ( MainFrame_getWindow().alert( "I am already monitoring a Build process.\nDo you want me to override and start a new compilation?",
782 "Build process monitoring", ui::alert_type::YESNO ) == ui::alert_response::YES ) {
783 // disconnect and set EIdle state
787 m_pCmd = str_ptr_array_clone( pCmd );
792 void CWatchBSP::ExternalListen(){
797 // the part of the watchbsp interface we export to plugins
798 // NOTE: in the long run, the whole watchbsp.cpp interface needs to go out and be handled at the BSP plugin level
799 // for now we provide something really basic and limited, the essential is to have something that works fine and fast (for 1.1 final)
800 void QERApp_Listen(){
801 // open the listening socket
802 GetWatchBSP()->ExternalListen();