公式サイト

タグ: C言語

SDL2 は良い。

Ubuntu環境で、とある装置向けのコンソールを実装すべく、最初はGTK3ライブラリなんかでウィンドウ,ボタン,テキストボックスみたいなウィジェット配置プログラムを試してみるも、なーんか作法が煩雑すぎてプログラムがスッキリ作れなくてストレスを感じておりまして・・・

何かこうもう少し自由度の高いGUIライブラリは無いものかと探してたら見付けたのがSDL

https://www.libsdl.org

SDL2の日本語マニュアル がとてもありがたい<(_ _)>

インストールはWindowsなら利用するC/C++用の各開発環境向けのパッケージをダウンロードして、ストレージの適当な所(とは言えストレージドライブのルート(C:\)にライブラリ用のフォルダを作ってソコ)に展開する。プロジェクトには追加のincludeディレクトリとLibraryディレクトリを設定してSDLのlibのパスを通すか、libファイルをプログラムと同じフォルダに置くかすれば使えます。

Ubuntuならaptのインストールコマンドを実行すればいいので簡単(^_^)

sudo apt-get install libsdl2-dev

必要に応じて追加ライブラリも以下のうち使いたいものだけインストールすればいい

sudo apt-get install libsdl2-ttf-dev
sudo apt-get install libsdl2-image-dev
sudo apt-get install libsdl2-net-dev
sudo apt-get install libsdl2-mixer-dev

チナミに私は基本的なグラフィック描画と文字の描画が出来れば良いので、libsdl-ttf-dev だけ追加しました。

SDLは、WindowsもLinuxもMacもスマホもほぼ同じ作法で利用できるため、C/C++のプリプロセッサーディレクティブ等でちょっと工夫すれば、マルチプラットフォームなプログラムが作れてしまう点がとても良いですね。

で、

今回はUbuntuで作ったのですが、実はちょっとSDL2の設定にハマりまして

最初懸命にCMakeLists.txtを試みたのですが、どうしてもfind_package(SLD2 REQUIRED)のCMake構成が通らず、困っていました。ちなみにこの時はCMakeLists.txtは以下のようにしました。

#これだけではうまくいきませんでした
find_package(SDL2 REQUIRED)
find_package(SDL2_ttf REQUIRED)

#うまくいかないんだな
target_link_libraries(${PROJECT_NAME} SDL2::Main SDL2::TTF)

困り果ててcopilotやGoogle先生に訊きまくってようやくたどり着いた「ネ申」が以下リンク

https://github.com/aminosbh/sdl2-cmake-modules

どうやら現行のSDL2は公式にはCMakeにそもそも未対応らしく、ビルドコマンドで直接ライブラリを指定するのが正解らしいのですが、けれどもやーっぱCMakeの便利さから抜けられない私のような方のために、何と親切にSDL2のCMakeモジュールを自作して公開してくれている正に「ネ申」でございましたm(_ _)m

このサイトにある内容に従ってやった事が以下。

〇自分のプロジェクトディレクトリでターミナルを開いて以下コマンド

mkdir cmake
mkdir cmake/sdl2
git clone https://gitlab.com/aminosbh/sdl2-cmake-modules.git cmake/sdl2
rm -rf cmake/sdl2/.git

※git を使う場合は違う設定らしいので詳しくは本家を・・(私は使わないのでわかりませぬ<(_ _)>

〇CMakeLists.txt には以下構文を追加しました。

# SDL2 Module Path Setting
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2)

# Find SDL2, SDL2_ttf libraries
find_package(SDL2 REQUIRED)
find_package(SDL2_ttf REQUIRED)

# Link SDL2::Main, SDL2::ttf to our project
target_link_libraries(${PROJECT_NAME} SDL2::Main SDL2::TTF)

これで無事SDLライブラリを自分のプロジェクトに設定できました。嬉しい(*’ω’*)

実際にSDLを自分のプログラムで記述する方法は日本語マニュアル 見てやるのが手っ取り早いです。

例えば、以下のような感じでグラフィックスプログラム書けちゃうんですよね。DirectXとかUnityより全然取っ付きやすくて良いですね。

#include"SDL.h"

SDL_Window *hWnd;
SDL_Renderer *hRenderer;

int main()
{
    //SDL初期設定
    if(SDL_Init(SDL_INIT_VIDEO) == 0)
    {
        //Window & Renderer
        if(SDL_CreateWindowAndRenderer(800,600,0,&hWnd,&hRenderer)==0)
        {
            while(1)
            {
                SDL_Event aEvent;

                //描画する色を黒(R:0,G:0,B:0)に設定
                SDL_SetRenderDrawColor(hRenderer,0,0,0,SDL_ALPHA_OPAQUE);
                //レンダラーをクリア
                SDL_RenderClear(hRenderer);

                //==========※ここからグラフィックの描画記述

                //描画する色を緑っぽい色(R:63,G:255,B:63)に設定
                SDL_SetRenderDrawColor(hRenderer,63,255,63,SDL_ALPHA_OPAQUE);
                //x:400,y:100の位置に点(ドット)を描く
                SDL_RenderDrawPoint(hRenderer,400,300);
                //(x:0,y:300)-(x:800,y:300)の座標間に直線を描く
                SDL_RenderDrawLine(hRenderer,0,300,800,300);
                //x:200,y:200の位置にW:400,H:200のサイズで長方形をRendererに描く
                SDL_Rect aRect = {200,200,400,200};
                SDL_RenderDrawRect(hRenderer,&aRect);
            
                //==========※ここまでがグラフィックの描画記述
                
                //描画したレンダラーをウィンドウに反映
                SDL_RenderPresent(hRenderer);

                //プログラム終了確認
                SDL_PollEvent(&aEvent);
                if (aEvent.type == SDL_QUIT)
                {   //終了要求があるのでwhileループ脱出
                    break;
                }
            }
            //Renderer破棄
            SDL_DestroyRenderer(hRenderer);
            //Window破棄
            SDL_DestroyWindow(hWnd);
        }
        //SDL利用終了
        SDL_Quit();
    }
    return 0;
}

Ubuntu C/C++プログラミング~シリアル通信メモ~

Ubuntu環境向けに制御ソフトウェアを作ることになったので、その一環としてシリアル通信のCプログラムをデバッグしようとしてたら、結構つまずいたのでその克服ナレッジを記録。

[環境]

  • 開発環境OS:Windows 11
  • IDE: Visual Studio Community 2022(Linux向けCMakeプロジェクト)
  • プログラミング言語: C/C++
  • ターゲット Ubuntu 20.04@WSL

このような環境で、シリアル通信プログラムのデバッグをWSL上のUbuntuでやろうとしておおよそ以下のようなプログラムを作った。(Windows上のCOM1 を使うので ttyS1 が対象デバイスとなるつもりで・・・)

    //シリアルポートを開く
    int serial_port = open("/dev/ttyS1", O_RDWR);

    //シリアルポートの設定
    struct termios tty;
    if(tcgetattr(serial_port, &tty) != 0)
    {   //設定データ取得失敗
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
        return 1;
    }

    tty.c_cflag &= ~PARENB; // パリティビットをクリアし、パリティなしに設定
    tty.c_cflag &= ~CSTOPB; // ストップビットをクリアし、1つのストップビットに設定
    tty.c_cflag |= CS8;     // 8ビットの文字サイズを設定
    tty.c_cflag &= ~CRTSCTS;// ハードウェアフロー制御を無効化
    tty.c_cflag |= CREAD | CLOCAL; // 受信を有効化し、モデム制御を無視

    tty.c_lflag &= ~ICANON; // カノニカルモードを無効化
    tty.c_lflag &= ~ECHO;   // エコーを無効化
    tty.c_lflag &= ~ECHONL; // 改行エコーを無効化
    tty.c_lflag &= ~ISIG;   // シグナル文字を無効化

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // ソフトウェアフロー制御を無効化
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // 特殊入力処理を無効化

    tty.c_oflag &= ~OPOST; // 特殊出力処理を無効化
    tty.c_oflag &= ~ONLCR; // 改行変換を無効化

    // ボーレートの設定
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    // シリアルポートの設定を保存
    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
        return 1;
    }
    
    //とりあえず送信
    char* message="Hello\r";
    int result = write(serial_port,message,sizeof(message));
    :
    :
    //シリアルポートを閉じる
    close(serial_port)

これを実行すると、open(“/dev/ttyS1”,O_RDWR)でのファイルディスクリプタは正常に取れている様だが、その後どーしてもtcgetattr(serial_port,&tty)のとこで失敗する。errnoは5、Device I/O Error とのことだ。

デバイス名をttyS0に変えてみたりとか色々試すけど一向にうまくいかないのでcopilotに訊いてみる。

申し訳ありませんが、現在のところWSL2ではオンボードのシリアルデバイスを直接扱うことはできません1234。これは、WSL2が仮想マシン内で動作しているため、Win32側のデバイスに直接アクセスすることができないからです1234

しかし、一部のUSBデバイスについては、usbipd-winというツールを使用することで、WSL2からWin32側に接続しているUSBデバイスを利用することが可能です1234。このツールは、USBデバイスをWindowsホスト側で使うかWSL2側で使うかを切り替えることができます1234

それでも問題が解決しない場合は、さらに詳しい情報を提供していただけますか?それにより、より具体的な解決策を提供することができます。1

copilotより回答

つまり、オンボードのシリアルポートはWSL2では使えないと言うのだ。これをどうしても使いたい場合はWSL2をWSL1にバージョンダウンする必要があるという訳である。

WSLのバージョンを変更する方法もCopilotに訊いてみた

はい、WSL2をWSL1に変更することは可能です。以下の手順で行うことができます123

  1. まず、現在のWSLのバージョンとディストリビューションを確認します。以下のコマンドを実行します:
wsl --list --verbose
  1. 次に、変更したいディストリビューションをWSL1に設定します。以下のコマンドを実行します(<ディストリビューション名>は確認したディストリビューションの名前に置き換えてください):
wsl --set-version <ディストリビューション名> 1

これらの手順を試してみてください。それでも問題が解決しない場合は、さらに詳しい情報を提供していただけますか?それにより、より具体的な解決策を提供することができます。4

Copilotより回答

で、実際やってみると・・・

> wsl --list --verbose
NAME STATE VERSION
* Ubuntu Stopped 2
Ubuntu-20.04 Stopped 2

はい現在2つのUbuntuがWSL上にインストールされ、いずれもWSLバージョンは「2」となっています。今回はターゲットがUbuntu-20.04なので、これをWSL1へ変更してみます

> wsl --set-version Ubuntu-20.04 1
変換中です。これには数分かかる場合があります。
この操作を正しく終了しました。
> wsl --list --verbose
NAME STATE VERSION
* Ubuntu Stopped 2
Ubuntu-20.04 Stopped 1

うまく行きました。で、この操作がうまく行かない環境もあるみたいで、その場合は、

コントロールパネル→プログラム→Windowsの機能の有効化または無効化

↑Linux用Windowsサブシステム の所にチェックを入れてインストール→PC再起動すると大抵解決するはずですが、何せ未確認w

私の環境ではWSL1の環境にすることで、オンボードのシリアルポートがWSL環境で利用することができ、無事に通信プログラムがテストできました。めでたし(^^)/

Algorithm Biorhytm 's musics are legacy electronic sounds and performance.