Add initial server support for resizing terminal windows.
This commit is contained in:
parent
3ae1f5f9a7
commit
166c553f15
@ -1,6 +1,7 @@
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include "Agent.h"
|
||||
#include "../Shared/DebugClient.h"
|
||||
#include "../Shared/AgentMsg.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QLocalSocket>
|
||||
#include <QtDebug>
|
||||
@ -8,6 +9,10 @@
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
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);
|
||||
|
@ -20,6 +20,7 @@ private slots:
|
||||
void socketReadyRead();
|
||||
//void socketReadFinished();
|
||||
void socketDisconnected();
|
||||
void resizeWindow(unsigned short cols, unsigned short rows);
|
||||
void pollTimeout();
|
||||
void scrapeOutput();
|
||||
|
||||
|
@ -18,6 +18,7 @@ SOURCES += main.cc \
|
||||
|
||||
HEADERS += ConsoleWindow.h \
|
||||
../Shared/AgentClient.h \
|
||||
../Shared/AgentMsg.h \
|
||||
TextWidget.h
|
||||
|
||||
FORMS += ConsoleWindow.ui
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "TextWidget.h"
|
||||
#include "../Shared/AgentClient.h"
|
||||
#include "../Shared/AgentMsg.h"
|
||||
#include <QKeyEvent>
|
||||
#include <QtDebug>
|
||||
#include <windows.h>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
12
Notes.txt
Normal file
12
Notes.txt
Normal file
@ -0,0 +1,12 @@
|
||||
Test programs
|
||||
-------------
|
||||
|
||||
Cygwin
|
||||
emacs
|
||||
vim
|
||||
mc (Midnight Commander)
|
||||
lynx
|
||||
links
|
||||
less
|
||||
more
|
||||
wget
|
@ -6,6 +6,7 @@
|
||||
#include <QtDebug>
|
||||
#include <windows.h>
|
||||
#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()
|
||||
|
@ -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();
|
||||
|
||||
|
24
Shared/AgentMsg.h
Normal file
24
Shared/AgentMsg.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef AGENTMSG_H
|
||||
#define AGENTMSG_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
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
|
@ -3,6 +3,7 @@
|
||||
#include <QLocalSocket>
|
||||
#include <QtDebug>
|
||||
#include "../Shared/AgentClient.h"
|
||||
#include "../Shared/AgentMsg.h"
|
||||
#include "../Shared/DebugClient.h"
|
||||
#include <windows.h>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ SOURCES += main.cc \
|
||||
|
||||
HEADERS += \
|
||||
../Shared/AgentClient.h \
|
||||
../Shared/AgentMsg.h \
|
||||
../Shared/DebugClient.h \
|
||||
Session.h \
|
||||
Server.h
|
||||
|
Loading…
Reference in New Issue
Block a user