C console programming

夏树的C语言篇 – 控制台获取鼠标操作

0.写在前面

在前一周的大一C语言课设Ⅱ结束之后, 虽然下周还有新开的课程, 但是总算有时间来更新一下博客了。毕竟这段时间一鸽就是几个月, 对于获取鼠标操作,班里大部分同学应该都是用EasyX的函数实现的样子, 如果不使用EasyX我们应该怎么做呢?那就不得不提上一篇说的ReadConsoleInput这个函数了, 当然还需要其他的一些函数配合使用。

1.一篇文章: 控制台读取鼠标操作

夏树在之前查找资料的时候看到了这篇文章《控制台界面控制(十):读取鼠标操作》, 通过ReadConsoleInput可以判断鼠标的左键单击、左键双击、右键单击等等,不过鼠标的坐标是以行和列的形式显示的,以下是这篇文章实现的代码:

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

int main(void)  
{  
    // 获取标准输入输出设备句柄  
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);        
    HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); 

    CONSOLE_SCREEN_BUFFER_INFO bInfo;
    INPUT_RECORD    mouseRec;
    DWORD           res;
    COORD           crPos, crHome = {0, 0};

    printf("[Cursor Position] X: %2lu  Y: %2lu\n", 0, 0); // 初始状态

    while (1)
    {
        ReadConsoleInput(hIn, &mouseRec, 1, &res);

        if (mouseRec.EventType == MOUSE_EVENT)
        {
            if (mouseRec.Event.MouseEvent.dwButtonState==FROM_LEFT_1ST_BUTTON_PRESSED)
            {
                if (mouseRec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
                {
                    break;  // 左键双击 退出循环
                }
            }           

            crPos = mouseRec.Event.MouseEvent.dwMousePosition;
            GetConsoleScreenBufferInfo(hOut, &bInfo);
            SetConsoleCursorPosition(hOut, crHome);
            printf("[Cursor Position] X: %2lu  Y: %2lu", crPos.X, crPos.Y);
            SetConsoleCursorPosition(hOut, bInfo.dwCursorPosition);

            switch (mouseRec.Event.MouseEvent.dwButtonState)
            {
            case FROM_LEFT_1ST_BUTTON_PRESSED:          // 左键 输出A
                FillConsoleOutputCharacter(hOut, 'A', 1, crPos, &res);
                break;      // 如果使用printf输出,则之前需要先设置光标的位置

            case RIGHTMOST_BUTTON_PRESSED:              // 右键 输出a
                FillConsoleOutputCharacter(hOut, 'a', 1, crPos, &res);
                break;

            default:
                break;
            }
        }       
    }

    CloseHandle(hOut);  // 关闭标准输出设备句柄  
    CloseHandle(hIn);   // 关闭标准输入设备句柄  
    return 0;  
}

当然ReadConsoleInput也可以对鼠标滚轮,甚至是键盘的按键进行监听,具体的可以参考MSDN文档中对于ReadConsoleInput的介绍:https://docs.microsoft.com/en-us/windows/console/readconsoleinput,具体读取的操作都存储在INPUT_RECORD的结构体当中,也可以参考:https://docs.microsoft.com/en-us/windows/console/input-record-str

2.更细致的鼠标坐标

上面的例子似乎已经可以实现我们大部分的需求, 但是如果如果需要获得更细致的鼠标坐标呢?(精确到像素, 而不是控制台的行列)。
那么我们可以使用到GetCursorPos这个函数, 鼠标对于屏幕的坐标会存储在POINT类型的结构体变量curPos中, 可以通过curPos.x和curPos.y取得:

POINT curPos;
GetCursorPos(&curPos);

而如果要转换为当前窗口的坐标, 可以用到ScreenToClient这个函数, 传入窗口句柄和待转换的坐标即可, 上面的例子可以修改为:

HWND hWnd;
TCHAR title[256];
POINT curPos;
//获取窗口句柄
GetConsoleTitle(title, 256);
hwnd = FindWindow(0, title);
//获取对于屏幕的鼠标坐标(精确到像素)
GetCursorPos(&curPos);
//转换为窗口的相对坐标
ScreenToClient(hWnd, &curPos);

3.关闭快速编辑模式

如果我们仅仅只使用第一个部分的例子来获取鼠标位于控制台的行和列时, 在win8乃至更高的windows系统版本中, 有些情况无论鼠标怎么移动, 坐标始终显示为(0,0), 我们需要在控制台属性中关闭快速编辑模式:
TurnOffConsoleQuickEditMode.png
当然让每个用户在运行时手动关闭快速编辑模式是不现实的, 关闭快速编辑模式还可以通过代码实现, 夏树在这边已经将这部分的代码封装成一个函数CloseQuickEditMode,直接调用即可:

void CloseQuickEditMode()
{
    HANDLE hIn;
    unsigned long mode;

    hIn = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(hIn, &mode);  
    mode &= ~ENABLE_QUICK_EDIT_MODE;
    mode &= ~ENABLE_INSERT_MODE;
    SetConsoleMode(hIn, mode); 
}

4.总结

至此, 我们对于获取控制台鼠标操作可以通过GetCursorPosScreenToClientReadConsoleInput的组合来判断鼠标在当前窗口的什么像素位置按下,可以摆脱了C语言初学者对于EasyX的依赖,进而编写适合自己需求的输入监听函数。
(完)

发布者

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注