+#include <glib.h>
+#include "xmlstuff.h"
+
+class CWatchBSP
+{
+private:
+// a flag we have set to true when using an external BSP plugin
+// the resulting code with that is a bit dirty, cleaner solution would be to seperate the succession of commands from the listening loop
+// (in two seperate classes probably)
+bool m_bBSPPlugin;
+
+// EIdle: we are not listening
+// DoMonitoringLoop will change state to EBeginStep
+// EBeginStep: the socket is up for listening, we are expecting incoming connection
+// incoming connection will change state to EWatching
+// EWatching: we have a connection, monitor it
+// connection closed will see if we start a new step (EBeginStep) or launch Quake3 and end (EIdle)
+enum EWatchBSPState { EIdle, EBeginStep, EWatching } m_eState;
+socket_t *m_pListenSocket;
+socket_t *m_pInSocket;
+netmessage_t msg;
+GPtrArray *m_pCmd;
+// used to timeout EBeginStep
+GTimer *m_pTimer;
+std::size_t m_iCurrentStep;
+// name of the map so we can run the engine
+char *m_sBSPName;
+// buffer we use in push mode to receive data directly from the network
+xmlParserInputBufferPtr m_xmlInputBuffer;
+xmlParserInputPtr m_xmlInput;
+xmlParserCtxtPtr m_xmlParserCtxt;
+// call this to switch the set listening mode
+bool SetupListening();
+// start a new EBeginStep
+void DoEBeginStep();
+// the xml and sax parser state
+char m_xmlBuf[MAX_NETMESSAGE];
+bool m_bNeedCtxtInit;
+message_info_t m_message_info;
+
+public:
+CWatchBSP(){
+ m_pCmd = 0;
+ m_bBSPPlugin = false;
+ m_pListenSocket = NULL;
+ m_pInSocket = NULL;
+ m_eState = EIdle;
+ m_pTimer = g_timer_new();
+ m_sBSPName = NULL;
+ m_xmlInputBuffer = NULL;
+ m_bNeedCtxtInit = true;
+}
+virtual ~CWatchBSP(){
+ EndMonitoringLoop();
+ Net_Shutdown();
+
+ g_timer_destroy( m_pTimer );
+}
+
+bool HasBSPPlugin() const
+{ return m_bBSPPlugin; }
+
+// called regularly to keep listening
+void RoutineProcessing();
+// start a monitoring loop with the following steps
+void DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName );
+void EndMonitoringLoop(){
+ Reset();
+ if ( m_sBSPName ) {
+ string_release( m_sBSPName, string_length( m_sBSPName ) );
+ m_sBSPName = 0;
+ }
+ if ( m_pCmd ) {
+ g_ptr_array_free( m_pCmd, TRUE );
+ m_pCmd = 0;
+ }
+}
+// close everything - may be called from the outside to abort the process
+void Reset();
+// start a listening loop for an external process, possibly a BSP plugin
+void ExternalListen();
+};
+
+CWatchBSP* g_pWatchBSP;
+
+// watch the BSP process through network connections
+// true: trigger the BSP steps one by one and monitor them through the network
+// false: create a BAT / .sh file and execute it. don't bother monitoring it.
+bool g_WatchBSP_Enabled = true;
+// do we stop the compilation process if we come accross a leak?
+bool g_WatchBSP_LeakStop = true;
+bool g_WatchBSP_RunQuake = false;
+// store prefs setting for automatic sleep mode activation
+bool g_WatchBSP_DoSleep = true;
+// timeout when beginning a step (in seconds)
+// if we don't get a connection quick enough we assume something failed and go back to idling
+int g_WatchBSP_Timeout = 10;
+
+
+void Build_constructPreferences( PreferencesPage& page ){
+ GtkWidget* monitorbsp = page.appendCheckBox( "", "Enable Build Process Monitoring", g_WatchBSP_Enabled );
+ GtkWidget* leakstop = page.appendCheckBox( "", "Stop Compilation on Leak", g_WatchBSP_LeakStop );
+ GtkWidget* runengine = page.appendCheckBox( "", "Run Engine After Compile", g_WatchBSP_RunQuake );
+ GtkWidget* sleep = page.appendCheckBox ( "", "Sleep When Running the Engine", g_WatchBSP_DoSleep );
+ Widget_connectToggleDependency( leakstop, monitorbsp );
+ Widget_connectToggleDependency( runengine, monitorbsp );
+ Widget_connectToggleDependency( sleep, runengine );
+}
+void Build_constructPage( PreferenceGroup& group ){
+ PreferencesPage page( group.createPage( "Build", "Build Preferences" ) );
+ Build_constructPreferences( page );
+}
+void Build_registerPreferencesPage(){
+ PreferencesDialog_addSettingsPage( FreeCaller1<PreferenceGroup&, Build_constructPage>() );
+}
+
+#include "preferencesystem.h"
+#include "stringio.h"
+
+void BuildMonitor_Construct(){
+ g_pWatchBSP = new CWatchBSP();
+
+ g_WatchBSP_Enabled = !string_equal( g_pGameDescription->getKeyValue( "no_bsp_monitor" ), "1" );
+
+ GlobalPreferenceSystem().registerPreference( "WatchBSP", BoolImportStringCaller( g_WatchBSP_Enabled ), BoolExportStringCaller( g_WatchBSP_Enabled ) );
+ GlobalPreferenceSystem().registerPreference( "RunQuake2Run", BoolImportStringCaller( g_WatchBSP_RunQuake ), BoolExportStringCaller( g_WatchBSP_RunQuake ) );
+ GlobalPreferenceSystem().registerPreference( "LeakStop", BoolImportStringCaller( g_WatchBSP_LeakStop ), BoolExportStringCaller( g_WatchBSP_LeakStop ) );
+ GlobalPreferenceSystem().registerPreference( "SleepMode", BoolImportStringCaller( g_WatchBSP_DoSleep ), BoolExportStringCaller( g_WatchBSP_DoSleep ) );
+
+ Build_registerPreferencesPage();
+}
+
+void BuildMonitor_Destroy(){
+ delete g_pWatchBSP;
+}
+
+CWatchBSP *GetWatchBSP(){
+ return g_pWatchBSP;
+}
+
+void BuildMonitor_Run( GPtrArray* commands, const char* mapName ){
+ GetWatchBSP()->DoMonitoringLoop( commands, mapName );
+}
+