// console2.cpp --- 仮想コンソール2
#include "DxLib.h" // DXライブラリ
#include <string> // for std::string
#include <cassert> // for assert
#define CHAR_HEIGHT 32 // 文字の高さ
#define CHAR_WIDTH (CHAR_HEIGHT / 2) // 半角文字の幅
#define COLUMNS 40 // 列の個数
#define ROWS 15 // 行の個数
#define CONSOLE_WIDTH (COLUMNS * CHAR_WIDTH) // コンソールの幅
#define CONSOLE_HEIGHT (ROWS * CHAR_HEIGHT) // コンソールの高さ
#define BLINK_RATE 200 // 点滅間隔(ミリ秒)
#define PROMPT "C:\\> " // プロンプト
char gp_buf[ROWS][COLUMNS]; // 仮想コンソール
int gp_cx = 0, gp_cy = 0; // キャレット位置(文字単位)
int gp_px = 0, gp_py = 0; // キャレットを動かせる限界位置
int gp_font; // フォントハンドル
bool gp_blink = false; // キャレットの点滅用フラグ
bool gp_exit_flag = false; // 終了フラグ
std::string gp_output; // コンソール出力文字列
// 仮想コンソールを描画する。
void gp_DrawConsole(void)
{
char str[2]; // 文字列を格納する文字配列。
str[1] = 0; // strがゼロ終端になるようにする。
// それぞれの行について
for (int y = 0; y < ROWS; ++y)
{
// それぞれの列について
for (int x = 0; x < COLUMNS; ++x)
{
// 文字を取り出す。strはゼロ終端のC文字列になる。
str[0] = gp_buf[y][x];
assert(str[1] == 0);
// 指定されたフォントを使って指定位置へ描画する。
DrawStringToHandle(x * CHAR_WIDTH, y * CHAR_HEIGHT,
str, 0xFFFFFF, gp_font);
// もしキャレット位置であり、点灯状態であれば、
if (gp_cx == x && gp_cy == y && gp_blink)
{
// キャレットを描画する。
str[0] = '_';
assert(str[1] == 0);
DrawStringToHandle(x * CHAR_WIDTH, y * CHAR_HEIGHT,
str, 0xFF9999, gp_font);
}
}
}
}
// 背景を描画する。
void gp_DrawBackground(void)
{
// 三角形を描く。
DrawLine(CONSOLE_WIDTH / 2, 0, 0, CONSOLE_HEIGHT, 0x666666);
DrawLine(0, CONSOLE_HEIGHT, CONSOLE_WIDTH, CONSOLE_HEIGHT / 2, 0x666666);
DrawLine(CONSOLE_WIDTH, CONSOLE_HEIGHT / 2, CONSOLE_WIDTH / 2, 0, 0x666666);
}
// 上にスクロールする。
void gp_ScrollUp(void)
{
// gp_bufの(ROWS - 1)個の行をずらす。
memmove(gp_buf[0], gp_buf[1], COLUMNS * (ROWS - 1));
// 最後の行を空白にする。
memset(gp_buf[ROWS - 1], ' ', COLUMNS);
// キャレットを動かせる限界位置を修正する。
if (gp_py > 0)
--gp_py;
else
gp_px = 0;
}
// コンソール画面での位置関係を判定する。(x0, y0)≦(x1, y1)
bool LessOrEqual(int x0, int y0, int x1, int y1)
{
if (y0 > y1)
return false;
if (y0 < y1)
return true;
if (x0 <= x1)
return true;
return false;
}
// 入力行を取得する。
std::string gp_GetLine(void)
{
int x, y;
std::string str;
// (gp_cx, gp_cy)≦(gp_px, gp_py)ならば空文字列を返す。
if (LessOrEqual(gp_cx, gp_cy, gp_px, gp_py))
return str; // 空文字列
// (gp_px, gp_py)~(gp_cx, gp_cy)の文字列を取得する。
x = gp_px;
y = gp_py;
while (x != gp_cx || y != gp_cy)
{
// 文字をstrに追加する。
str += gp_buf[y][x];
// 次の文字位置まで移動する。
++x;
if (x >= COLUMNS)
{
x = 0;
++y;
if (y >= ROWS)
break;
}
}
return str; // 成功。取得した文字列。
}
// キャレットを一文字左に移動。
void gp_MoveLeft(void)
{
// (gp_cx, gp_cy)≦(0,0) ならば何もしない。
if (LessOrEqual(gp_cx, gp_cy, 0, 0))
return;
// (gp_cx, gp_cy)≦(gp_px, gp_py)ならば何もしない。
if (LessOrEqual(gp_cx, gp_cy, gp_px, gp_py))
return;
// 左へ移動。
--gp_cx;
if (gp_cx >= 0)
return;
// 画面の左端を超えた。修正。
gp_cx = COLUMNS - 1;
--gp_cy;
}
// キャレットを一文字右に移動。
void gp_MoveRight(void)
{
// 右へ。
++gp_cx;
if (gp_cx < COLUMNS)
return;
// 画面の右端を超えた。修正。
gp_cx = 0;
++gp_cy;
if (gp_cy < ROWS)
return;
// 画面の右下端を超えた。修正。
--gp_cy;
// 上にスクロールする。
gp_ScrollUp();
}
// 改行。
void gp_NewLine(void)
{
// 次の行へ。
gp_cx = 0;
++gp_cy;
if (gp_cy < ROWS)
return;
// 画面の右下端を超えた。修正。
--gp_cy;
// 上にスクロールする。
gp_ScrollUp();
}
// 一文字出力する。
void gp_PutChar(char ch)
{
// 文字をgp_bufにセットする。
gp_buf[gp_cy][gp_cx] = ch;
// 右へ動く。
gp_MoveRight();
}
// 文字列を出力する。
void gp_PutStr(std::string str)
{
// strに含まれる文字を順番に出力する。
for (size_t i = 0; i < str.size(); ++i)
{
gp_PutChar(str[i]);
}
}
// 直前の一文字を消して戻る(バックスペース)。
void gp_BackSpace(void)
{
// 左へ動く。
gp_MoveLeft();
// gp_bufの一部をずらす。
memmove(&gp_buf[gp_cy][gp_cx], &gp_buf[gp_cy][gp_cx + 1],
COLUMNS - gp_cx - 1);
// 行の終わりに空白文字をセットする。
gp_buf[gp_cy][COLUMNS - 1] = ' ';
}
// 画面をクリアする。
void gp_Cls(void)
{
// 仮想コンソールを空白で埋め尽くす。
memset(gp_buf, ' ', sizeof(gp_buf));
// キャレット位置などを初期化する。
gp_cx = gp_cy = 0;
gp_px = gp_py = 0;
}
// 文字列の前後の空白を取り除く。
std::string gp_TrimStr(std::string str)
{
size_t i = str.find_first_not_of(" \t");
if (i != std::string::npos)
{
str = str.substr(i);
i = str.find_last_not_of(" \t");
if (i != std::string::npos)
{
str = str.substr(0, i + 1);
}
}
else
{
str.clear();
}
return str;
}
// 何かを実行する。
void gp_Execute(std::string str)
{
// 文字列の前後の空白を取り除く。
str = gp_TrimStr(str);
// 空文字列なら何もしない。
if (str.empty())
return;
// 画面消去コマンド。
if (str == "cls" || str == "CLS")
{
gp_Cls();
return;
}
// 終了コマンド。
if (str == "exit" || str == "EXIT")
{
gp_exit_flag = true;
return;
}
// その他の場合。
gp_PutStr("You typed: '");
gp_PutStr(str);
gp_PutStr("'");
gp_NewLine();
}
// 入力を受け取り、入力に従って処理を行う。
void gp_ReadInput(void)
{
// 文字を入力する。
char ch = GetInputChar(TRUE);
if (ch == 0)
return; // 入力文字なし。
#ifdef _DEBUG
// 文字コードをデバッグ出力。
char buf[64];
wsprintf(buf, TEXT("CharCode: %d (0x%02X)\n"), ch, ch);
OutputDebugString(buf);
#endif
// 入力された文字コードに従って処理を行う。
switch (ch)
{
case 8: // バックスペース
gp_BackSpace();
break;
case 28: // 右矢印
gp_MoveRight();
break;
case 29: // 左矢印
gp_MoveLeft();
break;
case '\r': // 改行
{
std::string line = gp_GetLine();
gp_NewLine();
gp_Execute(line);
gp_output = PROMPT;
}
break;
default: // その他
gp_PutChar(ch);
}
}
// このプログラム内部の初期化を行う。
void gp_Init(void)
{
// 画面サイズの確認。
#if 0
TCHAR gp_buf[32];
wsprintf(gp_buf, TEXT("%d, %d"), CONSOLE_WIDTH, CONSOLE_HEIGHT);
MessageBox(NULL, gp_buf, NULL, 0);
#endif
assert(CONSOLE_WIDTH == 640);
assert(CONSOLE_HEIGHT == 480);
//assert(CONSOLE_WIDTH == 800);
//assert(CONSOLE_HEIGHT == 600);
// 後ろの画面に描画する。
SetDrawScreen(DX_SCREEN_BACK);
// 画面をクリアする。
gp_Cls();
// 指定されたサイズのフォントの作成。アンチエイリアシングを効かせる。
gp_font = CreateFontToHandle("MS ゴシック", CHAR_HEIGHT, 0,
DX_FONTTYPE_ANTIALIASING);
// プロンプトを出力する。
gp_output = PROMPT;
}
// メイン関数。
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
{
// 画面サイズとモードの設定。
SetGraphMode(CONSOLE_WIDTH, CONSOLE_HEIGHT, 32);
// フルスクリーンにしない。
ChangeWindowMode(TRUE);
// DxLibの初期化。
if (DxLib_Init() == -1)
{
return -1;
}
// このプログラム内部の初期化。
gp_Init();
// 点滅用のカウンタ。
int counter = GetNowCount();
// メインループ。
while (!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen())
{
// 背景を描画する。
gp_DrawBackground();
// コンソールを描画する。
gp_DrawConsole();
// 出力中か?
if (gp_output.empty())
{
// 入力を受け取り、入力に従って処理を行う。
gp_ReadInput();
// 点滅のため、一定時間ごとにフラグを切り替える。
if (GetNowCount() - counter >= BLINK_RATE)
{
// 点滅する。
gp_blink = !gp_blink;
// カウンタを更新する。
counter = GetNowCount();
}
}
else
{
// 一文字出力する。
gp_PutChar(gp_output[0]);
gp_output.erase(gp_output.begin());
// キャレットを動かせる限界位置を更新。
gp_px = gp_cx;
gp_py = gp_cy;
// 点滅しない。
gp_blink = false;
}
// 終了フラグが立っていれば終了。
if (gp_exit_flag)
break;
}
// DxLibの終了。
DxLib_End();
return 0;
} // WinMain