2024/04/05(金) [n年前の日記]
#1 [prog] 自作スクリーンセーバをLinuxに対応させたい。その2
C++とOpenGLを使った自作スクリーンセーバをLinuxにも対応させようとしているところ。GLFWを使ったデモプログラムとして動かすつもり。
_mieki256/ssp3droadgl
Ubuntu Linux 22.04 LTS (g++ 11.4.0, ld 2.38)上でビルドしようとしたらリンクエラーが出てしまって悩んでしまったけれど。ソースコードに手を入れたら改善した。実行バイナリに埋め込んだpngやjpgを展開させる際、サイズの求め方を変えてみたらエラーが出ない状態になった。
ざっくり説明。
画像バイナリ(.png .jpg)を実行バイナリに含める方法は色々あるけど、今回は ld を使ってバイナリファイルをオブジェクトファイルに変換する方法を選んだ。
こうしてできたオブジェクトファイルをリンクしてやることで、実行バイナリの中に画像バイナリを含めることができる。
バイナリファイルを ld でオブジェクトファイルに変換すると、*_start, *_end, *_size の3つのシンボルを、C/C++側から参照できる状態になる。
以下は objdump -x を使って、オブジェクトファイルに含まれている情報をダンプしてみた例。「SYMBOL TABLE:」で、3つのシンボルが ―― *_end、*_size、*_start が並んでることが分かる。
C/C++側からこれらのシンボルを利用したい時は、以下のように書いておく。これで各シンボルが参照できるようになる。
このシンボルを利用して、実行バイナリ内の画像バイナリの展開処理を行うわけだけど…。今までは *_size を使って処理してた(つもりだった)。
これを、end - start でサイズを求めるように修正してみた。
このように書き換えてみたところ、エラーが出なくなってビルドが通るようになった。実行バイナリも期待した通りに動作してくれた。
また、この書き換えをしたことで、Ubuntu Linux 20.04 LTS上で発生していた不具合も ―― テクスチャの展開が時々失敗する問題も何故か解決してしまった。何度試しても、必ず展開できる…。もしかして今までは、サイズが不定値になっていたのだろうか…?
_mieki256/ssp3droadgl
Ubuntu Linux 22.04 LTS (g++ 11.4.0, ld 2.38)上でビルドしようとしたらリンクエラーが出てしまって悩んでしまったけれど。ソースコードに手を入れたら改善した。実行バイナリに埋め込んだpngやjpgを展開させる際、サイズの求め方を変えてみたらエラーが出ない状態になった。
ざっくり説明。
画像バイナリ(.png .jpg)を実行バイナリに含める方法は色々あるけど、今回は ld を使ってバイナリファイルをオブジェクトファイルに変換する方法を選んだ。
$ ld -r -b binary -o sprites.o sprites.png
こうしてできたオブジェクトファイルをリンクしてやることで、実行バイナリの中に画像バイナリを含めることができる。
バイナリファイルを ld でオブジェクトファイルに変換すると、*_start, *_end, *_size の3つのシンボルを、C/C++側から参照できる状態になる。
以下は objdump -x を使って、オブジェクトファイルに含まれている情報をダンプしてみた例。「SYMBOL TABLE:」で、3つのシンボルが ―― *_end、*_size、*_start が並んでることが分かる。
$ ld -r -b binary -o sprites.o sprites.png $ objdump -x sprites.o sprites.o: ファイル形式 elf64-x86-64 sprites.o アーキテクチャ: i386:x86-64, フラグ 0x00000010: HAS_SYMS 開始アドレス 0x0000000000000000 セクション: Idx Name Size VMA LMA File off Algn 0 .data 00875cae 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, DATA SYMBOL TABLE: 0000000000000000 l d .data 0000000000000000 .data 0000000000875cae g .data 0000000000000000 _binary_sprites_png_end 0000000000875cae g *ABS* 0000000000000000 _binary_sprites_png_size 0000000000000000 g .data 0000000000000000 _binary_sprites_png_start
C/C++側からこれらのシンボルを利用したい時は、以下のように書いておく。これで各シンボルが参照できるようになる。
extern unsigned char _binary_sprites_png_start; // binary start address extern unsigned char _binary_sprites_png_end; // binary end address extern unsigned char _binary_sprites_png_size; // binary size
このシンボルを利用して、実行バイナリ内の画像バイナリの展開処理を行うわけだけど…。今までは *_size を使って処理してた(つもりだった)。
img_ptr = &_binary_sprites_png_start; // start address img_size = (size_t)&_binary_sprites_png_size; // size gw.spr_tex = createTextureFromMemory(img_ptr, img_size);
これを、end - start でサイズを求めるように修正してみた。
img_ptr = &_binary_sprites_png_start; // start address img_size = (size_t)((&_binary_sprites_png_end) - (&_binary_sprites_png_start)); // size gw.spr_tex = createTextureFromMemory(img_ptr, img_size);
このように書き換えてみたところ、エラーが出なくなってビルドが通るようになった。実行バイナリも期待した通りに動作してくれた。
また、この書き換えをしたことで、Ubuntu Linux 20.04 LTS上で発生していた不具合も ―― テクスチャの展開が時々失敗する問題も何故か解決してしまった。何度試しても、必ず展開できる…。もしかして今までは、サイズが不定値になっていたのだろうか…?
[ ツッコむ ]
#2 [prog] 自作スクリーンセーバを高DPIに対応させる
今時のWindowsは、デスクトップ設定で、文字表示その他を、125%とか150%とかに変更できる。おそらく高DPIと呼ばれる状態なのだろうけど…。
この状態にすると、スクリーンセーバ設定ダイアログ上で、スクリーンセーバのプレビュー表示がおかしな位置に表示されてしまう。この問題を解決したい。
結論を先に書いておく。マニフェストファイルを作成して、スクリーンセーバのリソースファイル内でマニフェストファイルを指定して埋め込んでおけば、一応は正常な表示になる。
環境は、MinGW g++ 6.3.0、ld 2.28、windres 2.28。Windows10 x64 22H2上で作業。
この状態にすると、スクリーンセーバ設定ダイアログ上で、スクリーンセーバのプレビュー表示がおかしな位置に表示されてしまう。この問題を解決したい。
結論を先に書いておく。マニフェストファイルを作成して、スクリーンセーバのリソースファイル内でマニフェストファイルを指定して埋め込んでおけば、一応は正常な表示になる。
環境は、MinGW g++ 6.3.0、ld 2.28、windres 2.28。Windows10 x64 22H2上で作業。
◎ 不具合を発見した経緯 :
自作したWindows用スクリーンセーバを ―― 疑似3D道路を描画するスクリーンセーバを、親父さんのPC上で動かして動作確認してみたところ、スクリーンセーバ設定ダイアログ上のプレビュー表示がおかしくなってることに気づいた。本来表示されるべき位置からかなり左上のほうに表示されてしまって、描画内容の右下だけが、プレビュー画面の左上のほうにちょっとだけ表示されてしまっている。何故だ。どうしてこうなった。
そこでふと気が付いた。親父さんは視力が衰えてしまっているので、Windowsのディスプレイ設定で、「テキスト、アプリ、その他の項目のサイズを変更する」を150%にしていた…。たぶんそのせいじゃないだろうか。
自分のメインPC上でも、そのあたりを変更してみたところ、親父さん用PCと全く同じ不具合が再現できた。
今までビルドしてきた各スクリーンセーバについても一通り試してみたところ、OpenGLを使っている/使ってないに係わらず、どのスクリーンセーバも、プレビュー表示は表示位置がおかしくなった。
この結果は、当然な気もする。自分がググって今まで参考にしてきた、スクリーンセーバの作成方法を解説しているページは、「高DPI? ナニソレ?」「WindowsのDPIと言えば96DPIに固定されてるもんだろJK」という時代に書かれているので、こういった不具合が起きることも、もちろん解決策についても、一切言及していない。故に、どのサンプルを動かしてもこういう不具合が発生するのは必然、ということなのだろう…。
そこでふと気が付いた。親父さんは視力が衰えてしまっているので、Windowsのディスプレイ設定で、「テキスト、アプリ、その他の項目のサイズを変更する」を150%にしていた…。たぶんそのせいじゃないだろうか。
自分のメインPC上でも、そのあたりを変更してみたところ、親父さん用PCと全く同じ不具合が再現できた。
今までビルドしてきた各スクリーンセーバについても一通り試してみたところ、OpenGLを使っている/使ってないに係わらず、どのスクリーンセーバも、プレビュー表示は表示位置がおかしくなった。
この結果は、当然な気もする。自分がググって今まで参考にしてきた、スクリーンセーバの作成方法を解説しているページは、「高DPI? ナニソレ?」「WindowsのDPIと言えば96DPIに固定されてるもんだろJK」という時代に書かれているので、こういった不具合が起きることも、もちろん解決策についても、一切言及していない。故に、どのサンプルを動かしてもこういう不具合が発生するのは必然、ということなのだろう…。
◎ マニフェストファイルがあれば動作が変わる :
ただ、試しているうちに、FreeBASICで作成したスクリーンセーバだけが、高DPIにしても正常なプレビュー表示になっていることに気づいた。
_freeBASIC Screensaver Kit Updated - freebasic.net
_FreeBASIC Screensaver Kit - freebasic.net
一体何が違うのか調べてみたところ、FreeBASICのスクリーンセーバ作成用kitには、マニフェストファイル(manifest)が添付されていることに気づいた。
そのマニフェストファイルに書かれていた内容は以下。
内容についてググった感じでは…。この記述は、Windows Vistaの時点で追加されたDPI変更機能に対応させるための記述、ということらしい。
ちなみに今現在は、Windows10 Version 1607で追加された機能もあるらしくて、完全に対応させるならそのあたりもアプリ側で行わないといかんらしい…。
更に、このマニフェストファイルは、リソースファイルの中で取り込むように指定されていた。リソースファイルの内容は以下。
一番最後に、「1 24 "MambazoDoodle.xml"」 という記述がある。
_freeBASIC Screensaver Kit Updated - freebasic.net
_FreeBASIC Screensaver Kit - freebasic.net
一体何が違うのか調べてみたところ、FreeBASICのスクリーンセーバ作成用kitには、マニフェストファイル(manifest)が添付されていることに気づいた。
そのマニフェストファイルに書かれていた内容は以下。
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>true</dpiAware> </asmv3:windowsSettings> </asmv3:application> </assembly>
内容についてググった感じでは…。この記述は、Windows Vistaの時点で追加されたDPI変更機能に対応させるための記述、ということらしい。
ちなみに今現在は、Windows10 Version 1607で追加された機能もあるらしくて、完全に対応させるならそのあたりもアプリ側で行わないといかんらしい…。
更に、このマニフェストファイルは、リソースファイルの中で取り込むように指定されていた。リソースファイルの内容は以下。
FB_SCRNSAVER_ABOUT DIALOGEX 6, 18, 160, 62 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_NOFAILCREATE FONT 10,"Arial" CAPTION "Mambazo Doodle" BEGIN CTEXT "Mambazo Doodle" -1, 0, 5, 160, 8 CTEXT "Version 1.0" -1, 0, 15, 160, 8 CTEXT "http://langfordtavern.com/" -1, 0, 25, 160, 8 DEFPUSHBUTTON "OK" IDOK, 55, 40, 50, 14 END 1 24 "MambazoDoodle.xml"
一番最後に、「1 24 "MambazoDoodle.xml"」 という記述がある。
- ここで指定されているxmlファイルがマニフェストファイル。
- 最初のほうにある「1 24」は、マニフェストファイルをリソースファイル内で指定する際のお約束らしい。
◎ 自作スクリーンセーバにも導入してみた :
試しに、自作のスクリーンセーバでも同じことをやってみた。
この状態でビルドし直したところ、WindowsのデスクトップのDPIを変更しても、スクリーンセーバ設定ダイアログ上で正常な位置にプレビューが表示される状態になった。
_mieki256/ssp3droadgl
とりあえず今回は、この対応だけでいいんじゃないかな…。
- マニフェストファイル名を、「スクリーンセーバファイル名.manifest」にする。
- リソースファイル(resource.rc)の最後に、該当行を追加。
1 24 "ssp3droadgl.scr.manifest"
この状態でビルドし直したところ、WindowsのデスクトップのDPIを変更しても、スクリーンセーバ設定ダイアログ上で正常な位置にプレビューが表示される状態になった。
_mieki256/ssp3droadgl
とりあえず今回は、この対応だけでいいんじゃないかな…。
◎ 他のスクリーンセーバも対応させておいた :
[ ツッコむ ]
#3 [linux] CUIファイラーRangerでファイルのアクセス権を変更したい
Linux上で動作するCUIファイラー Ranger を使って、複数のファイルのアクセス権を変更したい。今までは bash上で chmod 666 hoge.txt とか打ってたけど、何度もやってると面倒臭い。
以下の手順で変更できた。
あるいは、変更したいファイルにカーソルを合わせて、いきなり「666=」と打ってもいいらしい。見た目で「変更したよ」とは言ってこないけど、実はちゃんと変更されてる。数値を打った後、一番最後に「=」を打つことで、そのアクセス権に変更せよ、という指示を出しているらしい。
以下の手順で変更できた。
- SPACEキーで複数のファイルを選択。
- 「:chmod 666」と打ち込めば変更できる。
- 「uv」と打ち込めばファイル全選択解除。
あるいは、変更したいファイルにカーソルを合わせて、いきなり「666=」と打ってもいいらしい。見た目で「変更したよ」とは言ってこないけど、実はちゃんと変更されてる。数値を打った後、一番最後に「=」を打つことで、そのアクセス権に変更せよ、という指示を出しているらしい。
◎ 参考ページ :
[ ツッコむ ]
以上、1 日分です。