diff --git a/rpcs3/Emu/Cell/lv2/sys_tty.cpp b/rpcs3/Emu/Cell/lv2/sys_tty.cpp index 0a4a7db0f6..7953515353 100644 --- a/rpcs3/Emu/Cell/lv2/sys_tty.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_tty.cpp @@ -1,17 +1,87 @@ #include "stdafx.h" #include "sys_tty.h" +#include +#include + logs::channel sys_tty("sys_tty"); extern fs::file g_tty; extern atomic_t g_tty_size; +extern std::array, 16> g_tty_input; +extern std::mutex g_tty_mutex; error_code sys_tty_read(s32 ch, vm::ptr buf, u32 len, vm::ptr preadlen) { - sys_tty.fatal("sys_tty_read(ch=%d, buf=*0x%x, len=%d, preadlen=*0x%x)", ch, buf, len, preadlen); + sys_tty.trace("sys_tty_read(ch=%d, buf=*0x%x, len=%d, preadlen=*0x%x)", ch, buf, len, preadlen); - // We currently do not support reading from the Console - fmt::throw_exception("Unimplemented" HERE); + if (false) // TODO: debug mode check + { + return CELL_EIO; + } + + if (ch > 15 || !buf) + { + return CELL_EINVAL; + } + + if (ch < SYS_TTYP_USER1) + { + sys_tty.warning("sys_tty_read called with system channel %d", ch); + } + + size_t chars_to_read = 0; // number of chars that will be read from the input string + std::string tty_read; // string for storage of read chars + + if (len > 0) + { + std::lock_guard lock(g_tty_mutex); + + if (g_tty_input[ch].size() > 0) + { + // reference to our first queue element + std::string& input = g_tty_input[ch].front(); + + // we have to stop reading at either a new line, the param len, or our input string size + size_t new_line_pos = input.find_first_of("\n"); + + if (new_line_pos != input.npos) + { + chars_to_read = std::min(new_line_pos, static_cast(len)); + } + else + { + chars_to_read = std::min(input.size(), static_cast(len)); + } + + // read the previously calculated number of chars from the beginning of the input string + tty_read = input.substr(0, chars_to_read); + + // remove the just read text from the input string + input = input.substr(chars_to_read, input.size() - 1); + + if (input.size() == 0) + { + // pop the first queue element if it was completely consumed + g_tty_input[ch].pop_front(); + } + } + } + + if (!preadlen) + { + return CELL_EFAULT; + } + + *preadlen = (u32)chars_to_read; + + if (chars_to_read > 0) + { + std::memcpy(buf.get_ptr(), tty_read.c_str(), chars_to_read); + sys_tty.success("sys_tty_read(ch=%d, len=%d) read %s with length %d", ch, len, tty_read, *preadlen); + } + + return CELL_OK; } error_code sys_tty_write(s32 ch, vm::cptr buf, u32 len, vm::ptr pwritelen) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 68569312b7..e6874acf64 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -64,6 +64,8 @@ extern void network_thread_init(); fs::file g_tty; atomic_t g_tty_size{0}; +std::array, 16> g_tty_input; +std::mutex g_tty_mutex; // Progress display server synchronization variables atomic_t g_progr{nullptr}; diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index 20b8c68cba..422607f8f3 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -9,8 +9,15 @@ #include #include #include +#include +#include +#include "Utilities/sema.h" + +extern fs::file g_tty; extern atomic_t g_tty_size; +extern std::array, 16> g_tty_input; +extern std::mutex g_tty_mutex; constexpr auto qstr = QString::fromStdString; @@ -123,8 +130,26 @@ log_frame::log_frame(std::shared_ptr guiSettings, QWidget *parent) m_tty->setContextMenuPolicy(Qt::CustomContextMenu); m_tty->installEventFilter(this); + m_tty_input = new QLineEdit(); + if (m_tty_channel >= 0) + { + m_tty_input->setPlaceholderText(tr("Channel %0").arg(m_tty_channel)); + } + else + { + m_tty_input->setPlaceholderText(tr("All User Channels")); + } + + QVBoxLayout* tty_layout = new QVBoxLayout(); + tty_layout->addWidget(m_tty); + tty_layout->addWidget(m_tty_input); + tty_layout->setContentsMargins(0, 0, 0, 0); + + m_tty_container = new QWidget(); + m_tty_container->setLayout(tty_layout); + m_tabWidget->addTab(m_log, tr("Log")); - m_tabWidget->addTab(m_tty, tr("TTY")); + m_tabWidget->addTab(m_tty_container, tr("TTY")); setWidget(m_tabWidget); @@ -212,6 +237,30 @@ void log_frame::CreateAndConnectActions() m_clearTTYAct = new QAction(tr("Clear"), this); connect(m_clearTTYAct, &QAction::triggered, m_tty, &QTextEdit::clear); + m_tty_channel_acts = new QActionGroup(this); + + // Special Channel: All + QAction* all_channels_act = new QAction(tr("All user channels"), m_tty_channel_acts); + all_channels_act->setCheckable(true); + all_channels_act->setChecked(m_tty_channel == -1); + connect(all_channels_act, &QAction::triggered, [this]() + { + m_tty_channel = -1; + m_tty_input->setPlaceholderText(tr("All user channels")); + }); + + for (int i = 3; i < 16; i++) + { + QAction* act = new QAction(tr("Channel %0").arg(i), m_tty_channel_acts); + act->setCheckable(true); + act->setChecked(i == m_tty_channel); + connect(act, &QAction::triggered, [this, i]() + { + m_tty_channel = i; + m_tty_input->setPlaceholderText(tr("Channel %0").arg(m_tty_channel)); + }); + } + // Action groups make these actions mutually exclusive. m_logLevels = new QActionGroup(this); m_nothingAct = new QAction(tr("Nothing"), m_logLevels); @@ -253,7 +302,7 @@ void log_frame::CreateAndConnectActions() QMenu* menu = m_log->createStandardContextMenu(); menu->addAction(m_clearAct); menu->addSeparator(); - menu->addActions({ m_nothingAct, m_fatalAct, m_errorAct, m_todoAct, m_successAct, m_warningAct, m_noticeAct, m_traceAct }); + menu->addActions(m_logLevels->actions()); menu->addSeparator(); menu->addAction(m_stackAct); menu->addSeparator(); @@ -265,6 +314,8 @@ void log_frame::CreateAndConnectActions() { QMenu* menu = m_tty->createStandardContextMenu(); menu->addAction(m_clearTTYAct); + menu->addSeparator(); + menu->addActions(m_tty_channel_acts->actions()); menu->exec(mapToGlobal(pos)); }); @@ -274,6 +325,42 @@ void log_frame::CreateAndConnectActions() m_find_dialog->close(); }); + connect(m_tty_input, &QLineEdit::returnPressed, [this]() + { + std::string text = m_tty_input->text().toStdString(); + + { + std::lock_guard lock(g_tty_mutex); + + if (m_tty_channel == -1) + { + for (int i = 3; i < 16; i++) + { + g_tty_input[i].push_back(text + "\n"); + } + } + else + { + g_tty_input[m_tty_channel].push_back(text + "\n"); + } + } + + // Write to tty + if (m_tty_channel == -1) + { + text = "All channels > " + text + "\n"; + } + else + { + text = fmt::format("%s > %s\n", "Ch.%d", m_tty_channel, text); + } + g_tty_size -= (1ll << 48); + g_tty.write(text.c_str(), text.size()); + g_tty_size += (1ll << 48) + text.size(); + + m_tty_input->clear(); + }); + LoadSettings(); } diff --git a/rpcs3/rpcs3qt/log_frame.h b/rpcs3/rpcs3qt/log_frame.h index 6394f6a4f1..c899445824 100644 --- a/rpcs3/rpcs3qt/log_frame.h +++ b/rpcs3/rpcs3qt/log_frame.h @@ -49,12 +49,15 @@ private: QList m_color; QColor m_color_stack; QTextEdit* m_log; - QTextEdit* m_tty; QString m_old_text; ullong m_log_counter; bool m_stack_log; fs::file m_tty_file; + QWidget* m_tty_container; + QTextEdit* m_tty; + QLineEdit* m_tty_input; + int m_tty_channel = -1; QAction* m_clearAct; QAction* m_clearTTYAct; @@ -73,5 +76,7 @@ private: QAction* m_TTYAct; + QActionGroup* m_tty_channel_acts; + std::shared_ptr xgui_settings; };