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


引言  -  getch簡述

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

#include <conio.h>

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

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

那位作者評價就是, 由於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游戲, 讓其支持跨平台, 並支持配置擴展.

 

后記   -   未來有 ∞ 次

Get Over  http://music.163.com/#/song?id=22771653

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM