[C++] 173 FLTK : メニューの階層を増やす Fl_Menu_Item

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

開発中のビデオツールアプリでメニューの階層を2層に増やしました。

2層の場合はFl_Menu_Itemの最後に{0}を3つ付加する必要があります。1層では2つ付加します。

int main(int argc, char **argv) {
    static Fl_Menu_Item	items[] = {
    { "Menu", 0, 0, 0, FL_SUBMENU },
    { "Window切替", 0, 0, 0, FL_SUBMENU },
    { "Video Tool", 0, switchWindow0},
    { "FFmpeg Command Maker", 0, switchWindow1},
    { "Video Editor", 0, switchWindow2},
    { 0 },
    { 0 },
    { 0 }
    };

    Fl_Sys_Menu_Bar *menubar;
    menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
    menubar->box(FL_FLAT_BOX);
    menubar->menu(items);

    <以下略>

}

[C++] 172 FLTK : 別ウィンドウへの画面遷移 

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

開発中のビデオツールアプリで別ウィンドウへ画面遷移できるようにしました。

これができなければFLTKを使いこなせているとは言えない、と思いながらついつい放置してきましたが、ようやく本腰を入れて取り組みました。

初めは同一Fl_Windowでredrawさせようかとも考えたものの、コンストラクタ内で条件分岐するとウィンドウが真っ黒になり挫折、別WindowとしてSubWindowクラスを作成したらうまくできました。表示する位置が元ウィンドウと完全一致するように工夫を入れています。

着手2時間で挫折、昼寝中に解決方法を着想、起きて30分で実装という流れでした。職場なら2、3時間の昼寝はご法度でしょうが、プログラミングの場合は比較的長時間の馬上(ドライブ・乗り物他)・枕上(昼寝)・厠上(トイレ)の三上を認めてもらいたいものです。あと電話が鳴る環境も思考深化の妨げになるので厳禁です。

ゲームや動画鑑賞など、頭をリラックスさせて着想に導く方法は人それぞれなので、何でも取り入れてみるべきですね。着手して1回泥沼にハマる(周辺知識を一気に詰め込む作業でもある)ことが前提条件です。

Fl_Window *window;
SubWindow *subWindow;
int mode; // 0:メイン、1:サブ

void switchWindow(Fl_Widget*, void*){
    if (mode == 1){
        mode = 0;
        subWindow -> hide();

        int xs_win = subWindow->x_root();
        int ys_win = subWindow->y_root();
        cout<<"xs_win "<< xs_win <<" ys_win "<< ys_win <<endl;
        
        window->resize(xs_win,ys_win,720,480);
        window -> show();

    } else {
        mode = 1;
        window -> hide();

        int x_win = window->x_root();
        int y_win = window->y_root();
        cout<<"x_win "<< x_win <<" y_win "<< y_win <<endl;
        
        subWindow->resize(x_win,y_win,720,480);
        subWindow -> show();

    }
}

int main(int argc, char **argv) {
    static Fl_Menu_Item	items[] = {
    { "設定", 0, 0, 0, FL_SUBMENU },
    { "ウィンドウ切替", 0, switchWindow, 0, 0 },
    { 0 },
    { 0 }
    };
    Fl_Sys_Menu_Bar *menubar;
    menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
    menubar->box(FL_FLAT_BOX);
    menubar->menu(items);

    window = new Fl_Window(100,100,720,480,"Video Tool");
    window->color(fl_rgb_color(0,163,175));

    <中略>

    window->end();
    window->show(argc, argv);

    // SubWindow(最初は表示しない)
    int x_win = window->x_root();
    int y_win = window->y_root();

    subWindow = new SubWindow(720,480,"Video Tool 2nd");
    subWindow->resize(x_win,y_win,720,480);

    return Fl::run();
}
#pragma once
#include <FLstd.h> // 自製ヘッダファイル FLTKライブラリ
#include <cppstd.h> // 自製ヘッダファイル C++標準ライブラリ

class SubWindow: public Fl_Window
{
public:
	SubWindow(int w,int h,const char *title);
	~SubWindow();
};
#include <SubWindow.h>

SubWindow::SubWindow(int w,int h,const char *title=0)
	:Fl_Window(w,h,title){
	this->Fl_Widget::color(fl_rgb_color(65,154,202)); // ふじむらさき
}
SubWindow::~SubWindow(){}

[C++] 171 FLTK : 動画のリサイズ FFmpeg

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

ビデオツールアプリにリサイズ機能を搭載しました。

簡単な動画加工にはFinal Cut Proではなくこのツールを使えるよう、さらに機能を充実させていきたいです。

void resize(){
    char    cmd[512];
    char    count[100];
    const char* del = ".";

    // 元ファイルパス取得
    const char* path = input_line->value();
    cout << "path "<< path << endl;

    // stringへ変換
    string path_str = string(path);

    // 拡張子を削除
    string prefix0 = spt.splitJoin(path_str, del, 0, -2);
    cout << "prefix0 "<< prefix0 << endl;
    prefix0.pop_back();

    // _resizedを付加
    string prefix = prefix0 + "_resized";

    // 拡張子を付加
    vector<string> list_path = spt.splits(path_str, del);

    string extension = list_path[list_path.size()-1];
    cout << "extension "<< extension << endl;

    string newPath = prefix + "." + extension;
    cout << "newPath "<< newPath << endl;

    // リサイズcmd作成
    sprintf(cmd,"/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffmpeg -i %s -s %d:%d %s 2>&1 && echo ffmpeg完了", path_str.c_str(), width2_int, height2_int, newPath.c_str());
    cout << "cmd: "<< cmd << endl;

    // cmdをtextBufferに追記
    string cmd_str(cmd, 512);
    string cmdstr2 = "cmd: " + cmd_str + "\n";
    textBuffer->append(cmdstr2.c_str());

    // 文字数と行数をカウントしtextBufferに追記
    int length_buf = textBuffer -> length();
    int num_lines = textBuffer -> count_lines(0, length_buf);

    printf("length_buf %d num_lines %d\n",length_buf,num_lines);
    sprintf(count, "length_buf %d num_lines %d\n",length_buf,num_lines);
    textBuffer->append(count);

    // textDisplayを最終行表示する
    textDisplay->buffer(textBuffer);
    textDisplay->scroll(num_lines + 1, 0);

    // cmd実行
    outputTextMake(cmd);

    // browserを最終行表示する
    browser->load(outputText);
    int line_num = browser->size();
    browser->bottomline(line_num);

    cout << "リサイズ完了!" << endl;
}

[C++] 170 FLTK : 動画のリサイズ設定

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

開発中のビデオツールアプリについて、アスペクト比固定での縦横サイズを算出できるようにしました。

LOCK ASPECT RATIOボタンにチェックを入れると、空白にしている方のサイズを算出してくれます。

リサイズ操作については次回以降書きます。

// LOCK ASPECT RATIOボタンをチェック時の動作
void lock_cb(Fl_Widget*, void*) {
    onoffLock = lock_cbtn -> value();

    if (onoffLock == 1){
        cout << "lock_cb実行" << endl;
        width2 = width_input -> value();
        height2 = height_input -> value();

        if ((string(height2)).empty()){
            cout << "height算出" << endl;
            width2_int = stoi(string(width2));
            cout << "width2_int " << width2_int << endl;

            height2_int = std::round(width2_int * ratio);
            cout << "height2_int " << height2_int << endl;

            height_input -> value((to_string(height2_int)).c_str());

        } else {
            cout << "width算出" << endl;
            height2_int = stoi(string(height2));
            cout << "height2_int " << height2_int << endl;

            width2_int = std::round(height2_int * (float)(1/ratio));
            cout << "width2_int " << width2_int << endl;

            width_input -> value((to_string(width2_int)).c_str());
        }
    } else {
        cout << "lock_cb実行なし" << endl;
    }
}

[C++] 169 FLTK : 動画の縦横サイズを取得・表示

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

開発中のビデオツールアプリで動画の縦横サイズを取得し、Fl_Inputに表示させました。

ffproveコマンドの出力をFILE構造体として取得、fgets関数でcharとしてデータを取り出し、xを区切り文字にしてリスト化しました。0番目の要素がwidth、1番目の要素がheightになります。

最初、変数resultのメモリ領域を確保せずにchar *resultとしていたところ、ビルドは成功しましたが、アプリとして動作させるとセグメンテーション違反が発生しました。正しくは、char result[受け取るデータの最大バイト数]になります。データを格納する時はポインタ変数を設定するのではなく、あらかじめその領域を確保しておく必要があります。初心者にありがちなミスでした。

void inspect(){
    char    count[100];

    // 対象ファイルパス取得
    const char* path = input_line->value();
    cout << "path "<< path << endl;

    string path_str = string(path);
    bufferstr += "path_str: " + path_str + "\n";

    // path内半角スペースをアンダースコアへ置き換え
    string path2 = underScoreReplace(string(path_str));
    bufferstr += "path2: " + path2 + "\n";

    // 元ファイルをリネーム
    rename(path, path2.c_str());

    // ファイル縦横サイズ出力コマンド作成
    string cmd = "/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffprobe -v error -select_streams v -show_entries stream=width,height -of csv=p=0:s=x " + path2;
    cout << "cmd: "<< cmd << endl;
        
    // cmdをtextBufferに追記
    string cmdstr = "cmd: " + cmd + "\n";
    textBuffer->append(cmdstr.c_str());
    
    // 文字数と行数をカウントしtextBufferに追記
    int length_buf = textBuffer -> length();
    int num_lines = textBuffer -> count_lines(0, length_buf);

    printf("length_buf %d num_lines %d\n",length_buf,num_lines);
    sprintf(count, "length_buf %d num_lines %d\n",length_buf,num_lines);
    textBuffer->append(count);

    // textDisplayを最終行表示する
    textDisplay->buffer(textBuffer);
    textDisplay->scroll(num_lines + 1, 0);
    
    // cmd実行
    showInspectResult(cmd);

    cout << "inspect完了!" << endl;

}
void showInspectResult(string cmd){
    char result[10];

    FILE* fp = popen(cmd.c_str(), "r");
    fgets(result, 10, fp);
    pclose(fp);

    string result_str = string(result);
    cout << "result_str " << result_str << endl;

    vector<string> data = spt.splits(result_str,"x"); // sptは自製クラス
    cout << "data[0] " << data[0] << endl;
    cout << "data[1] " << data[1] << endl;

    width = stoi(data[0]);
    height = stoi(data[1]);
    cout << "width " << width << endl;
    cout << "height " << height << endl;

    width_input0 -> value((to_string(width)).c_str());
    height_input0 -> value((to_string(height)).c_str());
}

[C++] 168 FLTK : Fl_Text_DisplayとFl_Browserの最終行表示

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

開発中のビデオツールアプリでFl_Text_DisplayとFl_Browserの最終行を常に表示するようにしました。

Fl_Browser(下図下)の方はすぐに方法が分かったものの、Fl_Text_Display(下図右上)はFl_Text_Bufferの方でしか行数を把握できないと知るまで時間がかかり難航しました。

Fl_Text_Display::scroll関数の第1引数を10000行などあり得ない大きな数字に設定しても最終行表示は可能ですが、さすがに荒っぽいやり方なので行数を正確にカウントしました。

これくらいの機能は用意されていて当たり前と考えがちですが、簡易ツールのFLTKには通用しませんでした。使いこなせるかどうかはユーザーの工夫次第でしょう。

人気面でwxWidgetsに水を開けられてしまうのも致し方なしです。

void inspect(){
    char    count[100];

    // 対象ファイルパス取得
    const char* path = input_line->value();
    cout << "path "<< path << endl;

    string path_str = string(path);
    bufferstr += "path_str: " + path_str + "\n";

    // path内半角スペースをアンダースコアへ置き換え
    string path2 = underScoreReplace(string(path_str));
    bufferstr += "path2: " + path2 + "\n";

    // 元ファイルをリネーム
    rename(path, path2.c_str());

    // ファイル情報出力コマンド作成
    string cmd = "/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffprobe -i " + path2 + " 2>&1 && echo ffprobe完了";
    
    // cmdをtextBufferに追記
    string appendstr = "cmd: " + cmd + "\n";
    bufferstr += appendstr;
    cout << "bufferstr: " << bufferstr << endl;
    textBuffer->append(bufferstr.c_str());

    // 文字数と行数をカウントしtextBufferに追記
    int length_buf = textBuffer -> length();
    int num_lines = textBuffer -> count_lines(0, length_buf);

    printf("length_buf %d num_lines %d\n",length_buf,num_lines);
    sprintf(count, "length_buf %d num_lines %d\n",length_buf,num_lines);
    textBuffer->append(count);

    // textDisplayを最終行表示する
    textDisplay->buffer(textBuffer);
    textDisplay->scroll(num_lines + 1, 0);
    
    // cmd実行
    outputTextMake(cmd);

    // browserを最終行表示する
    browser->load(outputText);
    int line_num = browser->size();
    browser->bottomline(line_num);

    cout << "inspect完了!" << endl;

}

[C++] 167 FLTK : FFmpegのログ取得・表示

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

開発中のビデオツールアプリでFFmpegのログをGUI上に表示させました。

手順は以下の通りです。

1.ffmpegコマンド内でFFmpegログの出力先を標準エラー出力から標準出力にリダイレクトする。
2.ffmpegコマンドの標準出力をFILE型データで取得する。
3.ログ用テキストファイルに書き込み、Fl_BrouserでGUIに表示する。

void inspect(){
    // 対象ファイルパス取得
    const char* path = input_line->value();
    cout << "path "<< path << endl;

    string path_str = string(path);
    bufferstr += "path_str: " + path_str + "\n";

    // path内半角スペースをアンダースコアへ置き換え
    string path2 = underScoreReplace(string(path_str));
    bufferstr += "path2: " + path2 + "\n";

    // 元ファイルをリネーム
    rename(path, path2.c_str());

    // ファイル情報出力コマンド作成(標準出力へリダイレクト)
    string cmd = "/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffprobe -i " + path2 + " 2>&1 && echo ffprobe完了";
    
    // cmdをbufferへ追記
    string appendstr = "cmd: " + cmd + "\n";
    bufferstr += appendstr;
    cout << "bufferstr: " << bufferstr << endl;

    textBuffer->append(bufferstr.c_str());
    textDisplay->buffer(textBuffer);

    // cmd実行
    outputTextMake(cmd);
    browser->load(outputText);
    int line_num = browser->size();
    browser->bottomline(line_num);

    cout << "inspect完了!" << endl;

}
void outputTextMake(string cmd){
    char buf[READ_SIZE];
    size_t ret;

    FILE* fp = popen(cmd.c_str(), "r");
    FILE* fo = fopen(outputText, "a+");

    while(1){
        ret = fread(buf, sizeof(char), READ_SIZE, fp);
        fwrite(buf, sizeof(char), ret, fo);
        
        if(ret < READ_SIZE){
            break; 
        } 
    }

    // 文字列追記
    string finish = "標準出力ファイル化完了\n";
    fprintf(fo, "%s", finish.c_str());

    pclose(fp);
    fclose(fo);
}

[C++] 166 FLTK : SFTP補助アプリの製作 日本語ファイル名への対応

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

前回の続きです。

SFTP補助アプリですが、ファイル名に日本語が含まれる場合はクリップボードへコピーされないことが判明しました。毎度ながら実行ファイルでは問題なく、appファイルでおかしくなります。

解決方法はpbcopyの際のロケール設定です。日本語を全く受け付けないのではなく、文字化けでも構わないのでコピーして欲しかったです。解決の手掛かりになりますので。

AppleScriptで同じトラブルに見舞われた方のブログ記事を参考にしました。まさかpbcopyに変なクセがあるとは思いもよりませんでした。

void cmd_cb(Fl_Widget*, void*) {
    string cmd0;
    string cmd1;
    string cmd;

    int onoff_put = put_rbtn->value();
    int onoff_dir = dir_rbtn->value();

    if (onoff_put == 1){
        cmd0 = "put";
    } else {
        cmd0 = "get";
    }

    if (onoff_dir == 1){
        cmd1 = "-r";
    } else {
        cmd1 = "";
    }

    const char* fileName = file_input -> value();
    const char* toName = to_input -> value();

    cmd = cmd0 + " " + cmd1 + " \"" + string(fileName) + "\" " + string(toName);
    cout << "cmd " << cmd << endl;

    cmd_input -> value("");
    cmd_input -> value(cmd.c_str());

    string cmdCopy = "echo '" + cmd + "' | LANG=ja_JP.UTF-8 pbcopy"; // 対応箇所
    output_line -> insert(cmdCopy.c_str());
    output_line -> insert("\n");
    system(cmdCopy.c_str());
}

参考記事

[C++] 165 FLTK : SFTP補助アプリの製作

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

ローカル環境のPC間でファイルを転送するためのコマンドを生成するSFTP補助アプリを製作しました。

CONNボタンを押すとSFTPコマンド、CMDボタンを押すとPUTあるいはGETコマンドがクリップボードにコピーされます。

これでMacOS、Windows、Linux間のファイル転送が楽になりそうです。

#include <SFTPAssist.h>
#include <FileChooser.h>

Fl_Window *window;
Fl_Group *radio_btns, *radio_btns2;
Fl_Radio_Round_Button *get_rbtn, *put_rbtn, *file_rbtn, *dir_rbtn;
Fl_Box *pc_label, *file_label, *to_label, *conn_label, *cmd_label;
Fl_Input *file_input, *to_input, *conn_input, *cmd_input;
Fl_Button *conn_btn, *cmd_btn, *clear_btn, *fileFcBtn, *toFcBtn;
Fl_Choice *choice;
FileChooser* chooser;

void conn_cb(Fl_Widget*, void*) {
    string cmd_conn;
    string pc;

    int choice_num = choice -> value();
    switch(choice_num){
        case 0:{
            pc = "aaa.local";
            break;
        }
        case 1:{
            pc = "bbb.local";
            break;
        }
        case 2:{
            pc = "ccc.local";
            break;
        }
    }

    string cmd_pwd = "pwd";
    system(cmd_pwd.c_str());

    cmd_conn = "sftp " + pc;
    cout << "cmd_conn " << cmd_conn << endl;

    conn_input -> value("");
    conn_input -> value(cmd_conn.c_str());

    string cmdConnCopy = "printf '" + cmd_conn + "' | pbcopy";
    system(cmdConnCopy.c_str());

}

void cmd_cb(Fl_Widget*, void*) {
    string cmd0;
    string cmd1;
    string cmd;

    int onoff_put = put_rbtn->value();
    int onoff_dir = dir_rbtn->value();

    if (onoff_put == 1){
        cmd0 = "put";
    } else {
        cmd0 = "get";
    }

    if (onoff_dir == 1){
        cmd1 = "-r";
    } else {
        cmd1 = "";
    }

    const char* fileName = file_input -> value();
    const char* toName = to_input -> value();

    cmd = cmd0 + " " + cmd1 + " \"" + string(fileName) + "\" " + string(toName);
    cout << "cmd " << cmd << endl;

    cmd_input -> value("");
    cmd_input -> value(cmd.c_str());

    string cmdCopy = "printf '" + cmd + "' | pbcopy";
    system(cmdCopy.c_str());

}

void clear_cb(Fl_Widget*, void*) {
    file_input->value("");
    to_input->value("");
    conn_input->value("");
    cmd_input->value("");
}

void FileChooserCB(Fl_Widget*, void*) {
    int fc;
    string appdir = "/Volumes/Macintosh HD/Users/xxxxx/";
    int onoff_dir = dir_rbtn->value();

    if (onoff_dir == 1){
        fc = 4; // FileChooser::DIRECTORY
    } else {
        fc = 0; // FileChooser::SINGLE
    }

    int x_win = window->x_root();
    int y_win = window->y_root();
    cout<<"x_win "<< x_win <<" y_win "<< y_win <<endl;

    chooser = new FileChooser(appdir.c_str(), 
                        "*.*",
                        fc,
                        "File/Dir Chooser",490,380
                        );
    
    chooser->resize(x_win+85,y_win+50,490,380);
    chooser->set_modal();
    chooser->Fl_Window::show();

    while(chooser->Fl_Window::shown()){
        Fl::wait();
    }
}

int main(int argc, char **argv) {
    Fl::set_font(FL_HELVETICA, "Osaka");
    window = new Fl_Window(100,100,360,245,"SFTP Assist");
    window->color(fl_rgb_color(147,184,129)); // やなぎぞめ
    
    // file
    pc_label = new Fl_Box(18,15,35,16,"PC");
    window->add(pc_label);
    pc_label->labelsize(14);
    pc_label->labelcolor(fl_rgb_color(255,239,213));
    pc_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    // PC選択項目
    Fl_Menu_Item pc_type[4] = {
        {"Mac mini Intel"},
        {"WindowsPC"},
        {"LinuxPC"}
    };

    // PC選択メニュー
    choice = new Fl_Choice(55, 10, 220, 25, nullptr);
    choice->menu(pc_type);

    radio_btns = new Fl_Group(75,45,185,20,"");{
        radio_btns->labelsize(12);

        // put_rbtn
        put_rbtn = new Fl_Radio_Round_Button(75,45,90,20,"PUT");
        put_rbtn->labelcolor(fl_rgb_color(255,239,213));
        put_rbtn->setonly();
        
        // get_rbtn
        get_rbtn = new Fl_Radio_Round_Button(170,45,90,20,"GET");
        get_rbtn->labelcolor(fl_rgb_color(255,239,213));
            
    }
    radio_btns->end();

    radio_btns2 = new Fl_Group(75,75,185,20,"");{
        radio_btns2->labelsize(12);

        // file_rbtn
        file_rbtn = new Fl_Radio_Round_Button(75,75,90,20,"FILE");
        file_rbtn->labelcolor(fl_rgb_color(255,239,213));
        file_rbtn->setonly();
        
        // dir_rbtn
        dir_rbtn = new Fl_Radio_Round_Button(170,75,90,20,"DIR");
        dir_rbtn->labelcolor(fl_rgb_color(255,239,213));
            
    }
    radio_btns2->end();

    // FILE/DIR
    file_label = new Fl_Box(18,112,52,13,"FILE/DIR");
    file_label->labelcolor(fl_rgb_color(255,239,213));
    file_label->labelsize(12);
    file_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    file_input = new Fl_Input(75,105,165,25,"");
    file_input->textsize(12);

    // fileFcBtn
    fileFcBtn = new Fl_Button(245,108,30,20,"...");
    fileFcBtn->color(fl_rgb_color(112,128,144));
    fileFcBtn->labelcolor(fl_rgb_color(255,239,213));
    fileFcBtn->labelsize(14);
    fileFcBtn->callback(FileChooserCB);

    // TO
    to_label = new Fl_Box(18,146,20,14,"TO");
    to_label->labelcolor(fl_rgb_color(255,239,213));
    to_label->labelsize(14);
    to_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    to_input = new Fl_Input(75,140,165,25,"");
    to_input->textsize(12);

    // toFcBtn
    toFcBtn = new Fl_Button(245,143,30,20,"...");
    toFcBtn->color(fl_rgb_color(112,128,144));
    toFcBtn->labelcolor(fl_rgb_color(255,239,213));
    toFcBtn->labelsize(14);
    toFcBtn->callback(FileChooserCB);

    // CONN
    conn_label = new Fl_Box(18,181,44,14,"CONN");
    conn_label->labelcolor(fl_rgb_color(255,239,213));
    conn_label->labelsize(14);
    conn_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    conn_input = new Fl_Input(75,175,270,25,"");
    conn_input->color(fl_rgb_color(187,200,230)); // あわふじいろ
    conn_input->textsize(12);

    // CMD
    cmd_label = new Fl_Box(18,216,34,14,"CMD");
    cmd_label->labelcolor(fl_rgb_color(255,239,213));
    cmd_label->labelsize(14);
    cmd_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    cmd_input = new Fl_Input(75,210,270,25,"");
    cmd_input->color(fl_rgb_color(187,200,230));
    cmd_input->textsize(12);

    // CONN_BTN
    conn_btn = new Fl_Button(295,10,50,40,"CONN");
    conn_btn->color(fl_rgb_color(112,128,144));
    conn_btn->labelcolor(fl_rgb_color(255,239,213));
    conn_btn->labelsize(14);
    conn_btn->callback(conn_cb);

    // CMD_BTN
    cmd_btn = new Fl_Button(295,60,50,40,"CMD");
    cmd_btn->color(fl_rgb_color(112,128,144));
    cmd_btn->labelcolor(fl_rgb_color(255,239,213));
    cmd_btn->labelsize(14);
    cmd_btn->callback(cmd_cb);

    clear_btn = new Fl_Button(295,110,50,25,"CLR");
    clear_btn->color(fl_rgb_color(112,128,144));
    clear_btn->labelcolor(fl_rgb_color(255,239,213));
    clear_btn->labelsize(14);
    clear_btn->callback(clear_cb);

    window->end();
    window->show(argc, argv);
    return Fl::run();
}

[C++] 164 POP3プロトコル POP3サーバの仮設置 Dovecot

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

『C言語によるTCP/IPネットワークプログラミング』(2001年) 第3章はPOP3プロトコルです。Intel MacからM1 Macに戻ってきました。

DovecotというIMAP/POP3サーバを導入し、プログラムで接続するところまで進めました。これはかなり難易度が高かったです。朝から深夜まで休憩をはさみつつ丸一日掛かっています。実験環境構築に没頭するあまり、目指しているわけでもないブログの連続投稿が途切れました。

まずHomebrewからインストールしたDovecotは起動してもポートを開けられませんでした。次にソースコード(v 2.3.19.1)をビルドしようとしたところmakeで失敗。Intel Macでもダメでした。エラー内容を確認するとコンパイルオプション-std=gnu99が悪さをしているようです。

2019年8月リリースの安定版 v 2.2.36.4をビルドするとこれが上手くいき、そのまま接続までスムーズでした。M1 Mac発売から1年以上前のコードが動くとは正直思いませんでした。

こちらのMakefileには-std=gnu99が入っていません。最新版は古いコード仕様も使えるようにしてバグが発生したということでしょうか。

話は変わりますが、Homebrewはこれまでの経験から今一つ信用できないところがあります。Homebrew版が使えなくて、GitHubから入手したコードで成功した例が結構あります。意地でもやらないでしょうが、RubyにこだわらずPythonも開発言語に加えてはいかがでしょうか。

とにかく接続はできたので、あとはコードの詳細を詰めていきます。

DovecotによりPOP3のポート番号110開放(netstat -natコマンドで確認)
poptestコマンドで接続成功