簡易入力用関数ができましたとさ。


RGKT-NDS-STDY-007(013)


とりあえずファーストリリース。
つっても今日は疲れたのでソースだけここで晒しる!
出来不出来は置いといてガチでちゃんと書いたの初めてな気がするw
だらだらと昨日今日こればかりだったんでソースがどんな感じなのか客観視できてないかもしれないけど。


inputf() のコメントが多い。
最初の関数説明が多いのは、作った本人は分かってるけどもな内容書いてるから仕方ないとしても、
もしかしたら無駄なことも書いてしまってるかも。
嘘コメントは書いてないと思う(笑)。
プログラミング作法とか意識して組んだけど、コーディングの指針にはなっても
コメントの記述の指針にはもうちょい足りない気がした
「コメントがコードより多いならそのコードは書き直せ」ってのに引っかかりそうな気が(爆)。


単純過ぎるとこにもコメントを一応残したのはどうなのか。
インラインの自作関数に対してだから、無駄とまではならない気も…。
こんなのとか。

//入力動作開始時のプロンプト表示
putPrompt(input_mode);


流石に i = 0; に対する /* 変数 i に 0 を代入する。 */ といった
阿呆なコメントに近いものは書いてないつもり。


個人的に気に食わない箇所は
nextVal で数値入力時と文字・文字列入力時の iprintf を分けてるところ。
でもこれぐらいしか無難なの浮かばなかった。


555 行(ジャスティファイズ!)あります。ごめんなさい。
inputf の中身がコメント込み 84 行という以外、各インライン関数は 22 行〜 47 行程度です。
コメント書いてて、 enum 定数を外部から呼ばなさ気な名前にしてから
気に食わないルーチンとか直してたらインライン関数1個増えちゃったりして 50 行ぐらい増してしまったです。
仕様とかも当初と比べて色々変わってしまいました。


RGKT-NDS-STDY-007(012)で言及しといた
数値入力と文字列入力の値更新のタイミングも同じにした。
これで忘れた頃に弄るときも(多分)安心w


以下ソースとヘッダ。 devkitPro Version 1.4.9 用。
続きを読んでね。


input.h

#ifndef _INPUT_F_H_
#define _INPUT_F_H_
#include <nds/ndstypes.h>

#ifdef __cplusplus
extern "C" {
#endif

int inputf(const char *, int, void *);

#ifdef __cplusplus
}
#endif

#endif

inputf.c

#include <stdio.h>
#include <stdlib.h>
#include <nds/ndstypes.h>
#include <nds/arm9/input.h>


typedef enum _INPUT_SIZE{
	_INPUT_SIZE_MIN  = 1,	//入力可能最小桁数(文字数)
	_INPUT_SIZE_MAX  = 256	//入力可能最大桁数(文字数)
}_INPUT_SIZE;

typedef enum _INPUT_STATE{
	_INPUT_INCOMPLETE = 0,	//入力未確定
	_INPUT_COMPLETE   = 1	//入力確定
}_INPUT_STATE;

typedef enum _INPUT_OPERATION{
	_INPUT_DISABLE = 0,	//入力動作無効(停止中)
	_INPUT_ENABLE  = 1		//入力動作有効(動作中)
}_INPUT_OPERATION;

typedef enum _INPUT_MODE{
	_MODE_UNKNOWN = 0,		//書式不定
	_DECIMAL_MODE = 1,		//10進数入力モード
	_HEXA_MODE    = 2,		//16進数入力モード
	_CHAR_MODE    = 3,		//一文字入力モード
	_STRING_MODE  = 4		//文字列入力モード
}_INPUT_MODE;


static _INPUT_MODE checkSyntax(const char *, void *);	//フォーマット指定子・変数の型が正しいかチェックする
static int checkInputSize(_INPUT_MODE, int);		//入力可能桁数(文字数)の指定が正しいかチェックする
static void initVal(_INPUT_MODE);			//入力モード動作用初期化処理
static void putPrompt(_INPUT_MODE);			//入力動作開始時のプロンプト表示
static void incVal(_INPUT_MODE);			//数を増やす(次の文字に進む)
static void decVal(_INPUT_MODE);			//数を減らす(前の文字へ戻る)
static void revVal(_INPUT_MODE);			//前の桁へ戻る(前のマス目にカーソルを戻す)
static void nextVal(_INPUT_MODE);			//次の桁へ進む(次のマス目にカーソルを進める)
static void fixVal(_INPUT_MODE);			//入力を確定して、値を変数へアドレス渡しで返す


//入力動作用グローバル変数
static char input_array[_INPUT_SIZE_MAX] = {'\0'};	//入力データ保持用配列
static char *p_str = NULL;				//文字入力用ポインタ変数(入力確定時、ポインタ渡しでデータ更新を行う)
static int *p_num = NULL;				//数値入力用ポインタ変数(入力確定時、ポインタ渡しでデータ更新を行う)
static int i = 0;					//桁移動用インデックス変数
static int input_size = _INPUT_SIZE_MIN;		//入力桁数用変数


//---------------------------------------------------------------------------------
//
//	Name		inputf
//	Function		キーボード・マウス等の標準入力デバイスを持たない環境での
//			数字・文字入力を実現するための原始的な入力支援関数
//	Argument		*format		入力モードをフォーマット指定子で指定する
//					10進数入力:"%d", "%D"
//					16進数入力:"%x", "%X"
//					一文字入力:"%c", "%C"
//					文字列入力:"%s", "%S"
//			set_input_size	入力可能桁数(文字数)の指定
//					1〜256の範囲(文字列に限っては2〜256の範囲)で指定することが可能である
//					この範囲外の値を指定した場合、その値は範囲内の近値に補正される
//					一文字入力に対しては1以外を指定しても1として処理される
//					文字列入力の場合、+1文字はヌル文字が挿入されるため
//					指定した文字数から-1された数が実際の入力可能文字数となる
//					(例えば256と指定した場合、255文字まで入力が可能で
//					最後の1文字にヌル文字が入ることになる)
//			*value		入力モードに対応した変数をアドレス渡しで指定する
//					10進入力, 16進入力:int 型
//					一文字入力:char 型
//					文字列入力:char 型配列
//	Returan value	入力確定フラグを返す
//			入力確定:1, 入力未確定:0
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
int inputf(const char *format, int set_input_size, void *value)
{
	
	static _INPUT_OPERATION input_operation = _INPUT_DISABLE;	//入力動作状態を示す変数
	static _INPUT_MODE input_mode = _MODE_UNKNOWN;		//入力モードを示す変数
	
	static u32 kdr_state;	//keysDownRepeat() 状態データ用(ダウンエッジ検出にも利用)
	
	if(input_operation == _INPUT_DISABLE){	//入力動作無効時(初回動作)
		
		//引数で得た、フォーマット指定子・変数の型が正しいかチェックする
		input_mode = checkSyntax(format, value);
		
		if(input_mode){	//引数の指定が正しい場合(非ゼロ)
			
			//入力桁数を設定する
			input_size = checkInputSize(input_mode, set_input_size);
			
			//入力動作時に使用する配列データ(仮入力データ)の初期化
			initVal(input_mode);
			
			//入力動作開始時のプロンプト表示
			putPrompt(input_mode);
			
			//キーリピート値を設定する
			keysSetRepeat(30, 10);
			
			//入力動作を有効にする
			input_operation = _INPUT_ENABLE;
			
		}else {		//引数の指定が間違ってる場合(ゼロ)
			
			//入力動作を無効にする(必要ないブロックだが今はまだ残しておく)
			input_operation = _INPUT_DISABLE;
			
		}
		
	}else if(input_operation == _INPUT_ENABLE){	//入力動作有効時(入力受付状態、及び入力確定待ち)
		
		//keysDownRepeat() 状態データを更新する
		kdr_state = keysDownRepeat();
		
		if(kdr_state & KEY_UP){		//上キーが押された場合
			
			//数を増やす(次の文字に進む)
			incVal(input_mode);
			
		}else if(kdr_state & KEY_DOWN){	//下キーが押された場合
			
			//数を減らす(前の文字へ戻る)
			decVal(input_mode);
			
		}else if(kdr_state & KEY_LEFT){	//左キーが押された場合
			
			//前の桁へ戻る(前のマス目にカーソルを戻す)
			revVal(input_mode);
			
		}else if(kdr_state & KEY_RIGHT){	//右キーが押された場合
			
			//次の桁へ進む(次のマス目にカーソルを進める)
			nextVal(input_mode);
			
		}
		
		if(kdr_state & KEY_A){		//Aボタンが押された場合
			
			//入力を確定して、値を変数へアドレス渡しで返す
			fixVal(input_mode);
			iprintf("\n");
			
			//キーリピート値を関数内で設定されてる初期値に戻す
			keysSetRepeat(60, 30);
			
			//入力動作を無効にし、入力確定フラグを返す
			input_operation = _INPUT_DISABLE;
			return _INPUT_COMPLETE;
			
		}
		
	}
	
	return _INPUT_INCOMPLETE;
	
}
//---------------------------------------------------------------------------------
//
//	Name		checkSyntax
//	Function		入力モードの指定に使われるフォーマット指定子の解析処理
//	Argument		*format	入力モードの指定
//				10進数入力:"%d", "%D"
//				16進数入力:"%x", "%X"
//				一文字入力:"%c", "%C",
//				文字列入力:"%s", "%S"
//			*value	入力モードに対応した変数(ポインタ渡し)
//				10進入力, 16進入力:int 型
//				一文字入力:char 型
//				文字列入力:char 型配列
//	Returan value	解析成功:
//				10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//			解析失敗:0
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static _INPUT_MODE checkSyntax(const char *format, void *value)
{
	
	if((format == (char*)"%d") || (format == (char*)"%D")){
		//10進入力モード
		p_num = (int *)value;
		return _DECIMAL_MODE;
	}else if((format == (char*)"%x") || (format == (char*)"%X")){
		//16進入力モード
		p_num = (int *)value;
		return _HEXA_MODE;
	}else if((format == (char*)"%c") || (format == (char*)"%C")){
		//一文字入力モード
		p_str = (char *)value;
		return _CHAR_MODE;
	}else if((format == (char*)"%s") || (format == (char*)"%S")){
		//文字列入力モード
		p_str = (char *)value;
		return _STRING_MODE;
	}else{
		//書式不定
	}
	
	return _MODE_UNKNOWN;
	
}
//---------------------------------------------------------------------------------
//
//	Name		checkInputSize
//	Function		入力可能桁数(文字数)の指定が正しいか調べる
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//			set_input_size	入力可能桁数(文字数)
//	Returan value	入力可能桁数(文字数)が1〜256(文字列の場合2〜256)の範囲に収まってるか調べ、
//			問題がなければ指定された桁数をそのまま返し、
//			オーバーフローがあれば桁数を修正して返す
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static int checkInputSize(_INPUT_MODE input_mode, int set_input_size)
{
	
	switch(input_mode){
	
	case _STRING_MODE:
		//2文字未満の入力を指定してきた場合、2文字の入力を指定したものとして扱う
		if(set_input_size < _INPUT_SIZE_MIN + 1)
			return _INPUT_SIZE_MIN + 1;
		
	case _DECIMAL_MODE:
	case _HEXA_MODE:
		//入力桁数を1未満で指定してきた場合、1桁の入力を指定したものとして
		//256より大きい桁数(257桁以上)で指定してきた場合、256桁の入力を指定したものとして扱い
		//それ以外のオーバーフローしてない範囲で指定してきた場合、そのままの値を桁数として扱う
		if(set_input_size < _INPUT_SIZE_MIN)
			return _INPUT_SIZE_MIN;
		else if(set_input_size > _INPUT_SIZE_MAX)
			return _INPUT_SIZE_MAX;
		else
			return set_input_size;
		
		break;
		
	case _CHAR_MODE:
		//一文字入力モードは桁数の指定に関係なく、一文字入力として扱う
		return _INPUT_SIZE_MIN;
		break;
		
	default:
		break;
	}
	
	return _INPUT_SIZE_MIN;
	
}
//---------------------------------------------------------------------------------
//
//	Name		initVal
//	Function		入力モード動作開始時の配列変数初期化処理
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static void initVal(_INPUT_MODE input_mode)
{
	
	for(i = 0;i < _INPUT_SIZE_MAX;i++)
		input_array[i] = '\0';
	
	i = 0;
	
	switch(input_mode){
	case _DECIMAL_MODE:
	case _HEXA_MODE:
		input_array[i] = '0';
		break;
	case _CHAR_MODE:
	case _STRING_MODE:
		input_array[i] = ' ';
		break;
		
	default:
		break;
		
	}
	
}
//---------------------------------------------------------------------------------
//
//	Name		putPrompt
//	Function		入力開始時のプロンプト '?' を表示する
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static void putPrompt(_INPUT_MODE input_mode)
{
	
	switch(input_mode){
		
	case _DECIMAL_MODE:
	case _HEXA_MODE:
	case _CHAR_MODE:
	case _STRING_MODE:
		i = 0;
		iprintf("?\n%c", input_array[i]);
		break;
		
	default:
		break;
		
	}
	
}
//---------------------------------------------------------------------------------
//
//	Name		incVal
//	Function		数値入力時、数を増やす
//			一文字・文字列入力時、次の文字へ進む
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		数値入力時、最も大きい数の次は最も小さい数となるよう
//			(10進:9 -> 0, 16進:F -> 0)ローテート処理してる
//			一文字・文字列入力時、アスキーコードの最も大きいコードを持つ文字の次は
//			最も小さいコードを持つ文字となるよう(127 -> 32)ローテート処理してる
//
//---------------------------------------------------------------------------------
static void incVal(_INPUT_MODE input_mode)
{
	
	switch(input_mode){
		
	case _DECIMAL_MODE:
		
		if(input_array[i] < '9')
			input_array[i]++;
		else if(input_array[i] >= '9')
			input_array[i] = '0';
		
		break;
		
	case _HEXA_MODE:
			
		if(input_array[i] < '9')
			input_array[i]++;
		else if((input_array[i] >= '9') && (input_array[i] < 'A'))
			input_array[i] = 'A';
		else if((input_array[i] >= 'A') && (input_array[i] < 'F'))
			input_array[i]++;
		else if(input_array[i] >= 'F')
			input_array[i] = '0';
		
		break;
		
	case _CHAR_MODE:
	case _STRING_MODE:
		
		if(input_array[i] < '~')
			input_array[i]++;
		else if(input_array[i] >= '~')
			input_array[i] = ' ';
		
		break;
		
	default:
		break;
		
	}
	
	iprintf("\x1b[1D%c", input_array[i]);
	
}
//---------------------------------------------------------------------------------
//
//	Name		decVal
//	Function		数値入力時、数を減らす
//			一文字・文字列入力時、前の文字へ戻る
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		数値入力時、最も小さい数の次は最も大きい数となるよう
//			(10進:0 -> 9, 16進:0 -> F)ローテート処理してる
//			一文字・文字列入力時、アスキーコードの最も小さいコードを持つ文字の次は
//			最も大きいコードを持つ文字となるよう(32 -> 127)ローテート処理してる
//
//---------------------------------------------------------------------------------
static void decVal(_INPUT_MODE input_mode)
{
	
	switch(input_mode){
		
	case _DECIMAL_MODE:
		
		if(input_array[i] > '0')
			input_array[i]--;
		else if(input_array[i] <= '0')
			input_array[i] = '9';
		
		break;
		
	case _HEXA_MODE:
		
		if(input_array[i] <= '0')
			input_array[i] = 'F';
		else if((input_array[i] > '0') && (input_array[i] <= '9'))
			input_array[i]--;
			
		else if((input_array[i] > '9') && (input_array[i] <= 'A'))
			input_array[i] = '9';
			
		else if((input_array[i] > 'A') && (input_array[i] <= 'F'))
			input_array[i]--;
		
		break;
		
	case _CHAR_MODE:
	case _STRING_MODE:
		
		if(input_array[i] > ' ')
			input_array[i]--;
		else if(input_array[i] <= ' ')
			input_array[i] = '~';
		
		break;
		
	default:
		break;
		
	}
	
	iprintf("\x1b[1D%c", input_array[i]);
	
}
//---------------------------------------------------------------------------------
//
//	Name		revVal
//	Function		入力を前の桁へ戻す(前のマス目にカーソルを移す)
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static void revVal(_INPUT_MODE input_mode)
{
	
	switch(input_mode){
	
	case _DECIMAL_MODE:
	case _HEXA_MODE:
	case _CHAR_MODE:
	case _STRING_MODE:
		if(i > 0){
			iprintf("\x1b[1D ");
			input_array[i] = 0;
			i--;
		}
		break;
	default:
		break;
	}
	
	iprintf("\x1b[2D%c", input_array[i]);
	
}
//---------------------------------------------------------------------------------
//
//	Name		nextVal
//	Function		入力を次の桁へ進める(次のマス目にカーソルを移す)
//			一文字入力時のみ、この動作は機能しない
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static void nextVal(_INPUT_MODE input_mode)
{
	
	switch(input_mode){
		
	case _DECIMAL_MODE:
	case _HEXA_MODE:
		if(i < input_size - 1){
			i++;
			input_array[i] = '0';
			iprintf("%c", input_array[i]);
		}
		break;
		
	case _CHAR_MODE:
		break;
		
	case _STRING_MODE:
		//指定された入力可能文字数のうち最後の1文字はヌル文字とするため
		//実際の入力可能文字数は指定文字数-1としてる
		if(i < input_size - 2){
			i++;
			input_array[i] = ' ';
			iprintf("%c", input_array[i]);
		}
		break;

	default:
		break;
	}
	
}
//---------------------------------------------------------------------------------
//
//	Name		fixVal
//	Function		入力の確定があったら、値をポインタ渡しで更新する
//	Argument		input_mode	入力モード
//					10進入力:1, 16進入力:2, 一文字入力:3, 文字列入力:4
//	Returan value	なし
//	Remarks		特になし
//
//---------------------------------------------------------------------------------
static void fixVal(_INPUT_MODE input_mode)
{
	
	i = 0;
	
	switch(input_mode){
	case _DECIMAL_MODE:
		
		*p_num = atoi(input_array);
		break;
		
	case _HEXA_MODE:
		
		*p_num = strtol(input_array, NULL, 16);
		break;
		
	case _CHAR_MODE:
		
		*p_str = input_array[i];
		break;
		
	case _STRING_MODE:
		
		for(i = 0;i < input_size;i++)
			p_str[i] = input_array[i];
		break;
		
	default:
		break;
		
	}
	
}