Add initial server support for resizing terminal windows.

This commit is contained in:
Ryan Prichard 2011-11-14 03:26:13 -08:00
parent 3ae1f5f9a7
commit 166c553f15
10 changed files with 139 additions and 20 deletions

View File

@ -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);

View File

@ -20,6 +20,7 @@ private slots:
void socketReadyRead();
//void socketReadFinished();
void socketDisconnected();
void resizeWindow(unsigned short cols, unsigned short rows);
void pollTimeout();
void scrapeOutput();

View File

@ -18,6 +18,7 @@ SOURCES += main.cc \
HEADERS += ConsoleWindow.h \
../Shared/AgentClient.h \
../Shared/AgentMsg.h \
TextWidget.h
FORMS += ConsoleWindow.ui

View File

@ -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
View File

@ -0,0 +1,12 @@
Test programs
-------------
Cygwin
emacs
vim
mc (Midnight Commander)
lynx
links
less
more
wget

View File

@ -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()

View File

@ -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
View 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

View File

@ -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;
}
}

View File

@ -23,6 +23,7 @@ SOURCES += main.cc \
HEADERS += \
../Shared/AgentClient.h \
../Shared/AgentMsg.h \
../Shared/DebugClient.h \
Session.h \
Server.h