X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=radiant%2Fconsole.cpp;h=abb1def4655cb9fcb0c62ad3afbfdc2236dae0b7;hb=2546a57498e152b898d94dc85300c1eab314d2eb;hp=b68555232fa0bf6a292535f60aced77be9e902e7;hpb=225950494a606af9aac2bc2f06ece70cb2146481;p=xonotic%2Fnetradiant.git diff --git a/radiant/console.cpp b/radiant/console.cpp index b6855523..abb1def4 100644 --- a/radiant/console.cpp +++ b/radiant/console.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include "gtkutil/accelerator.h" #include "gtkutil/messagebox.h" @@ -32,7 +34,6 @@ #include "stream/stringstream.h" #include "convert.h" -#include "version.h" #include "aboutmsg.h" #include "gtkmisc.h" #include "mainframe.h" @@ -45,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] ) { @@ -63,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 ); } } @@ -80,40 +88,39 @@ 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" )); - g_signal_connect( G_OBJECT( item ), "activate", G_CALLBACK( console_clear ), 0 ); + item.connect( "activate", G_CALLBACK( console_clear ), 0 ); item.show(); - container_add_widget( menu, item ); + menu.add(item); } 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; @@ -124,8 +131,8 @@ ui::Widget Console_constructWindow( ui::Window toplevel ){ //g_consoleWidgetFocusPrinter.connect(g_console); - g_signal_connect( G_OBJECT( g_console ), "populate-popup", G_CALLBACK( console_populate_popup ), 0 ); - g_signal_connect( G_OBJECT( g_console ), "destroy", G_CALLBACK( destroy_set_null ), &g_console ); + g_console.connect( "populate-popup", G_CALLBACK( console_populate_popup ), 0 ); + g_console.connect( "destroy", G_CALLBACK( destroy_set_null ), &g_console ); } gtk_container_set_focus_chain( GTK_CONTAINER( scr ), NULL ); @@ -142,79 +149,100 @@ 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 ){ - gtk_text_buffer_insert_with_tags( textBuffer, iter, buffer, gint( length ), tag, 0 ); + gtk_text_buffer_insert_with_tags( textBuffer, iter, buffer, gint( length ), tag, NULL ); return 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(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 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 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, 0 ); - static GtkTextTag* warning_tag = gtk_text_buffer_create_tag( buffer, "yellow_foreground", "foreground-gdk", &yellow, 0 ); - static GtkTextTag* standard_tag = gtk_text_buffer_create_tag( buffer, "black_foreground", 0 ); - 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 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( 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; }