]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/console.cpp
radiant/console: rename Sys_LogFile to Sys_EnableLogFile
[xonotic/netradiant.git] / radiant / console.cpp
index 2f156dd27de9630087eff3b000f4f2c905eede99..abb1def4655cb9fcb0c62ad3afbfdc2236dae0b7 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.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 );
                }
        }
@@ -81,14 +88,13 @@ void Sys_LogFile( bool enable ){
        }
 }
 
-ui::Widget g_console;
+ui::TextView g_console{ui::null};
 
 void console_clear(){
-       GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( g_console ) );
-       gtk_text_buffer_set_text( buffer, "", -1 );
+       g_console.text("");
 }
 
-void console_populate_popup( GtkTextView* textview, ui::Menu menu, gpointer user_data ){
+void console_populate_popup( ui::TextView textview, ui::Menu menu, gpointer user_data ){
        menu_separator( menu );
 
        ui::Widget item(ui::MenuItem( "Clear" ));
@@ -98,23 +104,23 @@ void console_populate_popup( GtkTextView* textview, ui::Menu menu, gpointer user
 }
 
 gboolean destroy_set_null( ui::Window widget, ui::Widget* p ){
-       *p = ui::Widget();
+       *p = ui::Widget{ui::null};
        return FALSE;
 }
 
 WidgetFocusPrinter g_consoleWidgetFocusPrinter( "console" );
 
 ui::Widget Console_constructWindow( ui::Window toplevel ){
-       auto scr = ui::ScrolledWindow();
-       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scr ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+       auto scr = ui::ScrolledWindow(ui::New);
+       scr.overflow(ui::Policy::AUTOMATIC, ui::Policy::AUTOMATIC);
        gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scr ), GTK_SHADOW_IN );
        scr.show();
 
        {
-               ui::Widget text = ui::TextView();
-               gtk_widget_set_size_request( text, 0, -1 ); // allow shrinking
-               gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( text ), GTK_WRAP_WORD );
-               gtk_text_view_set_editable( GTK_TEXT_VIEW( text ), FALSE );
+               auto text = ui::TextView(ui::New);
+               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);
                text.show();
                g_console = text;
@@ -148,74 +154,95 @@ std::size_t write( const char* buffer, std::size_t length ){
 }
 };
 
+// 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);
+
+       auto buffer = gtk_text_view_get_buffer( g_console );
+
+       GtkTextIter iter;
+       gtk_text_buffer_get_end_iter( buffer, &iter );
+
+       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 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 ( args->level )
+       {
+       case SYS_WRN:
+               tag = warning_tag;
+               break;
+       case SYS_ERR:
+               tag = error_tag;
+               break;
+       case SYS_STD:
+       case SYS_VRB:
+       default:
+               tag = standard_tag;
+               break;
+       }
+
+       {
+               GtkTextBufferOutputStream textBuffer( buffer, &iter, tag );
+               if ( !globalCharacterSet().isUTF8() ) {
+                       BufferedTextOutputStream<GtkTextBufferOutputStream> buffered( textBuffer );
+                       buffered << StringRange( args->buf, args->buf + args->length );
+               }
+               else
+               {
+                       textBuffer << StringRange( args->buf, args->buf + args->length );
+               }
+       }
+
+       // update console widget immediatly if we're doing something time-consuming
+       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_LogFile( true );
+               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 ) {
-               if ( g_console != 0 ) {
-                       GtkTextBuffer* buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW( g_console ) );
-
-                       GtkTextIter iter;
-                       gtk_text_buffer_get_end_iter( buffer, &iter );
-
-                       static GtkTextMark* 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 );
-                       GtkTextTag* tag;
-                       switch ( level )
-                       {
-                       case SYS_WRN:
-                               tag = warning_tag;
-                               break;
-                       case SYS_ERR:
-                               tag = error_tag;
-                               break;
-                       case SYS_STD:
-                       case SYS_VRB:
-                       default:
-                               tag = standard_tag;
-                               break;
-                       }
-
-
-                       {
-                               GtkTextBufferOutputStream textBuffer( buffer, &iter, tag );
-                               if ( !globalCharacterSet().isUTF8() ) {
-                                       BufferedTextOutputStream<GtkTextBufferOutputStream> buffered( textBuffer );
-                                       buffered << StringRange( buf, buf + length );
-                               }
-                               else
-                               {
-                                       textBuffer << StringRange( buf, buf + length );
-                               }
-                       }
-
-                       // update console widget immediatly if we're doing something time-consuming
-                       if ( contains_newline ) {
-                               gtk_text_view_scroll_mark_onscreen( GTK_TEXT_VIEW( g_console ), end );
-
-                               if ( !ScreenUpdates_Enabled() && gtk_widget_get_realized( g_console ) ) {
-                                       ScreenUpdates_process();
-                               }
-                       }
+       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;
 }