Add a Win32Console class that provides a simpler, Qt/C++ interface to the
Win32 Console API.
This commit is contained in:
parent
c74f82abdb
commit
f77557aa95
105
Agent/Agent.cc
105
Agent/Agent.cc
@ -1,74 +1,28 @@
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include "Agent.h"
|
||||
#include "Win32Console.h"
|
||||
#include "../Shared/DebugClient.h"
|
||||
#include "../Shared/AgentMsg.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QLocalSocket>
|
||||
#include <QtDebug>
|
||||
#include <QTimer>
|
||||
#include <QSize>
|
||||
#include <QRect>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
const int BUFFER_LINE_COUNT = 5000;
|
||||
|
||||
static void resizeWindow(HANDLE conout, unsigned short cols, unsigned short rows)
|
||||
{
|
||||
// Windows has one API for resizing the screen buffer and a different one
|
||||
// for resizing the window. It seems that either API can fail if the
|
||||
// window does not fit on the screen buffer.
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO originalInfo;
|
||||
GetConsoleScreenBufferInfo(conout, &originalInfo);
|
||||
|
||||
COORD finalBufferSize = { cols, BUFFER_LINE_COUNT };
|
||||
SMALL_RECT finalWindowRect = {
|
||||
0,
|
||||
BUFFER_LINE_COUNT - rows,
|
||||
cols - 1,
|
||||
BUFFER_LINE_COUNT - 1,
|
||||
};
|
||||
|
||||
if (originalInfo.dwSize.Y != BUFFER_LINE_COUNT) {
|
||||
// TODO: Is it really safe to resize the window down to 1x1?
|
||||
// TODO: Is there a better way to do this?
|
||||
SMALL_RECT smallestWindowRect = { 0, 0, 0, 0 };
|
||||
SetConsoleWindowInfo(conout, TRUE, &smallestWindowRect);
|
||||
SetConsoleScreenBufferSize(conout, finalBufferSize);
|
||||
SetConsoleWindowInfo(conout, TRUE, &finalWindowRect);
|
||||
} else {
|
||||
if (cols > originalInfo.dwSize.X) {
|
||||
SetConsoleScreenBufferSize(conout, finalBufferSize);
|
||||
SetConsoleWindowInfo(conout, TRUE, &finalWindowRect);
|
||||
} else {
|
||||
SetConsoleWindowInfo(conout, TRUE, &finalWindowRect);
|
||||
SetConsoleScreenBufferSize(conout, finalBufferSize);
|
||||
}
|
||||
}
|
||||
// Don't move the cursor, even if the cursor is now off the screen.
|
||||
}
|
||||
|
||||
Agent::Agent(const QString &socketServer,
|
||||
int initialCols,
|
||||
int initialRows,
|
||||
QObject *parent) :
|
||||
QObject(parent), m_timer(NULL)
|
||||
{
|
||||
m_conin = CreateFile(
|
||||
L"CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
m_conout = CreateFile(
|
||||
L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
Q_ASSERT(m_conin != NULL);
|
||||
Q_ASSERT(m_conout != NULL);
|
||||
|
||||
resizeWindow(m_conout, initialCols, initialRows);
|
||||
COORD initialCursorPos = { 0, BUFFER_LINE_COUNT - initialRows };
|
||||
SetConsoleCursorPosition(m_conout, initialCursorPos);
|
||||
m_console = new Win32Console(this);
|
||||
resizeWindow(initialCols, initialRows);
|
||||
m_console->setCursorPosition(QPoint(0, BUFFER_LINE_COUNT - initialRows));
|
||||
|
||||
// Connect to the named pipe.
|
||||
m_socket = new QLocalSocket(this);
|
||||
@ -90,9 +44,7 @@ Agent::Agent(const QString &socketServer,
|
||||
|
||||
Agent::~Agent()
|
||||
{
|
||||
HWND hwnd = GetConsoleWindow();
|
||||
if (hwnd != NULL)
|
||||
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
||||
m_console->postCloseMessage();
|
||||
}
|
||||
|
||||
void Agent::socketReadyRead()
|
||||
@ -104,8 +56,7 @@ void Agent::socketReadyRead()
|
||||
m_socket->read((char*)&msg, sizeof(msg));
|
||||
switch (msg.type) {
|
||||
case AgentMsg::InputRecord: {
|
||||
DWORD dummy;
|
||||
WriteConsoleInput(m_conin, &msg.u.inputRecord, 1, &dummy);
|
||||
m_console->writeInput(&msg.u.inputRecord);
|
||||
break;
|
||||
}
|
||||
case AgentMsg::WindowSize: {
|
||||
@ -120,7 +71,7 @@ void Agent::socketReadyRead()
|
||||
continue;
|
||||
}
|
||||
Trace("resize started");
|
||||
resizeWindow(m_conout, msg.u.windowSize.cols, msg.u.windowSize.rows);
|
||||
resizeWindow(msg.u.windowSize.cols, msg.u.windowSize.rows);
|
||||
Trace("resize done");
|
||||
break;
|
||||
}
|
||||
@ -156,40 +107,28 @@ void Agent::pollTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::resizeWindow(int cols, int rows)
|
||||
{
|
||||
m_console->reposition(
|
||||
QSize(cols, BUFFER_LINE_COUNT),
|
||||
QRect(0, BUFFER_LINE_COUNT - rows, cols, rows));
|
||||
}
|
||||
|
||||
void Agent::scrapeOutput()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
if (!GetConsoleScreenBufferInfo(m_conout, &info)) {
|
||||
Trace("GetConsoleScreenBufferInfo failed");
|
||||
return;
|
||||
}
|
||||
|
||||
COORD size;
|
||||
size.X = info.srWindow.Right - info.srWindow.Left + 1;
|
||||
size.Y = info.srWindow.Bottom - info.srWindow.Top + 1;
|
||||
COORD zeroPos = {0, 0};
|
||||
SMALL_RECT readWindow = info.srWindow;
|
||||
|
||||
const QRect windowRect = m_console->windowRect();
|
||||
CHAR_INFO readBuffer[64 * 1024 / sizeof(CHAR_INFO)]; // TODO: buf overflow
|
||||
if (!ReadConsoleOutput(m_conout, readBuffer, size, zeroPos, &readWindow)) {
|
||||
Trace("ReadConsoleOutput failed");
|
||||
return;
|
||||
}
|
||||
|
||||
char writeBuffer[sizeof(readBuffer) / sizeof(CHAR_INFO)]; // TODO: buf overflow
|
||||
char *pwrite = writeBuffer;
|
||||
if (memcmp(&info.srWindow, &readWindow, sizeof(SMALL_RECT))) {
|
||||
Trace("ReadConsoleOutput returned a different-sized buffer");
|
||||
return;
|
||||
}
|
||||
m_console->read(windowRect, readBuffer);
|
||||
|
||||
// Simplest algorithm -- just send the whole screen.
|
||||
for (int y = 0; y < size.Y; ++y) {
|
||||
for (int x = 0; x < size.X; ++x) {
|
||||
CHAR_INFO *pch = &readBuffer[y * size.X + x];
|
||||
for (int y = 0; y < windowRect.height(); ++y) {
|
||||
for (int x = 0; x < windowRect.width(); ++x) {
|
||||
CHAR_INFO *pch = &readBuffer[y * windowRect.width() + x];
|
||||
*pwrite++ = pch->Char.AsciiChar;
|
||||
}
|
||||
if (y < size.Y - 1) {
|
||||
if (y < windowRect.height() - 1) {
|
||||
*pwrite++ = '\r';
|
||||
*pwrite++ = '\n';
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
#include <windows.h>
|
||||
|
||||
class Win32Console;
|
||||
class QLocalSocket;
|
||||
class QTimer;
|
||||
|
||||
@ -24,13 +25,13 @@ private slots:
|
||||
void pollTimeout();
|
||||
|
||||
private:
|
||||
void resizeWindow(int cols, int rows);
|
||||
void scrapeOutput();
|
||||
|
||||
private:
|
||||
Win32Console *m_console;
|
||||
QLocalSocket *m_socket;
|
||||
QTimer *m_timer;
|
||||
HANDLE m_conin;
|
||||
HANDLE m_conout;
|
||||
};
|
||||
|
||||
#endif // AGENT_H
|
||||
|
@ -19,8 +19,12 @@ TEMPLATE = app
|
||||
|
||||
SOURCES += main.cc \
|
||||
Agent.cc \
|
||||
../Shared/DebugClient.cc
|
||||
../Shared/DebugClient.cc \
|
||||
Win32Console.cc
|
||||
|
||||
HEADERS += \
|
||||
Agent.h \
|
||||
../Shared/DebugClient.h
|
||||
../Shared/DebugClient.h \
|
||||
Win32Console.h
|
||||
|
||||
|
||||
|
168
Agent/Win32Console.cc
Normal file
168
Agent/Win32Console.cc
Normal file
@ -0,0 +1,168 @@
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include "Win32Console.h"
|
||||
#include <QSize>
|
||||
#include <QRect>
|
||||
#include <windows.h>
|
||||
|
||||
static inline SMALL_RECT smallRectFromQRect(const QRect &rect)
|
||||
{
|
||||
SMALL_RECT smallRect = { rect.left(),
|
||||
rect.top(),
|
||||
rect.left() + rect.width() - 1,
|
||||
rect.top() + rect.height() - 1 };
|
||||
return smallRect;
|
||||
|
||||
}
|
||||
|
||||
Win32Console::Win32Console(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_conin = CreateFile(
|
||||
L"CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
m_conout = CreateFile(
|
||||
L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
Q_ASSERT(m_conin != NULL);
|
||||
Q_ASSERT(m_conout != NULL);
|
||||
}
|
||||
|
||||
Win32Console::~Win32Console()
|
||||
{
|
||||
CloseHandle(m_conin);
|
||||
CloseHandle(m_conout);
|
||||
}
|
||||
|
||||
HANDLE Win32Console::conin()
|
||||
{
|
||||
return m_conin;
|
||||
}
|
||||
|
||||
HANDLE Win32Console::conout()
|
||||
{
|
||||
return m_conout;
|
||||
}
|
||||
|
||||
HWND Win32Console::hwnd()
|
||||
{
|
||||
return GetConsoleWindow();
|
||||
}
|
||||
|
||||
void Win32Console::postCloseMessage()
|
||||
{
|
||||
HWND h = hwnd();
|
||||
if (h != NULL)
|
||||
PostMessage(h, WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
QSize Win32Console::bufferSize()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
GetConsoleScreenBufferInfo(m_conout, &info);
|
||||
// TODO: error handling
|
||||
return QSize(info.dwSize.X, info.dwSize.Y);
|
||||
}
|
||||
|
||||
QRect Win32Console::windowRect()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
GetConsoleScreenBufferInfo(m_conout, &info);
|
||||
// TODO: error handling
|
||||
return QRect(info.srWindow.Left,
|
||||
info.srWindow.Top,
|
||||
info.srWindow.Right - info.srWindow.Left + 1,
|
||||
info.srWindow.Bottom - info.srWindow.Top + 1);
|
||||
}
|
||||
|
||||
void Win32Console::resizeBuffer(const QSize &size)
|
||||
{
|
||||
COORD bufferSize = { size.width(), size.height() };
|
||||
SetConsoleScreenBufferSize(m_conout, bufferSize);
|
||||
// TODO: error handling
|
||||
}
|
||||
|
||||
void Win32Console::moveWindow(const QRect &rect)
|
||||
{
|
||||
SMALL_RECT windowRect = smallRectFromQRect(rect);
|
||||
SetConsoleWindowInfo(m_conout, TRUE, &windowRect);
|
||||
// TODO: error handling
|
||||
}
|
||||
|
||||
void Win32Console::reposition(const QSize &newBufferSize, const QRect &newWindowRect)
|
||||
{
|
||||
// Windows has one API for resizing the screen buffer and a different one
|
||||
// for resizing the window. It seems that either API can fail if the
|
||||
// window does not fit on the screen buffer.
|
||||
|
||||
const QRect origWindowRect(windowRect());
|
||||
const QRect origBufferRect(QPoint(), bufferSize());
|
||||
|
||||
Q_ASSERT(!newBufferSize.isEmpty());
|
||||
QRect bufferRect(QPoint(), newBufferSize);
|
||||
Q_ASSERT(bufferRect.contains(newWindowRect));
|
||||
|
||||
QRect tempWindowRect = origWindowRect.intersected(bufferRect);
|
||||
if (tempWindowRect.width() <= 0) {
|
||||
tempWindowRect.setLeft(newBufferSize.width() - 1);
|
||||
tempWindowRect.setWidth(1);
|
||||
}
|
||||
if (tempWindowRect.height() <= 0) {
|
||||
tempWindowRect.setTop(newBufferSize.height() - 1);
|
||||
tempWindowRect.setHeight(1);
|
||||
}
|
||||
|
||||
// Alternatively, if we can immediately use the new window size,
|
||||
// do that instead.
|
||||
if (origBufferRect.contains(newWindowRect))
|
||||
tempWindowRect = newWindowRect;
|
||||
|
||||
if (tempWindowRect != origWindowRect)
|
||||
moveWindow(tempWindowRect);
|
||||
resizeBuffer(newBufferSize);
|
||||
if (newWindowRect != tempWindowRect)
|
||||
moveWindow(newWindowRect);
|
||||
}
|
||||
|
||||
QPoint Win32Console::cursorPosition()
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
GetConsoleScreenBufferInfo(m_conout, &info);
|
||||
// TODO: error handling
|
||||
return QPoint(info.dwCursorPosition.X, info.dwCursorPosition.Y);
|
||||
}
|
||||
|
||||
void Win32Console::setCursorPosition(const QPoint &point)
|
||||
{
|
||||
COORD coord = { point.x(), point.y() };
|
||||
SetConsoleCursorPosition(m_conout, coord);
|
||||
// TODO: error handling
|
||||
}
|
||||
|
||||
void Win32Console::writeInput(const INPUT_RECORD *ir, int count)
|
||||
{
|
||||
DWORD dummy = 0;
|
||||
WriteConsoleInput(m_conin, ir, count, &dummy);
|
||||
// TODO: error handling
|
||||
}
|
||||
|
||||
void Win32Console::read(const QRect &rect, CHAR_INFO *data)
|
||||
{
|
||||
COORD bufferSize = { rect.width(), rect.height() };
|
||||
COORD zeroCoord = { 0, 0 };
|
||||
SMALL_RECT smallRect = smallRectFromQRect(rect);
|
||||
ReadConsoleOutput(m_conout, data, bufferSize, zeroCoord, &smallRect);
|
||||
// TODO: error handling
|
||||
}
|
||||
|
||||
void Win32Console::write(const QRect &rect, const CHAR_INFO *data)
|
||||
{
|
||||
COORD bufferSize = { rect.width(), rect.height() };
|
||||
COORD zeroCoord = { 0, 0 };
|
||||
SMALL_RECT smallRect = smallRectFromQRect(rect);
|
||||
WriteConsoleOutput(m_conout, data, bufferSize, zeroCoord, &smallRect);
|
||||
// TODO: error handling
|
||||
}
|
49
Agent/Win32Console.h
Normal file
49
Agent/Win32Console.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef WIN32CONSOLE_H
|
||||
#define WIN32CONSOLE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QRect>
|
||||
#include <QPoint>
|
||||
#include <windows.h>
|
||||
|
||||
class Win32Console : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Win32Console(QObject *parent = 0);
|
||||
virtual ~Win32Console();
|
||||
|
||||
HANDLE conin();
|
||||
HANDLE conout();
|
||||
HWND hwnd();
|
||||
void postCloseMessage();
|
||||
|
||||
// Buffer and window sizes.
|
||||
QSize bufferSize();
|
||||
QRect windowRect();
|
||||
void resizeBuffer(const QSize &size);
|
||||
void moveWindow(const QRect &rect);
|
||||
void reposition(const QSize &bufferSize, const QRect &windowRect);
|
||||
|
||||
// Cursor.
|
||||
QPoint cursorPosition();
|
||||
void setCursorPosition(const QPoint &point);
|
||||
|
||||
// Input stream.
|
||||
void writeInput(const INPUT_RECORD *ir, int count=1);
|
||||
|
||||
// Screen content.
|
||||
void read(const QRect &rect, CHAR_INFO *data);
|
||||
void write(const QRect &rect, const CHAR_INFO *data);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
HANDLE m_conin;
|
||||
HANDLE m_conout;
|
||||
};
|
||||
|
||||
#endif // WIN32CONSOLE_H
|
Loading…
Reference in New Issue
Block a user