在命令行下的文本编辑器中如何实现用方向键来移动光标,最好是VC中有的

时间:2008-05-22 05:02:01   来源:论坛整理  作者:  编辑:chinaitzhe
如题

网友回复:没有尝试过,呵呵
网友回复:为何没人回
网友回复:8.4 处理鼠标和键盘

  本节即将介绍的 Mouse 程序会让你了解到在控制台应用程序中如何处理鼠标和键盘的输入。这是一个简单的程序,它能报告鼠标当前的位置,鼠标键当前的状态以及用户按下的任何字母数字键。
  我想强调一下,在以命令行为基础的控制台应用程序中,C 的标准库中大多数 I/O 操作同样可以把这些事情做的很好。例如,可以调用 gets 函数来处理用户的正文输入。但是假如你想追踪鼠标,你当然要用到控制台内部例程。不仅如此,假如你从一个 GUI 应用程序中启动一个控制台窗口,某些标准的 I/O 库例程的动作会有问题。假如你想在 GUI 应用程序中运行一个控制台,你应该用我在此处介绍的几个 WIN32 自身的例程。
  在本章的前面章节中讨论了面向事件的程序设计,此处的程序是一个如何从头开始创建一个面向事件程序的例子。也就是说,这个代码说明了面向事件 Windows 例程内部是如何实际工作的。假如你以前没有见过这类代码,它能给你很大帮助,因为它对于面向消息的操作系统是如何构造的问题给出某些提示。
  现在让我们预备好 Mouse 程序并运行它。程序清单 8.4 是它的源代码。以下几小节将对这个简单的应用程序的要点展开讨论。

程序清单 8.4 Mouse 程序说明如何在控制台应用程序中使用鼠标
///////////////////////////////////////
// Mouse.cpp
// Copyleft (c) 1999 skyline
// Demostrate simple console application
///////////////////////////////////////

#include <windows.h>
#include <stdio.h>

void GotoXY(SHORT x,SHORT y);
void BlankLine(SHORT y);
void ClrScr();
void Write(LPSTR s);
void HandleMouse(MOUSE_EVENT_RECORD Mouse);
void HandleKey(KEY_EVENT_RECORD key);
void SayGoodBye();

HANDLE hOut,hIn;

/////////////////////////////
// Program entry point
/////////////////////////////
int main(void)
{
SetConsoleTitle("skyline console");

DWORD Result;
INPUT_RECORD Buf;

hOut=GetStdHandle(STD_OUTPUT_HANDLE);
hIn=GetStdHandle(STD_INPUT_HANDLE);

ClrScr();

GotoXY(0,24);
Write("Move mouse,press keys,double click to Exit ...");

do
{
ReadConsoleInput(hIn,&Buf,1,&Result);

if(Buf.EventType==MOUSE_EVENT)
HandleMouse(Buf.Event.MouseEvent);

if(Buf.EventType==KEY_EVENT)
HandleKey(Buf.Event.KeyEvent);
}while(!(Buf.EventType==MOUSE_EVENT&&
Buf.Event.MouseEvent.dwEventFlags==DOUBLE_CLICK));

SayGoodBye();

return 0;
}

//////////////////////////////////////////
// GotoXY Move cursor to Col,Row
//////////////////////////////////////////
void GotoXY(SHORT x,SHORT y)
{
COORD c;

c.X=x;
c.Y=y;

SetConsoleCursorPosition(hOut,c);
}

//////////////////////////////////////////
// Blank a line of text at position y
//////////////////////////////////////////
void BlankLine(SHORT y)
{
DWORD NumWritten;
COORD c;

c.X=0;
c.Y=y;
FillConsoleOutputCharacter(hOut,' ',80,c,&NumWritten);
}

//////////////////////////////////////////
// Clear the screen
//////////////////////////////////////////
void ClrScr()
{
SHORT i;

for(i=0;i <25;i )
BlankLine(i);

GotoXY(0,0);
}

//////////////////////////////////////////
// Write a line of text
//////////////////////////////////////////
void Write(LPSTR s)
{
DWORD result;

WriteConsole(hOut,s,strlen(s),&result,NULL);
}

//////////////////////////////////////////
// Report on position of mouse
//////////////////////////////////////////
void HandleMouse(MOUSE_EVENT_RECORD Mouse)
{
char s[150];

sprintf(s,"Buttons: %lu,X: %2lu Y: %2lu\n",
Mouse.dwButtonState,
Mouse.dwMousePosition.X,
Mouse.dwMousePosition.Y);
GotoXY(0,0);
Write(s);
}

//////////////////////////////////////////
// Report on key strokes entered
//////////////////////////////////////////
void HandleKey(KEY_EVENT_RECORD key)
{
char s[100];

sprintf(s,"You pressed: %c",key.uChar);
GotoXY(0,1);
Write(s);
sprintf(s,"Virtual key: =",key.wVirtualKeyCode);
GotoXY(0,2);
Write(s);
}

//////////////////////////////////////////
// Input a string,process it,spit it back out.
//////////////////////////////////////////
void SayGoodBye()
{
char s[100];

ClrScr();
printf("Enter your name:");
gets(s);
printf("\nGoodBye %s!\n",s);
}

  这个程序只做追踪鼠标的当前位置,报告用户按键动作这些事。它的核心代码是下列事件循环:

do
{
ReadConsoleInput(hIn,&Buf,1,&Result);

if(Buf.EventType==MOUSE_EVENT)
HandleMouse(Buf.Event.MouseEvent);

if(Buf.EventType==KEY_EVENT)
HandleKey(Buf.Event.KeyEvent);
}while(!(Buf.EventType==MOUSE_EVENT&&
Buf.Event.MouseEvent.dwEventFlags==DOUBLE_CLICK));

  这段代码反复循环运行直到用户用鼠标双击屏幕为止。每次循环开始都用 ReadConsoleInput 例程来检查用户的输入。

  语法:
  ReadConsoleInput

BOOL ReadConsoleInput(
HANDLE hConsoleInput,
PINPUT_RECORD lpBuffer,
DWORD nLength,
LPDWORD lpNumberOfEventsRead
);

  这个例程的许多参数和本章前面讨论过的例程是相同的,因此这里不必再重复说明。
  ReadConsoleInput 例程中新出现的信息是 INPUT_RECORD 类型的缓冲区:

typedef struct _INPUT_RECORD {
WORD EventType;
union {
KEY_EVENT_RECORD KeyEvent;
MOUSE_EVENT_RECORD MouseEvent;
WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
MENU_EVENT_RECORD MenuEvent;
FOCUS_EVENT_RECORD FocusEvent;
} Event;
} INPUT_RECORD;

  这个记录中包含了对键盘事件、鼠标事件、缓冲区大小的改变、菜单事件以及当前焦点事件等的追踪信息。比如,当用户移动了鼠标或用键盘做一些输入,这些动作的结果会被记录在这个结构中。下面我将讨论 MOUSE_EVENT_RECOND 和 KEY_EVENT_RECOND,假如你对其他结构感爱好,可以参阅联机帮助信息。后面还将讨论 EventType 域。
  对于 ReadConsoleInput 例程你要把握的基本思想是:它让系统把程序当前的状态信息填入到 INPUT_RECORD 结构中。这个例程并不等待用户输入信息,它只是简单地检查一下当前发生了什么事,假如没有什么事它就返回并把情况向你报告。
  下面是一个使用该函数的例子:
  ReadConsoleInput(hIn,&Buf,1,&Result);
  你应当花些时间仔细检测这个事件循环,并了解 ReadConsoleInput 函数是如何搭配这个循环的。要害的是你要理解,在 Windows 内部底层的某个地方,也有一个与此循环类似的循环正在执行着,所有面向事件的程序都是按这种方式组织的。
  在调用 ReadConsloeInput 函数后,下一步就是检查是否有某种重要事件已经发生。例如,要想了解是否有鼠标事件出现或键盘上是否有任何一个键被按下。对这些事件的检查是通过查看 INPUT_RECORD 记录中 EventType 字段来实现的:

if(Buf.EventType==MOUSE_EVENT)
HandleMouse(Buf.Event.MouseEvent);

if(Buf.EventType==KEY_EVENT)
HandleKey(Buf.Event.KeyEvent);

  假如出现鼠标事件,就调用下列例程:

void HandleMouse(MOUSE_EVENT_RECORD Mouse)
{
char s[150];

sprintf(s,"Buttons: %lu,X: %2lu Y: %2lu\n",
Mouse.dwButtonState,
Mouse.dwMousePosition.X,
Mouse.dwMousePosition.Y);
GotoXY(0,0);
Write(s);
}

  鼠标事件的记录为:

typedef struct _MOUSE_EVENT_RECORD {
COORD dwMousePosition;
DWORD dwButtonState;
DWORD dwControlKeyState;
DWORD dwEventFlags;
} MOUSE_EVENT_RECORD;

  这里要害的字段是前两个。当前的鼠标位置放置在第一个字段,鼠标按键按下的情况记录在第二字段。在大多数情况下,你可以假定按鼠标左键时按键状态置为 1,按鼠标第二个按键时置为 2。不过,要想确切地读出鼠标状态字段的信息来了解发生了什么事件,需要执行一些位操作。也就是说,每个鼠标按键用这个 32 位字段中的一位来代表。这也意味着该例程能够处理可以想象的设备,它们可以有多于二个或三个的按键。你可以通过联机帮助来了解有关的解释。
  第三个字段 dwControlKeyState 用来记录是否有非凡的键被按下。例如,它可以用来追踪 Ctrl 键,Alt 键和 Shift 键等。
  最后一个字段记录是否有双击鼠标事件发生或鼠标是否做了简单地移动。为了帮助阅读,定义了下列值:
  MOUSE_MOVED
  DOUBLE_CLICK
  单击按键用 0 表示。
  理解了这个记录也就能了解 HandleMouse 函数是怎么工作的,这个函数只是简单报告这个记录前两个字段的状态,把它们的值写在屏幕顶部。
  围绕 HandleMouse 例程的代码是面向事件程序的主要部分。在 Mouse 程序主要执行时间中,以极快的速度一遍遍执行循环。在循环中每一次检查到有事件发生时,就调用相应的例程。在此处的例子中,这些例程只是把有关事件的数据在屏幕上输出,在 Windows 内部没有向窗口发送任何东西,而是把消息打包并发送出去。你的程序捕捉到 WM_MOUSEMOVE、WM_KEYDOWN、WM_LBUTTONDOWN 和其它消息并作出响应。当你对这些了解了你就会觉得很简单了。
  处理键盘输入事件比处理鼠标输入更简单:

void HandleKey(KEY_EVENT_RECORD key)
{
char s[100];

sprintf(s,"You pressed: %c",key.uChar);
GotoXY(0,1);
Write(s);
sprintf(s,"Virtual key: =",key.wVirtualKeyCode);
GotoXY(0,2);
Write(s);
}

  其中 KEY_EVENT_RECORD 记录的结构如下:

typedef struct _KEY_EVENT_RECORD {
BOOL bKeyDown;
WORD wRepeatCount;
WORD wVirtualKeyCode;
WORD wVirtualScanCode;
union {
WCHAR UnicodeChar;
CHAR AsciiChar;
} uChar;
DWORD dwControlKeyState;
} KEY_EVENT_RECORD;

  其中最重要的字段是 wVirtualKeyCode 和 uChar。虚键的编码已在前面介绍过并且在联机帮助的 Virtual Key Codes 标题下列出。它有助于你追踪那些与非凡键相关的信息。这些非凡键有 Enter 键,功能键(F1,F2)和小键盘上的数字键等等。
  只要可能,就能把这些非凡键的代码转换成标准的 ASCII 字符并放在 uChar 字段。这就是说,假如这个键是字母数字值或 ASCII 字符集中前半部分的符号,就把它放入 uChar 字段。例如,对于 A 键,wVirtualKeyCode 字段的值将是 65,而 uChar 字段中放的字符将是 A 或 a,这将依据 Shift 键的状态而定。

网友回复:mark
关键字:命令行,文本编辑器,实现,方向键,移动,
上一篇:关于指针

文章评论

共有 0 位网友发表了评论 此处只显示部分留言 点击查看完整评论页面