歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C基礎 一個可以改變Linux的函數getch

C基礎 一個可以改變Linux的函數getch

日期:2017/3/1 9:13:35   编辑:Linux編程

引言 - getch簡述

引用老的TC版本getch說明. (文章介紹點有點窄, 應用點都是一些恐龍游戲時代的開發細節)

#include <conio.h>

/* * 立即從客戶端得到輸入的字符. 出錯返回EOF */ int __cdecl getch(void);

記得三年之前看過一本書 <<C專家編程>> 有一章提到在立即從標准輸入中得到輸入字符(後面還介紹了一種linux實現, 對於現在linux版本不行了)。

C專家編程 清晰 (有書簽索引) PDF 下載 http://www.linuxidc.com/Linux/2012-05/60077.htm

那位作者評價就是, 由於linux對於''getch''支持的不友好, 導致了linux錯失了很多游戲開發人員.

  當然現在版本, window 上也沒有這個函數了. 改成下面這個挫的樣子

#include <conio.h>    

_Check_return_     _DCRTIMP int __cdecl _getch(void);

總得而言''立即交互'' 是游戲開發的入口. 很有必要.

前言 - 從實際例子中了解getch

  現在Visual Studio 2015 Update3 中測試一段 getch 立即得到結果的代碼 main.c

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

/*
 * 制作等待, 函數
 */
int main(int argc, char * argv[]) {

    printf("請輸入任意字符結束程序......");
    int rt = _getch();
    printf("%d => %c\n", rt, rt);
    rt = _getch();
    printf("%d => %c\n", rt, rt);
    system("pause");
    return 0;
}

運行結果

從上可以看出, _getch 名字變了, 但是功能和getch沒有變化.

這裡我們封裝一下. 看新的文件, 一個演示小demo

#include <stdio.h>
#include <conio.h>

/*
 * 定義統一接口 sh_getch 理解得到玩家輸入
 *    : 返回 輸入int值, 錯誤為EOF
 */
#define sh_getch _getch

/*
 * 等待函數
 */
static void _pause(void) {
    printf("請按任意鍵繼續. . .");
rewind(stdin); sh_getch(); } /* * 繼續等待函數 */ int main(int argc, char * argv[]) { _pause(); return 0; }

來替代原先的 window 上 的 system("pause"), linux 上 pause(). rewind 重置文件FILE * 流, 清除輸入流保證當前流是干淨的.

正文 - linux上實現一個getch, 立即接收

  linux 需要借助 termio.h 終端控制頭文件. 主要實現如下

#include <termio.h>

/*
 * 得到用戶輸入的一個字符
 *        : 返回得到字符
 */
int 
sh_getch(void) {
    int cr;
    struct termios nts, ots;

    if (tcgetattr(0, &ots) < 0) // 得到當前終端(0表示標准輸入)的設置
        return EOF;

    nts = ots;
    cfmakeraw(&nts); // 設置終端為Raw原始模式,該模式下所有的輸入數據以字節為單位被處理
    if (tcsetattr(0, TCSANOW, &nts) < 0) // 設置上更改之後的設置
        return EOF;

    cr = getchar();
    if (tcsetattr(0, TCSANOW, &ots) < 0) // 設置還原成老的模式
        return EOF;

    return cr;
}

主要是設置終端為原始接收字符模式, 可以接收立即返回, 隨後還原老的環境設置. 終端緩沖, 也是出於效率考慮, 否則編程太復雜了.

同樣測試 一個 getch.c

#include <stdio.h>
#include <termio.h>

/*
 * 得到用戶輸入的一個字符
 *        : 返回得到字符
 */
int sh_getch(void);

/*
 * 測試標准快速輸入
 */
int main(int argc, char * argv[]) {
    int ch;
    
    printf("請按任意鍵繼續. . .");
    ch = sh_getch();
    printf("%d => %c\n", ch, ch);
    
    ch = sh_getch();
    printf("%d => %c\n", ch, ch);
    
    return 0;
}

linux上演示結果

 gcc -Wall -ggdb3 -o getch.out getch.c

一切正常.

  到這裡我們關閉getch跨平台實現細節都確定了. 那麼我們實現一個跨平台的getch版本. 先看頭文件聲明部分(*.h 文件插入).

/*
 * error        => 以後再說
 * 跨平台的丑陋從這裡開始
 * __GNUC        => linux 平台特殊操作
 * __MSC_VER    => window 平台特殊操作
 */
#ifdef __GUNC__  // 下面是依賴GCC編譯器實現

#include <termio.h>

/*
 * 得到用戶輸入的一個字符
 *        : 返回得到字符
 */
int sh_getch(void);

#elif _MSC_VER // 下面是依賴Visual Studio編譯器實現

#include <conio.h>

// window 上用_getch 替代了getch, 這裡為了讓其回來
#define sh_getch    _getch

#else
    #error "error : Currently only supports the Visual Studio and GCC!"
#endif

再看實現部分 (*.c 文件中插入)

// 為linux擴展一些功能
#if defined(__GUNC__)

/*
 * 得到用戶輸入的一個字符
 *        : 返回得到字符
 */
int 
sh_getch(void) {
    int cr;
    struct termios nts, ots;

    if (tcgetattr(0, &ots) < 0) // 得到當前終端(0表示標准輸入)的設置
        return EOF;

    nts = ots;
    cfmakeraw(&nts); // 設置終端為Raw原始模式,該模式下所有的輸入數據以字節為單位被處理
    if (tcsetattr(0, TCSANOW, &nts) < 0) // 設置上更改之後的設置
        return EOF;

    cr = getchar();
    if (tcsetattr(0, TCSANOW, &ots) < 0) // 設置還原成老的模式
        return EOF;

    return cr;
}

#endif

這就是getch跨平台實現的關鍵了. 從這裡開始,你就可以構建自己喜歡的游戲了, 通過 sh_getch 入口開始.

預備下次重構C字符串,再下次采用simplec框架重寫一個老的滅龍傳說V2.0.0游戲, 讓其支持跨平台, 並支持配置擴展.

Copyright © Linux教程網 All Rights Reserved