diff --git a/Agent/Agent.cc b/Agent/Agent.cc index 66dba81..fa83a49 100644 --- a/Agent/Agent.cc +++ b/Agent/Agent.cc @@ -1,6 +1,7 @@ #define _WIN32_WINNT 0x0501 #include "Agent.h" #include "../Shared/DebugClient.h" +#include "../Shared/AgentMsg.h" #include #include #include @@ -8,6 +9,10 @@ #include #include +const int BUFFER_LINE_COUNT = 5000; +const int DEFAULT_WINDOW_COLS = 80; +const int DEFAULT_WINDOW_ROWS = 25; + Agent::Agent(const QString &socketServer, QObject *parent) : QObject(parent) { m_conin = CreateFile( @@ -23,6 +28,20 @@ Agent::Agent(const QString &socketServer, QObject *parent) : QObject(parent) Q_ASSERT(m_conin != NULL); Q_ASSERT(m_conout != NULL); + // TODO: The agent should probably have an initial window size from the + // client/window, but it currently doesn't, so default to 80x25. + COORD bufferSize = { DEFAULT_WINDOW_COLS, BUFFER_LINE_COUNT }; + SetConsoleScreenBufferSize(m_conout, bufferSize); + SMALL_RECT windowPos = { + 0, + BUFFER_LINE_COUNT - DEFAULT_WINDOW_ROWS, + DEFAULT_WINDOW_COLS - 1, + BUFFER_LINE_COUNT - 1, + }; + SetConsoleWindowInfo(m_conout, TRUE, &windowPos); + COORD initialCursorPos = { windowPos.Left, windowPos.Top }; + SetConsoleCursorPosition(m_conout, initialCursorPos); + // Connect to the named pipe. m_socket = new QLocalSocket(this); m_socket->connectToServer(socketServer); @@ -51,11 +70,20 @@ Agent::~Agent() void Agent::socketReadyRead() { // TODO: This is an incomplete hack... - while (m_socket->bytesAvailable() >= sizeof(INPUT_RECORD)) { - INPUT_RECORD ir; - m_socket->read((char*)&ir, sizeof(ir)); - DWORD dummy; - WriteConsoleInput(m_conin, &ir, 1, &dummy); + while (m_socket->bytesAvailable() >= sizeof(AgentMsg)) { + AgentMsg msg; + m_socket->read((char*)&msg, sizeof(msg)); + switch (msg.type) { + case AgentMsg::InputRecord: { + DWORD dummy; + WriteConsoleInput(m_conin, &msg.u.inputRecord, 1, &dummy); + break; + } + case AgentMsg::WindowSize: { + resizeWindow(msg.u.windowSize.cols, msg.u.windowSize.rows); + break; + } + } } } @@ -64,6 +92,28 @@ void Agent::socketDisconnected() QCoreApplication::exit(0); } +void Agent::resizeWindow(unsigned short cols, unsigned short rows) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo(m_conout, &info); + + COORD bufferSize = { cols, BUFFER_LINE_COUNT }; + SMALL_RECT windowPos = { + 0, + BUFFER_LINE_COUNT - rows, + cols - 1, + BUFFER_LINE_COUNT - 1, + }; + if (cols > info.dwSize.X) { + SetConsoleScreenBufferSize(m_conout, bufferSize); + SetConsoleWindowInfo(m_conout, TRUE, &windowPos); + } else { + SetConsoleWindowInfo(m_conout, TRUE, &windowPos); + SetConsoleScreenBufferSize(m_conout, bufferSize); + } + // Don't move the cursor, even if the cursor is now off the screen. +} + void Agent::pollTimeout() { if (m_socket->state() == QLocalSocket::ConnectedState) { @@ -119,8 +169,10 @@ void Agent::scrapeOutput() CHAR_INFO *pch = &readBuffer[y * size.X + x]; *pwrite++ = pch->Char.AsciiChar; } - *pwrite++ = '\r'; - *pwrite++ = '\n'; + if (y < size.Y - 1) { + *pwrite++ = '\r'; + *pwrite++ = '\n'; + } } Trace("Agent poll -- writing %d bytes", pwrite - writeBuffer); m_socket->write(writeBuffer, pwrite - writeBuffer); diff --git a/Agent/Agent.h b/Agent/Agent.h index 60c06c7..de1fb93 100644 --- a/Agent/Agent.h +++ b/Agent/Agent.h @@ -20,6 +20,7 @@ private slots: void socketReadyRead(); //void socketReadFinished(); void socketDisconnected(); + void resizeWindow(unsigned short cols, unsigned short rows); void pollTimeout(); void scrapeOutput(); diff --git a/Console/Console.pro b/Console/Console.pro index a4747b6..bff4b40 100644 --- a/Console/Console.pro +++ b/Console/Console.pro @@ -18,6 +18,7 @@ SOURCES += main.cc \ HEADERS += ConsoleWindow.h \ ../Shared/AgentClient.h \ + ../Shared/AgentMsg.h \ TextWidget.h FORMS += ConsoleWindow.ui diff --git a/Console/TextWidget.cc b/Console/TextWidget.cc index c6dd102..d00c795 100644 --- a/Console/TextWidget.cc +++ b/Console/TextWidget.cc @@ -1,5 +1,6 @@ #include "TextWidget.h" #include "../Shared/AgentClient.h" +#include "../Shared/AgentMsg.h" #include #include #include @@ -21,8 +22,10 @@ void TextWidget::keyPressEvent(QKeyEvent *event) // to be portable across operating systems, so using // nativeVirtualKey is wrong. if (event->nativeVirtualKey() != 0) { - INPUT_RECORD ir; - memset(&ir, 0, sizeof(ir)); + AgentMsg msg; + memset(&msg, 0, sizeof(msg)); + msg.type = AgentMsg::InputRecord; + INPUT_RECORD &ir = msg.u.inputRecord; ir.EventType = KEY_EVENT; ir.Event.KeyEvent.bKeyDown = TRUE; ir.Event.KeyEvent.wVirtualKeyCode = event->nativeVirtualKey(); @@ -31,7 +34,7 @@ void TextWidget::keyPressEvent(QKeyEvent *event) ir.Event.KeyEvent.uChar.UnicodeChar = event->text().isEmpty() ? L'\0' : event->text().at(0).unicode(); ir.Event.KeyEvent.wRepeatCount = event->count(); - m_agentClient->writeInputRecord(&ir); + m_agentClient->writeMsg(msg); } } diff --git a/Notes.txt b/Notes.txt new file mode 100644 index 0000000..5b06fdb --- /dev/null +++ b/Notes.txt @@ -0,0 +1,12 @@ +Test programs +------------- + +Cygwin + emacs + vim + mc (Midnight Commander) + lynx + links + less + more + wget diff --git a/Shared/AgentClient.cc b/Shared/AgentClient.cc index 92bc6c4..04c2e93 100644 --- a/Shared/AgentClient.cc +++ b/Shared/AgentClient.cc @@ -6,6 +6,7 @@ #include #include #include "DebugClient.h" +#include "AgentMsg.h" // TODO: Note that this counter makes AgentClient non-thread-safe. int AgentClient::m_counter = 0; @@ -88,9 +89,9 @@ void AgentClient::sendKeyRelease(QKeyEvent *event) } */ -void AgentClient::writeInputRecord(INPUT_RECORD *ir) +void AgentClient::writeMsg(const AgentMsg &msg) { - m_socket->write((char*)ir, sizeof(*ir)); + m_socket->write((char*)&msg, sizeof(msg)); } int AgentClient::agentPid() diff --git a/Shared/AgentClient.h b/Shared/AgentClient.h index 9bc615f..cd9c3a3 100644 --- a/Shared/AgentClient.h +++ b/Shared/AgentClient.h @@ -5,6 +5,7 @@ class QLocalServer; class QLocalSocket; +class AgentMsg; typedef struct _PROCESS_INFORMATION PROCESS_INFORMATION; typedef struct _INPUT_RECORD INPUT_RECORD; @@ -16,7 +17,7 @@ public: //void sendKeyPress(QKeyEvent *event); //void sendKeyRelease(QKeyEvent *event); int agentPid(); - void writeInputRecord(INPUT_RECORD *ir); + void writeMsg(const AgentMsg &msg); void startShell(); QLocalSocket *getSocket(); diff --git a/Shared/AgentMsg.h b/Shared/AgentMsg.h new file mode 100644 index 0000000..51d6807 --- /dev/null +++ b/Shared/AgentMsg.h @@ -0,0 +1,24 @@ +#ifndef AGENTMSG_H +#define AGENTMSG_H + +#include + +class AgentMsg +{ +public: + enum Type { + InputRecord, + WindowSize + }; + + Type type; + union { + INPUT_RECORD inputRecord; + struct { + unsigned short cols; + unsigned short rows; + } windowSize; + } u; +}; + +#endif // AGENTMSG_H diff --git a/TestNetServer/Session.cc b/TestNetServer/Session.cc index 0cb15b4..e2a6f1f 100644 --- a/TestNetServer/Session.cc +++ b/TestNetServer/Session.cc @@ -3,6 +3,7 @@ #include #include #include "../Shared/AgentClient.h" +#include "../Shared/AgentMsg.h" #include "../Shared/DebugClient.h" #include @@ -24,20 +25,42 @@ void Session::onSocketReadyRead() Trace("session: read %d bytes", data.length()); - for (int i = 0; i < data.size(); ++i) { - Trace("input: %02x", (unsigned char)data[i]); + for (int i = 0; i < data.size(); ) { + const int remaining = data.size() - i; - short vk = VkKeyScan((unsigned char)data[i]); + if (remaining >= 12 && !strncmp(data.constData(), "\x1B[:r", 4)) { + // Terminal resize. + char buf[9]; + memcpy(buf, data.constData() + 4, 8); + i += 12; + buf[8] = '\0'; + unsigned int dim = strtol(buf, NULL, 16); + AgentMsg msg; + memset(&msg, 0, sizeof(msg)); + msg.type = AgentMsg::WindowSize; + msg.u.windowSize.cols = dim & 0xffff; + msg.u.windowSize.rows = dim >> 16; + Trace("resize: %d x %d", msg.u.windowSize.cols, msg.u.windowSize.rows); + m_agentClient->writeMsg(msg); + continue; + } + + const unsigned char ch = data[i++]; + const short vk = VkKeyScan(ch); if (vk != -1) { - INPUT_RECORD ir; - memset(&ir, 0, sizeof(ir)); + Trace("input: %02x", ch); + AgentMsg msg; + memset(&msg, 0, sizeof(msg)); + msg.type = AgentMsg::InputRecord; + INPUT_RECORD &ir = msg.u.inputRecord; ir.EventType = KEY_EVENT; ir.Event.KeyEvent.bKeyDown = TRUE; ir.Event.KeyEvent.wVirtualKeyCode = vk & 0xff; ir.Event.KeyEvent.wVirtualScanCode = 0; - ir.Event.KeyEvent.uChar.AsciiChar = data[i]; + ir.Event.KeyEvent.uChar.AsciiChar = ch; ir.Event.KeyEvent.wRepeatCount = 1; - m_agentClient->writeInputRecord(&ir); + m_agentClient->writeMsg(msg); + continue; } } diff --git a/TestNetServer/TestNetServer.pro b/TestNetServer/TestNetServer.pro index d8d5c02..e6fbbf1 100644 --- a/TestNetServer/TestNetServer.pro +++ b/TestNetServer/TestNetServer.pro @@ -23,6 +23,7 @@ SOURCES += main.cc \ HEADERS += \ ../Shared/AgentClient.h \ + ../Shared/AgentMsg.h \ ../Shared/DebugClient.h \ Session.h \ Server.h