diff --git a/common/include/Utilities/Console.h b/common/include/Utilities/Console.h
index 3cec1595c0..2f27446622 100644
--- a/common/include/Utilities/Console.h
+++ b/common/include/Utilities/Console.h
@@ -238,6 +238,9 @@ public:
extern IConsoleWriter Console;
+#ifdef __linux__
+extern void Console_SetStdout(FILE *fp);
+#endif
extern void Console_SetActiveHandler( const IConsoleWriter& writer, FILE* flushfp=NULL );
extern const wxString& ConsoleBuffer_Get();
extern void ConsoleBuffer_Clear();
diff --git a/common/src/Utilities/Console.cpp b/common/src/Utilities/Console.cpp
index d1a9dffb35..97f1623469 100644
--- a/common/src/Utilities/Console.cpp
+++ b/common/src/Utilities/Console.cpp
@@ -32,6 +32,15 @@ static DeclareTls(ConsoleColors) conlog_Color( DefaultConsoleColor );
static wxString m_buffer; // used by ConsoleBuffer
static Mutex m_bufferlock; // used by ConsoleBuffer
+#ifdef __linux__
+static FILE *stdout_fp = stdout;
+
+void Console_SetStdout(FILE *fp)
+{
+ stdout_fp = fp;
+}
+#endif
+
// This function re-assigns the console log writer(s) to the specified target. It makes sure
// to flush any contents from the buffered console log (which typically accumulates due to
// log suspension during log file/window re-init operations) into the new log.
@@ -71,11 +80,12 @@ void MSW_OutputDebugString( const wxString& text )
static bool hasDebugger = wxIsDebuggerRunning();
if( hasDebugger ) OutputDebugString( text );
#else
- fputs(text.utf8_str(), stdout);
- fflush(stdout);
+ fputs(text.utf8_str(), stdout_fp);
+ fflush(stdout_fp);
#endif
}
+
// --------------------------------------------------------------------------------------
// ConsoleNull
// --------------------------------------------------------------------------------------
@@ -165,17 +175,17 @@ static void __concall ConsoleStdout_Newline()
static void __concall ConsoleStdout_DoSetColor( ConsoleColors color )
{
#ifdef __linux__
- fprintf(stdout, "\033[0m%s", GetLinuxConsoleColor(color));
- fflush(stdout);
+ fprintf(stdout_fp, "\033[0m%s", GetLinuxConsoleColor(color));
+ fflush(stdout_fp);
#endif
}
static void __concall ConsoleStdout_SetTitle( const wxString& title )
{
#ifdef __linux__
- fputs("\033]0;", stdout);
- fputs(title.utf8_str(), stdout);
- fputs("\007", stdout);
+ fputs("\033]0;", stdout_fp);
+ fputs(title.utf8_str(), stdout_fp);
+ fputs("\007", stdout_fp);
#endif
}
diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt
index 119b1fcf78..3ed249db33 100644
--- a/pcsx2/CMakeLists.txt
+++ b/pcsx2/CMakeLists.txt
@@ -394,6 +394,7 @@ set(pcsx2IPUHeaders
# Linux sources
set(pcsx2LinuxSources
+ Linux/LnxConsolePipe.cpp
Linux/LnxKeyCodes.cpp
Linux/LnxFlatFileReader.cpp
)
diff --git a/pcsx2/Linux/LnxConsolePipe.cpp b/pcsx2/Linux/LnxConsolePipe.cpp
new file mode 100644
index 0000000000..30b8abd7a4
--- /dev/null
+++ b/pcsx2/Linux/LnxConsolePipe.cpp
@@ -0,0 +1,191 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2015 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include "App.h"
+#include "ConsoleLogger.h"
+#include
+
+using namespace Threading;
+
+// Reads data sent to stdout/stderr and sends it to the PCSX2 console.
+class LinuxPipeThread : public pxThread
+{
+ typedef pxThread _parent;
+
+protected:
+ const int& m_read_fd;
+ const ConsoleColors m_color;
+
+ void ExecuteTaskInThread();
+public:
+ LinuxPipeThread(const int& read_fd, ConsoleColors color);
+ virtual ~LinuxPipeThread() noexcept;
+};
+
+LinuxPipeThread::LinuxPipeThread(const int& read_fd, ConsoleColors color)
+ : pxThread(color == Color_Red ? L"Redirect_Stderr" : L"Redirect_Stdout")
+ , m_read_fd(read_fd)
+ , m_color(color)
+{
+}
+
+LinuxPipeThread::~LinuxPipeThread() noexcept
+{
+ _parent::Cancel();
+}
+
+void LinuxPipeThread::ExecuteTaskInThread()
+{
+ char buffer[2049];
+ while (ssize_t bytes_read = read(m_read_fd, buffer, sizeof(buffer) - 1)) {
+ TestCancel();
+
+ if (bytes_read == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ else {
+ // Should never happen.
+ Console.Error(wxString::Format(L"Redirect %s failed: read: %s",
+ m_color == Color_Red ? L"stderr" : L"stdout", strerror(errno)));
+ break;
+ }
+ }
+
+ buffer[bytes_read] = 0;
+
+ {
+ ConsoleColorScope cs(m_color);
+ Console.WriteRaw(fromUTF8(buffer));
+ }
+
+ TestCancel();
+ }
+}
+
+// Redirects data sent to stdout/stderr into a pipe, and sets up a thread to
+// forward data to the console.
+class LinuxPipeRedirection : public PipeRedirectionBase
+{
+ DeclareNoncopyableObject(LinuxPipeRedirection);
+
+protected:
+ FILE* m_stdstream;
+ FILE* m_fp;
+ int m_pipe_fd[2];
+ LinuxPipeThread m_thread;
+
+ void Cleanup() noexcept;
+public:
+ LinuxPipeRedirection(FILE* stdstream);
+ virtual ~LinuxPipeRedirection() noexcept;
+};
+
+LinuxPipeRedirection::LinuxPipeRedirection(FILE* stdstream)
+ : m_stdstream(stdstream)
+ , m_fp(nullptr)
+ , m_pipe_fd{-1, -1}
+ , m_thread(m_pipe_fd[0], stdstream == stderr ? Color_Red : Color_Black)
+{
+ pxAssert((stdstream == stderr) || (stdstream == stdout));
+
+ const wxChar* stream_name = stdstream == stderr? L"stderr" : L"stdout";
+
+ try {
+ int stdstream_fd = fileno(stdstream);
+
+ // Save the original stdout/stderr file descriptor...
+ int dup_fd = dup(stdstream_fd);
+ if (dup_fd == -1)
+ throw Exception::RuntimeError().SetDiagMsg(wxString::Format(
+ L"Redirect %s failed: dup: %s", stream_name, strerror(errno)));
+ m_fp = fdopen(dup_fd, "w");
+ if (m_fp == nullptr)
+ throw Exception::RuntimeError().SetDiagMsg(wxString::Format(
+ L"Redirect %s failed: fdopen: %s", stream_name, strerror(errno)));
+
+ // and now redirect stdout/stderr.
+ if (pipe(m_pipe_fd) == -1)
+ throw Exception::RuntimeError().SetDiagMsg(wxString::Format(
+ L"Redirect %s failed: pipe: %s", stream_name, strerror(errno)));
+ if (dup2(m_pipe_fd[1], stdstream_fd) != stdstream_fd)
+ throw Exception::RuntimeError().SetDiagMsg(wxString::Format(
+ L"Redirect %s failed: dup2: %s", stream_name, strerror(errno)));
+ close(m_pipe_fd[1]);
+ m_pipe_fd[1] = stdstream_fd;
+
+ // And send the final console output goes to the original stdout,
+ // otherwise we'll have an infinite data loop.
+ if (stdstream == stdout)
+ Console_SetStdout(m_fp);
+
+ m_thread.Start();
+ } catch (Exception::BaseThreadError& ex) {
+ // thread object will become invalid because of scoping after we leave
+ // the constructor, so re-pack a new exception:
+ Cleanup();
+ throw Exception::RuntimeError().SetDiagMsg(ex.FormatDiagnosticMessage());
+ } catch (...) {
+ Cleanup();
+ throw;
+ }
+}
+
+LinuxPipeRedirection::~LinuxPipeRedirection() noexcept
+{
+ Cleanup();
+}
+
+void LinuxPipeRedirection::Cleanup() noexcept
+{
+ // Restore stdout/stderr file descriptor - mostly useful if the thread
+ // fails to start, but then you have bigger issues to worry about.
+ if (m_pipe_fd[1] != -1) {
+ if (m_pipe_fd[1] == fileno(m_stdstream)) {
+ // FIXME: Use lock for better termination.
+ // The redirect thread is most likely waiting at read(). Send a
+ // newline so it returns and the thread begins to terminate.
+ fflush(m_stdstream);
+ m_thread.Cancel();
+ fputc('\n', m_stdstream);
+ fflush(m_stdstream);
+ dup2(fileno(m_fp), fileno(m_stdstream));
+ } else {
+ close(m_pipe_fd[1]);
+ }
+ }
+
+ if (m_fp != nullptr) {
+ if (m_stdstream == stdout)
+ Console_SetStdout(stdout);
+ fclose(m_fp);
+ }
+
+ if (m_pipe_fd[0] != -1)
+ close(m_pipe_fd[0]);
+}
+
+PipeRedirectionBase* NewPipeRedir(FILE* stdstream)
+{
+ try {
+ return new LinuxPipeRedirection(stdstream);
+ } catch (Exception::RuntimeError& ex) {
+ Console.Error(ex.FormatDiagnosticMessage());
+ }
+
+ return nullptr;
+}
diff --git a/pcsx2/Linux/LnxKeyCodes.cpp b/pcsx2/Linux/LnxKeyCodes.cpp
index 4c01bc6d36..55f513ce81 100644
--- a/pcsx2/Linux/LnxKeyCodes.cpp
+++ b/pcsx2/Linux/LnxKeyCodes.cpp
@@ -294,13 +294,3 @@ int TranslateGDKtoWXK( u32 keysym )
return key_code;
}
-
-// NewPipeRedir .. Homeless function for now .. This is as good a spot as any.
-// Eventually we might be so fancy as to have a linux console pipe to our own console
-// window, same as the Win32 one. Not sure how doable it is, and it's not as urgent
-// anyway since Linux has better generic console support and commandline piping.
-//
-PipeRedirectionBase* NewPipeRedir( FILE* stdstream )
-{
- return NULL;
-}
diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj
index 065f826e7e..f878f6fd66 100644
--- a/pcsx2/windows/VCprojects/pcsx2.vcxproj
+++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj
@@ -438,6 +438,9 @@
+
+ true
+
diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters
index 32363794bf..672aa9f2d1 100644
--- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters
+++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters
@@ -874,6 +874,9 @@
AppHost\Win32
+
+ AppHost\Linux
+