2022/4/9 バージョンのソースをベースにして更新しました。
Common Source Code Project for DevTerm
https://github.com/shikarunochi/CommonSourceCodeProjectDevTerm
オリジナルの Common Source Code Project 2022-04-09 バージョンのソースに上書きしてください。
Common Source Code Project 2022-04-09
http://takeda-toshiya.my.coocan.jp/common/history/
2022-04-09.7z
ビルド方法や操作方法は、こちらを参考にしてください。
PC-8801系の場合、リセット時にモード切替を選択できるようにしました。ボタン表示はGTKのダイアログを使っています。
以下のGTKダイアログの処理は、/src/DevTerm/osd_dialog.cpp に書いてあります。
gtk_init(0, NULL); GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT; GtkWidget *dialog = gtk_dialog_new_with_buttons ("BOOT MODE", NULL, flags, NULL);
これで、ボタンダイアログをボタンなしで作成して、PC-8801の種類に応じてボタンを追加しています。
#if defined(_PC8801MA) gtk_dialog_add_button (GTK_DIALOG(dialog), "N88-V1(S) mode", 0); gtk_dialog_add_button (GTK_DIALOG(dialog), "N88-V1(H) mode", 1); gtk_dialog_add_button (GTK_DIALOG(dialog), "N88-V2 mode", 2); gtk_dialog_add_button (GTK_DIALOG(dialog), "N mode", 3); #endif
ダイアログを閉じたときの戻り値で、モードを設定します。
gint res = gtk_dialog_run(GTK_DIALOG(dialog)); printf("Reset mode :%d\n",res); gtk_widget_destroy(dialog); while (gtk_events_pending ()){ gtk_main_iteration (); //GTK関連の処理がすべて完了するまで待ち } if(res >= 0){ return res; } return -1;
D88イメージファイルに複数のバンクがある場合、イメージ選択後にバンク選択を行うようにしました。これもボタンダイアログ使っています。
Common Source Code Project の DevTerm 移植、D88 ファイルイメージのバンク選択と PC-8801MA 起動モード選択もできるようになりました。 pic.twitter.com/psJfdPqcTf
— Nochi (@shikarunochi) April 2, 2022
ホントはボタンを縦並びにしたかったのですが、ボタンダイアログでは横並びしかできないのですよね。ダイアログのレイアウトを変えるには、ボタンダイアログが使えなくなるので、いろいろ処理を自分で書かないといけないみたいだし。まあ、横5つ並んでも大丈夫そうなので、これで良いでしょう!
for(int index = 0;index < emu->d88_file[drv].bank_num;index++){ gtk_dialog_add_button (GTK_DIALOG(dialog), emu->d88_file[drv].disk_name[index], index); }
バンクの数だけ、ボタンを追加しています。ダイアログを閉じたときの戻り値がバンク番号になります。
ステートセーブ/ロードに対応しました。
State Save / Load できるようになりました! pic.twitter.com/ZKdOzc3Scg
— Nochi (@shikarunochi) April 2, 2022
これは、ボタンダイアログだけではどうしようもなかったので、空ダイアログに自分でボタンを並べて作りました。
ダイアログを閉じたときも、単純な戻り値で判断できないので、ボタンごとに押されたときのコールバックを登録する感じです。
gtk_init(0, NULL); GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT; stateFileDialog = gtk_dialog_new_with_buttons("Save Data List", NULL, flags, "_Cancel", GTK_RESPONSE_REJECT,NULL);
キャンセルボタンだけの空ダイアログを用意。
GtkWidget * saveButton[10]; GtkWidget * loadButton[10]; SaveStatusDialogInfo saveStatusDialogInfo[10];
セーブ/ロードボタンWidgetとボタンを押したときの情報保持用の構造体を10個ずつ用意。
GtkWidget *stateFileInfo=gtk_box_new(GTK_ORIENTATION_HORIZONTAL,1); GtkWidget *stateLabel=gtk_label_new_with_mnemonic(timeStr); gtk_widget_set_size_request(stateLabel, 200, 25); gtk_box_pack_start(GTK_BOX(stateFileInfo),stateLabel,false,false,5); saveButton[i] = gtk_button_new_with_label("Save"); gtk_widget_set_size_request(saveButton[i], 100, 25); saveStatusDialogInfo[i].osd = this; saveStatusDialogInfo[i].selectNo = i; g_signal_connect(G_OBJECT(saveButton[i]),"clicked", G_CALLBACK(saveStateCallback),(void*)&(saveStatusDialogInfo[i])); gtk_box_pack_end(GTK_BOX(stateFileInfo),saveButton[i],false,false,5); if(isExist){ loadButton[i] = gtk_button_new_with_label("Load"); gtk_widget_set_size_request(loadButton[i], 100, 25); g_signal_connect(G_OBJECT(loadButton[i]),"clicked", G_CALLBACK(loadStateCallback),(void*)&(saveStatusDialogInfo[i])); gtk_box_pack_start(GTK_BOX(stateFileInfo),loadButton[i],false,false,5); } gtk_box_pack_start(GTK_BOX(content),stateFileInfo,true,true,1);
gtk_box_new で横並び一列のボックスを作成して、gtk_label_new_with_mnemonic でファイル時刻文字列を設定したラベルWidget作成。(ファイル無いときは”NO DATA”を入れてます。)
gtk_widget_set_size_request でラベルWidgetのサイズを指定、gtk_box_pack_start で表示ボックスに追加。
gtk_button_new_with_label でセーブボタンWidget作成、サイズ指定して、クリックされたときのコールバック先を設定します。
コールバックは、引数を一つしか与えられないので、構造体に入れて渡しています。
struct SaveStatusDialogInfo { OSD *osd; //自分自身のポインタ int selectNo; //押されたボタンの番号 };
コールバック先では、ボタン表示の変更、セーブ処理の呼び出し、ダイアログを閉じるところまでやっています。
static void saveStateCallback(GtkWidget *widget, gpointer userdata){ SaveStatusDialogInfo *saveStatusDialogInfo = (SaveStatusDialogInfo*)userdata; OSD *osd; osd = (OSD*)(saveStatusDialogInfo->osd); gtk_button_set_label(GTK_BUTTON(widget),"Saving..."); //ボタン表示の変更 while (gtk_events_pending ()){ //GTK関連の処理がすべて完了するまで待ち gtk_main_iteration (); } osd->save_state(saveStatusDialogInfo->selectNo); //実際のセーブ処理呼び出し if(osd->stateFileDialog != NULL){ //念のための NULL チェック gtk_widget_destroy(osd->stateFileDialog); //ダイアログ閉じる osd->stateFileDialog = NULL; // 閉じたら NULL にしておく } while (gtk_events_pending ()){ //GTK関連の処理がすべて完了するまで待ち gtk_main_iteration (); } }
ロードボタンは、保存ファイル存在チェックして、ファイル存在するときのみ表示するようにしています。
gtk_widget_show_all(stateFileDialog); gint result = gtk_dialog_run(GTK_DIALOG(stateFileDialog)); if(stateFileDialog != NULL){ gtk_widget_destroy(stateFileDialog); }
gtk_widget_show_all を行うと、追加された Widget が表示対象になるようです。gtk_dialog_run でダイアログ表示。
キャンセルボタンが押されるか、セーブ/ロードコールバック先でダイアログが閉じられるかすると、gtk_dialog_run が終了します。
セーブ/ロードコールバック先でダイアログが閉じられていた場合、stateFileDialog が NULL になっているので、それ以外の場合はここで閉じます。(コールバック先で閉じなくても素直に全部ここで閉じるようにした方がいいのかも?)
PC版とも、プログラムのバージョンが同じならデータをやり取りできます!
できたー! pic.twitter.com/zUxJxEmDpw
— Nochi (@shikarunochi) April 4, 2022
が、vm ディレクトリ以下のソース修正が必要です。
機種名.cpp ファイルの、bool VM::process_state(FILEIO* state_fio, bool loading) 関数のところです。
const _TCHAR *name = char_to_tchar(typeid(*device).name() + 6); // skip "class "
となっているところを、
#if defined (_DEVTERM) int offset = 1; if((int)strlen(typeid(*device).name()) > 10){ offset = 2; } const _TCHAR *name = char_to_tchar(typeid(*device).name() + offset); // skip length #else const _TCHAR *name = char_to_tchar(typeid(*device).name() + 6); // skip "class " #endif
こう変更してください。
typeid([CLASS]).name() の Windows と Linux での戻り値の違いですね…。
EVENTクラスの場合、Windows は「class EVENT」、Linuxは「5EVENT」が取得されますので、これを同じ内容になるよう、読み飛ばす文字数を調整しています。
vm ディレクトリ以下は、ビルドでエラーが出てしまう個所以外は、なるべく手を入れたくないので、動作確認のために作成した PC-8801/PC-8001系と、MZ-700以外はそのままにしてあります。STATE保存が必要な場合、各自で修正入れてくださいー。