mirror of https://github.com/PCSX2/pcsx2.git
Merge pull request #946 from turtleli/linux-console
gui:linux: Redirect stdout/stderr to the PCSX2 console
This commit is contained in:
commit
372db4b539
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -394,6 +394,7 @@ set(pcsx2IPUHeaders
|
|||
|
||||
# Linux sources
|
||||
set(pcsx2LinuxSources
|
||||
Linux/LnxConsolePipe.cpp
|
||||
Linux/LnxKeyCodes.cpp
|
||||
Linux/LnxFlatFileReader.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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "App.h"
|
||||
#include "ConsoleLogger.h"
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -438,6 +438,9 @@
|
|||
<ClCompile Include="..\..\gui\Panels\GameDatabasePanel.cpp" />
|
||||
<ClCompile Include="..\..\gui\Panels\MemoryCardListView.cpp" />
|
||||
<ClCompile Include="..\..\IPU\IPUdma.cpp" />
|
||||
<ClCompile Include="..\..\Linux\LnxConsolePipe.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\MultipartFileReader.cpp" />
|
||||
<ClCompile Include="..\..\Patch.cpp" />
|
||||
<ClCompile Include="..\..\Patch_Memory.cpp" />
|
||||
|
|
|
@ -874,6 +874,9 @@
|
|||
<ClCompile Include="..\Optimus.cpp">
|
||||
<Filter>AppHost\Win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Linux\LnxConsolePipe.cpp">
|
||||
<Filter>AppHost\Linux</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\Patch.h">
|
||||
|
|
Loading…
Reference in New Issue