]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/console.cpp
Merge commit '5e3961896b9e30ebbc736b542f177054188c05bf' into master-merge
[xonotic/netradiant.git] / radiant / console.cpp
index 8c72bcd77b3d107291b5f7b71bda28c49de2379c..489f8d61f4773dcae99d991ffab2c7d6d7236ed8 100644 (file)
@@ -24,6 +24,7 @@
 #include <time.h>
 #include <uilib/uilib.h>
 #include <gtk/gtk.h>
+#include <mutex>
 
 #include "gtkutil/accelerator.h"
 #include "gtkutil/messagebox.h"
@@ -33,7 +34,6 @@
 #include "stream/stringstream.h"
 #include "convert.h"
 
-#include "version.h"
 #include "aboutmsg.h"
 #include "gtkmisc.h"
 #include "mainframe.h"
@@ -46,8 +46,15 @@ FILE* g_hLogFile;
 
 bool g_Console_enableLogging = false;
 
+struct Gtk_Idle_Print_Data {
+       int level;
+       char *buf;
+       std::size_t length;
+       bool contains_newline;
+};
+
 // called whenever we need to open/close/check the console log file
-void Sys_LogFile( bool enable ){
+void Sys_EnableLogFile( bool enable ){
        if ( enable && !g_hLogFile ) {
                // settings say we should be logging and we don't have a log file .. so create it
                if ( !SettingsPath_get()[0] ) {
@@ -64,10 +71,10 @@ void Sys_LogFile( bool enable ){
                        time_t localtime;
                        time( &localtime );
                        globalOutputStream() << "Today is: " << ctime( &localtime )
-                                                                << "This is NetRadiant '" RADIANT_VERSION "' compiled " __DATE__ "\n" RADIANT_ABOUTMSG "\n";
+                                                                << "This is " RADIANT_NAME " " RADIANT_VERSION " compiled " __DATE__ "\n" RADIANT_ABOUTMSG "\n";
                }
                else{
-                       ui::root.window().alert( "Failed to create log file, check write permissions in Radiant directory.\n",
+                       ui::alert( ui::root, "Failed to create log file, check write permissions in " RADIANT_NAME " directory.\n",
                                                        "Console logging", ui::alert_type::OK, ui::alert_icon::Error );
                }
        }
@@ -111,7 +118,7 @@ ui::Widget Console_constructWindow( ui::Window toplevel ){
 
        {
                auto text = ui::TextView(ui::New);
-               gtk_widget_set_size_request( text, 0, -1 ); // allow shrinking
+               text.dimensions(0, -1); // allow shrinking
                gtk_text_view_set_wrap_mode( text, GTK_WRAP_WORD );
                gtk_text_view_set_editable( text, FALSE );
                scr.add(text);
@@ -133,6 +140,9 @@ ui::Widget Console_constructWindow( ui::Window toplevel ){
        return scr;
 }
 
+//#pragma GCC push_options
+//#pragma GCC optimize ("O0")
+
 class GtkTextBufferOutputStream : public TextOutputStream
 {
 GtkTextBuffer* textBuffer;
@@ -141,43 +151,34 @@ GtkTextTag* tag;
 public:
 GtkTextBufferOutputStream( GtkTextBuffer* textBuffer, GtkTextIter* iter, GtkTextTag* tag ) : textBuffer( textBuffer ), iter( iter ), tag( tag ){
 }
-std::size_t write( const char* buffer, std::size_t length ){
+std::size_t __attribute__((optimize("O0"))) write( const char* buffer, std::size_t length ){
        gtk_text_buffer_insert_with_tags( textBuffer, iter, buffer, gint( length ), tag, NULL );
        return length;
 }
 };
 
-std::size_t Sys_Print( int level, const char* buf, std::size_t length ){
-       bool contains_newline = std::find( buf, buf + length, '\n' ) != buf + length;
-
-       if ( level == SYS_ERR ) {
-               Sys_LogFile( true );
-       }
+//#pragma GCC pop_options
 
-       if ( g_hLogFile != 0 ) {
-               fwrite( buf, 1, length, g_hLogFile );
-               if ( contains_newline ) {
-                       fflush( g_hLogFile );
-               }
-       }
+// This function is meant to be used with gtk_idle_add. It will free its argument.
+static gboolean Gtk_Idle_Print( gpointer data ){
+       Gtk_Idle_Print_Data *args = reinterpret_cast<Gtk_Idle_Print_Data *>(data);
+       g_assert(g_console);
 
-       if ( level != SYS_NOCON ) {
-               if ( g_console ) {
-                       GtkTextBuffer* buffer = gtk_text_view_get_buffer( g_console );
+                       auto buffer = gtk_text_view_get_buffer( g_console );
 
                        GtkTextIter iter;
                        gtk_text_buffer_get_end_iter( buffer, &iter );
 
-                       static GtkTextMark* end = gtk_text_buffer_create_mark( buffer, "end", &iter, FALSE );
+                       static auto end = gtk_text_buffer_create_mark( buffer, "end", &iter, FALSE );
 
                        const GdkColor yellow = { 0, 0xb0ff, 0xb0ff, 0x0000 };
                        const GdkColor red = { 0, 0xffff, 0x0000, 0x0000 };
 
-                       static GtkTextTag* error_tag = gtk_text_buffer_create_tag( buffer, "red_foreground", "foreground-gdk", &red, NULL );
-                       static GtkTextTag* warning_tag = gtk_text_buffer_create_tag( buffer, "yellow_foreground", "foreground-gdk", &yellow, NULL );
-                       static GtkTextTag* standard_tag = gtk_text_buffer_create_tag( buffer, "black_foreground", NULL );
+                       static auto error_tag = gtk_text_buffer_create_tag( buffer, "red_foreground", "foreground-gdk", &red, NULL );
+                       static auto warning_tag = gtk_text_buffer_create_tag( buffer, "yellow_foreground", "foreground-gdk", &yellow, NULL );
+                       static auto standard_tag = gtk_text_buffer_create_tag( buffer, "black_foreground", NULL );
                        GtkTextTag* tag;
-                       switch ( level )
+       switch ( args->level )
                        {
                        case SYS_WRN:
                                tag = warning_tag;
@@ -192,29 +193,61 @@ std::size_t Sys_Print( int level, const char* buf, std::size_t length ){
                                break;
                        }
 
-
                        {
                                GtkTextBufferOutputStream textBuffer( buffer, &iter, tag );
                                if ( !globalCharacterSet().isUTF8() ) {
                                        BufferedTextOutputStream<GtkTextBufferOutputStream> buffered( textBuffer );
-                                       buffered << StringRange( buf, buf + length );
+                       buffered << StringRange( args->buf, args->buf + args->length );
                                }
                                else
                                {
-                                       textBuffer << StringRange( buf, buf + length );
+                       textBuffer << StringRange( args->buf, args->buf + args->length );
                                }
                        }
 
                        // update console widget immediatly if we're doing something time-consuming
-                       if ( contains_newline ) {
+       if ( args->contains_newline ) {
                                gtk_text_view_scroll_mark_onscreen( g_console, end );
 
                                if ( !ScreenUpdates_Enabled() && gtk_widget_get_realized( g_console ) ) {
                                        ScreenUpdates_process();
                                }
                        }
+
+       free( args->buf );
+       free( args );
+
+       return FALSE; // call this once, not repeatedly
                }
+
+// Print logs to the in-game console and/or to the log file.
+// This function is thread safe.
+std::size_t Sys_Print( int level, const char* buf, std::size_t length ){
+       bool contains_newline = std::find( buf, buf + length, '\n' ) != buf + length;
+
+       if ( level == SYS_ERR ) {
+               Sys_EnableLogFile( true );
        }
+
+       if ( g_hLogFile != 0 ) {
+               // prevent parallel write
+               static std::mutex log_file_mutex;
+               std::lock_guard<std::mutex> guard(log_file_mutex);
+
+               fwrite( buf, 1, length, g_hLogFile );
+               if ( contains_newline ) {
+                       fflush( g_hLogFile );
+               }
+       }
+
+       if ( level != SYS_NOCON && g_console ) {
+               auto data = reinterpret_cast<Gtk_Idle_Print_Data *>( malloc( sizeof(struct Gtk_Idle_Print_Data) ) );
+               if (data != nullptr) {
+                       *data = { level, g_strndup(buf, length), length, contains_newline };
+                       gdk_threads_add_idle(Gtk_Idle_Print, (gpointer)data);
+               }
+       }
+
        return length;
 }