2022/08/01(月) [n年前の日記]
#1 [ruby] ruby-gtk2をWindows10上で動かしたい
Windows10 x64 21H2 + Ruby で、ruby-gtk2 を動かしたい。昨日動作確認したところ、どのバージョンの Ruby でも動いてくれなかったのだけど、しつこく試していたら動く組み合わせが少しだけ見つかった。
Ruby は、RubyInstaller 版。
_RubyInstaller for Windows
前提知識として、各gem に x86-mingw32 や x64-mingw32 とついているバージョンは、バイナリ(*.soファイル)も含んでいるのでインストール時にビルドしなくて済む。ついてないバージョンはソースからビルドすることになるので、DevKit のインストールも必要になる。
Ruby は、RubyInstaller 版。
_RubyInstaller for Windows
前提知識として、各gem に x86-mingw32 や x64-mingw32 とついているバージョンは、バイナリ(*.soファイル)も含んでいるのでインストール時にビルドしなくて済む。ついてないバージョンはソースからビルドすることになるので、DevKit のインストールも必要になる。
◎ Ruby 2.2.6 x86 の場合。 :
Windows10 x64 21H2 + Ruby 2.2.6 p396 x86-mingw32 では、以下の組み合わせで動作した。
ただし、Ruby 2.2.6 x86 をインストールした直後は、gem が使えなかった。
gem を使うためには、rubygems-update-2.7.11.gem をダウンロードして、ローカルからインストールして、gem (rubygems)をアップデートする必要がある。ちなみに、rubygems-update 2.7.11 の次バージョンは 3.0.0 になるけれど、そこからは Ruby 2.3.0 以上を要求されるので Ruby 2.2.6 では使えない。
gem のアップデートは、以下のページを参考にして作業した。
_Windowsでgem installでSSLのエラーが出た話 - Qiita
gem のアップデートができたら、cairo、glib2、gtk2 を、バージョンを指定してインストール。
そんなわけで、Ruby 2.2.6 x86-mingw32 + cairo 1.15.9 x86-mingw32 + gtk2 3.1.1 x86-mingw32 の組み合わせなら、ruby-gtk2 が一応動いてくれた。
- Ruby 2.2.6 x86-mingw32 + cairo 1.15.9 x86-mingw32 + gtk2 3.1.1 x86-mingw32
> ruby -v ruby 2.2.6p396 (2016-11-15 revision 56800) [i386-mingw32] > gem list | grep -E "gobject|gio2|gdk_pixbuf2|cairo|pango|atk|gtk2|glib2" atk (3.1.1 x86-mingw32) cairo (1.15.9 x86-mingw32) gdk_pixbuf2 (3.1.1 x86-mingw32) gio2 (3.1.1 x86-mingw32) glib2 (3.1.1 x86-mingw32) gobject-introspection (3.1.1 x86-mingw32) gtk2 (3.1.1 x86-mingw32) pango (3.1.1 x86-mingw32)
ただし、Ruby 2.2.6 x86 をインストールした直後は、gem が使えなかった。
gem を使うためには、rubygems-update-2.7.11.gem をダウンロードして、ローカルからインストールして、gem (rubygems)をアップデートする必要がある。ちなみに、rubygems-update 2.7.11 の次バージョンは 3.0.0 になるけれど、そこからは Ruby 2.3.0 以上を要求されるので Ruby 2.2.6 では使えない。
gem のアップデートは、以下のページを参考にして作業した。
_Windowsでgem installでSSLのエラーが出た話 - Qiita
wget https://rubygems.org/downloads/rubygems-update-2.7.11.gem gem install --local ./rubygems-update-2.7.11.gem update_rubygems --no-ri --no-rdoc
gem のアップデートができたら、cairo、glib2、gtk2 を、バージョンを指定してインストール。
gem install cairo -v 1.15.9 gem install glib2 -v 3.1.1 gem install gtk2 -v 3.1.1
- cairo 1.15.9 (2017/06/03) ならインストールできたし、require "cairo" も実行できた。
- しかし、gtk2 3.2.1 をインストールしたら、pango.so がロードできなくてエラーになった。
- cairo 1.15.9 と同時期の pango は 3.1.6 のはずだけど、glib2 3.1.6 や gtk2 3.1.6 をインストールしても pango.so がロードできなくてエラーになった。
- gtk2 3.1.1 をインストールしたところ、require "gtk2" が通った。
そんなわけで、Ruby 2.2.6 x86-mingw32 + cairo 1.15.9 x86-mingw32 + gtk2 3.1.1 x86-mingw32 の組み合わせなら、ruby-gtk2 が一応動いてくれた。
◎ Ruby 2.3.3 x86 の場合。 :
手元のメモによると、2019/04/19 の時点では、以下の組み合わせでも動いていたらしいのだけど…。
2022/08/01現在、上記の組み合わせでは何故か動かなくなっていた。
しかし、バージョンを変えて試していったところ、以下の組み合わせなら動作した。
ただ、インストール時に注意点がある。glib2 3.1.1 その他をインストールしようとした際、何故か cairo 1.15.14 も一緒にインストールされてしまう。cairo 1.15.14 は Ruby 2.3.3 x86 上で動かないので、そのままだと require "gtk2" をした時に「cairo.so が読み込めない」と言ってきて動かない。gem uninstall cairo で 1.15.14 をアンインストールして、1.15.9 だけを残した状態にすれば動く。
ちなみに、Ruby 2.3.3 x86-mingw32 も、インストールした直後は gem が動かない。rubygems-update 最新版を入手してアップデートする必要がある。
_Download RubyGems | RubyGems.org
rubygems-3.3.19.zip を入手して解凍。中に入って、ruby setup.rb を実行すればアップデートされる。
- Ruby 2.3.3 x86-mingw32 + cairo 1.15.14 x86-mingw32 + gtk2 3.2.9 x86-mingw32
- Ruby 2.3.3 x86-mingw32 + cairo 1.15.11 x86-mingw32 + gtk2 3.2.1 x86-mingw32
2022/08/01現在、上記の組み合わせでは何故か動かなくなっていた。
しかし、バージョンを変えて試していったところ、以下の組み合わせなら動作した。
- Ruby 2.3.3 x86-mingw32 + cairo 1.15.9 x86-mingw32 + gtk2 3.1.1 x86-mingw32
> ruby -v ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32] > gem list | grep -E "gobject|gio2|gdk_pixbuf2|cairo|pango|atk|gtk2|glib2" atk (3.1.1 x86-mingw32) cairo (1.15.9 x86-mingw32) cairo-gobject (3.1.1 x86-mingw32) gdk_pixbuf2 (3.1.1 x86-mingw32) gio2 (3.1.1 x86-mingw32) glib2 (3.1.1 x86-mingw32) gobject-introspection (3.1.1 x86-mingw32) gtk2 (3.1.1 x86-mingw32) pango (3.1.1 x86-mingw32)
ただ、インストール時に注意点がある。glib2 3.1.1 その他をインストールしようとした際、何故か cairo 1.15.14 も一緒にインストールされてしまう。cairo 1.15.14 は Ruby 2.3.3 x86 上で動かないので、そのままだと require "gtk2" をした時に「cairo.so が読み込めない」と言ってきて動かない。gem uninstall cairo で 1.15.14 をアンインストールして、1.15.9 だけを残した状態にすれば動く。
ちなみに、Ruby 2.3.3 x86-mingw32 も、インストールした直後は gem が動かない。rubygems-update 最新版を入手してアップデートする必要がある。
_Download RubyGems | RubyGems.org
rubygems-3.3.19.zip を入手して解凍。中に入って、ruby setup.rb を実行すればアップデートされる。
◎ Ruby 1.8.7 の場合。 :
◎ 余談。 :
それにしても、昔は Ruby の各バージョンで動いてたのに、どうして動かなくなってしまったのか…。Windows10 x64 21H2 側に原因があるのだろうか。それとも自分の環境に起因している不具合なのだろうか。
どうも Windows上でGTK関係は鬼門だなと…。
どうも Windows上でGTK関係は鬼門だなと…。
[ ツッコむ ]
#2 [ruby] ruby-gtk2は殺されていた
関連情報をググっていて今頃知ったのだけど、gtk2 (ruby-gtk2) は 3.4.3 が最終版で、3.4.4 以降は存在していない模様。
_mikutter GTK3対応(4) ?そして 5.0リリースへ? - tsutsuiの作業記録置き場
_News (2021-04-22 No.1) - Ruby-GNOME2 Project Website (WebArchive)
_ruby-gnome/NEWS at master - ruby-gnome/ruby-gnome
Ruby-GNOME (かつては ruby-gtk2 と ruby-gtk3 の両方を含んでいた) は、3.4.4 で色々なサポートを打ち切っていたらしい。
Windows版 Ruby 2.6.10 (x86-mingw32) 上で gtk2 をインストールしようとしてもエラーになるのはそのせいだった。
つまり、Windows版 Ruby 2.6.x でビルドが通る gtk2 のソースはそもそも存在してなかった、というオチだった。道理で、何度インストールを試みてもビルドエラーになってしまうわけで…。
_mikutter GTK3対応(4) ?そして 5.0リリースへ? - tsutsuiの作業記録置き場
_News (2021-04-22 No.1) - Ruby-GNOME2 Project Website (WebArchive)
_ruby-gnome/NEWS at master - ruby-gnome/ruby-gnome
Ruby-GNOME (かつては ruby-gtk2 と ruby-gtk3 の両方を含んでいた) は、3.4.4 で色々なサポートを打ち切っていたらしい。
- CentOS 6、Ubuntu Linux 16.04 のサポートを打ち切った。
- Ruby 2.4、Ruby 2.5 のサポートも打ち切った。
- そして、3.4.4 になったタイミングで、Ruby/GTK2 (ruby-gtk2) も削除(remove)されていた。
Windows版 Ruby 2.6.10 (x86-mingw32) 上で gtk2 をインストールしようとしてもエラーになるのはそのせいだった。
- glib2 については、3.4.3 になった際に、Windows上でもビルドができるように修正されていた。(「NEWS」内を「3.4.3」で検索すれば変更内容が把握できる。「Windows: Fixed a link errors.」と書いてある。) だから、gem install glib2 -v 3.4.3 でビルドが通った。
- しかし、gtk2 3.4.3 については放置されてしまったっぽい。だから gem install gtk2 -v 3.4.3 をしてもビルドエラーになる。
- 更に、3.4.4 になった際に ruby-gtk2 自体が削除された・非サポートになった。
つまり、Windows版 Ruby 2.6.x でビルドが通る gtk2 のソースはそもそも存在してなかった、というオチだった。道理で、何度インストールを試みてもビルドエラーになってしまうわけで…。
◎ ruby-gtk3は生きている。 :
ちなみに、Ruby 2.6.10 x86-mingw32、及び、Ruby 3.0.4 x86-mingw32 上で gtk3 をインストールしたら(gem install gtk3)、cairo 1.17.1 + gtk3 3.5.1 のビルドが通って gtk3 を使うことができた。
ruby-gtk2 は削除されてしまったけれど、ruby-gtk3 は今もメンテナンスされているし、一般的なGUIアプリを作りたいなら ruby-gtk3 を使えば済むはず、という状況ではあるらしい。
ruby-gtk2 は削除されてしまったけれど、ruby-gtk3 は今もメンテナンスされているし、一般的なGUIアプリを作りたいなら ruby-gtk3 を使えば済むはず、という状況ではあるらしい。
◎ 将来的な不安。 :
しかし、困った…。Windows上ではともかく、Debian Linux上では、まだ ruby-gtk2パッケージが用意されているから、ruby-gtk2 を使ったアプリも作れるだろうし、xscreensaver用のスクリーンセーバを Ruby + ruby-gtk2 で書くことだってできるのかもしれないけれど。公式には ruby-gtk2 が殺されてしまったわけだから、将来的には Debian Linux 上でも ruby-gtk2パッケージが消滅してしまいそうな気がする。そうなると、xscreensaver用スクリーンセーバを Ruby で書くこともできなくなるわけで…。ノリとしては、今から Flash を使ってスクリーンセーバを書くぞ、みたいなソレに結構近い可能性もありそうだなと。
ruby-gtk2 ではなく、ruby-gtk3 を使って xscreenaver用のスクリーンセーバを書くことができればいいのだろうけど、そのあたりは gtk3 に詳しい人じゃないとできないことだよな…。しかし、gtk3 でそんなことができるのかどうか…。
ruby-gtk2 ではなく、ruby-gtk3 を使って xscreenaver用のスクリーンセーバを書くことができればいいのだろうけど、そのあたりは gtk3 に詳しい人じゃないとできないことだよな…。しかし、gtk3 でそんなことができるのかどうか…。
この記事へのツッコミ
[ ツッコミを読む(1) | ツッコむ ]
2022/08/02(火) [n年前の日記]
#1 [python][pygame] pygameでスクリーンセーバを作りたいのだけど
Python + pygame でWindows用のスクリーンセーバを作れないものかとググっていたら気になる話を見かけたのでメモ。2003年頃のやり取りらしい。
_[pygame] windows screen saver in pygame?
_IntegratingPyGame - wxPyWiki
環境変数 SDL_VIDEO_DRIVER と SDL_WINDOWID を設定することで、指定されたウインドウハンドルをSDLの描画領域として利用することができるかもしれない、とのことで。それが可能なら、Windows用のスクリーンセーバを作れるのかもしれないなと…。
ただ、今現在の pygame は SDL1.x から SDL2 になってるので、件の環境変数は反映されないかも、という話も見かけた。
_Using 'SDL_WINDOWID' does not embed pygame display correctly into another application - Issue #1574 - pygame/pygame
_"SDL_CreateWindowFrom" in "display" module by Rabbid76 - Pull Request #2981 - pygame/pygame
_class method "from_foreign_window" added to class "Window" by Rabbid76 - Pull Request #2982 - pygame/pygame
解決するためのパッチ(SDL_CreateWindowFrom()を利用するらしい)も投稿されてるけれど、マージされてるかはちょっと不明。
余談。Ruby/SDL も件の環境変数を使って、指定のウインドウハンドルで描画領域を用意できるっぽい。
_Ruby/SDL Reference Manual
ただ、Ruby/SDL って、今も使えるのだろうか…?
_[pygame] windows screen saver in pygame?
_IntegratingPyGame - wxPyWiki
環境変数 SDL_VIDEO_DRIVER と SDL_WINDOWID を設定することで、指定されたウインドウハンドルをSDLの描画領域として利用することができるかもしれない、とのことで。それが可能なら、Windows用のスクリーンセーバを作れるのかもしれないなと…。
ただ、今現在の pygame は SDL1.x から SDL2 になってるので、件の環境変数は反映されないかも、という話も見かけた。
_Using 'SDL_WINDOWID' does not embed pygame display correctly into another application - Issue #1574 - pygame/pygame
_"SDL_CreateWindowFrom" in "display" module by Rabbid76 - Pull Request #2981 - pygame/pygame
_class method "from_foreign_window" added to class "Window" by Rabbid76 - Pull Request #2982 - pygame/pygame
解決するためのパッチ(SDL_CreateWindowFrom()を利用するらしい)も投稿されてるけれど、マージされてるかはちょっと不明。
余談。Ruby/SDL も件の環境変数を使って、指定のウインドウハンドルで描画領域を用意できるっぽい。
_Ruby/SDL Reference Manual
ただ、Ruby/SDL って、今も使えるのだろうか…?
◎ Windows用スクリーンセーバについて少し説明。 :
ウインドウハンドル云々については、ちょっと説明が必要かもしれない。
Windows用のスクリーンセーバは、拡張子が .scr のファイルなのだけど。この .scr、実態は .exe の拡張子を変更しただけのファイル。3種類のコマンドラインオプション、/s、/c、/p xxxx のどれかしらを渡した際に動作が変わる .exe を作って、それを .scr にリネームすればWindows用のスクリーンセーバとして利用できる、ということになっている。
オプションの意味は以下。
/s か /c を指定した時の動作なら、おそらくはどんな言語でも、ある程度は実装できる。
問題は、/p xxxx を指定した場合。ウインドウハンドル xxxx に描画をしないといけないわけだけど、他から指定されたウインドウハンドルに対して描画ができる言語やフレームワークってそれほどなかったりするので、ちゃんとしたスクリーンセーバを作るなら C/C++ で書くしかないよね、という話になってしまう。まあ、C# の作例はあるし、HSP でも書けるのだけど…。 *1
そんなわけで、Python + pygame も与えられたウインドウハンドルで描画ウインドウを作れるなら、スクリーンセーバを作る際のハードルが随分と下がってくれそうだなと。
Windows用のスクリーンセーバは、拡張子が .scr のファイルなのだけど。この .scr、実態は .exe の拡張子を変更しただけのファイル。3種類のコマンドラインオプション、/s、/c、/p xxxx のどれかしらを渡した際に動作が変わる .exe を作って、それを .scr にリネームすればWindows用のスクリーンセーバとして利用できる、ということになっている。
オプションの意味は以下。
- /s : フルスクリーン表示。
- /c : 設定画面ダイアログを表示する。
- /p xxxx : プレビュー表示。xxxx は、Windowsのスクリーンセーバ選択画面のプレビュー窓のウインドウハンドル。
/s か /c を指定した時の動作なら、おそらくはどんな言語でも、ある程度は実装できる。
問題は、/p xxxx を指定した場合。ウインドウハンドル xxxx に描画をしないといけないわけだけど、他から指定されたウインドウハンドルに対して描画ができる言語やフレームワークってそれほどなかったりするので、ちゃんとしたスクリーンセーバを作るなら C/C++ で書くしかないよね、という話になってしまう。まあ、C# の作例はあるし、HSP でも書けるのだけど…。 *1
そんなわけで、Python + pygame も与えられたウインドウハンドルで描画ウインドウを作れるなら、スクリーンセーバを作る際のハードルが随分と下がってくれそうだなと。
*1: ただ、HSP で作ったスクリーンセーバは、起動時か終了時かタイミングが分からんけれど、Windowsのイベントログにエラーを残していくのだよな…。なんでだろ。
[ ツッコむ ]
2022/08/03(水) [n年前の日記]
#1 [nitijyou] 自宅サーバ止めてました
雷が鳴ったので、22:50-02:10の間、自宅サーバ止めてました。申し訳ないです。
夜中に鳴り始めて、そろそろ鳴り止んだかなと思ったら、また鳴り始めて…。TVニュースによると、東北地方の各地で警報が出るレベルの大雨が降っているそうで。その影響なのかしらん。
夜中に鳴り始めて、そろそろ鳴り止んだかなと思ったら、また鳴り始めて…。TVニュースによると、東北地方の各地で警報が出るレベルの大雨が降っているそうで。その影響なのかしらん。
[ ツッコむ ]
#2 [windows] PowerToysがアンインストールできない
Windows10 x64 21H2上で久々に PowerToys 0.57.0 を起動したら、「更新プログラムが利用可能です。v0.61.1」と表示されたので、「ダウンロードしてインストール」をクリックしてみたのだけど。いきなり Windows10 がブルースクリーンに…。
さてはセットアップファイルを自分でDLして実行しないとダメなパターンだろうかと、github から PowerToysSetup-0.61.1-x64.exe をダウンロードして実行してみたところ、ウインドウが出て次をクリックしたタイミングでブルースクリーン。
_Releases - microsoft/PowerToys
これは前バージョンをアンインストールしてから試さないとダメなヤツかなと、コントロールパネル経由でアンインストールしようとしたら、そこでもブルースクリーン。どうしろと。
こんなに頻繁にブルースクリーン+再起動をされたら、SSDやHDDが壊れてしまう…。一応、管理者権限で開いた PowerShell 上で chkdsk /f C: を打ってチェックはしたけど…。
何にせよ、こんな怖いツールはインストールしておけない…。どうにかしてアンインストールする手段を見つけないと…。
さてはセットアップファイルを自分でDLして実行しないとダメなパターンだろうかと、github から PowerToysSetup-0.61.1-x64.exe をダウンロードして実行してみたところ、ウインドウが出て次をクリックしたタイミングでブルースクリーン。
_Releases - microsoft/PowerToys
これは前バージョンをアンインストールしてから試さないとダメなヤツかなと、コントロールパネル経由でアンインストールしようとしたら、そこでもブルースクリーン。どうしろと。
こんなに頻繁にブルースクリーン+再起動をされたら、SSDやHDDが壊れてしまう…。一応、管理者権限で開いた PowerShell 上で chkdsk /f C: を打ってチェックはしたけど…。
何にせよ、こんな怖いツールはインストールしておけない…。どうにかしてアンインストールする手段を見つけないと…。
◎ クリーンアップツールがあるらしい。 :
以下のページによると、クリーンアップツールがあるらしい。
_PowerToys のインストール | Microsoft Docs
_PowerToys/tools/CleanUp_tool_powershell_script at main - microsoft/PowerToys
_PowerToys/tools/CleanUp_tool at main - microsoft/PowerToys
CleanUp_tool.ps1 を眺めた感じでは、設定ファイルとレジストリを削除する処理っぽい。実行ファイル本体には手を付けないように見える。どうにかしてアンインストーラを正常動作させないとダメ、ということかな…。
_PowerToys のインストール | Microsoft Docs
_PowerToys/tools/CleanUp_tool_powershell_script at main - microsoft/PowerToys
_PowerToys/tools/CleanUp_tool at main - microsoft/PowerToys
CleanUp_tool.ps1 を眺めた感じでは、設定ファイルとレジストリを削除する処理っぽい。実行ファイル本体には手を付けないように見える。どうにかしてアンインストーラを正常動作させないとダメ、ということかな…。
[ ツッコむ ]
2022/08/04(木) [n年前の日記]
#1 [windows] Windows10からPowerToysをアンインストールした
昨晩、Windows10 x64 21H2 上で PowerToys 0.57.0 を 0.61.1 にアップデートしようとしたら Windows10 がブルースクリーン(BSOD)になってしまう不具合に遭遇した。アンインストールしようとしても必ずブルースクリーンになる。ブルースクリーンには、thread がどうとか、Ntfs.sys がどうとか表示されている。
そんなわけで、どうにか PowerToys をアンインストールしようと四苦八苦していた。
ハードウェア環境は、CPU が AMD Ryzen 5 5600X。CドライブがSSD、DドライブがHDD。
以下、参考ページ。
_PowerToys のインストール | Microsoft Docs
_I can't delete PowerToys or Uninstall it - Issue #1490 - microsoft/PowerToys
_Cant uninstall Microsoft Power Toys. - Microsoft Community
そんなわけで、どうにか PowerToys をアンインストールしようと四苦八苦していた。
ハードウェア環境は、CPU が AMD Ryzen 5 5600X。CドライブがSSD、DドライブがHDD。
以下、参考ページ。
_PowerToys のインストール | Microsoft Docs
_I can't delete PowerToys or Uninstall it - Issue #1490 - microsoft/PowerToys
_Cant uninstall Microsoft Power Toys. - Microsoft Community
◎ 試したこと。 :
まず、PowerToys の設定画面を表示して、全機能をオフにしてみた。PowerToys はエクスプローラに色々な機能を追加するので、そのあたりで問題が発生してそうだよなと…。設定を変更したらOS再起動。
PowerToys 0.57.0 のセットアップファイル、PowerToysSetup-0.57.0-x64.exe を github から入手。管理者権限で PowerShell を開いて、以下を打ってみた。
セットアップファイルは .exe で提供されているけれど、アンインストール処理時は .msi が必要になる、という話をどこかで見かけた。--extract_msi とつけると、おそらくはどこかしらに .msi を解凍して、そちらから処理をするのではないか。たぶん。
アンインストール画面が出てきたので、「Uninstall」をクリック。ブルースクリーンにならないでくれと祈りながら処理が終わるのを数十秒ほど待った。幸い、ブルースクリーンにはならず、アンインストールも成功した。助かった。
またこんな目にあったら嫌なので、今後、PowerToys のインストールは避けよう…。機能が豊富な分、Windowsの奥深いところに関わってそうでもあるし…。
PowerToys 0.57.0 のセットアップファイル、PowerToysSetup-0.57.0-x64.exe を github から入手。管理者権限で PowerShell を開いて、以下を打ってみた。
.\PowerToysSetup-0.57.0-x64.exe --extract_msi
セットアップファイルは .exe で提供されているけれど、アンインストール処理時は .msi が必要になる、という話をどこかで見かけた。--extract_msi とつけると、おそらくはどこかしらに .msi を解凍して、そちらから処理をするのではないか。たぶん。
アンインストール画面が出てきたので、「Uninstall」をクリック。ブルースクリーンにならないでくれと祈りながら処理が終わるのを数十秒ほど待った。幸い、ブルースクリーンにはならず、アンインストールも成功した。助かった。
またこんな目にあったら嫌なので、今後、PowerToys のインストールは避けよう…。機能が豊富な分、Windowsの奥深いところに関わってそうでもあるし…。
[ ツッコむ ]
#2 [ruby][xscreensaver] ruby-gtk2を使ってxscreensaver用スクリーンセーバを作る
Ruby + ruby-gtk2 を使って、xscreensaver用スクリーンセーバを作れそうか試してみた。
一応ざっくり説明しておくと…。
環境は、Ubuntu Linux 20.04 LTS + Ruby 2.7.0 p0 + ruby-gtk2 3.4.1-2build1 + xscreensaver 6.04。ちなみに、Ubuntu Linux は Windows10 x64 21H2 + VMware Player上で動かしてる。
実行結果は以下のような感じ。
一応ざっくり説明しておくと…。
- xscreensaver : Linux や Mac で利用できる、スクリーンセーバの管理プログラム。
- Ruby : プログラミング言語。作者様は日本人。
- ruby-gtk2 : Ruby から gtk2(GUIツールキット)を制御できるライブラリ。今現在は gtk3 が主流になりつつあるけど…。
環境は、Ubuntu Linux 20.04 LTS + Ruby 2.7.0 p0 + ruby-gtk2 3.4.1-2build1 + xscreensaver 6.04。ちなみに、Ubuntu Linux は Windows10 x64 21H2 + VMware Player上で動かしてる。
実行結果は以下のような感じ。
◎ 参考にしたスクリプト。 :
今回参考にさせてもらった Rubyスクリプトは以下。お天気情報を表示してくれる、Ruby製の xscreensaver用スクリーンセーバらしい。
_zhum/rubysaver: Xscreensaver module, displaying weather, forecast, clock and now playing song title and author.
_rubysaver/rs.rb at master - zhum/rubysaver
_zhum/rubysaver: Xscreensaver module, displaying weather, forecast, clock and now playing song title and author.
_rubysaver/rs.rb at master - zhum/rubysaver
◎ 必要なパッケージのインストール。 :
端末を開いて、以下を打ってインストール。
ちなみに、Ubuntu Linux 20.04 LTS の場合、デフォルトで Ruby 2.7.0 がインストールされているっぽい。
sudo apt install ruby-gtk2
ちなみに、Ubuntu Linux 20.04 LTS の場合、デフォルトで Ruby 2.7.0 がインストールされているっぽい。
◎ ソース。 :
前述のスクリプトから、xscreensaver に絡んでいる部分だけを抜き出して、簡単なサンプルにしてみた。処理内容としては、赤い円がウインドウ内を跳ね回るだけのスクリプト。
_04_xscrsav.rb
chmod +x 04_xscrsav.rb で実行権限をつけて、端末上で ./04_xscrsav.rb としてテスト実行。ウインドウが開いて、赤い円が跳ね回った。これでひとまず、xscreensaver を経由しなくても、アニメーション部分の動作テストや調整はできそうだなと…。
xscreensaver から呼び出せるように、設定ファイル ~/.xscreensaver を編集。
xscreensaverの設定画面を表示して、上記で追加した「RUBYSAVER」を選んでみると、プレビュー画面の中でも赤い円が跳ね回ってくれた。「プレビュー」ボタンをクリックしたら、フルスクリーンでテスト表示された。
これで、ハードルの高い C/C++ で書かなくても、Ruby + ruby-gtk2 で xscreensaver用スクリーンセーバを書くことができそうだと分かった。
_04_xscrsav.rb
#!/usr/bin/ruby # encoding: UTF-8 require "gtk2" require "logger" # for gtk2 SIGNAL_DRAW = "expose_event" # for gtk3 # SIGNAL_DRAW = "draw" TMOUT = 16 $drawing_area = nil $logger = Logger.new("/tmp/rubysaver-#{ENV["USER"]}.log", "monthly") class RubyXscrApp < Gtk::Window def initialize super set_title "xscreensaver module" signal_connect "destroy" do $logger.warn "EXIT by destroy" Gtk.main_quit end signal_connect "delete-event" do $logger.warn "EXIT by delete-event" Gtk.main_quit false end signal_connect "delete_event" do $logger.warn "EXIT by delete_event" Gtk.main_quit false end realize ident = ENV["XSCREENSAVER_WINDOW"] if not ident.nil? $logger.warn "XSCREENSAVER_WINDOW = #{ident}" self.window = Gdk::Window.foreign_new(ident.to_i(16)) self.window.set_events(Gdk::Event::EXPOSURE_MASK | Gdk::Event::STRUCTURE_MASK) x, y, width, height, depth = self.window.geometry # puts "window = #{self.window}, (x,y,w,h) = #{x}, #{y}, #{width}, #{height}" $logger.warn "window = #{self.window}, (x,y,w,h) = #{x}, #{y}, #{width}, #{height}" set_default_size width, height move(x, y) # set_window_position :center else x, y, width, height, depth = self.window.geometry width, height = 640, 360 set_default_size width, height set_window_position :center puts "window = #{self.window}, (w,h) = #{width}, #{height}" end @x = width / 2 @y = height / 2 @dx = width.to_f / (60 * 1) @dy = height.to_f / (60 * 1.5) $drawing_area = @darea = Gtk::DrawingArea.new @bgcol = Gdk::Color.new(0x2000, 0x4000, 0xa000) @darea.modify_bg(:normal, @bgcol) @darea.set_size_request(width, height) @darea.signal_connect SIGNAL_DRAW do |widget, event| on_draw widget end @fixed = Gtk::Fixed.new @fixed.put(@darea, 0, 0) add(@fixed) show_all end def on_draw(widget) cr = widget.window.create_cairo_context x, y, width, height, depth = widget.window.geometry # update position r = 24 @x += @dx @y += @dy @dx *= -1 if (@x <= r or @x >= width - r) @dy *= -1 if (@y <= r or @y >= height - r) # clear bg cr.set_source_rgb(0.1, 0.3, 0.5) cr.paint # draw circle cr.set_source_rgb(1.0, 0.0, 0.0) cr.arc(@x, @y, r, 0, 2 * Math::PI) cr.fill cr.destroy end end Gtk.init window = RubyXscrApp.new # animation GLib::Timeout.add(TMOUT) do $drawing_area.queue_draw if (not $drawing_area.nil?) true end Gtk.main
chmod +x 04_xscrsav.rb で実行権限をつけて、端末上で ./04_xscrsav.rb としてテスト実行。ウインドウが開いて、赤い円が跳ね回った。これでひとまず、xscreensaver を経由しなくても、アニメーション部分の動作テストや調整はできそうだなと…。
xscreensaver から呼び出せるように、設定ファイル ~/.xscreensaver を編集。
programs: \ "RUBYSAVER" \ /home/USERNAME/prg/ruby/ruby-gtk2/04_xscrsav.rb \n\ maze -root \n\
- TAB幅は8文字、TAB文字有効で編集を行う。
- 「programs:」以下に、追加するスクリーンセーバの名前と、Rubyスクリプトのパスを記述。
- 行の最後が「\」なら継続行、「\n\」ならスクリーンセーバ1つ分の記述が終わったことを示す、のだと思う。
xscreensaverの設定画面を表示して、上記で追加した「RUBYSAVER」を選んでみると、プレビュー画面の中でも赤い円が跳ね回ってくれた。「プレビュー」ボタンをクリックしたら、フルスクリーンでテスト表示された。
これで、ハードルの高い C/C++ で書かなくても、Ruby + ruby-gtk2 で xscreensaver用スクリーンセーバを書くことができそうだと分かった。
◎ 問題点。 :
上記のサンプルは、少々問題が残ってる。xscreensaverの設定画面でスクリーンセーバ種類を切り替えた際に、以下のメッセージが表示されてしまう。
「GdkWindow 0xXXXX が予期せず破壊された」的な警告が出ている。おそらく、xscreensaver が強制的にスクリーンセーバを殺してしまうことで、こういう警告が出ているのかなと…。何か正しい終了手順があるなら、対策したいものだけど…。
$ xscreensaver-settings: 06:27:44: XScreenSaver-debug: Name com.canonical.AppMenu.Registrar does not exist on the session bus Gdk-WARNING **: GdkWindow 0x5000023 unexpectedly destroyed from /home/USERNAME/prg/ruby/ruby-gtk2/04_xscrsav.rb:125:in `<main>'
「GdkWindow 0xXXXX が予期せず破壊された」的な警告が出ている。おそらく、xscreensaver が強制的にスクリーンセーバを殺してしまうことで、こういう警告が出ているのかなと…。何か正しい終了手順があるなら、対策したいものだけど…。
◎ 少し解説。 :
xscreensaver は、スクリーンセーバを呼び出す際、「このウインドウを使ってスクリーンセーバの描画処理をするべし」的に、環境変数 XSCREENSAVER_WINDOW に16進数文字列でウインドウハンドルを設定してくれる。
_XScreenSaver Manual
_XScreenSaver FAQ
つまり、
実際、xscreensaver のFAQページには、mpv という動画再生ソフトを呼び出して動画を再生するサンプルが載っているけど、それは環境変数 XSCREENSAVER_WINDOW を mpv のコマンドラインオプションに渡すことで目的を果たしている。
まあ、xscreensaver のドキュメントには、「ウインドウハンドルを渡して処理できる言語やフレームワークはそれほど多くないから、実際にはC/C++で書くことになるだろうね」と書いてあったりするのだけど…。
_README.hacking.edit.txt
幸い、Ruby は環境変数の有無を調べることができるし、ruby-gtk2 は指定されたウインドウハンドルを自身のウインドウとして再設定する機能があるっぽいので、こうして xscreensaver用スクリーンセーバを書けそう、という話になるわけで。
一応、そのあたりの処理を以下に抜き出してみる。
ちょっとハマったのは、ウインドウの表示位置の指定方法。参考にしたスクリプトは、set_window_position :center を呼んで表示位置を決めていたっぽいのだけど、それだと xscreensaver設定画面のプレビュー窓に位置が合ってくれなかった。move(x, y) にしてみたら上手くいった。
さておき。今回、描画処理は、RubyXscrAppクラス内の on_draw() の中でやっている。件のメソッドの中を魔改造すれば違う描画処理になってくれるはず。
アニメーションさせるためには、一定の周期で描画処理を呼び出す指定が必要だけど、ruby-gtk2 の場合、GLib::Timeout.add(ミリ秒) do - end で指定ができるらしい。以前は Gtk.timeout_add(ミリ秒) で指定してたらしいけど、その指定は非推奨になったそうで。
描画は、Cairo::Context を使う方法と、Gdk::Drawable を使う方法の2種類があるそうだけど、今回は前者を使ってみた。ただ、Mac上で動かしたときは、Cairo::Context を毎回破棄しないとおかしくなるらしいので、一応 .destroy を呼んで破棄するようにしておいた。 *1
最初、DrawingArea をどこに登録すればいいのか分からなかったけど、どうやら gtk2 のウインドウに登録してやればいいようだなと…。
Gtk::Fixed は、座標指定で配置するためのWidgetらしい。
_XScreenSaver Manual
_XScreenSaver FAQ
つまり、
- 「環境変数 XSCREENSAVER_WINDOW があるかどうかを調べることができて」
- 「XSCREENSAVER_WINDOW で指定されたウインドウハンドルを使って描画できる」
実際、xscreensaver のFAQページには、mpv という動画再生ソフトを呼び出して動画を再生するサンプルが載っているけど、それは環境変数 XSCREENSAVER_WINDOW を mpv のコマンドラインオプションに渡すことで目的を果たしている。
Q. How do I make XScreenSaver play a video clip?
A. Install mpv and add something like the following to the "programs" preference in your .xscreensaver file:
"Movies" mpv --really-quiet --no-audio --fs --loop=inf \
--no-stop-screensaver --shuffle \
--wid=$XSCREENSAVER_WINDOW \
$HOME/Videos/poop.mp4 \n\
Or, point it at an .m3u file instead: a text file listing your video files, one per line.
まあ、xscreensaver のドキュメントには、「ウインドウハンドルを渡して処理できる言語やフレームワークはそれほど多くないから、実際にはC/C++で書くことになるだろうね」と書いてあったりするのだけど…。
_README.hacking.edit.txt
幸い、Ruby は環境変数の有無を調べることができるし、ruby-gtk2 は指定されたウインドウハンドルを自身のウインドウとして再設定する機能があるっぽいので、こうして xscreensaver用スクリーンセーバを書けそう、という話になるわけで。
一応、そのあたりの処理を以下に抜き出してみる。
ident = ENV["XSCREENSAVER_WINDOW"] if not ident.nil? # xscreensaver self.window = Gdk::Window.foreign_new(ident.to_i(16)) self.window.set_events(Gdk::Event::EXPOSURE_MASK | Gdk::Event::STRUCTURE_MASK) x, y, width, height, depth = self.window.geometry set_default_size width, height move(x, y) else # not xscreensaver x, y, width, height, depth = self.window.geometry width, height = 640, 360 set_default_size width, height set_window_position :center end
ちょっとハマったのは、ウインドウの表示位置の指定方法。参考にしたスクリプトは、set_window_position :center を呼んで表示位置を決めていたっぽいのだけど、それだと xscreensaver設定画面のプレビュー窓に位置が合ってくれなかった。move(x, y) にしてみたら上手くいった。
さておき。今回、描画処理は、RubyXscrAppクラス内の on_draw() の中でやっている。件のメソッドの中を魔改造すれば違う描画処理になってくれるはず。
アニメーションさせるためには、一定の周期で描画処理を呼び出す指定が必要だけど、ruby-gtk2 の場合、GLib::Timeout.add(ミリ秒) do - end で指定ができるらしい。以前は Gtk.timeout_add(ミリ秒) で指定してたらしいけど、その指定は非推奨になったそうで。
GLib::Timeout.add(TMOUT) do $drawing_area.queue_draw if (not $drawing_area.nil?) true end
描画は、Cairo::Context を使う方法と、Gdk::Drawable を使う方法の2種類があるそうだけど、今回は前者を使ってみた。ただ、Mac上で動かしたときは、Cairo::Context を毎回破棄しないとおかしくなるらしいので、一応 .destroy を呼んで破棄するようにしておいた。 *1
最初、DrawingArea をどこに登録すればいいのか分からなかったけど、どうやら gtk2 のウインドウに登録してやればいいようだなと…。
$drawing_area = @darea = Gtk::DrawingArea.new @bgcol = Gdk::Color.new(0x2000, 0x4000, 0xa000) @darea.modify_bg(:normal, @bgcol) @darea.set_size_request(width, height) ... @fixed = Gtk::Fixed.new @fixed.put(@darea, 0, 0) add(@fixed)
Gtk::Fixed は、座標指定で配置するためのWidgetらしい。
◎ 懸念事項。 :
Debian Linux において、ruby-gtk2 パッケージは、Debian 11 bullseye の時点までは公式に用意されている。故に、Debian系である Ubuntu Linux 20.04 LTS でもインストールして利用することができたのだけど。この ruby-gtk2 パッケージは、将来的にどうなるかちょっと分からないなと…。
_Debian -- bullseye の ruby-gtk2 パッケージに関する詳細
上記ページを見ると、Debian 9 stretch、Debian 10 buster、Debian 11 bullseye の時点までは公式パッケージとして ruby-gtk2 が用意されていた。ただ、おそらくは Debian 12 になるのであろう bookworm にはパッケージが用意されてないっぽい。sid には用意されているみたいだけど…。
例えば、ここで python3-pygame パッケージ情報を眺めると、こちらは Debian 12 bookworm 用パッケージも用意されているように見える。ruby-gtk2 とは扱いが違う。
_Debian -- bookworm の python3-pygame パッケージに関する詳細
そんなわけで、ひょっとすると次期バージョンの Debian系では、ruby-gtk2 を使って xscreensaver用スクリーンセーバを作成するのは難しくなるのかもしれない。ruby-gtk2 パッケージが無くなっているかもしれないので…。
ruby-gtk3 で書き直せたら、しばらくは安心(?)なのかもしれない。自分は知識が無いのでちょっと無理だけど…。
_Debian -- bullseye の ruby-gtk2 パッケージに関する詳細
上記ページを見ると、Debian 9 stretch、Debian 10 buster、Debian 11 bullseye の時点までは公式パッケージとして ruby-gtk2 が用意されていた。ただ、おそらくは Debian 12 になるのであろう bookworm にはパッケージが用意されてないっぽい。sid には用意されているみたいだけど…。
例えば、ここで python3-pygame パッケージ情報を眺めると、こちらは Debian 12 bookworm 用パッケージも用意されているように見える。ruby-gtk2 とは扱いが違う。
_Debian -- bookworm の python3-pygame パッケージに関する詳細
そんなわけで、ひょっとすると次期バージョンの Debian系では、ruby-gtk2 を使って xscreensaver用スクリーンセーバを作成するのは難しくなるのかもしれない。ruby-gtk2 パッケージが無くなっているかもしれないので…。
ruby-gtk3 で書き直せたら、しばらくは安心(?)なのかもしれない。自分は知識が無いのでちょっと無理だけど…。
◎ 参考ページ。 :
_Ruby で GTK+2 グラフィック - Marginalia
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(1)- 基本,ウィンドウ
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(2)- 部品の配置
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(4)- 画像、アニメーション
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(8)- 描画
_noanoa 日々の日記 : Ruby/GTK3を今時のGlade, XML, Builder, CSSで書く8 - 描画
_Rubyでクリエィティブコーディング - Qiita
_Ruby GTK2入門
_[gtk2] "DrawingArea + Cairo" on macOS; crash reports emerge - Issue #1081 - ruby-gnome/ruby-gnome
_A Python screensaver for xscreensaver (Linux) | alvinalexander.com
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(1)- 基本,ウィンドウ
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(2)- 部品の配置
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(4)- 画像、アニメーション
_noanoa 日々の日記 : Ruby/GTK2,GTK3 プログラミング Tips(8)- 描画
_noanoa 日々の日記 : Ruby/GTK3を今時のGlade, XML, Builder, CSSで書く8 - 描画
_Rubyでクリエィティブコーディング - Qiita
_Ruby GTK2入門
_[gtk2] "DrawingArea + Cairo" on macOS; crash reports emerge - Issue #1081 - ruby-gnome/ruby-gnome
_A Python screensaver for xscreensaver (Linux) | alvinalexander.com
*1: でも、Mac なら JavaScript でスクリーンセーバが書けるという話も見かけたのだよな…。だったらフツーは JavaScript で書くよな…。
[ ツッコむ ]
#3 [xscreensaver][pygame] pygameでxscreensaver用スクリーンセーバを作れないものか
Python + pygame で、xscreensaver用スクリーンセーバを作れないものだろうか。ちょっと気になったので、少し調べてみた。
◎ pygameに求められる条件。 :
SDL1.x を使っていた頃の pygame は ―― おそらく pygame 1.9.x の頃は、環境変数 SDL_VIDEO_DRIVER と SDL_WINDOWID を利用することで、指定されたウインドウハンドルに対して描画することができていたらしい。
_pygameでスクリーンセーバを作りたいのだけど - mieki256's diary
そういうことができるなら、xscreensaver が指定してきたウインドウハンドルを使って描画することもできそうな気がする。
ただ、最近の pygame 2.x.x は SDL2 を使うようになったそうで、SDL1.x 時代に利用できたその手の技(?)が使えなくなっている模様。
つまり、「xscreensaver用スクリーンセーバを pygame を使って作りたい」なら、「pygame 1.9.x が動く環境が必要」ということになる。たぶん。
_pygameでスクリーンセーバを作りたいのだけど - mieki256's diary
そういうことができるなら、xscreensaver が指定してきたウインドウハンドルを使って描画することもできそうな気がする。
ただ、最近の pygame 2.x.x は SDL2 を使うようになったそうで、SDL1.x 時代に利用できたその手の技(?)が使えなくなっている模様。
つまり、「xscreensaver用スクリーンセーバを pygame を使って作りたい」なら、「pygame 1.9.x が動く環境が必要」ということになる。たぶん。
◎ OS側の事情。 :
それを踏まえて。Ubuntu Linux 20.04 LTS (focal) の場合、python-pygame (Python 2.7.x用) や python3-pygame (Python 3.x用) といったパッケージをインストールすることで pygame 1.9.6 が利用できる。
_Ubuntu - focal の python-pygame パッケージに関する詳細
_Ubuntu - focal の python3-pygame パッケージに関する詳細
であれば、Ubuntu 20.04 LTS 環境なら、pygame を使って xscreensaver用スクリーンセーバを作ることも不可能ではないのかもしれない。
しかし、Ubuntu 22.04 LTS (jammy) の場合、python3-pygame (Python 3.x用) でインストールされるのは、pygame 2.1.2 らしい…。
_Ubuntu - jammy の python3-pygame パッケージに関する詳細
ちなみに、Ubuntu 22.04 LTS (jammy) において、python-pygame (Python 2.7.x用) は削除された模様。Ubuntu は積極的に Python 2.7系を殺しにかかってきているディストリビューションなので、まあ、仕方ない。
ということで、pygame で xscreensaver用スクリーンセーバを作成できたとしても、Ubuntu 20.04 LTS なら動くけど、Ubuntu 22.04 LTS では動かない、ということになってしまいそうだなと。
_Ubuntu - focal の python-pygame パッケージに関する詳細
_Ubuntu - focal の python3-pygame パッケージに関する詳細
であれば、Ubuntu 20.04 LTS 環境なら、pygame を使って xscreensaver用スクリーンセーバを作ることも不可能ではないのかもしれない。
しかし、Ubuntu 22.04 LTS (jammy) の場合、python3-pygame (Python 3.x用) でインストールされるのは、pygame 2.1.2 らしい…。
_Ubuntu - jammy の python3-pygame パッケージに関する詳細
ちなみに、Ubuntu 22.04 LTS (jammy) において、python-pygame (Python 2.7.x用) は削除された模様。Ubuntu は積極的に Python 2.7系を殺しにかかってきているディストリビューションなので、まあ、仕方ない。
ということで、pygame で xscreensaver用スクリーンセーバを作成できたとしても、Ubuntu 20.04 LTS なら動くけど、Ubuntu 22.04 LTS では動かない、ということになってしまいそうだなと。
◎ Windowsは条件が緩い。 :
ちなみに、Windows と Linux の場合、このあたりちょっと事情が違う気がする。
Windowsの場合、ローカルで pygame 1.9.x をインストールして何かしらを作って、ソレを最終的に .exe に変換すれば、どのPCにソレを持って行っても、pygame 1.9.x を使って動かせる、ということができそうな気がする。
しかし、Linux の場合、OSにインストールされたSDL関連ライブラリを利用して pygame が動くはずで…。おそらく Linux上では、バージョンが事なる pygame を共存させて使い分けるのは少々難しいのではなかろうか。分からんけど。
そんなわけで、もしかすると以下のような状況かもしれないなと。
何か抜け道は無いものか…。例えば AppImage を利用したりはできないものかな…。
_Linux Mint Tips : SNAP、FLATPAK、APPIMAGE 違い比較 | 221B Baker Street
Windowsの場合、ローカルで pygame 1.9.x をインストールして何かしらを作って、ソレを最終的に .exe に変換すれば、どのPCにソレを持って行っても、pygame 1.9.x を使って動かせる、ということができそうな気がする。
しかし、Linux の場合、OSにインストールされたSDL関連ライブラリを利用して pygame が動くはずで…。おそらく Linux上では、バージョンが事なる pygame を共存させて使い分けるのは少々難しいのではなかろうか。分からんけど。
そんなわけで、もしかすると以下のような状況かもしれないなと。
- Windows用スクリーンセーバを pygame を使って作成するのは、まだどうにか可能なのかもしれない。
- Linux + xscreensaver用スクリーンセーバを pygame を使って作成するのは、OSのバージョンが古ければできるけど、最新バージョンのOSではちょっと難しいかもしれない。
何か抜け道は無いものか…。例えば AppImage を利用したりはできないものかな…。
_Linux Mint Tips : SNAP、FLATPAK、APPIMAGE 違い比較 | 221B Baker Street
[ ツッコむ ]
2022/08/05(金) [n年前の日記]
#1 [pygame][xscreensaver] pygameを使ってxscreensaver用スクリーンセーバを作成
Python + pygame を使って xscreensaver用のスクリーンセーバを作れないか試していた。
環境は、Ubuntu Linux 20.04 LTS + Python 2.7.18 + pygame 1.9.61 + xscreensaver 6.04 + xwininfo 1.1.5。
ちなみに、Ubuntu Linux は Windows10 x64 21H2 + VMware Player上で動かしてる。
実行結果は以下。
- xscreensaver : Linux や Mac で利用できる、スクリーンセーバの管理プログラム。
- Python : プログラミング言語。
- pygame : Python を使って2Dゲームを制作できるライブラリ。
環境は、Ubuntu Linux 20.04 LTS + Python 2.7.18 + pygame 1.9.61 + xscreensaver 6.04 + xwininfo 1.1.5。
ちなみに、Ubuntu Linux は Windows10 x64 21H2 + VMware Player上で動かしてる。
実行結果は以下。
◎ 制約。 :
pygame を使って xscreensaver用スクリーンセーバを作成する場合、現時点では制約があるっぽい。pygame は 1.x.x を使わないといけない。pygame 2.x.x では動作しない可能性が高い。
何故かと言うと…。
_pygameでxscreensaver用スクリーンセーバを作れないものか - mieki256's diary
_pygameでスクリーンセーバを作りたいのだけど - mieki256's diary
もう一つ。今回のスクリプトを動かすには xwininfo というツールが必要になる。Ubuntu Linux の場合、x11-utils というパッケージに入ってる。
※ 2022/08/06追記。Ubuntu Linux 22.04 LTS を VMware Player上でインストールして動作確認してみたところ、やはり Ubuntu 22.04 + pygame 2.1.2 では正常動作しなかった。
何故かと言うと…。
- pygame 1.x.x は SDL 1.x を利用して描画その他を行う。
- pygame 2.x.x は SDL 2.x を利用して描画その他を行う。
- SDL 1.x なら、環境変数 SDL_VIDEO_DRIVER と SDL_WINDOWID を設定することで、SDL の動作を制御することができる。
- SDL 2.x は、環境変数 SDL_VIDEO_DRIVER と SDL_WINDOWID を設定しても反映されない。らしい。確認してないけどそういう話を見かけた。
_pygameでxscreensaver用スクリーンセーバを作れないものか - mieki256's diary
_pygameでスクリーンセーバを作りたいのだけど - mieki256's diary
- Ubuntu Linux 20.04 LTS は、python*-pygame で pygame 1.9.6 がインストールされるので大丈夫。
- Ubuntu Linux 22.04 LTS は、python3-pygame で pygame 2.1.2 がインストールされるので、今回のスクリプトはおそらく動かない。
もう一つ。今回のスクリプトを動かすには xwininfo というツールが必要になる。Ubuntu Linux の場合、x11-utils というパッケージに入ってる。
※ 2022/08/06追記。Ubuntu Linux 22.04 LTS を VMware Player上でインストールして動作確認してみたところ、やはり Ubuntu 22.04 + pygame 2.1.2 では正常動作しなかった。
◎ 必要なパッケージのインストール。 :
Ubuntu Linux 20.04 LTS の場合、以下で pygame 1.9.6 をインストールできる。
xwininfo は、以下でインストールできる。
sudo apt install python-pygame sudo apt install python3-pygame
- python-pygame は、Python 2.7 用のパッケージ。
- python3-pygame は、Python 3.x 用のパッケージ。
xwininfo は、以下でインストールできる。
sudo apt install x11-utils
◎ ソース。 :
ソースは以下。処理内容は、ウインドウ内でいくつかのボールが跳ね回るだけのもの。
_xscrsavpygame.py
使用画像は以下。
_ball_64x64.png
まずは xscreensaver を経由させずに動作確認してみる。
xscreensaver を経由させて実行してみる。xscreensaver の設定ファイル、~/.xscreensaver を編集。
xscreensaverの設定画面を表示して、上記で追加した「PYGAMESAVER」を選ぶと、プレビュー画面の中でもボールが跳ね回ってくれた。「プレビュー」ボタンをクリックしたら、フルスクリーンでテスト表示された。
これで、C/C++ を使わずに Python + pygame 1.x.x を使っても、xscreensaver用のスクリーンセーバを書けそうだと分かった。
_xscrsavpygame.py
import os import sys import math import platform import random import subprocess import re IMG_FILE = "ball_64x64.png" FPS = 60 class Ball: def __init__(self, img, scrw, scrh, t): self.img = img self.img_w = img.get_width() self.img_h = img.get_height() self.scrw = scrw self.scrh = scrh self.x = random.randint(16, scrw - self.img_w - 16) self.y = random.randint(16, scrh - self.img_h - 16) ang = random.randint(0, 360) spd = float(scrw) / t self.dx = spd * math.cos(math.radians(ang)) self.dy = spd * math.sin(math.radians(ang)) def update(self): self.x += self.dx self.y += self.dy if self.x <= 0 or self.x >= self.scrw - self.img_w: self.dx *= -1 if self.y <= 0 or self.y >= self.scrh - self.img_h: self.dy *= -1 def draw(self, screen): screen.blit(self.img, (self.x, self.y)) def main(): if platform.system() == "Windows": os.environ["SDL_VIDEODRIVER"] = "windib" print("$SDL_VIDEODRIVER = %s" % os.environ["SDL_VIDEODRIVER"]) else: # Linux or Darwin # os.environ["SDL_VIDEODRIVER"] = "x11" pass if "XSCREENSAVER_WINDOW" in os.environ: # xscreensaver hwnd = os.environ["XSCREENSAVER_WINDOW"] os.environ["SDL_WINDOWID"] = str(int(hwnd, 16)) # print("$XSCREENSAVER_WINDOW = %s" % os.environ["XSCREENSAVER_WINDOW"]) # print("$SDL_WINDOWID = %s" % os.environ["SDL_WINDOWID"]) # get window size using xwininfo cmd = ["xwininfo", "-id", os.environ["XSCREENSAVER_WINDOW"]] p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) ret = str(p.communicate()) size_info = re.search('Width:\s+(\d+)[^H]+Height:\s+(\d+)', ret) w, h = size_info.groups() scrw = int(w) scrh = int(h) else: # not xscreensaver hwnd = None scrw, scrh = 512, 256 import pygame # pygame.init() pygame.display.init() # print("driver = %s" % pygame.display.get_driver()) # print("pygame.display.get_wm_info()") # print(pygame.display.get_wm_info()) # print("pygame.display.Info()") # print(pygame.display.Info()) screen = pygame.display.set_mode((scrw, scrh)) # pygame.display.set_caption('xscreensaver by pygame') # load image base_dir = os.path.realpath(os.path.dirname(__file__)) file_path = os.path.join(base_dir, IMG_FILE) img = pygame.image.load(file_path).convert() img.set_colorkey(img.get_at((0, 0)), pygame.RLEACCEL) # create balls balls = [] for i in range(16): balls.append(Ball(img, scrw, scrh, 60.0 * 3.0)) clock = pygame.time.Clock() looping = True while looping: # main loop # event check for event in pygame.event.get(): if event.type == pygame.QUIT: looping = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE or event.key == pygame.K_q: looping = False if not looping: break # move balls for obj in balls: obj.update() # fill bg screen.fill((0, 0, 0)) # draw balls for obj in balls: obj.draw(screen) pygame.display.flip() clock.tick(FPS) pygame.quit() sys.exit() if __name__ == '__main__': main()
使用画像は以下。
_ball_64x64.png

まずは xscreensaver を経由させずに動作確認してみる。
- xscrsavpygame.py と ball_64x64.png を任意の場所に置く。
- chmod +x xscrsavpygame.py で実行権限をつける。
- ./xscrsavpygame.py と打てば、pygameのウインドウが開いてボールが跳ね回るはず。
xscreensaver を経由させて実行してみる。xscreensaver の設定ファイル、~/.xscreensaver を編集。
programs: \ "PYGAMESAVER" \ /home/USERNAME/hoge/fuga/xscrsavpygame.py \n\ maze -root \n\
- TAB幅は8文字、TAB文字有効で編集を行う。
- 「programs:」以下に、追加するスクリーンセーバの名前と、Pythonスクリプトのパスを記述。
- 行の最後が「\」なら継続行、「\n\」ならスクリーンセーバ1つ分の記述が終わったことを示す、のだと思う。
xscreensaverの設定画面を表示して、上記で追加した「PYGAMESAVER」を選ぶと、プレビュー画面の中でもボールが跳ね回ってくれた。「プレビュー」ボタンをクリックしたら、フルスクリーンでテスト表示された。
これで、C/C++ を使わずに Python + pygame 1.x.x を使っても、xscreensaver用のスクリーンセーバを書けそうだと分かった。
◎ 環境変数 XSCREENSAVER_WINDOW について。 :
以下、スクリプトソースについて少々解説。
環境変数 XSCREENSAVER_WINDOW 関係は、ruby-gtk2 で作ったスクリーンセーバの解説が参考になるかと。
_ruby-gtk2を使ってxscreensaver用スクリーンセーバを作る
環境変数 XSCREENSAVER_WINDOW が存在するかどうかは、以下の記述で調べられる。
環境変数 XSCREENSAVER_WINDOW 関係は、ruby-gtk2 で作ったスクリーンセーバの解説が参考になるかと。
_ruby-gtk2を使ってxscreensaver用スクリーンセーバを作る
環境変数 XSCREENSAVER_WINDOW が存在するかどうかは、以下の記述で調べられる。
if "XSCREENSAVER_WINDOW" in os.environ: # xscreensaver hwnd = os.environ["XSCREENSAVER_WINDOW"] else: # not xscreensaver pass
◎ 環境変数 SDL_WINDOWID について。 :
pygame 1.x.x は、環境変数 SDL_WINDOWID にウインドウハンドルを設定してある状態で pygame.display.init() もしくは pygame.init() を呼ぶと、SDL_WINDOWID で指定されたウインドウで pygame のウインドウ(SDL 1.x のウインドウ)を作成してくれる。
また、環境変数 SDL_VIDEODRIVER に x11 や windib 等を設定すれば、どのビデオドライバーを使って描画するかを変更できる。まあ、pygame 1.x.x の場合、Linux は x11、Windows は windib がデフォルトで利用されるようではあるけれど…。
ただし、注意点がある。環境変数 SDL_WINDOWID や SDL_VIDEODRIVER の設定は、import pygame を呼ぶ前にしておかないといけない。import pygame を呼んでから、それらの環境変数を変更しても反映されない、らしい。試してないけど。
また、環境変数 SDL_VIDEODRIVER に x11 や windib 等を設定すれば、どのビデオドライバーを使って描画するかを変更できる。まあ、pygame 1.x.x の場合、Linux は x11、Windows は windib がデフォルトで利用されるようではあるけれど…。
ただし、注意点がある。環境変数 SDL_WINDOWID や SDL_VIDEODRIVER の設定は、import pygame を呼ぶ前にしておかないといけない。import pygame を呼んでから、それらの環境変数を変更しても反映されない、らしい。試してないけど。
◎ ウインドウサイズの取得について。 :
指定されたウインドウハンドル(ウインドウID)のウインドウから、サイズ(横幅と縦幅)を取得したかったのだけど、Python で取得する方法が分からなくて、かなりハマった…。先日書いた条件に、もう一つ条件を追加しないといかんなと…。
ググってみた感じでは、pygame で環境変数 SDL_WINDOWID を使う事例として、「tkinter の Frame の中に pygame のウインドウを埋め込むサンプル」がほとんどだったのだけど。どのサンプルもウインドウサイズを事前に固定してるものばかり。どうやら Python のみを使ってウインドウサイズを取得するのは難しい模様。
Python でダメなら、他のツールではどうだろう。X11関連ツールの中にウインドウ情報を取得するツールがあったりしないか ―― と探してみたら、xwininfo というツールがあると知った。
_Ubuntu xwininfoコマンド その1 - ウィンドウの情報を調べる - kledgeb
このツールを Python から呼び出せば目的を果たせるのではないか。ググってみたら、そのものズバリ、Python から xwininfo を利用する事例が以下で紹介されていた。ありがたや。
_pythonで端末画面を保存する。 - Kinaconの技術ブログ
_pythonでウィンドウ座標を取得する - キラキラするのが筋トレです
そんなわけで、環境変数 XSCREENSAVER_WINDOW で指定されたウインドウのサイズを取得する記述は以下になる。
- 「環境変数 XSCREENSAVER_WINDOW があるかどうかを調べることができて」
- 「XSCREENSAVER_WINDOW で指定されたウインドウハンドルを使って描画ができて」
- 「XSCREENSAVER_WINDOW で指定されたウインドウのサイズを取得できる」 ← New!
ググってみた感じでは、pygame で環境変数 SDL_WINDOWID を使う事例として、「tkinter の Frame の中に pygame のウインドウを埋め込むサンプル」がほとんどだったのだけど。どのサンプルもウインドウサイズを事前に固定してるものばかり。どうやら Python のみを使ってウインドウサイズを取得するのは難しい模様。
Python でダメなら、他のツールではどうだろう。X11関連ツールの中にウインドウ情報を取得するツールがあったりしないか ―― と探してみたら、xwininfo というツールがあると知った。
_Ubuntu xwininfoコマンド その1 - ウィンドウの情報を調べる - kledgeb
このツールを Python から呼び出せば目的を果たせるのではないか。ググってみたら、そのものズバリ、Python から xwininfo を利用する事例が以下で紹介されていた。ありがたや。
_pythonで端末画面を保存する。 - Kinaconの技術ブログ
_pythonでウィンドウ座標を取得する - キラキラするのが筋トレです
そんなわけで、環境変数 XSCREENSAVER_WINDOW で指定されたウインドウのサイズを取得する記述は以下になる。
import subprocess import re # get window size using xwininfo cmd = ["xwininfo", "-id", os.environ["XSCREENSAVER_WINDOW"]] p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) ret = str(p.communicate()) size_info = re.search('Width:\s+(\d+)[^H]+Height:\s+(\d+)', ret) w, h = size_info.groups() scrw = int(w) scrh = int(h)
◎ 画像ファイル読み込みについて。 :
pygameスクリプトから画像を読み込むあたりでもハマった。スクリプトが置いてある場所をカレントディレクトリにした状態で動作確認している分には動くのだけど、別のカレントディレクトリから実行した際に「画像ファイルが読み込めない」と言われてしまった。
どうやら、スクリプトの置いてあるディレクトリを取得して、それを利用して画像のファイルパスを求めないといかんようだなと…。記述は以下。
_Pythonで実行中のファイルの場所(パス)を取得する__file__ | note.nkmk.me
_Python スクリプトが格納されているディレクトリのパスを取得する - まくまくPythonノート
どうやら、スクリプトの置いてあるディレクトリを取得して、それを利用して画像のファイルパスを求めないといかんようだなと…。記述は以下。
import os import pygame # load image base_dir = os.path.realpath(os.path.dirname(__file__)) file_path = os.path.join(base_dir, "hoge.png") img = pygame.image.load(file_path).convert() img.set_colorkey(img.get_at((0, 0)), pygame.RLEACCEL)
- __file__ にスクリプトのファイルパスが入っているから、それを利用してディレクトリを求める。
- os.path.join() で、ディレクトリと画像ファイル名を結合する。
_Pythonで実行中のファイルの場所(パス)を取得する__file__ | note.nkmk.me
_Python スクリプトが格納されているディレクトリのパスを取得する - まくまくPythonノート
◎ 余談。 :
pygame 2.x.x (Ubuntu Linux 22.04 LTS) では本当に動かないのかどうかがちょっと気になる。Ubuntu 22.04 LTS をどこかにインストールして実験してみないとダメかな…。
[ ツッコむ ]
2022/08/06(土) [n年前の日記]
#1 [xscreensaver][ubuntu][pygame] Ubuntu Linux 22.04 LTSを試用
Windows10 x64 21H2 + VMware Player上で、Ubuntu Linux 22.04 LTS をインストールして少し触ってみた。
先日作成した、Ruby + ruby-gtk2 を使った xscreensaver用スクリーンセーバ(の雛形サンプル)は動いてくれた。Ubuntu 22.04 にも ruby-gtk2 パッケージは存在していて、sudo apt install ruby-gtk2 でインストールできた。
_ruby-gtk2を使ってxscreensaver用スクリーンセーバを作る
Python + pygame 2.1.2 を使った xscreensaver用スクリーンセーバ(の雛形サンプル)は、予想通り動かなかった。やはり pygame 2.x.x (SDL2) は、環境変数 SDL_WINDOWID にウインドウハンドルを設定しても反映してくれないらしい。
_pygameを使ってxscreensaver用スクリーンセーバを作成
先日作成した、Ruby + ruby-gtk2 を使った xscreensaver用スクリーンセーバ(の雛形サンプル)は動いてくれた。Ubuntu 22.04 にも ruby-gtk2 パッケージは存在していて、sudo apt install ruby-gtk2 でインストールできた。
_ruby-gtk2を使ってxscreensaver用スクリーンセーバを作る
Python + pygame 2.1.2 を使った xscreensaver用スクリーンセーバ(の雛形サンプル)は、予想通り動かなかった。やはり pygame 2.x.x (SDL2) は、環境変数 SDL_WINDOWID にウインドウハンドルを設定しても反映してくれないらしい。
_pygameを使ってxscreensaver用スクリーンセーバを作成
◎ pygame 1.9.6を動かしたい。 :
Ubuntu 22.04 LTS上で pygame 1.9.6 を動かす方法はないものかと少し考えてみたけれど名案は思い付かず。一般的には sudo apt install python3-pygame で pygame をインストールするはずだから、Ubuntu 22.04 では pygame 2.1.2 が動くものと考えておかないといかんわけで…。
おそらくビルド等すれば pygame 1.9.6 も使えるのかもしれないけれど、pygame を使った他のスクリプトにも影響を与えるだろうから、そういうことはやらないほうがいいのだろうな、と…。
動かすだけなら pipenv 等を使って仮想環境を作成して、というのはどうだろうか。そのあたり、ちょっと勉強してみよう…。
おそらくビルド等すれば pygame 1.9.6 も使えるのかもしれないけれど、pygame を使った他のスクリプトにも影響を与えるだろうから、そういうことはやらないほうがいいのだろうな、と…。
動かすだけなら pipenv 等を使って仮想環境を作成して、というのはどうだろうか。そのあたり、ちょっと勉強してみよう…。
[ ツッコむ ]
#2 [pygame] tkinterの中にpygameを埋め込む
Windows10 x64 21H2 + Python 2.7.18 + pygame 1.9.6 を使って、tkinter のウインドウの中に pygame のウインドウを埋め込めそうか実験してみた。
実行結果は以下。tkinter のウインドウの中に、pygame のウインドウ(青い部分)が表示されて、赤いボールが跳ね回ってる。ボタンをクリックするとボールの座標が初期化される。
- tkinter : Python から Tk GUI ツールキットを制御してGUIアプリを作れるライブラリ。
- pygame : Python を使って2Dゲームを制作できるライブラリ。SDL というマルチメディア用ライブラリを利用している。
実行結果は以下。tkinter のウインドウの中に、pygame のウインドウ(青い部分)が表示されて、赤いボールが跳ね回ってる。ボタンをクリックするとボールの座標が初期化される。
◎ 必要なモジュールのインストール。 :
Windows10 x64 21H2 + Python 2.7.18 上で、pip install pygame と打って pygame をインストールしようとすると、現時点では pygame 2.0.3 がインストールされてしまう。
*1
しかし、pygame 2.x.x は、環境変数 SDL_WINDOWID を使った制御ができないので、今回はそのあたりの制御ができる pygame 1.9.6 をインストールしないといけない。
以下は、pygame をアンインストールしてから、pygame 1.9.6 をインストールする例。
pygame のバージョン確認は以下。
_コマンドラインからpygameバージョンを調べる - Qiita
それとは別に。これは Windows限定の話だけど、今回、win32gui というモジュールを利用してウインドウのサイズ(横幅と縦幅)を取得してみた。win32gui は pywin32 というモジュールの中に入っているらしいので、pywin32 をインストールしないといけない。
pywin32 228 がインストールされた。
しかし、pygame 2.x.x は、環境変数 SDL_WINDOWID を使った制御ができないので、今回はそのあたりの制御ができる pygame 1.9.6 をインストールしないといけない。
以下は、pygame をアンインストールしてから、pygame 1.9.6 をインストールする例。
pip uninstall pygame pip install pygame==1.9.6
pygame のバージョン確認は以下。
> python -c "import pygame; print(pygame.version.vernum);" pygame 1.9.6 Hello from the pygame community. https://www.pygame.org/contribute.html 1.9.6
_コマンドラインからpygameバージョンを調べる - Qiita
それとは別に。これは Windows限定の話だけど、今回、win32gui というモジュールを利用してウインドウのサイズ(横幅と縦幅)を取得してみた。win32gui は pywin32 というモジュールの中に入っているらしいので、pywin32 をインストールしないといけない。
pip install pywin32
> pip install pywin32 ... Collecting pywin32 Using cached pywin32-228-cp27-cp27m-win32.whl (6.9 MB) Installing collected packages: pywin32 Successfully installed pywin32-228
pywin32 228 がインストールされた。
◎ ソース。 :
ソースは以下。処理内容としては、tkinter のウインドウの中に pygame のウインドウが埋め込まれて、赤いボールが跳ね回るだけ。
_01_embed_pygamewindow.py
python 01_embed_pygamewindow.py で実行できる。
終了は、ウインドウの右上の閉じるボタンをクリック。
_01_embed_pygamewindow.py
""" Windows10 x64 21H2 + Python 2.7.18 32bit + pygame 1.9.6 + tkinter """ import Tkinter import os import sys import platform w, h = 640, 360 x, y = (w / 2), (h / 2) dx = float(w) / (60 * 1.0) dy = float(h) / (60 * 1.5) def update_pygame_window(): global x, y, dx, dy, screen, root w = screen.get_width() h = screen.get_height() x += dx y += dy r = 24 if x <= r or x >= w - r: dx *= -1 if y <= r or y >= h - r: dy *= -1 # draw pygame window screen.fill(pygame.Color(20, 80, 160)) pygame.draw.circle(screen, pygame.Color(255, 0, 0), (int(x), int(y)), r) # Update the pygame display pygame.display.flip() root.after(16, update_pygame_window) def reset_pos(): global x, y, w, h x, y = (w / 2), (h / 2) # init tkinter widget root = Tkinter.Tk() root.title("Embed pygame window in tkinter") embed = Tkinter.Frame(root, width=w, height=h) embed.pack() btn = Tkinter.Button(root, text="Reset Position", command=reset_pos) btn.pack() root.update() root.update_idletasks() # set SDL environ hwnd = embed.winfo_id() os.environ['SDL_WINDOWID'] = str(hwnd) if platform.system() == "Windows": # Windows os.environ['SDL_VIDEODRIVER'] = 'windib' # get window size (Windows only) import win32gui rect = win32gui.GetWindowRect(hwnd) x0, y0, x1, y1 = rect[0], rect[1], rect[2], rect[3] frm_size = ((x1 - x0), (y1 - y0)) print("size : %d x %d" % frm_size) else: # Linux or Darwin os.environ['SDL_VIDEODRIVER'] = 'x11' # init pygame import pygame pygame.display.init() # get tkinter.widget size frm_w = embed.winfo_width() frm_h = embed.winfo_height() # print("frame size : %d x %d" % (frm_w, frm_h)) screen = pygame.display.set_mode((frm_w, frm_h)) # screen = pygame.display.set_mode((0, 0)) update_pygame_window() root.mainloop() # tkinter main loop pygame.quit() sys.exit()
python 01_embed_pygamewindow.py で実行できる。
終了は、ウインドウの右上の閉じるボタンをクリック。
◎ tkinter widget のウインドウハンドルを取得する。 :
pygame 1.x.x は、環境変数 SDL_WINDOWID にウインドウハンドル(ウインドウID)を指定してから、pygame.display.init() (または pygame.init()) を呼ぶことで、SDL_WINDOWID で指定されたウインドウを pygame のウインドウとして使ってくれる。ソレを利用して、tkinter のウインドウ内に pygame ウインドウを埋め込んでいる。
tkinter でウインドウハンドル(ウインドウID) を取得するには、.winfo_id() を使うらしい。
また、import pygame をする前に、環境変数 SDL_WINDOWID を設定しておかないといけない、という話も見かけたので、一応そのような流れにしておいた。
tkinter でウインドウハンドル(ウインドウID) を取得するには、.winfo_id() を使うらしい。
embed = Tkinter.Frame(root, width=w, height=h) embed.pack() # ... hwnd = embed.winfo_id() os.environ['SDL_WINDOWID'] = str(hwnd) # ... import pygame pygame.display.init()
また、import pygame をする前に、環境変数 SDL_WINDOWID を設定しておかないといけない、という話も見かけたので、一応そのような流れにしておいた。
◎ ウインドウサイズを取得する。 :
pygame ウインドウの描画用サーフェイスを取得するには、pygame.display.set_mode((横幅, 縦幅)) を呼ばないといけない。そのためには、埋め込み先の tkinter.Frame のサイズ(横幅と縦幅)が分かってないといけない。
tkinter を使ってる場合は、.winfo_width()、.winfo_height() を使えば、横幅、縦幅を取得できる模様。
ただし、事前に tkinter のウインドウが表示されて、レイアウト等が決まった状態になってないとサイズの取得はできないそうで。そのため、.winfo_width() 等を呼ぶ前に .update_idletasks() か .update() を一度呼んでおく必要がある。
ところで、.winfo_width() 等は tkinter の利用時しか使えない。tkinter を使っていない場合はどうやってウインドウサイズを取得すればいいのだろう…? 別の方法も調べてみた。
これは Windows 限定の話だけれど、pywin32 の中に入っている win32gui を使ってウインドウサイズを取得する方法もあると知った。win32gui を使えば、Windows上のどのウインドウでもサイズを取得できるはずなので、こちらのやり方のほうが色んな場面で利用できそうな気もする。もっとも、pywin32 をインストールする必要が出てくるけれど…。
win32gui.GetWindowRect(ウインドウID) を呼ぶと、左上 x 座標、左上 y 座標、右下 x 座標、右下 y 座標を返してくる。それらの値を使えば、横幅と縦幅は求められる。
tkinter を使ってる場合は、.winfo_width()、.winfo_height() を使えば、横幅、縦幅を取得できる模様。
root.update() root.update_idletasks() # ... frm_w = embed.winfo_width() frm_h = embed.winfo_height() print("frame size : %d x %d" % (frm_w, frm_h)) screen = pygame.display.set_mode((frm_w, frm_h))
ただし、事前に tkinter のウインドウが表示されて、レイアウト等が決まった状態になってないとサイズの取得はできないそうで。そのため、.winfo_width() 等を呼ぶ前に .update_idletasks() か .update() を一度呼んでおく必要がある。
ところで、.winfo_width() 等は tkinter の利用時しか使えない。tkinter を使っていない場合はどうやってウインドウサイズを取得すればいいのだろう…? 別の方法も調べてみた。
これは Windows 限定の話だけれど、pywin32 の中に入っている win32gui を使ってウインドウサイズを取得する方法もあると知った。win32gui を使えば、Windows上のどのウインドウでもサイズを取得できるはずなので、こちらのやり方のほうが色んな場面で利用できそうな気もする。もっとも、pywin32 をインストールする必要が出てくるけれど…。
import platform if platform.system() == "Windows": # Windows os.environ['SDL_VIDEODRIVER'] = 'windib' # get window size (Windows only) import win32gui rect = win32gui.GetWindowRect(hwnd) x0, y0, x1, y1 = rect[0], rect[1], rect[2], rect[3] frm_size = ((x1 - x0), (y1 - y0)) print("size : %d x %d" % frm_size) else: # Linux or Darwin os.environ['SDL_VIDEODRIVER'] = 'x11'
win32gui.GetWindowRect(ウインドウID) を呼ぶと、左上 x 座標、左上 y 座標、右下 x 座標、右下 y 座標を返してくる。それらの値を使えば、横幅と縦幅は求められる。
◎ tkinter の mainloop を呼びたい。 :
これは tkinter を使う時だけ意識しないといけない話だけれど。tkinter を使うなら、最後に .mainloop() を呼ぶことで、そこでずっとメインループを回してやらないといけない。らしい。もし、.mainloop() を呼ばずに、独自に while 等でメインループを回してしまうと、例えばウインドウの閉じるボタンをクリックした時にエラーが表示されてしまったりする。.mainloop() の中で tkinter の動作に必要なアレコレを行っているのだろうなと…。
しかし、tkinter 側の .mainloop() を回してしまうと、今度は pygame を利用した処理部分をメインループとして回せなくなる。
そんな時は、tkinter の .after() を使うといいらしい。
.after(ミリ秒, 関数名) で、指定したミリ秒が経過したら、指定した関数を呼んでくれる模様。つまり、その関数の最後で、また .after() を呼んでやれば、おおよそ一定周期で処理が行われる状態になるはず。まあ、処理内容によっては、そこで処理時間も変動してしまうだろうから、正確に一定周期で呼ばれるわけでもないだろうけど…。
何にせよ、この書き方をしたら、tkinter ウインドウの閉じるボタンをクリックしてもエラーが出ずに終了してくれる状態になった。
しかし、tkinter 側の .mainloop() を回してしまうと、今度は pygame を利用した処理部分をメインループとして回せなくなる。
そんな時は、tkinter の .after() を使うといいらしい。
def update_pygame_window(): global x, y, dx, dy, screen, root # ... # draw pygame window screen.fill(pygame.Color(20, 80, 160)) pygame.draw.circle(screen, pygame.Color(255, 0, 0), (int(x), int(y)), r) pygame.display.flip() root.after(16, update_pygame_window) # ... update_pygame_window() root.mainloop() # tkinter main loop pygame.quit() sys.exit()
.after(ミリ秒, 関数名) で、指定したミリ秒が経過したら、指定した関数を呼んでくれる模様。つまり、その関数の最後で、また .after() を呼んでやれば、おおよそ一定周期で処理が行われる状態になるはず。まあ、処理内容によっては、そこで処理時間も変動してしまうだろうから、正確に一定周期で呼ばれるわけでもないだろうけど…。
何にせよ、この書き方をしたら、tkinter ウインドウの閉じるボタンをクリックしてもエラーが出ずに終了してくれる状態になった。
◎ 参考ページ。 :
_python - tkinter and pygame do not want to work in one window - Stack Overflow
_python - Embedding a Pygame window into a Tkinter or WxPython frame - Stack Overflow
_python - Draw a circle in Pygame using Tkinter - Stack Overflow
_python - interrupting embedded pygame in tkinter skips KEYUP events and thinks the key is still pressed - Stack Overflow
_python - I'm embedding a pygame window into Tkinter, how do I manipulate the pygame window? - Stack Overflow
_user-interface - Tkinterでのpygame機能の使用 | TagsQA
_Python 3 - Tkinter メインウィンドウを表示する
_【Python tkinter】after()メソッド:関数を指定時間経過後(定期的)に実行する | OFFICE54
_python - tkinterを用いて動的にカウントを更新したい - スタック・オーバーフロー
_Get window position & size with python - Stack Overflow
_Python の win32gui を使ってアクティブウインドウの記録を取るスクリプトを作ってみた - Qiita
_僕のwin32gui(Python) - Qiita
_python - Embedding a Pygame window into a Tkinter or WxPython frame - Stack Overflow
_python - Draw a circle in Pygame using Tkinter - Stack Overflow
_python - interrupting embedded pygame in tkinter skips KEYUP events and thinks the key is still pressed - Stack Overflow
_python - I'm embedding a pygame window into Tkinter, how do I manipulate the pygame window? - Stack Overflow
_user-interface - Tkinterでのpygame機能の使用 | TagsQA
_Python 3 - Tkinter メインウィンドウを表示する
_【Python tkinter】after()メソッド:関数を指定時間経過後(定期的)に実行する | OFFICE54
_python - tkinterを用いて動的にカウントを更新したい - スタック・オーバーフロー
_Get window position & size with python - Stack Overflow
_Python の win32gui を使ってアクティブウインドウの記録を取るスクリプトを作ってみた - Qiita
_僕のwin32gui(Python) - Qiita
*1: ちなみに、Python 3.9 上で pygame をインストールしようとすると、pygame 2.1.2 がインストールされる。
[ ツッコむ ]
2022/08/07(日) [n年前の日記]
#1 [python][ubuntu] virtualenvでPythonの別バージョン環境を作成
Ubuntu Linux 22.04 LTS上で、Python + pygame 1.9.6 が利用できる状態にしたい。そこで、複数の Python 動作環境を作成できるらしい virtualenv を試用してみた。
ちなみに、Ubuntu 22.04 は仮想PC上で ―― Windows10 x64 21H2 + VMware Player 16.2.4 build-20089737 上で動かしてる。
一応、Ubuntu 22.04 の公式リポジトリに virtualenv (20.13.0+ds-2) というパッケージは存在するのだけど…。その版をインストールしても、何故かエラーが出て動かない。
動かないのでは意味が無いので、アンインストールしておいた。
どうやら pip3 でインストールした版ならそれらしく動くようだなと…。以下はユーザ権限でインストールする例。
アンインストールは以下。
管理者権限でインストールしなおした。
ユーザディレクトリ/.local/bin に各種コマンドがインストールされるみたいなので、~/.bashrc の最後のあたりでPATHを設定しておいた。
ちなみに、Ubuntu 22.04 は仮想PC上で ―― Windows10 x64 21H2 + VMware Player 16.2.4 build-20089737 上で動かしてる。
一応、Ubuntu 22.04 の公式リポジトリに virtualenv (20.13.0+ds-2) というパッケージは存在するのだけど…。その版をインストールしても、何故かエラーが出て動かない。
sudo apt install virtualenv
動かないのでは意味が無いので、アンインストールしておいた。
sudo apt purge virtualenv sudo apt autoremove
どうやら pip3 でインストールした版ならそれらしく動くようだなと…。以下はユーザ権限でインストールする例。
pip3 install virtualenv
$ virtualenv --version virtualenv 20.16.3 from /home/USERNAME/.local/lib/python3.10/site-packages/virtualenv/__init__.py
アンインストールは以下。
pip3 uninstall virtualenv
管理者権限でインストールしなおした。
sudo -H pip3 install virtualenv
$ virtualenv --version virtualenv 20.16.3 from /usr/local/lib/python3.10/dist-packages/virtualenv/__init__.py
ユーザディレクトリ/.local/bin に各種コマンドがインストールされるみたいなので、~/.bashrc の最後のあたりでPATHを設定しておいた。
vi ~/.bashrc
export PATH=$PATH:/home/USERNAME/.local/bin
◎ Python 2.7 の環境を作成。 :
Ubuntu 22.04 にインストールされている Python 2.7 をクローン(?)して、Python 2.7 の環境を作成してみる。
myenv27 というディレクトリを作成して、その中に環境を入れてみる。
環境を有効化。
プロンプトの最初に「(ディレクトリ名)」が表示されていたら、その環境で動いてる。Python のバージョンを確認。
環境を無効化。
myenv27 というディレクトリを作成して、その中に環境を入れてみる。
virtualenv -p python2.7 myenv27
環境を有効化。
source myenv27/bin/activate
プロンプトの最初に「(ディレクトリ名)」が表示されていたら、その環境で動いてる。Python のバージョンを確認。
(myenv27) ... $ python --version Python 2.7.18
環境を無効化。
(myenv27) ... $ deactivate
◎ Python 3.10 の環境を作成、有効化、無効化。 :
Ubuntu 22.04 にインストールされている Python 3.10.4 をクローンして Python 3.10 の環境を作成、有効化、無効化するなら以下。
$ virtualenv -p python3.10 myenv310 $ source myenv310/bin/activate (myenv310) ... $ python --version Python 3.10.4 (myenv310) ... $ deactivate
◎ システムに入ってないPythonバージョンで環境を作成。 :
virtualenv は、システムにインストール済みの Python をコピーして仮想環境を作る。システムにインストールされていないPythonバージョンを使いたい場合は、事前に別バージョンをインストールしておかないといけない。らしい。
_virtualenvで特定バージョンのPythonを指定する[Ubuntu]
_New Python Versions : “deadsnakes” team
Ubuntu 22.04 LTS の場合、Python 3.7、3.8、3.9 ならインストールできる。それより前のバージョンは用意されてない模様。
_virtualenvで特定バージョンのPythonを指定する[Ubuntu]
_New Python Versions : “deadsnakes” team
sudo add-apt-repository ppa:deadsnakes/ppa sudo apt update
Ubuntu 22.04 LTS の場合、Python 3.7、3.8、3.9 ならインストールできる。それより前のバージョンは用意されてない模様。
sudo apt install python3.7 python3.7-dev python3.7-tk python3.7-full sudo apt install python3.8 python3.8-dev python3.8-tk python3.8-full sudo apt install python3.9 python3.9-dev python3.9-tk python3.9-full
◎ pygame 1.9.6 を Python 3.8環境上でインストール。 :
Ubuntu 22.04 は、sudo apt install python3-pygame をすると、pygame 2.1.2 がインストールされてしまう。どうにかして pygame 1.9.6 を動かしてみたい。
以下で、pygame 1.9.x をビルドするために必要なパッケージ群が紹介されていた。インストールしておく。
_python - Trying to install pygame on ubuntu which gives error - Stack Overflow
pygame 1.9.6 は Python 2.7, 3.4 - 3.8 をサポートしてるので、今回は Python 3.8環境を作成してみる。
_pygame - PyPI
Python 3.8環境を有効化。
pygame 1.9.6 を pip を使ってインストールしてみる。
これで、Ubuntu 22.04上でも、Python 3.8 + pygame 1.9.6 がインストールできた。
昨日作成した、tkinter ウインドウ内に pygame ウインドウを埋め込むスクリプトを動かしてみた。Python 2.7 ではなく Python 3.8 上で動かすので、昨日のスクリプトの import 関連のあたりを少し修正してある。 *1
_01_embed_pygamewindow.py
Ubuntu 22.04 + Python 3.8.13 + pygame 1.9.6 上でも、このスクリプトが動いてくれた。ちゃんと pygame のウインドウが埋め込まれてる。pygame 2.x.x では埋め込まれないはずなので、たしかに pygame 1.9.6 が利用できている。
ただ、これはあくまで開発用の環境で動いてるだけなので…。フツーのアプリとして、「Python 3.8 + pygame 1.9.6 + Pythonスクリプト」を呼び出せるわけではなさそうだなと…。
以下で、pygame 1.9.x をビルドするために必要なパッケージ群が紹介されていた。インストールしておく。
_python - Trying to install pygame on ubuntu which gives error - Stack Overflow
sudo apt install libfreetype6-dev libjpeg-dev libportmidi-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsdl1.2-dev libsmpeg-dev libx11-dev libavformat-dev libswscale-dev
pygame 1.9.6 は Python 2.7, 3.4 - 3.8 をサポートしてるので、今回は Python 3.8環境を作成してみる。
_pygame - PyPI
sudo apt install python3.8 python3.8-dev python3.8-tk virtualenv -p python3.8 myenv38
Python 3.8環境を有効化。
source myenv38/bin/activate
(myenv38) ... $ python -V Python 3.8.13
pygame 1.9.6 を pip を使ってインストールしてみる。
pip install pygame==1.9.6
$ pip list | grep pygame pygame 1.9.6
$ python -c "import pygame; print(pygame.version.vernum);" pygame 1.9.6 Hello from the pygame community. https://www.pygame.org/contribute.html 1.9.6
これで、Ubuntu 22.04上でも、Python 3.8 + pygame 1.9.6 がインストールできた。
昨日作成した、tkinter ウインドウ内に pygame ウインドウを埋め込むスクリプトを動かしてみた。Python 2.7 ではなく Python 3.8 上で動かすので、昨日のスクリプトの import 関連のあたりを少し修正してある。 *1
_01_embed_pygamewindow.py
Ubuntu 22.04 + Python 3.8.13 + pygame 1.9.6 上でも、このスクリプトが動いてくれた。ちゃんと pygame のウインドウが埋め込まれてる。pygame 2.x.x では埋め込まれないはずなので、たしかに pygame 1.9.6 が利用できている。
ただ、これはあくまで開発用の環境で動いてるだけなので…。フツーのアプリとして、「Python 3.8 + pygame 1.9.6 + Pythonスクリプト」を呼び出せるわけではなさそうだなと…。
◎ virtualenvwrapperを導入。 :
virtualenvwrapper をインストールすると、環境の管理や切り替えが楽になるらしい。試用してみた。
_Virtualenvwrapperの導入 - Qiita
_virtualenvでpython環境を管理する - Qiita
インストール。
各環境を入れておくディレクトリ、~/.virtualenvs を作成しておく。
virtualenvwrapper.sh の置き場所を確認。
~/.bashrc の最後に記述を追加。
使い方は以下。
_Virtualenvwrapperの導入 - Qiita
_virtualenvでpython環境を管理する - Qiita
インストール。
sudo -H pip3 install virtualenv sudo -H pip3 install virtualenvwrapper
各環境を入れておくディレクトリ、~/.virtualenvs を作成しておく。
mkdir ~/.virtualenvs
virtualenvwrapper.sh の置き場所を確認。
$ which virtualenvwrapper.sh /usr/local/bin/virtualenvwrapper.sh
~/.bashrc の最後に記述を追加。
vi ~/.bashrc
if [ -f /usr/local/bin/virtualenvwrapper.sh ]; then export WORKON_HOME=$HOME/.virtualenvs source /usr/local/bin/virtualenvwrapper.sh fi
使い方は以下。
- mkvirtualenv --python=python3.8 env38 : 環境を作成
- deactivate : 環境を無効化
- workon : 環境を一覧表示
- workon env38 : 環境を有効化
- rmvirtualenv env38 : 環境を削除
◎ 参考ページ。 :
_virtualenvで特定バージョンのPythonを指定する[Ubuntu]
_【Python】Virtualenvを使った仮想環境の構築 | Hbk project
_virtualenvでpython環境を管理する - Qiita
_Virtualenv の使い方 - Python 開発環境 - Python 入門
_【Python】Virtualenvを使った仮想環境の構築 | Hbk project
_virtualenvでpython環境を管理する - Qiita
_Virtualenv の使い方 - Python 開発環境 - Python 入門
*1: Python 2.7 は Tkinter を import するが、Python 3.x は tkinter を import しないといけない。
[ ツッコむ ]
2022/08/08(月) [n年前の日記]
#1 [python] Windows10 + virtualenvでPython環境を作成
昨日、Ubuntu Linux 22.04 LTS 上で virtualenv を使って Python 3.8 環境を作成したのだけど。
_virtualenvでPythonの別バージョン環境を作成 - mieki256's diary
Windows10上でも同じことができるのか試してみた。
環境は、Windows10 x64 21H2 + Python 3.9.13 + Python 3.8.10。
最初は venv を使って環境を作ろうとしたのだけど、何故か pip が利用できない環境ができてしまったので、代わりに virtualenv ならどうだろうと試してみた次第。
DOS窓を開いて以下を打つ。
virtualenvのインストール。
環境作成 / 有効化 / 無効化。
ただ、環境を作成した直後は、pip が正常動作しなかった。一旦DOS窓を終了して、再度開いてから試したら、何故か pip が動作した…。 *1
pip -V と打ってみて、仮想環境ディレクトリ内のパスが表示されていることを確認。もし、違うパスが表示されていたら、正常に切り替えることができていない。
pip を使って、作業に必要なモジュールをインストールしていく。今回は、pygame 1.9.6、pywin32 (win32gui)、exe化ツールを使いたい。
必要なパッケージをインストールできた。
先日作成した、tkinterのウインドウ内に pygameウインドウを埋め込むスクリプト、01_embed_pygamewindow.py を使って動作確認。
_01_embed_pygamewindow.py
正常に動作した。このスクリプトは、pygame 1.x.x じゃないと動かないので、pygame 1.9.6 が使えていることが分かった。―― Python 3.9 には pygame 2.1.2 をインストールしてあるので、そちらが使われていたらこのスクリプトは正常動作しないはず。
_virtualenvでPythonの別バージョン環境を作成 - mieki256's diary
Windows10上でも同じことができるのか試してみた。
環境は、Windows10 x64 21H2 + Python 3.9.13 + Python 3.8.10。
最初は venv を使って環境を作ろうとしたのだけど、何故か pip が利用できない環境ができてしまったので、代わりに virtualenv ならどうだろうと試してみた次第。
DOS窓を開いて以下を打つ。
virtualenvのインストール。
pych 38-64 python -m pip install -U setuptools virtualenv virtualenvwrapper-win or pip install virtualenv virtualenvwrapper-win setuptools -U
> virtualenv --version virtualenv 20.16.3 from C:\Python\Python38-64\Lib\site-packages\virtualenv\__init__.py
- pych 38-64 : 自作のPython切り替えbatファイル。Python 3.8 x64 に切り替えてる。
環境作成 / 有効化 / 無効化。
virtualenv -p python3.8 env38 .\env38\Scripts\activate deactivate
- virtualenv -p python3.8 env38 : システムにインストール済みの Python 3.8 を使って環境を作成。作成する環境のディレクトリ名は env38 にしている。
- .\env38\Scripts\activate : 環境の有効化
- deactivate : 環境の無効化
ただ、環境を作成した直後は、pip が正常動作しなかった。一旦DOS窓を終了して、再度開いてから試したら、何故か pip が動作した…。 *1
pip -V と打ってみて、仮想環境ディレクトリ内のパスが表示されていることを確認。もし、違うパスが表示されていたら、正常に切り替えることができていない。
> .\env38\Scripts\activate > pip -V pip 22.2.2 from D:\...\venv_test\env38\lib\site-packages\pip (python 3.8)
pip を使って、作業に必要なモジュールをインストールしていく。今回は、pygame 1.9.6、pywin32 (win32gui)、exe化ツールを使いたい。
pip install pygame==1.9.6 pip install pywin32 pip install py2exe pip install pyinstaller
> pip list Package Version ------------------------- --------- altgraph 0.17.2 cachetools 5.2.0 future 0.18.2 pefile 2022.5.30 pip 22.2.2 py2exe 0.11.1.1 pygame 1.9.6 pyinstaller 5.3 pyinstaller-hooks-contrib 2022.8 pywin32 304 pywin32-ctypes 0.2.0 setuptools 63.4.1 wheel 0.37.1
必要なパッケージをインストールできた。
先日作成した、tkinterのウインドウ内に pygameウインドウを埋め込むスクリプト、01_embed_pygamewindow.py を使って動作確認。
_01_embed_pygamewindow.py
python 01_embed_pygamewindow.py
正常に動作した。このスクリプトは、pygame 1.x.x じゃないと動かないので、pygame 1.9.6 が使えていることが分かった。―― Python 3.9 には pygame 2.1.2 をインストールしてあるので、そちらが使われていたらこのスクリプトは正常動作しないはず。
◎ virtualenvwrapper-winを使う。 :
Linux上では virtualenv を使いやすくするためのツール、virtualenvwrapper が存在するけれど。Windowsに移植した virtualenvwrapper-win というツールが存在するらしい。試用してみた。
デフォルトでは、C:\Users\USERNAME\Envs\ 以下に各環境(ディレクトリ)が作成される。環境変数 WORKON_HOME を設定しておけば場所を変更できる。
問題無く動作したけれど…。ただ、自分が把握できてないフォルダ内で、色々なファイルが増えていくのはちょっと引っ掛かるので、virtualenv だけ使って作業したいなと思えてきた。virtualenvwrapper-win はアンインストールした。
pip install virtualenvwrapper-win -U
- mkvirtualenv <name> : 環境作成
- lsvirtualenv : 一覧表示
- rmvirtualenv <name> : 環境削除
- workon [<name>] : 環境を有効化
- deactivate : 環境を無効化
デフォルトでは、C:\Users\USERNAME\Envs\ 以下に各環境(ディレクトリ)が作成される。環境変数 WORKON_HOME を設定しておけば場所を変更できる。
問題無く動作したけれど…。ただ、自分が把握できてないフォルダ内で、色々なファイルが増えていくのはちょっと引っ掛かるので、virtualenv だけ使って作業したいなと思えてきた。virtualenvwrapper-win はアンインストールした。
pip uninstall virtualenvwrapper-win
◎ 参考ページ。 :
_virtualenv, virtualenv-wrapper のインストール(Windows 上)
_virtualenv を用いて Python バージョン3 環境の新規作成(Windows 上)
_virtualenvwrapper-win - PyPI
_windows環境でのvirutalenvwrapper - Qiita
_virtualenv を用いて Python バージョン3 環境の新規作成(Windows 上)
_virtualenvwrapper-win - PyPI
_windows環境でのvirutalenvwrapper - Qiita
◎ venvも試用してみた。 :
Python 3.3以降は、virtualenv と似た機能を持つ venv というツールが同梱されている。
Windows10 x64 21H2 + Python 3.9.13 + Python 3.8.10 の環境で、デフォルトでパスが通してある Python 3.9.13 から venv を使った場合は、問題無く pip が利用できた。
しかし、Python 3.8 + venv で環境を作ると、pip が見つからないと表示されてしまう…。
色々試してたら原因が分かった。自作の Pythonバージョン切替batファイル、pych.bat を使って Python 3.8 に切り替えるとおかしな動作になる。
py.exe 経由で venv を使えば、Python 3.9 と同様に pip を利用できる環境を作成できた。
Windows10 x64 21H2 + Python 3.9.13 + Python 3.8.10 の環境で、デフォルトでパスが通してある Python 3.9.13 から venv を使った場合は、問題無く pip が利用できた。
しかし、Python 3.8 + venv で環境を作ると、pip が見つからないと表示されてしまう…。
色々試してたら原因が分かった。自作の Pythonバージョン切替batファイル、pych.bat を使って Python 3.8 に切り替えるとおかしな動作になる。
py.exe 経由で venv を使えば、Python 3.9 と同様に pip を利用できる環境を作成できた。
py -3.8 -m venv myenv38 myenv38\Scripts\activate (myenv38) ... > python -V Python 3.8.10 (myenv38) ... > python -m pip -V pip 21.1.1 from D:\home\prg\python\_test_sample\pygame\venv_test\myenv38\lib\site-packages\pip (python 3.8) deactivate
- py -3.8 -m venv myenv38 : Python 3.8 を利用して、venv で myenv38 と名前で環境を作成。
- myenv38\Scripts\activate : 環境を有効化。
- python -V : Python のバージョンを表示。
- python -m pip -V : pip のバージョンと、pip が置いてある場所を表示。
- deactivate : 環境を無効化。
◎ 自作のPython切替bat。 :
一応、自作の、Python切替batファイルも置いておく。今回のように、時々問題が発生するけど…。
_pych.bat
Windows版のPythonなら py.exe がインストールされているだろうから、そちらを使ったほうがいいと思う。
_pych.bat
@echo off @rem 各Pythonは、C:\Python\PythonXX\ にインストールされてることが前提。 @rem XXの部分を指定して PATH を切り替えてる。 if "%1"=="39-64" goto SETPATH if "%1"=="38-64" goto SETPATH if "%1"=="27" goto SETPATH if "%1"=="26" goto SETPATH if "%1"=="25" goto SETPATH if "%1"=="24" goto SETPATH echo Pythonのパスを切替えます。 echo Usage: echo pych 39-64 (default) echo pych 38-64 echo pych 27 echo pych 26 echo pych 25 echo pych 24 goto END :SETPATH set PPATH=C:\Python\Python%1 goto SETPATHCOMMON :SETPATHCOMMON @rem set PATH=%PPATH%;%PPATH%\Scripts;%PPATH%\Lib\site-packages\PyQt4;%PATH% set PATH=%PPATH%;%PPATH%\Scripts;%PATH% set PYTHONPATH=%PPATH%\Lib\site-packages set PYTHON_ROOT=%PPATH% @rem ftype Python.CompiledFile=%PPATH%\python.exe "%%1" %%* @rem ftype python.file=%PPATH%\\python.exe "%%1" %%* @rem ftype Python.NoConFile=%PPATH%\pythonw.exe "%%1" %%* echo. echo Pythonのパスを %PPATH% に設定しました echo PYTHONPATH=%PYTHONPATH% echo PYTHON_ROOT=%PYTHON_ROOT% echo. :END
Windows版のPythonなら py.exe がインストールされているだろうから、そちらを使ったほうがいいと思う。
> py --list Installed Pythons found by py Launcher for Windows -3.9-64 * -3.8-64 -2.7-32 -2.6-32 -2.5-32 -2.4-32 > py -2.4 -V Python 2.4.4 > py -2.7 -V Python 2.7.18 > py -3.9 -V Python 3.9.13
*1: もしかすると、自前のPythonバージョン切り替えbatファイル、pych.bat の中で、環境変数 PYTHONPATH まで設定していたのがマズかったのかもしれない。DOS窓を終了して再度開けば PYTHONPATH が未設定の状態になるので、それで正常動作してくれたのかも。
[ ツッコむ ]
#2 [nitijyou] 茶の間のPCを自室に戻した
茶の間に置いてあった Windows PC を ―― 朱鼓という名前の、円筒状のPCケースに入れてあるPCを、自室に持ってきた。
以前、親父さんが骨折してあまり動けなくなった際、茶の間でソリティアを遊べるようにと設置したものの。親父さん自身がそのPCの存在をすっかり忘れて「おい。これは一体なんだ?」と言い出す始末。埃だらけになってたので、置いといてもしょうがないなと。
そもそも茶の間には、先日お袋さんが会社から持ち帰ってきたものも含めて、3台もノートPCが置いてあるわけで。PCを使いたくなったらノートPCを使ったほうがいいよなと…。
以前、親父さんが骨折してあまり動けなくなった際、茶の間でソリティアを遊べるようにと設置したものの。親父さん自身がそのPCの存在をすっかり忘れて「おい。これは一体なんだ?」と言い出す始末。埃だらけになってたので、置いといてもしょうがないなと。
そもそも茶の間には、先日お袋さんが会社から持ち帰ってきたものも含めて、3台もノートPCが置いてあるわけで。PCを使いたくなったらノートPCを使ったほうがいいよなと…。
◎ 無線LAN子機を変えた。 :
今までつけていた無線LAN子機、ELECOM WDC-300SU2SWH (RTL8192CU) が不調。とにかく切れる。ガンガン切断される。熱で壊れたのかな…。
_300Mbps USB無線小型LANアダプタ - WDC-300SU2SWH
今まで Ubuntu機で使っていた ELECOM WDC-150SU2MBK (RTL8188EU? RTL8188EUS?) と交換してみた。こちらなら全然切断されない。
_150Mbps USB無線超小型LANアダプタ - WDC-150SU2MBK
どうもウチの環境は、300MBpsで接続できると謳う製品群と相性が悪い…。IO-DATA WN-G300UA (RTL8192CU) も頻繁に切断されてしまうから、ずっと埃を被ってるし…。
_300Mbps USB無線小型LANアダプタ - WDC-300SU2SWH
今まで Ubuntu機で使っていた ELECOM WDC-150SU2MBK (RTL8188EU? RTL8188EUS?) と交換してみた。こちらなら全然切断されない。
_150Mbps USB無線超小型LANアダプタ - WDC-150SU2MBK
どうもウチの環境は、300MBpsで接続できると謳う製品群と相性が悪い…。IO-DATA WN-G300UA (RTL8192CU) も頻繁に切断されてしまうから、ずっと埃を被ってるし…。
◎ Windows Updateが大変。 :
Windows Update をしてみたら、めちゃくちゃ時間がかかった。
Windows10 x64 21H1 の状態でパッチを全部当ててから、21H2 にアップグレードしたけど、ダウンロードは比較的サクサク進むものの、そこから先が…。再起動を指示した直後の終了時、「Windowsの準備をしています」系のメッセージが、少なくとも1時間ずっと表示されたまま。
仕方ないので放置して寝てしまったけど、結局全部終わるまで、12時間ぐらいかかった気がする。AMD Athlon 5350 という非力なCPUのせいだろうか。ベースクロックは低いけど4コア4スレッドだし、CドライブはSSDなのだけどな…。でも、TDP 25W のCPUだから遅いのも仕方ないか…。
ちょっとググってみたら、Core 2 Duo E8400 より遅いCPUらしい。もっとも、Core 2 Duo E8400 は TDP 65W。内蔵GPUも無し。Athlon 5350 はGPUも内蔵しているのに TDP 25W だから、たしかに省電力ではある…。
Windows10 x64 21H1 の状態でパッチを全部当ててから、21H2 にアップグレードしたけど、ダウンロードは比較的サクサク進むものの、そこから先が…。再起動を指示した直後の終了時、「Windowsの準備をしています」系のメッセージが、少なくとも1時間ずっと表示されたまま。
仕方ないので放置して寝てしまったけど、結局全部終わるまで、12時間ぐらいかかった気がする。AMD Athlon 5350 という非力なCPUのせいだろうか。ベースクロックは低いけど4コア4スレッドだし、CドライブはSSDなのだけどな…。でも、TDP 25W のCPUだから遅いのも仕方ないか…。
ちょっとググってみたら、Core 2 Duo E8400 より遅いCPUらしい。もっとも、Core 2 Duo E8400 は TDP 65W。内蔵GPUも無し。Athlon 5350 はGPUも内蔵しているのに TDP 25W だから、たしかに省電力ではある…。
◎ ケースがベタベタする。 :
この朱鼓というPCケース、真っ赤な色のケースなのだけど。触るとベタベタする上に、指や床に赤い塗料がくっついてしまう状態になってしまった。高温多湿な日本の気候では酷いことになる材料を吹きつけたのだろうな…。見た目をよくしたいと思ったのか、触り心地をよくしたいと思ったのか分からんけど…。
ウチにある、他のアレコレも、いくつか似たような状態になっていて…。Gateway製ノートPCも表面がベタベタになったし、DELL製タブレットPCもベタベタだし、Microsoft製キーボードのパームレストのラバー部分もベタベタだし。どうしてこういう材料を使ってしまうのか。
この手の失敗した設計?が、一切どこにもフィードバックされないまま、今もどこかで、いずれべたべたになってしまう製品として作られ続けているあたりが悲しい。こういった製品を設計/製造してる地域は、一体どういう気候なのか…。
ウチにある、他のアレコレも、いくつか似たような状態になっていて…。Gateway製ノートPCも表面がベタベタになったし、DELL製タブレットPCもベタベタだし、Microsoft製キーボードのパームレストのラバー部分もベタベタだし。どうしてこういう材料を使ってしまうのか。
この手の失敗した設計?が、一切どこにもフィードバックされないまま、今もどこかで、いずれべたべたになってしまう製品として作られ続けているあたりが悲しい。こういった製品を設計/製造してる地域は、一体どういう気候なのか…。
[ ツッコむ ]
2022/08/09(火) [n年前の日記]
#1 [nitijyou] 自宅サーバ止めてました
雷が鳴ったので、16:40-18:30の間、自宅サーバを止めてました。申し訳ないです。
東のほうで結構落ちてたようでヒヤヒヤしました…。
東のほうで結構落ちてたようでヒヤヒヤしました…。
[ ツッコむ ]
#2 [ubuntu] Ubuntu 20.04 LTS上でr8169ドライバが使われてしまう問題
手元のサブPC、Core2Duo E8400機 + Ubuntu Linux 20.04 LTS の環境で、LAN? NIC? のドライバが間違ってロードされてることに今頃気づいた。
Core2Duo E8400機で使ってる M/B、GIGABYTE GA-G31M-ES2L は、LAN用のチップとして Realtek RTL8111C が載っている。
_GA-G31M-ES2L (rev. 1.x) スペック | マザーボード - GIGABYTE Japan
しかし、Ubuntu 20.04 上では r8169 というドライバが読み込まれていた。本来、RTL8111 なら、r8169 ではなく r8168 が読み込まれないといけない。
そんなわけで、r8168 が使われるように変更してみた。
まずは現状を確認。lsmod と打ってみる。r8169 が表示されたら、間違ったドライバが読み込まれている。
Ubuntu は、r8168 に変更するためのパッケージが公式リポジトリにちゃんと用意されていた。ありがたや。インストールする。
そのままだと r8169 が相変わらず読み込まれるっぽいので、r8169 をブラックリストに登録する。r8168-dkms パッケージをインストールした際に、r8169 をブラックリストに登録するためのファイルもインストールされているのだけど、該当行がコメントアウトされているので一部修正。
sudo reboot で再起動後、lsmod で r8168 が表示されることを確認。
Core2Duo E8400機で使ってる M/B、GIGABYTE GA-G31M-ES2L は、LAN用のチップとして Realtek RTL8111C が載っている。
_GA-G31M-ES2L (rev. 1.x) スペック | マザーボード - GIGABYTE Japan
> LAN 1. RTL 8111C chip (10/100/1000 Mbit)
しかし、Ubuntu 20.04 上では r8169 というドライバが読み込まれていた。本来、RTL8111 なら、r8169 ではなく r8168 が読み込まれないといけない。
そんなわけで、r8168 が使われるように変更してみた。
まずは現状を確認。lsmod と打ってみる。r8169 が表示されたら、間違ったドライバが読み込まれている。
Ubuntu は、r8168 に変更するためのパッケージが公式リポジトリにちゃんと用意されていた。ありがたや。インストールする。
sudo apt install r8168-dkms
そのままだと r8169 が相変わらず読み込まれるっぽいので、r8169 をブラックリストに登録する。r8168-dkms パッケージをインストールした際に、r8169 をブラックリストに登録するためのファイルもインストールされているのだけど、該当行がコメントアウトされているので一部修正。
sudo vi /etc/modprobe.d/r8168-dkms.conf
#blacklist r8169 ↓ blacklist r8169
sudo reboot で再起動後、lsmod で r8168 が表示されることを確認。
$ lsmod | grep r8 r8168 548864 0
◎ 参考ページ。 :
_networking - Ubuntu 20.04 Ethernet R8168 - Ask Ubuntu
_ubuntu 20.04 Realtek RTL8168 / r8168 ネットワーク ドライバ インストール - rokkonet
_RTL8111/8168/8411 のインストールに苦悩した話 | tarufulog
_Ubuntu 16.04 で ASUS H110T の Ethernet ポートが認識されない問題の対処方法
_ubuntu 20.04 Realtek RTL8168 / r8168 ネットワーク ドライバ インストール - rokkonet
_RTL8111/8168/8411 のインストールに苦悩した話 | tarufulog
_Ubuntu 16.04 で ASUS H110T の Ethernet ポートが認識されない問題の対処方法
◎ 余談。 :
lspci とか、sudo lshw -c network と打つと、もう少し情報が出てくるらしい。
$ lspci | grep Eth 03:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 02) $ lspci -v | grep r8 Kernel driver in use: r8168 Kernel modules: r8168 $ sudo lshw -c network *-network 詳細: イーサネット interface 製品: RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller ベンダー: Realtek Semiconductor Co., Ltd. 物理ID: 0 バス情報: pci@0000:03:00.0 論理名: enp3s0 バージョン: 02 シリアル: 00:1f:d0:cf:6c:96 サイズ: 1Gbit/s 容量: 1Gbit/s 幅: 64 bits クロック: 33MHz 性能: pm msi pciexpress msix vpd bus_master cap_list rom __________________ physical tp 10bt 10bt-fd 100bt 100bt-fd 1000bt-fd autonegotiation 設定: autonegotiation=on broadcast=yes driver=r8168 driverversion=8.048.00-NAPI duplex=full ip=192.168.1.14 latency=0 link=yes multicast=yes port=twisted pair speed=1Gbit/s リソース: irq:17 IOポート:be00(サイズ=256) メモリー:fdeff000-fdefffff メモリー:fdee0000-fdeeffff メモリー:fd900000-fd90ffff
◎ 余談その2。 :
このあたりの作業をしていて、ふと気づいた。ひょっとして、足元に置いてある、別のサブPC、A8-3850機も同じ状態なのではないか…?
A8-3850機のM/B は GIGABYTE GA-A75M-UD2H (rev. 1.0)。
_GA-A75M-UD2H (rev. 1.0) スペック | マザーボード - GIGABYTE Japan
これも RTL8111C が載ってると書いてある。後で r8169 がロードされてないかチェックしてみないと…。
※ 2022/08/10追記。A8-3850機、GIGABYTE GA-A75M-UD2H (rev. 1.0) も r8169 がロードされてしまっていた。r8168 に変更しておいた。
A8-3850機のM/B は GIGABYTE GA-A75M-UD2H (rev. 1.0)。
_GA-A75M-UD2H (rev. 1.0) スペック | マザーボード - GIGABYTE Japan
これも RTL8111C が載ってると書いてある。後で r8169 がロードされてないかチェックしてみないと…。
※ 2022/08/10追記。A8-3850機、GIGABYTE GA-A75M-UD2H (rev. 1.0) も r8169 がロードされてしまっていた。r8168 に変更しておいた。
[ ツッコむ ]
#3 [ubuntu][linux] ELECOM WDC-300SU2SWH を Ubuntu 20.04 で利用
USB接続無線LAN子機 ELECOM WDC-300SU2SWH を Core2Duo E8400 + Ubuntu Linux 20.04 LTS 機に繋いでみた。
_300Mbps USB無線小型LANアダプタ - ELECOM WDC-300SU2SWH
しかし、認識してくれない。
この WDC-300SU2SWH を、Ubuntu Linux 20.04 LTSで利用したい。ちなみに、末尾の WH や BK は色を表しているようなので、型番としては WDC-300SU2S でいいのだろうか。
ググったところ、WDC-300SU2S に載っているチップは Realtek RTL8192CU らしい。
_300Mbps USB無線小型LANアダプタ - ELECOM WDC-300SU2SWH
しかし、認識してくれない。
この WDC-300SU2SWH を、Ubuntu Linux 20.04 LTSで利用したい。ちなみに、末尾の WH や BK は色を表しているようなので、型番としては WDC-300SU2S でいいのだろうか。
ググったところ、WDC-300SU2S に載っているチップは Realtek RTL8192CU らしい。
◎ 情報を調べる。 :
lsusb と打てば、ベンダーID(メーカー名を示す)とプロダクトID(製品型番を示す)は出てくる。
_Ubuntu 20.04でUSBのWiFiアダプタ(WDC-300SU2SBK)を認識させる方法 - Runner in the High
WDC-300SU2S のベンダIDとプロダクトIDは、056e:4009 のようだなと…。このIDを持っている機器を、どのドライバで動かせばいいのか、その情報を Linux 側で持っていないので、この機器が利用できない状態なのだろう。
ちなみに、lsusb -s 1:2 -v と打てば、もう少し情報が出てくる。-s X:Y は、X が Busナンバー、Y が Deviceナンバー。
_【 lsusb 】コマンド――USBデバイスの一覧と詳細情報を表示する:Linux基本コマンドTips(273) - @IT
$ lsusb Bus 001 Device 002: ID 056e:4009 Elecom Co., Ltd WDC-300SU2S Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub ...
_Ubuntu 20.04でUSBのWiFiアダプタ(WDC-300SU2SBK)を認識させる方法 - Runner in the High
WDC-300SU2S のベンダIDとプロダクトIDは、056e:4009 のようだなと…。このIDを持っている機器を、どのドライバで動かせばいいのか、その情報を Linux 側で持っていないので、この機器が利用できない状態なのだろう。
ちなみに、lsusb -s 1:2 -v と打てば、もう少し情報が出てくる。-s X:Y は、X が Busナンバー、Y が Deviceナンバー。
_【 lsusb 】コマンド――USBデバイスの一覧と詳細情報を表示する:Linux基本コマンドTips(273) - @IT
$ lsusb --help Usage: lsusb [options]... List USB devices -v, --verbose Increase verbosity (show descriptors) -s [[bus]:][devnum] Show only devices with specified device and/or bus numbers (in decimal) -d vendor:[product] Show only devices with the specified vendor and product ID numbers (in hexadecimal) -D device Selects which device lsusb will examine -t, --tree Dump the physical USB device hierarchy as a tree -V, --version Show version of program -h, --help Show usage and help
$ lsusb -s 1:2 -v Bus 001 Device 002: ID 056e:4009 Elecom Co., Ltd WDC-300SU2S Couldn't open device, some information will be missing Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x056e Elecom Co., Ltd idProduct 0x4009 bcdDevice 2.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x002e bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 4 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 255 Vendor Specific Protocol iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x03 EP 3 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x84 EP 4 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1
◎ rtl8xxxuをロード。 :
WDC-300SU2S のベンダIDとプロダクトID、056e:4009 を、ドライバ rtl8192cu の関連ファイルに追加してやれば自動認識(?)されるのだろうけど。しかし、rtl8192cu のドライバはとにかく不安定という話を目にした記憶があり…。
なので、Ubuntu 16.04時代は、改善されているドライバを github から入手して、ビルドして導入するのが妥当だったらしいけど。
_pvaret/rtl8192cu-fixes: Realtek 8192 chipset driver, ported to kernel 3.11.
ただ、そのドライバを配布してる上記ページでは、
「このドライバはもうメンテナンスされてないよ」
「kernel 4.4 から rtl8xxxu というドライバが使えるからそっちを利用すべき」
「若干いくつか設定しないといけないけどね」
みたいなことが書いてある。
であれば、まずは rtl8xxxu を導入できないか試してみよう。以下を参考に作業。
_Ubuntu 20.04でUSBのWiFiアダプタ(WDC-300SU2SBK)を認識させる方法 - Runner in the High
_Kali Linuxで無線LANアダプタ LAN-W150N/U2BK を使う | tarufulog
_wireless - Compile and install rtl8192cu driver - Ask Ubuntu
まずは、rtl8192cu が使われないように、ブラックリストに入れておく。
/etc/modprobe.d/blacklist.conf の最後に、以下を追加。
rtl8xxxu ドライバをロード。
ベンダーIDとプロダクトIDを、new_id というファイルに追記する。
new_id なるファイルは無いと言われてしまった…。rtl8192cu だったら存在するファイルということだろうか。
なので、Ubuntu 16.04時代は、改善されているドライバを github から入手して、ビルドして導入するのが妥当だったらしいけど。
_pvaret/rtl8192cu-fixes: Realtek 8192 chipset driver, ported to kernel 3.11.
ただ、そのドライバを配布してる上記ページでは、
「このドライバはもうメンテナンスされてないよ」
「kernel 4.4 から rtl8xxxu というドライバが使えるからそっちを利用すべき」
「若干いくつか設定しないといけないけどね」
みたいなことが書いてある。
であれば、まずは rtl8xxxu を導入できないか試してみよう。以下を参考に作業。
_Ubuntu 20.04でUSBのWiFiアダプタ(WDC-300SU2SBK)を認識させる方法 - Runner in the High
_Kali Linuxで無線LANアダプタ LAN-W150N/U2BK を使う | tarufulog
_wireless - Compile and install rtl8192cu driver - Ask Ubuntu
まずは、rtl8192cu が使われないように、ブラックリストに入れておく。
sudo modprobe -r rtl8192cu sudo vi /etc/modprobe.d/blacklist.conf
/etc/modprobe.d/blacklist.conf の最後に、以下を追加。
blacklist rtl8192cu
rtl8xxxu ドライバをロード。
sudo modprobe rtl8xxxu
$ ls -al /sys/bus/usb/drivers/ 合計 0 drwxr-xr-x 6 root root 0 8月 9 2022 . drwxr-xr-x 4 root root 0 8月 9 2022 .. drwxr-xr-x 2 root root 0 8月 9 2022 hub drwxr-xr-x 2 root root 0 8月 9 11:12 rtl8xxxu drwxr-xr-x 2 root root 0 8月 9 2022 usb drwxr-xr-x 2 root root 0 8月 9 2022 usbfs「rtl8xxxu」というディレクトリが見える。ロードできたっぽい。
ベンダーIDとプロダクトIDを、new_id というファイルに追記する。
$ echo "056E 4009" | sudo tee /sys/bus/usb/drivers/rtl8xxxu/new_id tee: /sys/bus/usb/drivers/rtl8xxxu/new_id: そのようなファイルやディレクトリはありません 056E 4009
new_id なるファイルは無いと言われてしまった…。rtl8192cu だったら存在するファイルということだろうか。
◎ githubからドライバをDLしてインストール。 :
rtl8xxxu を使うのは一旦諦めて、以下のドライバをインストールしてみた。
_pvaret/rtl8192cu-fixes: Realtek 8192 chipset driver, ported to kernel 3.11.
再起動したけれど、反映されない。WDC-300SU2S はサポートしてない製品だから当たり前だろうか。
ドライバをアンインストール。
_pvaret/rtl8192cu-fixes: Realtek 8192 chipset driver, ported to kernel 3.11.
sudo apt-get update sudo apt-get install git linux-headers-generic build-essential dkms git clone https://github.com/pvaret/rtl8192cu-fixes.git sudo dkms add ./rtl8192cu-fixes sudo dkms install 8192cu/1.11 sudo depmod -a sudo cp ./rtl8192cu-fixes/blacklist-native-rtl8192.conf /etc/modprobe.d/
再起動したけれど、反映されない。WDC-300SU2S はサポートしてない製品だから当たり前だろうか。
ドライバをアンインストール。
sudo rm /etc/modprobe.d/blacklist-native-rtl8192.conf sudo dkms status sudo dkms remove -m 8192cu/1.11 --all
◎ rtl8192cuを使ってみる。 :
ダメ元で rtl8192cu を使ってみる。
/etc/modprobe.d/blacklist.conf を編集して、先ほど行った rtl8192cu のブラックリスト登録をコメントアウト(行頭に「#」をつける)。
rtl8192cu のロード。
rtl8192cu を使う分には、new_id というファイルがちゃんと存在している模様。
ベンダーIDとプロダクトIDを追記。
iwconfig を使って、無線LAN関係の状態を確認。
無線LANが使えるようになった。たしかに、rtl8192cu ドライバを使えば、ELECOM WDC-300SU2S は使えるっぽい。
ただ、この状態では再起度すると元に戻ってしまうそうで。
以下のやり取りでは、/etc/rc.local を利用して、OS起動時に、ベンダIDとプロダクトIDを new_id に書き込むことで対処する方法が紹介されている。
_wireless - Compile and install rtl8192cu driver - Ask Ubuntu
それと…。たぶん、やっぱり、rtl8192cu は不安定じゃないのかと…。どの情報を見ても、「rtl8192cu ドライバは不安定」「rtl8xxxu を使うべし」と書いてあるし…。
/etc/modprobe.d/blacklist.conf を編集して、先ほど行った rtl8192cu のブラックリスト登録をコメントアウト(行頭に「#」をつける)。
sudo vi /etc/modprobe.d/blacklist.conf
rtl8192cu のロード。
sudo modprobe rtl8192cu
$ ls -al /sys/bus/usb/drivers/rtl8192cu/ 合計 0 drwxr-xr-x 2 root root 0 8月 9 12:06 . drwxr-xr-x 6 root root 0 8月 9 2022 .. --w------- 1 root root 4096 8月 9 12:07 bind lrwxrwxrwx 1 root root 0 8月 9 12:07 module -> ../../../../module/rtl8192cu -rw-r--r-- 1 root root 4096 8月 9 12:07 new_id -rw-r--r-- 1 root root 4096 8月 9 12:07 remove_id --w------- 1 root root 4096 8月 9 12:06 uevent --w------- 1 root root 4096 8月 9 12:07 unbind
rtl8192cu を使う分には、new_id というファイルがちゃんと存在している模様。
ベンダーIDとプロダクトIDを追記。
echo "056E 4009" | sudo tee /sys/bus/usb/drivers/rtl8192cu/new_id
iwconfig を使って、無線LAN関係の状態を確認。
$ iwconfig enp3s0 no wireless extensions. lo no wireless extensions. wlxXXXXXXXXXXXX IEEE 802.11 ESSID:off/any Mode:Managed Access Point: Not-Associated Tx-Power=20 dBm Retry short limit:7 RTS thr=2347 B Fragment thr:off Power Management:off
無線LANが使えるようになった。たしかに、rtl8192cu ドライバを使えば、ELECOM WDC-300SU2S は使えるっぽい。
ただ、この状態では再起度すると元に戻ってしまうそうで。
以下のやり取りでは、/etc/rc.local を利用して、OS起動時に、ベンダIDとプロダクトIDを new_id に書き込むことで対処する方法が紹介されている。
_wireless - Compile and install rtl8192cu driver - Ask Ubuntu
それと…。たぶん、やっぱり、rtl8192cu は不安定じゃないのかと…。どの情報を見ても、「rtl8192cu ドライバは不安定」「rtl8xxxu を使うべし」と書いてあるし…。
◎ rtl8xxxu と new_id。 :
rtl8xxxu で new_id というファイルに書き込んで対応製品を増やすのは難しいっぽい。driver_info というのを見て処理してるらしいけど、カーネルモジュールのソースを修正してビルドする、という話になっていくようで…。
_rtl8xxxu 4.4.5(from f23): I get a panic adding a new device to the driver - Patchwork
_jetson nanoでWiFiドングルElecom WDC-433DU2HBK を使う
_Raspberry PiでWDC-433DU2H2-Bを動かす - Qiita
_rtl8xxxu 4.4.5(from f23): I get a panic adding a new device to the driver - Patchwork
_jetson nanoでWiFiドングルElecom WDC-433DU2HBK を使う
_Raspberry PiでWDC-433DU2H2-Bを動かす - Qiita
◎ 再度gtihubのドライバに挑戦。 :
再度、github版のドライバを使ってみることにする。ソースを編集して、WDC-300SU2S に対応させることができるんじゃないのかなと閃いたので…。
_pvaret/rtl8192cu-fixes: Realtek 8192 chipset driver, ported to kernel 3.11.
rtl8192cu-fixes/os_dep/linux/usb_intf.c を編集して、WDC-300SU2S のベンダIDとプロダクトIDを追加してみる。
_rtl8192cu-fixes/usb_intf.c at master - pvaret/rtl8192cu-fixes
93行目あたりに記述を追加。行頭に「+」がついている行がソレ。「+」は省く。
github の readme.md に従って作業を続行。
OSを再起動したら、WDC-300SU2S を自動認識してくれるようになった。
wlx* というのが増えている。これが無線LAN子機経由の接続。
_ワイヤレス設定 - ArchWiki
ちなみに、iw というツールも状態確認に使えるっぽい。
使えるようになったものの、安定性は…これはまだちょっとよく分からない。
ちなみに、WDC-300SU2S は USB2.0ポートに繋ぐと不安定になって、USB3.0ポートに繋ぐと安定するという話も見かけたけれど。今回実験に使った M/B、GIGABYTE GA-G31M-ES2L (rev. 1.x) にはUSB2.0ポートしかないので、USB2.0ポートに繋ぐしかなく…。でもまあ、それでも一応動いてるように見える。
_pvaret/rtl8192cu-fixes: Realtek 8192 chipset driver, ported to kernel 3.11.
sudo apt-get update sudo apt-get install git linux-headers-generic build-essential dkms git clone https://github.com/pvaret/rtl8192cu-fixes.git
rtl8192cu-fixes/os_dep/linux/usb_intf.c を編集して、WDC-300SU2S のベンダIDとプロダクトIDを追加してみる。
_rtl8192cu-fixes/usb_intf.c at master - pvaret/rtl8192cu-fixes
93行目あたりに記述を追加。行頭に「+」がついている行がソレ。「+」は省く。
$ diff -u rtl8192cu-fixes/os_dep/linux/usb_intf.c.orig rtl8192cu-fixes/os_dep/linux/usb_intf.c --- rtl8192cu-fixes/os_dep/linux/usb_intf.c.orig 2022-08-09 21:11:39.962416889 +0900 +++ rtl8192cu-fixes/os_dep/linux/usb_intf.c 2022-08-09 21:12:58.022374097 +0900 @@ -91,6 +91,7 @@ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817C)},/* 8192CE-VAU USB minCard */ \ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8191)},/* 8192CU 2*2 */ \ {USB_DEVICE(0x1058, 0x0631)},/* Alpha, 8192CU */ \ + {USB_DEVICE(0x056E, 0x4009)},/* ELECOM WDC-300SU2S */ \ /*=== Customer ID ===*/ \ /****** 8188CUS Dongle ********/ \ {USB_DEVICE(0x2019, 0xED17)},/* PCI - Edimax */ \
github の readme.md に従って作業を続行。
sudo dkms add ./rtl8192cu-fixes sudo dkms install 8192cu/1.11 sudo depmod -a sudo cp ./rtl8192cu-fixes/blacklist-native-rtl8192.conf /etc/modprobe.d/ sudo cp ./rtl8192cu-fixes/8192cu-disable-power-management.conf /etc/modprobe.d/ sudo reboot
OSを再起動したら、WDC-300SU2S を自動認識してくれるようになった。
$ lsmod | grep 81 snd_hda_codec_generic 81920 1 snd_hda_codec_realtek 8192cu 573440 0 cfg80211 708608 1 8192cu r8168 548864 0
$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp3s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000 link/ether 00:1f:d0:cf:6c:96 brd ff:ff:ff:ff:ff:ff 3: wlxXXXXXXXXXXXX: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000 link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
wlx* というのが増えている。これが無線LAN子機経由の接続。
_ワイヤレス設定 - ArchWiki
ちなみに、iw というツールも状態確認に使えるっぽい。
$ iw dev phy#0 Interface wlxXXXXXXXXXXXX ifindex 3 wdev 0x1 addr xx:xx:xx:xx:xx:xx ssid hoge-router type managed txpower 12.00 dBm
$ iw dev wlxXXXXXXXXXXXX link Connected to yy:yy:yy:yy:yy:yy (on wlxXXXXXXXXXXXX) SSID: hoge-router freq: 2457 signal: -45 dBm tx bitrate: 144.4 MBit/s
使えるようになったものの、安定性は…これはまだちょっとよく分からない。
ちなみに、WDC-300SU2S は USB2.0ポートに繋ぐと不安定になって、USB3.0ポートに繋ぐと安定するという話も見かけたけれど。今回実験に使った M/B、GIGABYTE GA-G31M-ES2L (rev. 1.x) にはUSB2.0ポートしかないので、USB2.0ポートに繋ぐしかなく…。でもまあ、それでも一応動いてるように見える。
◎ 速度測定。 :
速度の測定には、iperf が使える。
Ubuntu Linux側で iperf -s を実行。
別PC(Windows10機)で、iperf -c IPアドレス、を打てば速度が出てくる。
50Mbps は出てるっぽい。遅いような気もするけれど、IEEE802.11b/g/n対応製品ならこんなものかも…。
sudo apt install iperf iperf3パッケージ名 iperf が 2.x.x系。iperf3 が 3.x.x系。
$ iperf --version iperf version 2.0.13 (21 Jan 2019) pthreads
Ubuntu Linux側で iperf -s を実行。
iperf -siperf -s の終了は Ctrl + C。
別PC(Windows10機)で、iperf -c IPアドレス、を打てば速度が出てくる。
> iperf -c 192.168.1.6 ------------------------------------------------------------ Client connecting to 192.168.1.6, TCP port 5001 TCP window size: 64.0 KByte (default) ------------------------------------------------------------ [452] local 192.168.1.9 port 52252 connected with 192.168.1.6 port 5001 [ ID] Interval Transfer Bandwidth [452] 0.0-10.0 sec 61.8 MBytes 51.8 Mbits/sec
50Mbps は出てるっぽい。遅いような気もするけれど、IEEE802.11b/g/n対応製品ならこんなものかも…。
[ ツッコむ ]
2022/08/10(水) [n年前の日記]
#1 [python][windows] Pythonスクリプトをexe化したい
Windows10 x64 21H2 + Python 3.8.10 x64 上で、Pythonスクリプトをexeファイルにしたい。pygame を使ってWindows用のスクリーンセーバを作ってみたいのだけど、そのためには Pythonスクリプトファイルをexe化しないといけないので…。
今回は virtualenv を使って、Python 3.8 が動作する環境を作った。と言うのも、各exe化ツールは動作に不要なファイルまで含めてexe化してしまう時があるそうで。故に、virtualenv や venv 等を使って、動作に必要な最低限のモジュールだけがインストールされている環境を用意して、ソレをexe化するのが望ましいのだとか。
Pythonスクリプトをexe化するツールは色々あるけれど、今回は PyInstaller と py2exe を試用してみた。
virtualenv で環境を作った場合、PyInstaller、py2exe も、その環境にインストールして、その環境内で実行しないといけない。そうしないと、システム側にインストールした PyInstaller や py2exe を使ってしまって、動作に不要なモジュールまで入ってしまう。
ちなみに、昔の py2exe は Python 3.4 までしか対応してなかったらしいけど、今現在の py2exe 0.11.1.1 は Python 3.7 - 3.10 まで対応している。
_py2exe - PyPI
今回は virtualenv を使って、Python 3.8 が動作する環境を作った。と言うのも、各exe化ツールは動作に不要なファイルまで含めてexe化してしまう時があるそうで。故に、virtualenv や venv 等を使って、動作に必要な最低限のモジュールだけがインストールされている環境を用意して、ソレをexe化するのが望ましいのだとか。
Pythonスクリプトをexe化するツールは色々あるけれど、今回は PyInstaller と py2exe を試用してみた。
virtualenv で環境を作った場合、PyInstaller、py2exe も、その環境にインストールして、その環境内で実行しないといけない。そうしないと、システム側にインストールした PyInstaller や py2exe を使ってしまって、動作に不要なモジュールまで入ってしまう。
> env38\Scripts\activate (env38) ... > python -V Python 3.8.10 (env38) ... > python -m pip install pyinstaller -U (env38) ... > python -m pip install py2exe -U (env38) ... > python -m pip list | grep -e pyinstaller -e py2exe py2exe 0.11.1.1 pyinstaller 5.3 pyinstaller-hooks-contrib 2022.8 (env38) ... > deactivate
ちなみに、昔の py2exe は Python 3.4 までしか対応してなかったらしいけど、今現在の py2exe 0.11.1.1 は Python 3.7 - 3.10 まで対応している。
_py2exe - PyPI
◎ PyInstallerでexe化。 :
Python 3.8.10 + PyInstaller 5.3 を使って、先日作成した、tkinterのウインドウ内にpygameのウインドウを埋め込むスクリプト、01_embed_pygamewindow.py をexe化してみた。
_01_embed_pygamewindow.py
以下を打てば、exe化される。
distディレクトリ以下に、01_embed_pygamewindow.exe が作成された。実行したらちゃんと動作した。
ただ、ウインドウが開くまでちょっと時間がかかる。
PyInstaller で作成したexeは、実行すると C:\Users\USERNAME\AppData\Local\Temp\ 以下に仮ディレクトリを作成して、その中に実行に必要なファイルを全て解凍して、それらのファイルを実行しているらしい。また、アプリを終了すると、その仮ディレクトリも削除される。毎回実行するたびに、圧縮ファイルを解凍する処理が行われるので、起動が遅いのも当然だろうか。
ちなみに、一度 exeファイルに変換すると、.specファイルが作られる。その .specファイルの内容を修正して、pyinstaller hoge.spec とすれば、その .specファイルの内容に従って exeファイルが作成される。ただし、.spec を使わないで exe を作成すると、既に存在していた .spec は上書きされてしまうので注意。
余談。今回、実行ファイルを圧縮してファイルサイズを小さくしてくれる、UPX というツールを使わないように、--noupx を指定してみたのだけど。生成された exeファイルは、--noupx を指定しない場合と変わらないファイルサイズだった…。このオプション、実は機能していないのでは…?
_01_embed_pygamewindow.py
以下を打てば、exe化される。
pyinstaller --onefile --noconsole --noupx 01_embed_pygamewindow.py
- --onefile : 1つのexeファイルにする。
- --noconsole : exe実行時にコンソール(DOS窓)を開かない。
- --noupx : 実行ファイル圧縮ツール UPX を使わない。
distディレクトリ以下に、01_embed_pygamewindow.exe が作成された。実行したらちゃんと動作した。
ただ、ウインドウが開くまでちょっと時間がかかる。
PyInstaller で作成したexeは、実行すると C:\Users\USERNAME\AppData\Local\Temp\ 以下に仮ディレクトリを作成して、その中に実行に必要なファイルを全て解凍して、それらのファイルを実行しているらしい。また、アプリを終了すると、その仮ディレクトリも削除される。毎回実行するたびに、圧縮ファイルを解凍する処理が行われるので、起動が遅いのも当然だろうか。
ちなみに、一度 exeファイルに変換すると、.specファイルが作られる。その .specファイルの内容を修正して、pyinstaller hoge.spec とすれば、その .specファイルの内容に従って exeファイルが作成される。ただし、.spec を使わないで exe を作成すると、既に存在していた .spec は上書きされてしまうので注意。
余談。今回、実行ファイルを圧縮してファイルサイズを小さくしてくれる、UPX というツールを使わないように、--noupx を指定してみたのだけど。生成された exeファイルは、--noupx を指定しない場合と変わらないファイルサイズだった…。このオプション、実は機能していないのでは…?
◎ py2exeでexe化。 :
Python 3.8.10 + py2exe 0.11.1.1 で exe化してみる。
変換したい .py と同じ階層に setup.py を作成。この setup.py の記述で、以下のような指定ができるらしい。
_setup.py
python setup.py py2exe と打てば、distディレクトリ以下にexeファイルが生成される。実行したところ、ちゃんと動作してくれた。なんとなくだけど、PyInstaller と比べると起動は若干速いような気がした。
ただ、今回、"bundle_files": 1, を指定して、1つのexeファイルにするように指定したはずが、そうはなってくれなかった。
どうやら、py2exe を使って1つのexeファイルにするのは難しい模様…。となると、pygame でスクリーンセーバを作るとしたら、PyInstaller 一択のようだなと。
変換したい .py と同じ階層に setup.py を作成。この setup.py の記述で、以下のような指定ができるらしい。
- どのスクリプトを exe化するか。
- 1つのファイルにするか、複数のファイルにするか。
- 動作にはどんなモジュールが必要か。
_setup.py
from distutils.core import setup import py2exe option = { "packages": ["pygame", "tkinter", "win32gui"], "compressed": 1, "optimize": 2, "bundle_files": 1, } setup( options = { 'py2exe': option, }, console = [ {'script': "01_embed_pygamewindow.py"} ], zipfile = None, )
python setup.py py2exe と打てば、distディレクトリ以下にexeファイルが生成される。実行したところ、ちゃんと動作してくれた。なんとなくだけど、PyInstaller と比べると起動は若干速いような気がした。
ただ、今回、"bundle_files": 1, を指定して、1つのexeファイルにするように指定したはずが、そうはなってくれなかった。
- distディレクトリ内には、pygame の動作に必要になるSDL関連dllファイルが9ファイルほどコピーされた。
- dist/libディレクトリも作成されて、その中には tkinter の動作に必要なのであろう tcl と tk というフォルダがあり、中には1000ファイルほど入っていた。
> dir ... 2022/08/08 05:16 <DIR> . 2022/08/08 05:16 <DIR> .. 2022/08/08 05:16 9,367,824 01_embed_pygamewindow.exe 2022/08/08 05:16 <DIR> lib 2021/05/03 11:54 3,406,016 libcrypto-1_1.dll 2021/05/03 11:54 32,792 libffi-7.dll 2021/05/03 11:54 690,368 libssl-1_1.dll 2022/08/08 01:48 300,544 SDL.dll 2022/08/08 01:48 2,326,016 SDL_image.dll 2022/08/08 01:48 165,888 SDL_mixer.dll 2022/08/08 01:48 623,616 SDL_ttf.dll 2021/05/03 11:54 1,705,120 tcl86t.dll 2021/05/03 11:54 1,468,064 tk86t.dll 10 個のファイル 20,086,248 バイト 3 個のディレクトリ 88,550,535,168 バイトの空き領域 > dir lib ... 2022/08/08 05:16 <DIR> . 2022/08/08 05:16 <DIR> .. 2021/05/08 06:29 <DIR> tcl 2021/05/08 06:29 <DIR> tk 0 個のファイル 0 バイト 4 個のディレクトリ 88,550,535,168 バイトの空き領域
どうやら、py2exe を使って1つのexeファイルにするのは難しい模様…。となると、pygame でスクリーンセーバを作るとしたら、PyInstaller 一択のようだなと。
[ ツッコむ ]
#2 [python][windows] Windows用スクリーンセーバの呼び出し動作をPythonで確認
Windows用スクリーンセーバを、Python を使って作ってみたい。
環境は、Windows10 x64 21H2 + Python 3.8.10 x64。
まずは、Windows用スクリーンセーバが呼び出される時、どんなオプションが渡されているのかを念のために調べておきたい。そこで、渡されたオプションを、デスクトップ上のテキストファイルに追記していくだけの Pythonスクリプトを書いてみた。
_01_scrsavchk.py
このスクリプトファイルを、PyInstaller を使ってexe化する。
01_scrsavchk.exe を、01_scrsavchk.scr にリネーム。Windows用スクリーンセーバの実行ファイル、.scr の実体は、拡張子を .scr にリネームした .exeファイルなので…。
今回、Python 3.8.10 x64 (64bit版) でexe化したので…。
ということで、01_scrsavchk.scr を、C:\Windows\System32\ 以下にコピーした。
スクリーンセーバとして選んでみる。
スクリーンセーバ一覧の中に、01_scrsavchk という種類が増えている。これを選ぶと、DOS窓が開いて、2秒ほど経ったら閉じる。「設定」「プレビュー」ボタン等も押してみて様子を伺う。
デスクトップに、temp_log.txt というテキストファイルができているはず。中身を確認。
そんなわけで、巷の解説通り、Windows用スクリーンセーバは、以下の3種類のオプションを渡した状態で実行されることが分かった。
また、「設定」「プレビュー」ボタンを押して実行・終了した直後、すぐさま /p xxxxx を渡して実行されていることも分かった。
Windows用のスクリーンセーバは多重起動を禁止するように作るべしと言われているけど、このあたりの呼び出しタイミングがシビア、ということだろうか…。それとも、既にフルスクリーンで起動してるのに、再度実行されてしまう場面もあり得るのだろうか…。
環境は、Windows10 x64 21H2 + Python 3.8.10 x64。
まずは、Windows用スクリーンセーバが呼び出される時、どんなオプションが渡されているのかを念のために調べておきたい。そこで、渡されたオプションを、デスクトップ上のテキストファイルに追記していくだけの Pythonスクリプトを書いてみた。
_01_scrsavchk.py
import os import sys import datetime import time def main(): desktop_dir = os.path.expanduser('~/Desktop') log_file = os.path.join(desktop_dir, "temp.log.txt") dt_now = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") print("Screensaver option check") print("%s" % dt_now) print("look %s" % log_file) kind = "none" hwnd = 0 if len(sys.argv) >= 2: s = sys.argv[1][0:2].lower() if s == "/s": # full screen kind = "fullscreen" elif s == "/c": # config / settings kind = "config" elif s == "/p": # preview kind = "preview" hwnd = int(sys.argv[2]) with open(log_file, 'a') as f: f.write("[%s] %s , [%s]\n" % (dt_now, kind, " ".join(sys.argv))) time.sleep(2) if __name__ == '__main__': main()
このスクリプトファイルを、PyInstaller を使ってexe化する。
pyinstaller --onefile --noupx 01_scrsavchk.pydistディレクトリに、01_scrsavchk.exe というexeファイルが作られた。
01_scrsavchk.exe を、01_scrsavchk.scr にリネーム。Windows用スクリーンセーバの実行ファイル、.scr の実体は、拡張子を .scr にリネームした .exeファイルなので…。
今回、Python 3.8.10 x64 (64bit版) でexe化したので…。
- 64bit版のプログラム → C:\Windows\System32\ 以下に入れる。
- 32bit版のプログラム → C:\Windows\SysWOW64\ 以下に入れる。
ということで、01_scrsavchk.scr を、C:\Windows\System32\ 以下にコピーした。
スクリーンセーバとして選んでみる。
- Windows10の左下の検索欄に「スクリーンセーバー」と打ち込んで、リストアップされた「スクリーンセーバーの変更」を起動。
- もしくは、デスクトップを右クリック → 個人用設定 → ロック画面 → スクリーンセーバー設定、でもいい。
スクリーンセーバ一覧の中に、01_scrsavchk という種類が増えている。これを選ぶと、DOS窓が開いて、2秒ほど経ったら閉じる。「設定」「プレビュー」ボタン等も押してみて様子を伺う。
デスクトップに、temp_log.txt というテキストファイルができているはず。中身を確認。
[2022/08/10 19:51:13] preview , [C:\WINDOWS\system32\01_scrsavchk.scr /p 68114] [2022/08/10 19:51:21] config , [C:\WINDOWS\system32\01_scrsavchk.scr /c:330168] [2022/08/10 19:51:24] preview , [C:\WINDOWS\system32\01_scrsavchk.scr /p 68114] [2022/08/10 19:51:29] fullscreen , [C:\WINDOWS\system32\01_scrsavchk.scr /s] [2022/08/10 19:51:32] preview , [C:\WINDOWS\system32\01_scrsavchk.scr /p 68114] [2022/08/10 19:51:41] preview , [C:\WINDOWS\system32\01_scrsavchk.scr /p 68114]これで、Windows10 がどんなオプションをつけてスクリーンセーバを呼び出しているのかが分かる。
そんなわけで、巷の解説通り、Windows用スクリーンセーバは、以下の3種類のオプションを渡した状態で実行されることが分かった。
- /s : フルスクリーン表示。
- /c:xxxxx : 設定画面表示。
- /p xxxxx : スクリーンセーバー設定画面内のプレビュー表示。xxxxx はウインドウハンドル。
また、「設定」「プレビュー」ボタンを押して実行・終了した直後、すぐさま /p xxxxx を渡して実行されていることも分かった。
Windows用のスクリーンセーバは多重起動を禁止するように作るべしと言われているけど、このあたりの呼び出しタイミングがシビア、ということだろうか…。それとも、既にフルスクリーンで起動してるのに、再度実行されてしまう場面もあり得るのだろうか…。
[ ツッコむ ]
#3 [nitijyou] 日記ページをアップロード
2022/07/13を最後に日記をアップロードしてなかったのでアップロード。
[ ツッコむ ]
2022/08/11(木) [n年前の日記]
#1 [gimp] GIMP 2.10で利用できるフォントフォーマットが減っていた
Windows10 x64 21H2 + GIMP 2.10.32 x64 Portable samj版を使っていて、いくつかのフォントが使えなくなっていることに気づいた。フォント名は選べるのだけど、フォントの形状が反映されない…。
問題を起こすフォントは、Type1フォント (.pfb) 群。GIMP 2.4 や 2.6 時代に追加フォントとして配布されていた多数の個性的なフォント達。
_GIMP Type1フリーフォント一覧
GIMP 2.8.22 Portable を起動して確認してみたところ、そちらはちゃんとフォントの形状が反映される。
samj版の問題だろうかと、PortableApps版の GIMP 2.10.32 Portable をインストールして確認してみたものの、そちらもフォントが反映されない。
_GIMP Portable (image editor) | PortableApps.com
どうやら GIMP 2.10.xx は、Type1フォントが利用できなくなった気配がする。
問題を起こすフォントは、Type1フォント (.pfb) 群。GIMP 2.4 や 2.6 時代に追加フォントとして配布されていた多数の個性的なフォント達。
_GIMP Type1フリーフォント一覧
GIMP 2.8.22 Portable を起動して確認してみたところ、そちらはちゃんとフォントの形状が反映される。
samj版の問題だろうかと、PortableApps版の GIMP 2.10.32 Portable をインストールして確認してみたものの、そちらもフォントが反映されない。
_GIMP Portable (image editor) | PortableApps.com
どうやら GIMP 2.10.xx は、Type1フォントが利用できなくなった気配がする。
◎ pangoのバージョンが新しくなったせいらしい。 :
色々ググってみたら、GIMP が使っている pango というライブラリが、数年前にType1フォントやビットマップフォントのサポートを打ち切っていたそうで。
_Font Changing - GIMP Chat
_Pango dropped support for Bitmap fonts with version 1.44 (was: Bitmap fonts are broken) (#4411) - Issues - GNOME / GIMP - GitLab
_Pango 1.44 and the removal of support for bitmap fonts (#386) - Issues - GNOME / pango - GitLab
最近の GIMP 2.10 は、その新しくなった pango を使うようになったので、Type1フォントやビットマップフォントが使えなくなったのだろう…。
数年前からそういう不具合というか、機能のダウングレードが起きてたのに、自分、今頃になって気づくとは…。
_Font Changing - GIMP Chat
_Pango dropped support for Bitmap fonts with version 1.44 (was: Bitmap fonts are broken) (#4411) - Issues - GNOME / GIMP - GitLab
_Pango 1.44 and the removal of support for bitmap fonts (#386) - Issues - GNOME / pango - GitLab
最近の GIMP 2.10 は、その新しくなった pango を使うようになったので、Type1フォントやビットマップフォントが使えなくなったのだろう…。
数年前からそういう不具合というか、機能のダウングレードが起きてたのに、自分、今頃になって気づくとは…。
◎ 使えるバージョンを探してみた。 :
GIMP 2.10 は、必ず Type1フォント(.pfb)やビットマップフォントが使えない…というわけでもなく。ある時期までは使えていたらしい。
どのバージョンなら使えるのか、PortableApps の GIMP 2.10.xx Portable版を片っ端からインストールして調べてみた。Portable版は異なるバージョンも共存できるから、こういう動作確認もできるのでありがたい。
_PortableApps.com - Browse /GIMP Portable at SourceForge.net
つまり、GIMP Portable版の場合…。
今後サポートが復活する可能性は、望み薄だろうな…。
とりあえず、Type1フォントが使える最終版、GIMP 2.10.20-1 Portable をインストールして、フォントを使った画像を作成したい時はソレを利用することにしよう…。
いやまあ、自分の環境には GIMP 2.8.22 Portable もインストール済みなので、そちらを使う手もあるけれど。GIMP 2.8 は起動がとんでもなく遅いので…。自分の環境だと起動するまで数分かかる…。HDDからSSDに移しても起動時間の遅さは変わらなかった…。
どのバージョンなら使えるのか、PortableApps の GIMP 2.10.xx Portable版を片っ端からインストールして調べてみた。Portable版は異なるバージョンも共存できるから、こういう動作確認もできるのでありがたい。
_PortableApps.com - Browse /GIMP Portable at SourceForge.net
- GIMPPortable_2.10.12-3.paf.exe : Pass
- GIMPPortable_2.10.14-1.paf.exe : Pass
- GIMPPortable_2.10.18-2.paf.exe : Pass
- GIMPPortable_2.10.20-1.paf.exe : Pass
- GIMPPortable_2.10.22_Rev_2.paf.exe : Fail
つまり、GIMP Portable版の場合…。
- GIMP 2.10.20-1 Portable までは、Type1フォントやビットマップフォントも使える。
- GIMP 2.10.22 Rev2 Portable から、Type1フォントやビットマップフォントが使えなくなった。
今後サポートが復活する可能性は、望み薄だろうな…。
とりあえず、Type1フォントが使える最終版、GIMP 2.10.20-1 Portable をインストールして、フォントを使った画像を作成したい時はソレを利用することにしよう…。
いやまあ、自分の環境には GIMP 2.8.22 Portable もインストール済みなので、そちらを使う手もあるけれど。GIMP 2.8 は起動がとんでもなく遅いので…。自分の環境だと起動するまで数分かかる…。HDDからSSDに移しても起動時間の遅さは変わらなかった…。
◎ pfbをttfに変換できないものか。 :
Type1フォント(pfbフォント)を、ttfフォントに変換できないものだろうか。それができれば、GIMP 2.10.22以降も従来のフォントが利用できるのだろうけど…。いやまあ、フォントのライセンス的にその変換は許されるのでしょうかという疑問もあるのだけど。
ライセンスの問題はともかくとして、FontForge あたりで変換できないものかな。
ライセンスの問題はともかくとして、FontForge あたりで変換できないものかな。
◎ 余談。 :
ググってたら以下のページに遭遇。
_DTPの2023年問題、知ってますか? 終末の日が近いType 1フォントの“遺産”にどう対応すべきか(1/3 ページ) - ITmedia NEWS
Adobe のせいか…。Flash もそうだけど、Adobe が何かを決めると皆で右往左往しちゃうな…。
_DTPの2023年問題、知ってますか? 終末の日が近いType 1フォントの“遺産”にどう対応すべきか(1/3 ページ) - ITmedia NEWS
Adobe のせいか…。Flash もそうだけど、Adobe が何かを決めると皆で右往左往しちゃうな…。
◎ 2022/08/12追記。 :
Linu Mint 19.3上では、GIMP 2.10.18 の時点でType1フォントが使えなくなっているという話を見かけた。
_Removing fonts from GIMP 2.10.18 - Linux Mint Forums
Ubuntu 20.04 + GIMP 2.10.30 では /usr/share/fonts/type1/urw-base35/ 内の Type1フォントが全滅という話も。ただ、AppImage版の GIMP 2.10.14 はType1フォントが使えているらしい。
_Broken fonts after Ubuntu upgrade to 20.04
_PFB fonts are broken in certain applications and missing in others (Kubuntu 20.04) - Ask Ubuntu
Windows版、Linux版、Portable版等々でサポート状況は違ってるようで…。このバージョンなら大丈夫というわけでもないっぽい…。
_Removing fonts from GIMP 2.10.18 - Linux Mint Forums
Ubuntu 20.04 + GIMP 2.10.30 では /usr/share/fonts/type1/urw-base35/ 内の Type1フォントが全滅という話も。ただ、AppImage版の GIMP 2.10.14 はType1フォントが使えているらしい。
_Broken fonts after Ubuntu upgrade to 20.04
_PFB fonts are broken in certain applications and missing in others (Kubuntu 20.04) - Ask Ubuntu
Windows版、Linux版、Portable版等々でサポート状況は違ってるようで…。このバージョンなら大丈夫というわけでもないっぽい…。
[ ツッコむ ]
#2 [gimp] GIMP 2.10 Portable は Python-Fu が使えなかった
GIMP 2.10.32 Portable は Type1フォント(.pfb) やビットマップフォントが使えなくなっていたので、そのあたりのフォントが利用できる GIMP 2.10.20-1 Portable (GIMPPortable_2.10.20-1.paf.exe) を Windows10 x64 21H2上でインストールしたのだけど。
_PortableApps.com - Browse /GIMP Portable at SourceForge.net
少し触っていたら、GIMP 2.10.20-1 Portable は Python-Fu が動かないことに気が付いた…。
_PortableApps.com - Browse /GIMP Portable at SourceForge.net
少し触っていたら、GIMP 2.10.20-1 Portable は Python-Fu が動かないことに気が付いた…。
- フィルター → Python-Fu → コンソール、を選んでもエラーになる。
- また、Python-Fuスクリプト群もメニューに出てこない。
◎ 原因。 :
原因を調べたら、pygimp.interp の記述がおかしかった。
やっかいなのは、この pygimp.interp、毎回GIMPの起動時に、GIMPPortable.ini 内の指定により上書きされてしまうこと。
パス指定だけ間違っているなら、GIMPPortable.ini 内のパス指定を修正すれば済むのだけど…。改行コードを LF で上書きしてほしいのに CRLF にされてしまうので、GIMPPortable.ini の修正だけでは対応できない。
(Gimp-Portableインストールフォルダ)\App\gimp\lib\gimp\2.0\interpreters\pygimp.interp
- GIMP 2.10 は GIMP 2.8 とフォルダ構成が変わっているのに、pygimp.interp 内では GIMP 2.8 時代のフォルダ構成が指定されている。それでは動くわけがない。
- 加えて、改行コードをLFにしておかないといけないのに、CRLF になってる。それでは GIMPが pygimp.interp の解析時に失敗してしまう。
やっかいなのは、この pygimp.interp、毎回GIMPの起動時に、GIMPPortable.ini 内の指定により上書きされてしまうこと。
パス指定だけ間違っているなら、GIMPPortable.ini 内のパス指定を修正すれば済むのだけど…。改行コードを LF で上書きしてほしいのに CRLF にされてしまうので、GIMPPortable.ini の修正だけでは対応できない。
◎ 対策。 :
そんなわけで、まずは GIMPPortable.ini を修正。関連してる行をコメントアウトして、pygimp.interp の上書きをさせないようにする。
「pygimp」で検索して、関連がありそうな行の先頭に「; 」をつけてコメントアウト。
次に、pygimp.interp を修正。
例えば、GIMP 2.10.20-1 Portable のインストールフォルダが D:\GIMP_INSTALL_DIR\なら、pygimp.interp の内容は以下になる。
また、改行コードは LF にしておくこと。デフォルト状態では CRLF になっていて、ファイル内容の解析時に問題が起きる。
修正が終わったら、GIMPインストールフォルダでDOS窓を開いて、以下を打って GIMP Portable を起動。GIMP起動時の情報が別のDOS窓で表示される。
最初のほう、pygimp.interp を読み込んだあたりで「警告」等のメッセージが出ていなければ、修正は上手く行ってる。
(Gimp-Portableインストールフォルダ)\App\AppInfo\Launcher\GIMPPortable.ini
「pygimp」で検索して、関連がありそうな行の先頭に「; 」をつけてコメントアウト。
; [FileWrite15] ; Type=ConfigWrite ; File=%PAL:AppDir%\gimp\lib\gimp\2.0\interpreters\pygimp.interp ; Entry=python= ; Value=%PAL:AppDir%\gimp\Python\pythonw.exe ; ; [FileWrite16] ; Type=ConfigWrite ; File=%PAL:AppDir%\gimp\lib\gimp\2.0\interpreters\pygimp.interp ; Entry=/usr/bin/python= ; Value=%PAL:AppDir%\gimp\Python\pythonw.exe ; ; [FileWrite17] ; Type=ConfigWrite ; File=%PAL:AppDir%\gimp\lib\gimp\2.0\interpreters\pygimp.interp ; Entry=:Python:E::py: ; Value=:python:
次に、pygimp.interp を修正。
(Gimp-Portableインストールフォルダ)\App\gimp\lib\gimp\2.0\interpreters\pygimp.interp
例えば、GIMP 2.10.20-1 Portable のインストールフォルダが D:\GIMP_INSTALL_DIR\なら、pygimp.interp の内容は以下になる。
python=D:\GIMP_INSTALL_DIR\App\gimp\bin\pythonw.exe python2=D:\GIMP_INSTALL_DIR\App\gimp\bin\pythonw.exe /usr/bin/python=D:\GIMP_INSTALL_DIR\App\gimp\bin\pythonw.exe /usr/bin/python2=D:\GIMP_INSTALL_DIR\App\gimp\bin\pythonw.exe :Python:E::py::\GIMP_INSTALL_DIR\App\gimp\bin\pythonw.exe:
また、改行コードは LF にしておくこと。デフォルト状態では CRLF になっていて、ファイル内容の解析時に問題が起きる。
修正が終わったら、GIMPインストールフォルダでDOS窓を開いて、以下を打って GIMP Portable を起動。GIMP起動時の情報が別のDOS窓で表示される。
GIMPPortable.exe --verbose --console-messages
最初のほう、pygimp.interp を読み込んだあたりで「警告」等のメッセージが出ていなければ、修正は上手く行ってる。
◎ Python-Fuコンソールが動作しない問題。 :
上記の修正をすることで、Python-Fuスクリプト群は動作して、メニューにも出てくるようになったけれど。相変わらず、Python-Fuコンソールが ―― フィルタ → Python-Fu → コンソール、がエラーを出して起動しない。
以下のファイルを修正する。
最初の行のシバン行(shebang、#! から始まる行のこと)が「#!/usr/bin/env python2」になってるのがよろしくないのだろう。*NIXならともかく、Windowsに /usr/bin/env というプログラムなんて存在しないので、エラーになるのではなかろうか。
なので、「#!/usr/bin/python2」もしくは「#!/usr/bin/python」に書き換えてやれば、pygimp.interp に記述した「/usr/bin/python2=」「/usr/bin/python=」の指定が反映されて動いてくれると思う。たぶん。少なくとも自分の手元の環境では、こういった修正で、Python-Fuコンソールが動いてくれた。
以下のファイルを修正する。
(GIMP Portableインストールフォルダ)\App\gimp\lib\gimp\2.0\plug-ins\python-console\python-console.py
最初の行のシバン行(shebang、#! から始まる行のこと)が「#!/usr/bin/env python2」になってるのがよろしくないのだろう。*NIXならともかく、Windowsに /usr/bin/env というプログラムなんて存在しないので、エラーになるのではなかろうか。
なので、「#!/usr/bin/python2」もしくは「#!/usr/bin/python」に書き換えてやれば、pygimp.interp に記述した「/usr/bin/python2=」「/usr/bin/python=」の指定が反映されて動いてくれると思う。たぶん。少なくとも自分の手元の環境では、こういった修正で、Python-Fuコンソールが動いてくれた。
◎ 関連情報。 :
_2018/08/10 - GIMP 2.10.4 Portable をインストール
_2018/08/11 - GIMP 2.10.4 Portable の pygimp.interp がおかしい
GIMP 2.10.4 Portable の頃の不具合が、GIMP 2.10.20 Portable になっても修正されてなかったということだろうけど…。これひょっとして、「誰も消防車を呼んでいないのである!」案件だったりするのだろうか…。もっとも、そもそもどこに連絡すればいいのかも分からんのだけど。さすがに最新版は直ってるのだろうか。どうなんだろ。
_2018/08/11 - GIMP 2.10.4 Portable の pygimp.interp がおかしい
GIMP 2.10.4 Portable の頃の不具合が、GIMP 2.10.20 Portable になっても修正されてなかったということだろうけど…。これひょっとして、「誰も消防車を呼んでいないのである!」案件だったりするのだろうか…。もっとも、そもそもどこに連絡すればいいのかも分からんのだけど。さすがに最新版は直ってるのだろうか。どうなんだろ。
[ ツッコむ ]
2022/08/12(金) [n年前の日記]
#1 [pygame][windows] スクリーンセーバと多重起動抑止
Windows10 x64 21H2上で、Python + pygame を使ってスクリーンセーバ(.scr)を作れないものかと実験しているのだけど、なかなか難しいことがちょっとだけ分かってきた。多重起動を抑止する処理が重要になってくるんだなと…。
◎ プレビューモードの問題。 :
Windows用のスクリーンセーバは、スクリーンセーバ設定画面のプレビュー窓の中に表示させるために、コマンドラインオプションとして「/p xxxx」を指定して実行される場合があるのだけど。この呼び出しがとにかく多い。
とにかく、何かしらちょっと操作する度に、Windows はスクリーンセーバを /p xxxx 付きで実行するので…。もし、多重起動を抑止する処理を何も入れてないと、そのスクリーンセーバのプロセスが3つも4つも大量に走っている状態になる…。
例えばそのスクリーンセーバが、プレビュー窓に対して、何かしらのアニメーションを延々描画し続けるような作りだと…。
各プロセスが、「俺がこの窓に描き込むぞ」「いや、俺が描き込む」「いやいや、俺が」「いや待て。ここは拙者が」「僕も描くよ」「私だって」てな状態になって…。プレビュー窓内のアニメーションが、それはもうグチャグチャな状態に…。動作確認中、その奪い合いぶりを目にしてかなり慌ててしまった。見るからにヤバイ。
そんなわけで、/p xxxx 付きで実行された場合は、多重起動抑止処理が絶対に必要になるのだなと分かってきた…。
ただ、この件については、プレビュー窓内でアニメーションをさせようとするからおかしなことになるような気もする。
例えば、/p xxxx で実行された際は、プレビュー窓内に画像1枚だけを描画して即座に終了する作りにすれば、プロセスが大量に発生して残り続けることも避けられるのではなかろうか。そういう作りなら、多重起動抑止の処理も不要かなと想像しているのだけど、さてどうだろう。
- スクリーンセーバの設定画面を開いて、そのスクリーンセーバが選択済みなら、/p xxxx 付きで呼ばれる。
- リストから選択した際も、/p xxxx で呼ばれる。
- 「設定」ボタンをクリックして、設定ダイアログを表示した後にも呼ばれる。
- 「プレビュー」ボタンをクリックしてフルスクリーン表示をして、そこから抜けた際にも呼ばれる。
とにかく、何かしらちょっと操作する度に、Windows はスクリーンセーバを /p xxxx 付きで実行するので…。もし、多重起動を抑止する処理を何も入れてないと、そのスクリーンセーバのプロセスが3つも4つも大量に走っている状態になる…。
例えばそのスクリーンセーバが、プレビュー窓に対して、何かしらのアニメーションを延々描画し続けるような作りだと…。
各プロセスが、「俺がこの窓に描き込むぞ」「いや、俺が描き込む」「いやいや、俺が」「いや待て。ここは拙者が」「僕も描くよ」「私だって」てな状態になって…。プレビュー窓内のアニメーションが、それはもうグチャグチャな状態に…。動作確認中、その奪い合いぶりを目にしてかなり慌ててしまった。見るからにヤバイ。
そんなわけで、/p xxxx 付きで実行された場合は、多重起動抑止処理が絶対に必要になるのだなと分かってきた…。
ただ、この件については、プレビュー窓内でアニメーションをさせようとするからおかしなことになるような気もする。
例えば、/p xxxx で実行された際は、プレビュー窓内に画像1枚だけを描画して即座に終了する作りにすれば、プロセスが大量に発生して残り続けることも避けられるのではなかろうか。そういう作りなら、多重起動抑止の処理も不要かなと想像しているのだけど、さてどうだろう。
◎ 待ち時間の問題。 :
もう一つ問題がある。Windows用スクリーンセーバの作成方法について書いてある記事を眺めてたら、ちょっと怖いことが書いてあった。
例えば、スクリーンセーバの待ち時間を10分に設定してあって、かつ、何の操作もしてない状態がずっと続いたら…。Windowsは、スクリーンセーバが実行されていようといまいと、10分置きにスクリーンセーバの実行を指示するそうで。
その話が本当ならヤバイ。試しに、先日作成した動作チェック用の .scr を使って、待ち時間を1分にして様子を伺ってみたら、たしかに1分間隔でスクリーンセーバが何度も何度も実行されていた…。
_Windows用スクリーンセーバの呼び出し動作をPythonで確認 - mieki256's diary
そんなわけで、/s をつけてフルスクリーン表示を要求された際も、多重起動抑止処理が絶対に必要になりそうだなと。
ちなみに、自分が今まで作成したスクリーンセーバモドキは、そういう処理を一切入れてなかったので…。これは非常にマズイなと今頃になって気づかされた…。あくまで、モドキでしかなかった…。
例えば、スクリーンセーバの待ち時間を10分に設定してあって、かつ、何の操作もしてない状態がずっと続いたら…。Windowsは、スクリーンセーバが実行されていようといまいと、10分置きにスクリーンセーバの実行を指示するそうで。
その話が本当ならヤバイ。試しに、先日作成した動作チェック用の .scr を使って、待ち時間を1分にして様子を伺ってみたら、たしかに1分間隔でスクリーンセーバが何度も何度も実行されていた…。
_Windows用スクリーンセーバの呼び出し動作をPythonで確認 - mieki256's diary
[2022/08/12 00:03:28] preview , [C:\WINDOWS\system32\01_scrsavchk.scr /p 262910] [2022/08/12 00:04:40] fullscreen , [C:\WINDOWS\system32\01_SCR~1.SCR /s] [2022/08/12 00:05:43] fullscreen , [C:\WINDOWS\system32\01_SCR~1.SCR /s] [2022/08/12 00:06:46] fullscreen , [C:\WINDOWS\system32\01_SCR~1.SCR /s] [2022/08/12 00:07:49] fullscreen , [C:\WINDOWS\system32\01_SCR~1.SCR /s] [2022/08/12 00:08:53] fullscreen , [C:\WINDOWS\system32\01_SCR~1.SCR /s] [2022/08/12 00:09:56] fullscreen , [C:\WINDOWS\system32\01_SCR~1.SCR /s] [2022/08/12 00:10:04] preview , [C:\WINDOWS\system32\01_scrsavchk.scr /p 1049354]
そんなわけで、/s をつけてフルスクリーン表示を要求された際も、多重起動抑止処理が絶対に必要になりそうだなと。
ちなみに、自分が今まで作成したスクリーンセーバモドキは、そういう処理を一切入れてなかったので…。これは非常にマズイなと今頃になって気づかされた…。あくまで、モドキでしかなかった…。
◎ 多重起動抑止の種類。 :
多重起動の抑止処理は、スクリーンセーバ1つにつき1種類で十分、というわけでもないっぽい。
というのも、スクリーンセーバ設定画面のプレビュー窓にスクリーンセーバが表示されている状態で、フルスクリーン表示のスクリーンセーバが走る場面もあるそうで…。
その場合、既にプレビュー窓でプロセスが走っているから、フルスクリーン表示用のプロセスは走ってくれないことになってしまう。「プレビュー」ボタンをいくらクリックしても、何の変化も起きない状態になる。
つまり、プレビューモード用と、フルスクリーン表示用で、別々に多重起動抑止処理を入れておかないといけないようだなと…。
というのも、スクリーンセーバ設定画面のプレビュー窓にスクリーンセーバが表示されている状態で、フルスクリーン表示のスクリーンセーバが走る場面もあるそうで…。
その場合、既にプレビュー窓でプロセスが走っているから、フルスクリーン表示用のプロセスは走ってくれないことになってしまう。「プレビュー」ボタンをいくらクリックしても、何の変化も起きない状態になる。
つまり、プレビューモード用と、フルスクリーン表示用で、別々に多重起動抑止処理を入れておかないといけないようだなと…。
◎ 多重起動抑止の方法。 :
多重起動抑止処理はどうやって作ればいいのか少しググったけれど、Windowsの場合は Mutex なるものが使えるらしい。
他にも、ロックファイルを作成して、ロックファイルが存在するなら既にプロセスが走ってるから後続は起動させないというやり方もあるらしいけど。もし、ロックファイルを作ったプロセスが異常終了してしまうと、ロックファイルが残り続けて、その後ずっとプロセスが走らなくなる問題もあるそうで。
そんなわけで、Mutex なるソレを Pythonから利用する方法について調べ始めているところ。ctypes というモジュールを使えば実現できるっぽいけど…。
他にも、ロックファイルを作成して、ロックファイルが存在するなら既にプロセスが走ってるから後続は起動させないというやり方もあるらしいけど。もし、ロックファイルを作ったプロセスが異常終了してしまうと、ロックファイルが残り続けて、その後ずっとプロセスが走らなくなる問題もあるそうで。
そんなわけで、Mutex なるソレを Pythonから利用する方法について調べ始めているところ。ctypes というモジュールを使えば実現できるっぽいけど…。
[ ツッコむ ]
2022/08/13(土) [n年前の日記]
#1 [gimp] pfbフォントをttfフォントに変換してみたい
最近の GIMP 2.10 は Type1フォント(.pfb)を扱えなくなっていたわけだけど。
_GIMP 2.10で利用できるフォントフォーマットが減っていた
.pfb を .ttf に変換できないものだろうかと疑問が湧いた。FontForge でどうにかできないものか。
_GIMP 2.10で利用できるフォントフォーマットが減っていた
.pfb を .ttf に変換できないものだろうかと疑問が湧いた。FontForge でどうにかできないものか。
◎ FontForgeで変換。 :
ちょっとググってみたところ、FontForge のチュートリアルページに、そのものズバリのスクリプトが紹介されてた。
_FontForge スクリプトのチュートリアル
_fontforgeでotfからttfに変換する - 俺の外付けHDD
_fontforgeによるotf->ttf変換 - Qiita
試しに、Ubuntu Linux 20.04 LTS上で作業してみた。ちなみに、Ubuntu 20.04 は Windows10 x64 21H2 + VMware Player上で動かしている。
まず、.pfbファイル群が置いてある場所で、conv.pe というスクリプトを作成する。
内容は以下。
chmod +x conv.pe で実行権限をつける。
以下で、.pfbファイル群に対して、まとめて変換。
.pfb と同じ場所に、.ttf が生成された。
Windows10 x64 21H2 側で、各ttf を右クリック → プレビュー、を選んだところ、それらしく変換されているように見えた。
まあ、フォントのライセンスの問題で、.pfb から変換した .ttf を使ってしまっていいのかどうかは、ちょっと分からないのだけど…。
_FontForge スクリプトのチュートリアル
_fontforgeでotfからttfに変換する - 俺の外付けHDD
_fontforgeによるotf->ttf変換 - Qiita
試しに、Ubuntu Linux 20.04 LTS上で作業してみた。ちなみに、Ubuntu 20.04 は Windows10 x64 21H2 + VMware Player上で動かしている。
まず、.pfbファイル群が置いてある場所で、conv.pe というスクリプトを作成する。
vi conv.pe
内容は以下。
#!/usr/bin/fontforge i=1 while ( i<$argc ) Open($argv[i]) Generate($argv[i]:r+".ttf") i = i+1 endloop
chmod +x conv.pe で実行権限をつける。
以下で、.pfbファイル群に対して、まとめて変換。
./conv.pe *.pfb
.pfb と同じ場所に、.ttf が生成された。
Windows10 x64 21H2 側で、各ttf を右クリック → プレビュー、を選んだところ、それらしく変換されているように見えた。
まあ、フォントのライセンスの問題で、.pfb から変換した .ttf を使ってしまっていいのかどうかは、ちょっと分からないのだけど…。
◎ gimp-freefonts の入手先。 :
GIMP に追加できる Type1フォントの一覧は以下で確認できるけど。
_GIMP Type1フリーフォント一覧
紹介されているこれらのフォントは、元々どこから入手できるのか気になった。その入手先に行けば、ライセンスも分かるのではないかと…。
色々ググってみたら、今も GIMP のダウンロードページから入手できなくもないらしい。
_Index of /mirror/pub/gimp/fonts
freefonts-0.10.tar.gz が元ファイル。DLして解凍してみたら、各フォントに対して .license というファイルも同梱されていた。
ちなみに、同梱されていた .license、.tgz等々を、GIMPのフォントフォルダに .pfb と一緒に入れておいたら、GIMP がフォントを読み込む際にエラーが発生してしまった。.pfb 以外は別のフォルダに移動しておいたほうが良さそう。削除してしまうと後からライセンスが分からなくなるので…。
ちなみに、前述のダウンロード先には、urw-fonts.tar.gz というファイルもあった。中を眺めたら、Ghostscript関係で使われることが多い、URW base35 と呼ばれるフォント群だった。また、.pfb の他に、.afm、.pfm も入っていた。
余談。各 .licenseファイルを眺めてみたところ、一応、商業利用しない限りは配布は自由と書いてあるものの…。
_GIMP Type1フリーフォント一覧
紹介されているこれらのフォントは、元々どこから入手できるのか気になった。その入手先に行けば、ライセンスも分かるのではないかと…。
色々ググってみたら、今も GIMP のダウンロードページから入手できなくもないらしい。
_Index of /mirror/pub/gimp/fonts
freefonts-0.10.tar.gz が元ファイル。DLして解凍してみたら、各フォントに対して .license というファイルも同梱されていた。
ちなみに、同梱されていた .license、.tgz等々を、GIMPのフォントフォルダに .pfb と一緒に入れておいたら、GIMP がフォントを読み込む際にエラーが発生してしまった。.pfb 以外は別のフォルダに移動しておいたほうが良さそう。削除してしまうと後からライセンスが分からなくなるので…。
ちなみに、前述のダウンロード先には、urw-fonts.tar.gz というファイルもあった。中を眺めたら、Ghostscript関係で使われることが多い、URW base35 と呼ばれるフォント群だった。また、.pfb の他に、.afm、.pfm も入っていた。
余談。各 .licenseファイルを眺めてみたところ、一応、商業利用しない限りは配布は自由と書いてあるものの…。
- 「フリーウェアだよ。好きにしていいよ」
- 「フリーウェアだけど君の故郷の絵葉書を送ってくれ」
- 「何か黒い絵葉書を送ってほしい」
- 「こまめに部屋の掃除と洗濯をしてるなら使うことを許そう」
- 「使ったら○○大学に寄付をせよ」
- 「シェアウェアだぞ。$25払え」
- 「$15払えばカーニングを整えたフォントを送るで」
◎ URW-base35の入手先。 :
URW-base35 は本来どこから入手できるのだろう。
「Ghostscript fonts」でググれば辿り着けそう。
_Ghostscript fonts - Browse /gs-fonts at SourceForge.net
_Ghostscript fonts Japanese Information - OSDN
ただ、gnu-gs-fonts-std-6.0.tar.gz をダウンロードして解凍してみたところ、中には .afm、.pfb、.pfm しか入ってなかった。
.ttf は無いのだろうか…。
ググっていたら、以下に辿り着いた。
_Index of /download
std35ttf.zip というのが .ttf版らしい。
以下のページも見かけた。
_ArtifexSoftware/urw-base35-fonts: Repo for URW++ base 35 font set
_urw-base35-fonts/fonts at master - ArtifexSoftware/urw-base35-fonts
.ttf も並んでいる。ただ、ダウンロードして確認してみたところ、フォント名が std35ttf.zip のソレと違っているように見えた。
HDDの中を探してみたら、昔は urw-gs35-tt.zip というファイルもWebから入手できたらしい。.ttf をまとめて .ttc にしてある模様。ただ、今はもう配布先のblogが消滅していて入手できない。WebArchive にもblogのトップページしか残ってなかった。
_Recycler (WebArchive)
.zip の中を覗いたら、GPL云々と書いてあった。であれば、そっくりそのままの状態なら配布しても良さそうな気がする。ということで一応バックアップを兼ねて、 _ココ に置いときます。
「Ghostscript fonts」でググれば辿り着けそう。
_Ghostscript fonts - Browse /gs-fonts at SourceForge.net
_Ghostscript fonts Japanese Information - OSDN
ただ、gnu-gs-fonts-std-6.0.tar.gz をダウンロードして解凍してみたところ、中には .afm、.pfb、.pfm しか入ってなかった。
.ttf は無いのだろうか…。
ググっていたら、以下に辿り着いた。
_Index of /download
std35ttf.zip というのが .ttf版らしい。
以下のページも見かけた。
_ArtifexSoftware/urw-base35-fonts: Repo for URW++ base 35 font set
_urw-base35-fonts/fonts at master - ArtifexSoftware/urw-base35-fonts
.ttf も並んでいる。ただ、ダウンロードして確認してみたところ、フォント名が std35ttf.zip のソレと違っているように見えた。
HDDの中を探してみたら、昔は urw-gs35-tt.zip というファイルもWebから入手できたらしい。.ttf をまとめて .ttc にしてある模様。ただ、今はもう配布先のblogが消滅していて入手できない。WebArchive にもblogのトップページしか残ってなかった。
_Recycler (WebArchive)
.zip の中を覗いたら、GPL云々と書いてあった。であれば、そっくりそのままの状態なら配布しても良さそうな気がする。ということで一応バックアップを兼ねて、 _ココ に置いときます。
[ ツッコむ ]
2022/08/14(日) [n年前の日記]
#1 [python] Pythonスクリプトの多重起動を抑止したい
Windows10 x64 21H2 + Python 3.8.10、3.9.13 上で、Pythonスクリプトの多重起動を抑止したい。
◎ 参考ページ。 :
以下のページを参考にして試してみた。
_Creating a single instance application < Python recipes < ActiveState Code
_Ptyhonでプロセス間排他を試す(Windows限定) | The only neEt thing to do.
_windowsで実行中のプロセスのフルパスを、pythonから調べたい
_多重起動禁止処理 for Windows
_benhoyt/namedmutex: namedmutex.py, a simple ctypes wrapper for Win32 named mutexes
_Windows上で多重起動を防止する方法
_Module win32event
Windowsの場合、Mutex なるものを使うと多重起動してるかどうかを判別することができる模様。キーワードとして「python CreateMutex」でググれば情報に辿り着けそう。
その Mutex を利用する方法として、以下の2つがあるようで。
_Creating a single instance application < Python recipes < ActiveState Code
_Ptyhonでプロセス間排他を試す(Windows限定) | The only neEt thing to do.
_windowsで実行中のプロセスのフルパスを、pythonから調べたい
_多重起動禁止処理 for Windows
_benhoyt/namedmutex: namedmutex.py, a simple ctypes wrapper for Win32 named mutexes
_Windows上で多重起動を防止する方法
_Module win32event
Windowsの場合、Mutex なるものを使うと多重起動してるかどうかを判別することができる模様。キーワードとして「python CreateMutex」でググれば情報に辿り着けそう。
その Mutex を利用する方法として、以下の2つがあるようで。
- win32event、win32api、winerror等を経由して呼び出す。
- ctypes モジュールで呼び出す。
◎ ソースその1。win32event版。 :
まずは win32event等を経由して使う方法を試してみた。動作には pywin32 のインストールが必要。今回は pywin32 304 がインストールされた。
_01_mutex.py
python 01_mutex.py で実行すると、1秒毎に、0 から 9 までの数字を出す。
DOS窓を複数開いておいて、どこかのDOS窓でスクリプトを動かしてから、すかさず別のDOS窓で同じスクリプトを動かしてみると、後から実行したほうは「〜 already exists」と出力して即座に終了してくれた。たしかに、多重起動を禁止するスクリプトになってくれた模様。
ちょっとよく分からなかったのが、win32event.ReleaseMutex(mtx) を呼ぶと必ずエラーになること。これは呼ばなくてもいいのだろうか…? クローズ処理として win32api.CloseHandle(mtx) か mtx.Close() は呼んでおくらしいけど…。巷のサンプルを眺めても、win32event.ReleaseMutex(mtx) を呼んでる事例は見かけなかったので、呼ばなくてもいいのかもしれない。
pip install pywin32
_01_mutex.py
import win32event import win32api import winerror import win32security import time import sys MUTEXNAME = "python_mutex_sample_01" def main(): sa = win32security.SECURITY_ATTRIBUTES() sa.SECURITY_DESCRIPTOR.SetSecurityDescriptorDacl(True, None, False) mtx = win32event.CreateMutex(sa, False, MUTEXNAME) err = win32api.GetLastError() if not mtx or err == winerror.ERROR_ALREADY_EXISTS: # Process exists print("%s already exists" % MUTEXNAME) sys.exit(0) else: # New process print("New process.") for i in range(10): print(i) time.sleep(1) if mtx: # win32event.ReleaseMutex(mtx) # win32api.CloseHandle(mtx) mtx.Close() if __name__ == '__main__': main()
- Mutex を作る際は、他のプロセスと被らないような独自の文字列を渡す。
- CreateMutex() を呼んだ直後にエラー情報を調べることで、その Mutex が既にあるかどうかが分かる模様。
- 処理が終わったら、mtx.Close() を呼んでハンドルをクローズする。
python 01_mutex.py で実行すると、1秒毎に、0 から 9 までの数字を出す。
DOS窓を複数開いておいて、どこかのDOS窓でスクリプトを動かしてから、すかさず別のDOS窓で同じスクリプトを動かしてみると、後から実行したほうは「〜 already exists」と出力して即座に終了してくれた。たしかに、多重起動を禁止するスクリプトになってくれた模様。
ちょっとよく分からなかったのが、win32event.ReleaseMutex(mtx) を呼ぶと必ずエラーになること。これは呼ばなくてもいいのだろうか…? クローズ処理として win32api.CloseHandle(mtx) か mtx.Close() は呼んでおくらしいけど…。巷のサンプルを眺めても、win32event.ReleaseMutex(mtx) を呼んでる事例は見かけなかったので、呼ばなくてもいいのかもしれない。
◎ ソースその2。ctypes版。 :
ctypesを使って呼び出す方法も試してみた。
_02_mutex_ctypes.py
インポートするモジュール数は少なくなってくれた気がする。ただ、ソースが少し分かりづらく…。いや、あまり違いはないか…。
ctypes 経由で呼び出す版は、最後に .ReleaseMutex() と .CloseHandle() を呼び出してもエラーにならなかった。
_02_mutex_ctypes.py
import ctypes import time MUTEXNAME = "python_mutex_sample_02" def main(): knl32 = ctypes.windll.Kernel32 mtx = knl32.CreateMutexA(0, 1, MUTEXNAME) result = knl32.WaitForSingleObject(mtx, 0) if result != 0: print("%s already exists" % MUTEXNAME) else: print("New process.") for i in range(10): print(i) time.sleep(1) knl32.ReleaseMutex(mtx) knl32.CloseHandle(mtx) if __name__ == '__main__': main()
インポートするモジュール数は少なくなってくれた気がする。ただ、ソースが少し分かりづらく…。いや、あまり違いはないか…。
ctypes 経由で呼び出す版は、最後に .ReleaseMutex() と .CloseHandle() を呼び出してもエラーにならなかった。
[ ツッコむ ]
#2 [python][pygame][windows] pygameでWindows用スクリーンセーバを作る
Windows10 x64 21H2 + Python 3.8.10 64bit + pygame 1.9.6 を使って、Windows用のスクリーンセーバが作れそうかどうか試してみた。
動作画面は以下のような感じ。解像度が低過ぎてアレだけど、雰囲気ぐらいは分かるかなと。また、かなり後で話が出てくるけれど、プレビューが表示されるまで妙に待たされてることも分かるかと思う。
動作画面は以下のような感じ。解像度が低過ぎてアレだけど、雰囲気ぐらいは分かるかなと。また、かなり後で話が出てくるけれど、プレビューが表示されるまで妙に待たされてることも分かるかと思う。
◎ 制限事項。 :
pygame でWindows用スクリーンセーバを作成するにあたって、いくつか制限事項(?)がある。
- pygame は 1.9.x であること。pygame 2.x.x は、環境変数 SDL_WINDOWID が反映されないので作れない。(pygame 1.x.x はSDL1.xを、pygame 2.x.x はSDL2を使っていて、SLD2 は件の環境変数が反映されない。)
- pygame 1.9.x の最終バージョン、pygame 1.9.6 は、Python 3.8 までの対応なので、pygame 1.9.6 を使いたいなら、Python 2.7 - 3.8 のどれかで作ることになる。
◎ Windows用スクリーンセーバについてのおさらい。 :
Windows用スクリーンセーバ(.scr)はどんな仕様を要求されるのか、念のために再度列挙してみる。
コマンドラインオプションについて。
「/p xxxx」にも対応させることを考えると、以下の条件を満たすプログラミング言語/ライブラリ/フレームワークなら、Windows用スクリーンセーバを作れることになる。
多重起動禁止処理について。
インストール場所について。
- Windows用スクリーンセーバは、.exe を .scr にリネームしたもの。
コマンドラインオプションについて。
- .scr は、3種類のコマンドラインオプション、「/s」「/c:xxxx」「/p xxxx」のどれかが渡されて実行される。
- .scrファイルを右クリックして出てくるメニューから、「テスト」「構成」等を選んだ場合、コマンドラインオプションがつかない状態で実行される場合もある。
- /s、/c、/p は、大文字だったり小文字だったりするので、プログラム側で大文字化、または小文字化をしてから判別したほうが良い。
- /s はフルスクリーン表示モード。キーボード操作やマウス操作を検出して、操作があったら自分で終了するように作っておく。
- /c:xxxx は、そのスクリーンセーバの設定ダイアログ表示モード。本来は設定内容を .ini やレジストリ等に記録することになるけれど、特に設定項目が無いなら、スクリーンセーバ名を表示するダイアログが出るだけで十分。
- /p xxxx は、スクリーンセーバ設定画面のプレビュー窓の中に表示するモード。xxxx はプレビュー窓のウインドウハンドル。
「/p xxxx」にも対応させることを考えると、以下の条件を満たすプログラミング言語/ライブラリ/フレームワークなら、Windows用スクリーンセーバを作れることになる。
- 与えられたウインドウハンドルからウインドウサイズを取得できて、
- そのウインドウハンドルのウインドウに対して描画ができる。
多重起動禁止処理について。
- スクリーンセーバ設定画面で何かしら操作をすると、その都度 /p xxxx つきでスクリーンセーバが何度も何度も実行されるので、特に何もしないと同じ処理をするプロセスが2つも3つも走ってしまう。多重起動を抑止する処理が必要になる。
- スクリーンセーバが実行されるまでの時間の間隔で、スクリーンセーバが何度も実行される可能性がある。フルスクリーン表示時も、一応、多重起動の禁止処理を入れておいたほうが安心。
インストール場所について。
- Windows が64bit版、スクリーンセーバが32bit版のプログラムなら、C:\Windows\SysWOW64\ 以下に .scr をコピーする。
- Windows が64bit版、スクリーンセーバが64bit版のプログラムなら、C:\Windows\System32\ 以下に .scr をコピーする。
- Windows が32bit版なら、C:\Windows\System32\ 以下に .scr をコピーする。
◎ 開発環境。 :
今回は、Windows10 x64 21H2上で Python 3.9.13 64bit版や 3.8.10 64bit版をインストールしてある状態で、virtualenv を使って Python 3.8 64bit の開発環境を用意してから作業した。
後々、PyInstaller や Nuitka を使って、.py ファイルをexe化するわけだけど。不要なモジュールまで exe に含められるのは困るので、必要最低限のモジュールしか入っていないクリーン(?)な環境を用意できたほうが exe化するには都合がいい。まあ、virtualenv ではなく、venv 等を使っても同じことはできると思うけど…。
virtualenv のインストールと、Python 3.8 の開発環境を作成。
開発環境の有効化。
Pythonのバージョンを確認。
ちなみに、開発環境の無効化は以下。
後々、PyInstaller や Nuitka を使って、.py ファイルをexe化するわけだけど。不要なモジュールまで exe に含められるのは困るので、必要最低限のモジュールしか入っていないクリーン(?)な環境を用意できたほうが exe化するには都合がいい。まあ、virtualenv ではなく、venv 等を使っても同じことはできると思うけど…。
virtualenv のインストールと、Python 3.8 の開発環境を作成。
python -m pip install -U setuptools virtualenv virtualenv -p python3.8 venv38
開発環境の有効化。
.\env38\Scripts\activate
Pythonのバージョンを確認。
python -V
(venv38) ... > python -V Python 3.8.10
ちなみに、開発環境の無効化は以下。
deactivate
◎ 必要なモジュールをインストール。 :
スクリーンセーバを作る際に必要になるモジュールをインストール。pygame、pywin32 と、exe化するための何かしらが入っていれば済む。
手元の開発環境では、以下が入っている。余計なモジュールも入ってるけど…。(Pythonスクリプトの整形ツール、autopep8 とか…。)
python -m pip install pygame==1.9.6 python -m pip install pywin32 python -m pip install pyinstaller python -m pip install nuitka zstandard orderedset
手元の開発環境では、以下が入っている。余計なモジュールも入ってるけど…。(Pythonスクリプトの整形ツール、autopep8 とか…。)
(venv38) ... > python -m pip list Package Version ------------------------- --------- altgraph 0.17.2 autopep8 1.7.0 cachetools 5.2.0 future 0.18.2 Nuitka 1.0.3 orderedset 2.0.3 pefile 2022.5.30 pip 22.2.2 pycodestyle 2.9.1 pygame 1.9.6 pyinstaller 5.3 pyinstaller-hooks-contrib 2022.8 pywin32 304 pywin32-ctypes 0.2.0 setuptools 63.4.1 toml 0.10.2 wheel 0.37.1 zstandard 0.18.0
◎ ソース。 :
Python 3.8 + pygame 1.9.6 + pywin32 で作成した、Windows用スクリーンセーバのソースは以下。画面の中を赤いボールが跳ね回るだけの、いつもの処理内容。
_scrsavpygame.py
使用画像は以下。scrsavpygame.py と同階層に、resources というディレクトリを作成して、その中に入れておく。
_ball_64x64.png
_preview_bg.png
_preview_logo.png
_scrsavpygame.py
import ctypes import datetime import math import os import sys import time import win32api import win32event import win32gui import win32security import winerror DBG = False IMG_NAME = "resources/ball_64x64.png" PREVIEW_BG_IMG = "resources/preview_bg.png" PREVIEW_LOGO_IMG = "resources/preview_logo.png" APPLI_NAME = "screensaver sample by using pygame" VER_NUM = "0.0.1" MUTEX_NAME_FILLSCR = "screensaver_pygame_fullscr" MUTEX_NAME_CONFIG = "screensaver_pygame_config" MUTEX_NAME_PREVIEW = "screensaver_pygame_preview" def resource_path(filename): """Get resource file path.""" if hasattr(sys, "_MEIPASS"): # use PyInstaller base_dir = sys._MEIPASS else: base_dir = os.path.abspath(".") return os.path.join(base_dir, filename) def full_screen(screen): """drawing pygame window.""" # invisivle mouse cursor pygame.mouse.set_visible(False) # load image img = pygame.image.load(resource_path(IMG_NAME)).convert() img.set_colorkey(img.get_at((0, 0)), pygame.RLEACCEL) # initialize work scrw, scrh = screen.get_width(), screen.get_height() x, y = scrw / 2, scrh / 2 dx = float(scrw) / (60 * 2) dy = float(scrh) / (60 * 3) clock = pygame.time.Clock() counter = 0 looping = True # main loop while looping: # check key, mouse for event in pygame.event.get(): if event.type == pygame.QUIT: looping = False if event.type == pygame.KEYUP: # any key up looping = False if event.type == pygame.MOUSEMOTION: # move mouse if counter >= 120: looping = False if not looping: break # update sprite position w, h = img.get_width(), img.get_height() x += dx y += dy if x <= (w / 2) or x >= scrw - (w / 2): dx *= -1 if y <= (h / 2) or y >= scrh - (h / 2): dy *= -1 # clear bg screen.fill((16, 16, 16)) # draw sprite px, py = int(x - (w / 2)), int(y - (h / 2)) screen.blit(img, (px, py)) counter += 1 pygame.display.flip() clock.tick(60) # visible mouse cursor pygame.mouse.set_visible(True) def preview(screen): frames = 360 * 2 bgimg = pygame.image.load(resource_path(PREVIEW_BG_IMG)).convert() logoimg = pygame.image.load(resource_path(PREVIEW_LOGO_IMG)).convert() logoimg.set_colorkey(logoimg.get_at((0, 0)), pygame.RLEACCEL) clock = pygame.time.Clock() ang = 0 looping = True for i in range(frames): for event in pygame.event.get(): if event.type == pygame.QUIT: looping = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: looping = False if not looping: break scrw, scrh = screen.get_width(), screen.get_height() screen.fill((100, 150, 200)) iw, ih = bgimg.get_width(), bgimg.get_height() px = int((scrw - iw) / 2) py = int((scrh - ih) / 2) screen.blit(bgimg, (px, py)) iw, ih = logoimg.get_width(), logoimg.get_height() px = int((scrw - iw) / 2) py = int((scrh * 0.15) * math.sin(math.radians(ang)) + (scrh / 2) - (ih / 2)) screen.blit(logoimg, (px, py)) ang += 3 pygame.display.flip() clock.tick(60) def get_window_size(hwnd): """Get window size.""" rect = win32gui.GetWindowRect(hwnd) x0, y0, x1, y1 = rect[0], rect[1], rect[2], rect[3] return ((x1 - x0), (y1 - y0)) def msg_box(msg, title): """Display MessageBox by Windows API.""" user32 = ctypes.WinDLL("user32") # user32.MessageBoxW.restype = ctypes.c_int32 # user32.MessageBoxW.argtypes = (ctypes.c_void_p, ctypes.c_wchar_p, # ctypes.c_wchar_p, ctypes.c_uint32) user32.MessageBoxW(0, msg, title, 0x00000040) def write_log(s): global DBG if DBG: desktop_dir = os.path.expanduser('~/Desktop') log_file = os.path.join(desktop_dir, "temp.log.txt") dt_now = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") with open(log_file, 'a') as f: f.write("[%s] %s\n" % (dt_now, s)) def get_mutex(name): sa = win32security.SECURITY_ATTRIBUTES() sa.SECURITY_DESCRIPTOR.SetSecurityDescriptorDacl(True, None, False) mtx = win32event.CreateMutex(sa, False, name) err = win32api.GetLastError() if not mtx or err == winerror.ERROR_ALREADY_EXISTS: return (mtx, False) return (mtx, True) def close_mutex(mtx): global cmdopt if mtx: # win32event.ReleaseMutex(mtx) # win32api.CloseHandle(mtx) mtx.Close() write_log("%s : Close mutex. Exit." % cmdopt) # ---------------------------------------- # parse commandline option kind = "None" hwnd = 0 if len(sys.argv) >= 2: s = sys.argv[1][0:2].lower() if s == "/p": kind = "preview" hwnd = int(sys.argv[2]) elif s == "/c": kind = "config" elif s == "/s": kind = "fullscreen" cmdopt = " ".join(sys.argv) if kind == "config": # ---------------------------------------- # config / setting mtx, success = get_mutex(MUTEX_NAME_CONFIG) if not success: # Process exists write_log("%s : %s already exists. Exit." % (cmdopt, MUTEX_NAME_CONFIG)) sys.exit(0) # new process write_log("%s : New process." % cmdopt) msg_box("%s\nver.%s" % (APPLI_NAME, VER_NUM), "Information") close_mutex(mtx) sys.exit() if kind == "preview": # ---------------------------------------- # preview mtx, success = get_mutex(MUTEX_NAME_PREVIEW) if not success: write_log("%s : %s already exists. Exit." % (cmdopt, MUTEX_NAME_PREVIEW)) sys.exit(0) os.environ['SDL_VIDEODRIVER'] = 'windib' if hwnd != 0: # set SDL_WINDOWID os.environ['SDL_WINDOWID'] = str(hwnd) wdw_size = get_window_size(hwnd) write_log("Windows size: %d x %d" % (wdw_size)) else: wdw_size = (152, 112) write_log("%s : New process. hwnd = %d , Wdw size = %d x %d" % (cmdopt, hwnd, wdw_size[0], wdw_size[1])) import pygame # nopep8 pygame.init() screen = pygame.display.set_mode(wdw_size) preview(screen) pygame.quit() close_mutex(mtx) sys.exit() # ---------------------------------------- # full screen mtx, success = get_mutex(MUTEX_NAME_FILLSCR) if not success: # Process exists write_log("%s : %s already exists. Exit." % (cmdopt, MUTEX_NAME_FILLSCR)) sys.exit(0) # New process write_log("%s : New process." % cmdopt) import pygame # nopep8 pygame.init() screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) full_screen(screen) pygame.quit() close_mutex(mtx) sys.exit()
使用画像は以下。scrsavpygame.py と同階層に、resources というディレクトリを作成して、その中に入れておく。
_ball_64x64.png
_preview_bg.png
_preview_logo.png
(venv38) ... > tree /A /f D:. | scrsavpygame.py | +---resources ball_64x64.png preview_bg.png preview_logo.png
◎ ソースについて少し説明。 :
/p xxxx を渡して実行した時(プレビューモード)の動作は、一定時間アニメーションを表示して、その後終了する作りにしてみた。
当初、与えられたウインドウハンドルに対して何かを描画して即座に終了する処理で十分だろうと思っていたのだけど。その方法を実際に試してみたら、描画内容が全く反映されず。どうやら数秒ほど延々と描画を続けてみないと、見た目が反映されないようだなと…。何故かは分からないけれど…。
しかし、そのままいつまでもアニメーションを描画し続けるわけにもいかなくて。スクリーンセーバ設定画面が閉じられた際、スクリーンセーバも終了しなければいけないはずだけど、どうすればスクリーンセーバ設定画面の終了を検出できるのか、そこが分からない。仕方なく、一定時間が経過したら問答無用で終了してしまうことにしてお茶を濁した。このあたり、解決策はあるのだろうか…?
多重起動抑止については、以下を参照のこと。
_Pythonスクリプトの多重起動を抑止したい
環境変数 SDL_WINDOWID を使って、pygame のウインドウを埋め込む処理については、以下を参照のこと。
_tkinterの中にpygameを埋め込む
当初、与えられたウインドウハンドルに対して何かを描画して即座に終了する処理で十分だろうと思っていたのだけど。その方法を実際に試してみたら、描画内容が全く反映されず。どうやら数秒ほど延々と描画を続けてみないと、見た目が反映されないようだなと…。何故かは分からないけれど…。
しかし、そのままいつまでもアニメーションを描画し続けるわけにもいかなくて。スクリーンセーバ設定画面が閉じられた際、スクリーンセーバも終了しなければいけないはずだけど、どうすればスクリーンセーバ設定画面の終了を検出できるのか、そこが分からない。仕方なく、一定時間が経過したら問答無用で終了してしまうことにしてお茶を濁した。このあたり、解決策はあるのだろうか…?
多重起動抑止については、以下を参照のこと。
_Pythonスクリプトの多重起動を抑止したい
環境変数 SDL_WINDOWID を使って、pygame のウインドウを埋め込む処理については、以下を参照のこと。
_tkinterの中にpygameを埋め込む
◎ Pythonスクリプトとして動作確認。 :
動作確認は以下。
「/p 0」を渡したときは、仮で小さなウインドウを生成して、その中にプレビュー窓用の内容をテスト描画するようにしてみた。本番では、例えば「/p 1049354」のように、何かしらの大きな数値(実在するウインドウハンドル)が渡されるはず。
python scrsavpygame.py /s python scrsavpygame.py /c python scrsavpygame.py /p 0 python scrsavpygame.py
「/p 0」を渡したときは、仮で小さなウインドウを生成して、その中にプレビュー窓用の内容をテスト描画するようにしてみた。本番では、例えば「/p 1049354」のように、何かしらの大きな数値(実在するウインドウハンドル)が渡されるはず。
◎ exeに変換。 :
Windows用スクリーンセーバとして利用するためには、ファイルの拡張子が .scr でなければならず、その .scr は .exe をリネームして作られるわけで…。つまり、この Pythonスクリプトをexe化しないと、スクリーンセーバとして利用できない。
今回は PyInstaller 5.3 を使ってexe化してみた。
まずは、以下を打って変換する。
distディレクトリが作られて、その中に scrsavpygame.exe というファイルが出来上がった。また、scrsavpygame.spec というファイルも生成された。
ただ、この状態では、動作に必要な画像ファイル群が ―― resources/*.png が同梱されていない。
scrsavpygame.spec を編集して、画像ファイル群も同梱させる指定をする。
「Tree('resources',prefix='resources'),」という行を挿入することで、「resourcesディレクトリも同梱せよ」と指示してる。
今度は、.specファイルを指定してexe化する。
dist/scrsavpygame.exe を実行して動作確認をしておく。
動作したら、.scr にリネームコピー。
出来上がった .scr を、既定の場所にコピーしてインストールする。
今回は PyInstaller 5.3 を使ってexe化してみた。
python -m pip install pyinstaller -U
まずは、以下を打って変換する。
pyinstaller --onefile --noconsole --noupx scrsavpygame.py
distディレクトリが作られて、その中に scrsavpygame.exe というファイルが出来上がった。また、scrsavpygame.spec というファイルも生成された。
ただ、この状態では、動作に必要な画像ファイル群が ―― resources/*.png が同梱されていない。
scrsavpygame.spec を編集して、画像ファイル群も同梱させる指定をする。
exe = EXE( pyz, a.scripts, a.binaries, ↓ exe = EXE( pyz, Tree('resources',prefix='resources'), a.scripts, a.binaries,
「Tree('resources',prefix='resources'),」という行を挿入することで、「resourcesディレクトリも同梱せよ」と指示してる。
今度は、.specファイルを指定してexe化する。
pyinstaller scrsavpygame.spec
dist/scrsavpygame.exe を実行して動作確認をしておく。
scrsavpygame.exe /s scrsavpygame.exe /c scrsavpygame.exe /p 0 scrsavpygame.exe
動作したら、.scr にリネームコピー。
copy scrsavpygame.exe scrsavpygame.scr
出来上がった .scr を、既定の場所にコピーしてインストールする。
- Windows が64bit版、スクリーンセーバが32bit版のプログラムなら、C:\Windows\SysWOW64\ 以下に .scr をコピーする。
- Windows が64bit版、スクリーンセーバが64bit版のプログラムなら、C:\Windows\System32\ 以下に .scr をコピーする。
- Windows が32bit版なら、C:\Windows\System32\ 以下に .scr をコピーする。
◎ スクリーンセーバとして動作確認。 :
スクリーンセーバの設定画面を呼び出して、「scrsavpygame」が増えているか確認する。Windows10における、スクリーンセーバ設定画面の呼び出し方は以下。
スクリーンセーバ設定画面が表示されるまで、妙に待たされた気もするけれど、それでも一応動作してくれた。
そんなわけで、Python 3.8 + pygame 1.9.6 + pywin32 を使ってWindows用スクリーンセーバを作れそうだ、と分かった。
- デスクトップの何もないところを右クリック → 個人用設定 → ロック画面 → スクリーンセーバー設定。
- あるいは、Windows10の左下の検索欄に「スクリーンセーバー」と打てば、「スクリーンセーバーの変更」という項目が出てくるので、それを選んでもいい。
スクリーンセーバ設定画面が表示されるまで、妙に待たされた気もするけれど、それでも一応動作してくれた。
そんなわけで、Python 3.8 + pygame 1.9.6 + pywin32 を使ってWindows用スクリーンセーバを作れそうだ、と分かった。
◎ 問題点。 :
一応それらしく動いたものの。現状では、ちょっと問題が…。
スクリーンセーバの設定画面で「scrsavpygame」を選ぶと、プレビュー窓の中に何かが表示されるまで随分待たされる…。おそらくだけど、以下が原因なのではなかろうか。
更に、出来上がった .scr に対して、Windows Defender が「これはウイルスだ!」と誤判定してきた。
PyInstaller を使って生成した .exe はウイルスとして誤判定されやすいらしい。巷のウイルスが PyInstaller等を使って作られているせいで、ウイルス対策ソフトが「PyInstaller で作られた .exe はウイルス」と決めつけてる節があるそうで。
もちろん、こうして自分で作ったプログラムだから、ウイルスなわけもなく…。Windows Defender に「このファイルはウイルス判定を除外せよ」と設定して使うしかなさそう。
ただ、出来上がった .scr を配布したいと思った場合はフツーに困る…。ダウンロードした人達が「これ、ウイルスじゃん!」と慌てちゃうだろうな…。
スクリーンセーバの設定画面で「scrsavpygame」を選ぶと、プレビュー窓の中に何かが表示されるまで随分待たされる…。おそらくだけど、以下が原因なのではなかろうか。
- PyInstaller で作成された .exe は、毎回一時フォルダに解凍する処理が走るので、起動がとにかく遅い。
- 毎回、Windows Defender によるウイルスチェックで時間がかかっているのかもしれない。
更に、出来上がった .scr に対して、Windows Defender が「これはウイルスだ!」と誤判定してきた。
PyInstaller を使って生成した .exe はウイルスとして誤判定されやすいらしい。巷のウイルスが PyInstaller等を使って作られているせいで、ウイルス対策ソフトが「PyInstaller で作られた .exe はウイルス」と決めつけてる節があるそうで。
もちろん、こうして自分で作ったプログラムだから、ウイルスなわけもなく…。Windows Defender に「このファイルはウイルス判定を除外せよ」と設定して使うしかなさそう。
ただ、出来上がった .scr を配布したいと思った場合はフツーに困る…。ダウンロードした人達が「これ、ウイルスじゃん!」と慌てちゃうだろうな…。
◎ Nuitkaでexe化してみる。 :
Pythonスクリプトをexe化するツールは、PyInstaller、py2exe、cx_Freeze など色々あるけれど。Pythonスクリプトを一旦C言語のソースに変換して、gccでコンパイルしてexeを作ってくれる Nuitka なるツールがあるそうで。PyInstaller で作ったexeより起動が速く、ファイルサイズも小さくなる傾向があるとのこと。興味が湧いた。試してみる。
インストールは以下。
自分の環境では、orderedset というモジュールもインストールしないと、処理が先に進まなかった。
Nuitka用に修正したソースは以下。
_scrsavpygame.py
変更点は2ヵ所。
リソースファイルパスを求める部分は以下になる。
exeへの変換は以下。
上記を実行したところ、途中で「gccその他が見つからないがダウンロードするか?」と尋ねてきた。「Yes」を入力してダウンロードすることにした。それぞれ、 C:\Users\(USERNAME)\AppData\Local\Nuitka\ 以下にキャッシュとしてダウンロードされる模様。ファイル数を調べてみたら、7923ファイル、全体で1.14GBが入っていた。まあ、Nuitka が要求する gccのバージョン等が固定されている可能性もあるので、このくらいは目を瞑ったほうが面倒臭くないのかもしれない。
数分かかって、.py と同階層に scrsavpygame.exe が作成された。ファイルサイズは 8.59MB。PyInstaller で生成した .exe は 13.06MB だったので、若干ファイルサイズも小さくなっている。
別ディレクトリに .exe をコピーして実行してみたところ、動作してくれた。
なんとなくだけど、PyInstaller で作った .exe より起動がちょっと速いような気もする。
スクリーンセーバとして利用できるか動作確認。.exe を .scr にリネームコピーして、スクリーンセーバの既定の置き場所にコピー。スクリーンセーバ設定画面を出してリストから選んだ。これも一応動いてくれた。
ただ、相変わらず、プレビュー窓内が表示されるまで結構待たされる…。こうなると、一時ディレクトリを作成して、動作に必要なファイルを全て展開してから実行するという、その仕組み自体が厳しいのかもしれない…。
ちなみに、--onefile ではなく --standalone を指定すれば、1つのファイルにまとめずに、*.dist というディレクトリに動作に必要なファイルを全てコピーした状態で .exe を生成してくれる。ファイル数は106ファイル、27.52MB だった。
つまり、1つの .exe にした場合、実行時は毎回106ファイルを一時ディレクトリに展開しているわけで…。それなりに起動が遅くて当然ですよね、という気もしてきた。
インストールは以下。
python -m pip install nuitka zstandard orderedset
自分の環境では、orderedset というモジュールもインストールしないと、処理が先に進まなかった。
Nuitka用に修正したソースは以下。
_scrsavpygame.py
変更点は2ヵ所。
- 一行目のシバン(shebang)を、「#!python」から「#!/usr/bin/python3.8」に変更。この変更をしないと、「Pythonのバージョンが違う」と文句を言ってきて処理が進まなかった。
- 画像ファイル(リソースファイル)を読み込む際の、ファイルパスの求め方を修正。
リソースファイルパスを求める部分は以下になる。
def resource_path(filename): """Get resource file path.""" if False: if hasattr(sys, "_MEIPASS"): # use PyInstaller base_dir = sys._MEIPASS else: base_dir = os.path.abspath(".") else: base_dir = os.path.dirname(__file__) return os.path.join(base_dir, filename)「if False:」で無効化してあるところが、PyInstaller、あるいは通常のPythonスクリプトとして実行する際の処理。Nuitka用は、os.path.dirname(__file__) で基準ディレクトリ(?)を求めることになる模様。
exeへの変換は以下。
python -m nuitka --mingw64 --windows-disable-console --include-data-file=".\\resources\\*.png=.\\resources\\" --onefile scrsavpygame.py
- python -m nuika : nuika の呼び出し。
- --mingw64 : mingw64 (gcc) を使うことを指定。
- --windows-disable-console : exe実行時にコンソールを非表示にする。
- --include-data-file=".\\resources\\*.png=.\\resources\\" : リソースファイル(*.png)を同梱させる。
- --onefile : リソースファイルも含めて1つのexeファイルにする。
- scrsavpygame.py : exe化したいPythonスクリプト名。
上記を実行したところ、途中で「gccその他が見つからないがダウンロードするか?」と尋ねてきた。「Yes」を入力してダウンロードすることにした。それぞれ、 C:\Users\(USERNAME)\AppData\Local\Nuitka\ 以下にキャッシュとしてダウンロードされる模様。ファイル数を調べてみたら、7923ファイル、全体で1.14GBが入っていた。まあ、Nuitka が要求する gccのバージョン等が固定されている可能性もあるので、このくらいは目を瞑ったほうが面倒臭くないのかもしれない。
数分かかって、.py と同階層に scrsavpygame.exe が作成された。ファイルサイズは 8.59MB。PyInstaller で生成した .exe は 13.06MB だったので、若干ファイルサイズも小さくなっている。
別ディレクトリに .exe をコピーして実行してみたところ、動作してくれた。
scrsavpygame.exe /s scrsavpygame.exe /c scrsavpygame.exe /p 0 scrsavpygame.exe
なんとなくだけど、PyInstaller で作った .exe より起動がちょっと速いような気もする。
スクリーンセーバとして利用できるか動作確認。.exe を .scr にリネームコピーして、スクリーンセーバの既定の置き場所にコピー。スクリーンセーバ設定画面を出してリストから選んだ。これも一応動いてくれた。
ただ、相変わらず、プレビュー窓内が表示されるまで結構待たされる…。こうなると、一時ディレクトリを作成して、動作に必要なファイルを全て展開してから実行するという、その仕組み自体が厳しいのかもしれない…。
ちなみに、--onefile ではなく --standalone を指定すれば、1つのファイルにまとめずに、*.dist というディレクトリに動作に必要なファイルを全てコピーした状態で .exe を生成してくれる。ファイル数は106ファイル、27.52MB だった。
つまり、1つの .exe にした場合、実行時は毎回106ファイルを一時ディレクトリに展開しているわけで…。それなりに起動が遅くて当然ですよね、という気もしてきた。
◎ 参考ページ。 :
_Pyinstaller でリソースを含めたexeを作成する - Qiita
_UI に画像を埋め込んだ Python プログラムを exe化する - NakaNote
_【python, pyinstaller】画像や音楽などの外部ファイルも一括でexe化して配布する - MSBOOKS
_Pythonのexe化で画像を組み込みたい
_python - Pyinstaller and --onefile: How to include an image in the exe file - Stack Overflow
_PyInstaller より圧倒的に優れている Nuitka の使い方とハマったポイント | つくみ島だより
_KivyのGUIアプリをNuitkaで簡単に小サイズの実行ファイル(exe)にする(Windows10) - Qiita
_Nuitkaで失敗しやすいポイントを解説 - Qiita
_Nuitkaを使ってスクリプトをバイナリ化してみよう - PythonOsaka
_Kivyで作ったアプリをNuitkaでexeファイル化した時の試行錯誤のメモの巻
_UI に画像を埋め込んだ Python プログラムを exe化する - NakaNote
_【python, pyinstaller】画像や音楽などの外部ファイルも一括でexe化して配布する - MSBOOKS
_Pythonのexe化で画像を組み込みたい
_python - Pyinstaller and --onefile: How to include an image in the exe file - Stack Overflow
_PyInstaller より圧倒的に優れている Nuitka の使い方とハマったポイント | つくみ島だより
_KivyのGUIアプリをNuitkaで簡単に小サイズの実行ファイル(exe)にする(Windows10) - Qiita
_Nuitkaで失敗しやすいポイントを解説 - Qiita
_Nuitkaを使ってスクリプトをバイナリ化してみよう - PythonOsaka
_Kivyで作ったアプリをNuitkaでexeファイル化した時の試行錯誤のメモの巻
[ ツッコむ ]
2022/08/15(月) [n年前の日記]
#1 [ruby][windows] Ruby/SDLでスクリーンセーバを作れないものか
Python + pygame を使ってWindows用スクリーンセーバを作れそうなことは分かったけれど。
_pygameでWindows用スクリーンセーバを作る
Ruby/SDL でも似たようなことができないものかと疑問が湧いた。
Ruby/SDL も SDL1.x を使っていた記憶があるし、ドキュメントを眺めると SDL_WINDOWID という環境変数名が書かれていたので、コマンドラインオプション経由で得られたウインドウハンドルを使って、そのウインドウに Ruby/SDL のウインドウを埋め込むことができそうだなと。
_Ruby/SDL Reference Manual (2.x)
そんなわけで関連情報を色々ググって調べてた。
余談。Ruby/SDL 2.x のリファレンスマニュアルは Unicode (UTF-8) で書かれているっぽいのだけど、何故かhtmlソースの最初で euc-jp が指定されていて文字化けしてしまう…。Google Chrome なら「テキストエンコーディング」という拡張機能をインストールすれば文字コードを手動で選べる。
_Chromeでサイトを見ると文字化けするときの対処法 | シゴトハカドル
Firefox なら、Altキーを押して、表示 → テキストエンコーディングを修復、で文字化けが直ると思う。たぶん。
_pygameでWindows用スクリーンセーバを作る
Ruby/SDL でも似たようなことができないものかと疑問が湧いた。
Ruby/SDL も SDL1.x を使っていた記憶があるし、ドキュメントを眺めると SDL_WINDOWID という環境変数名が書かれていたので、コマンドラインオプション経由で得られたウインドウハンドルを使って、そのウインドウに Ruby/SDL のウインドウを埋め込むことができそうだなと。
_Ruby/SDL Reference Manual (2.x)
そんなわけで関連情報を色々ググって調べてた。
余談。Ruby/SDL 2.x のリファレンスマニュアルは Unicode (UTF-8) で書かれているっぽいのだけど、何故かhtmlソースの最初で euc-jp が指定されていて文字化けしてしまう…。Google Chrome なら「テキストエンコーディング」という拡張機能をインストールすれば文字コードを手動で選べる。
_Chromeでサイトを見ると文字化けするときの対処法 | シゴトハカドル
Firefox なら、Altキーを押して、表示 → テキストエンコーディングを修復、で文字化けが直ると思う。たぶん。
◎ Rubyのバージョン。 :
まず、Windows上で Ruby/SDL を使えるRubyのバージョンを調べた。Ruby 1.8 とRuby 1.9 なら動きそう。手元の環境、Windows10 x64 21H2上では、以下の2つが利用できた。
_Download Archives
どちらも32bit版であること。
どちらも32bit版であること。
大事なことなので2回言いました。Ruby/SDL のセットアップ用ファイルに含まれている各種バイナリが32bit版なので、64bit版 Ruby と組み合わせて動かそうとしてもダメだったはず。
ちなみに、Windows上でRubyのバージョンを切り替えたい時は、pik 0.2.8 もしくは uru 0.8.5 というツールが使える。
_vertiginous/pik: Ruby version manager for Windows
_jonforums / uru - Bitbucket
_uruでRubyを切り替えできるようにする - Qiita
_WindowsのRubyのバージョンをuruで切り替える方法 | TechAcademyマガジン
- ActiveScriptRuby 1.8.7 p330 i386-mswin32 (ActiveRuby.msi)
- Ruby 1.9.3 p551 i386-mingw32 (rubyinstaller-1.9.3-p551.exe)
_Download Archives
どちらも32bit版であること。
どちらも32bit版であること。
大事なことなので2回言いました。Ruby/SDL のセットアップ用ファイルに含まれている各種バイナリが32bit版なので、64bit版 Ruby と組み合わせて動かそうとしてもダメだったはず。
ちなみに、Windows上でRubyのバージョンを切り替えたい時は、pik 0.2.8 もしくは uru 0.8.5 というツールが使える。
_vertiginous/pik: Ruby version manager for Windows
_jonforums / uru - Bitbucket
_uruでRubyを切り替えできるようにする - Qiita
_WindowsのRubyのバージョンをuruで切り替える方法 | TechAcademyマガジン
◎ Ruby/SDLの入手。 :
◎ GetWindowRect や CreateMutex について。 :
スクリーンセーバを作るには、ウインドウサイズを取得したり、多重起動を抑止したりしないといけない。
となると、Ruby から .dll を使う何かしらが必要になるわけだけど…。ググったところ、win32api とか、DL とか、fiddle とか、ffi とか、そのあたりを使うことになりそうだなと分かった。
ただ、Ruby 1.8時代は win32api が使われていたけれど、Ruby 1.9 になると DL を使うことになって、Ruby 2.0以降は fiddle を使うことになっているようで…。
_DLとWin32APIとFiddle - mirichiの日記
win32api も DL も fiddle も互換性が無いそうで、Ruby 1.8 + win32api を勉強しても、Ruby 1.9 + DL を勉強しても、後々一切何の役にも立たないあたりがちょっともやもやする。
もしかして、ffi を使えば、互換性云々で悩まなくて済んだりしないのでは…? Ruby 1.9.3 p551 で使える ffi のバージョンは… ffi 1.9.14 が最終版だろうか。
_ffi | RubyGems.org
一応、上記の指定で、ffi-1.9.14-x86-mingw32.gem が落ちてきてインストールはできた。実際に動くかどうかは試してないけど…。
ただ、Ruby 1.8.7 p330 i386-mswin32版で同じことをしたら、cl.exe が無いと言われてビルドに失敗した。x86-mingw32 のバイナリを利用するわけにはいかないのかな…。
以下を打ったらインストールできたように見えるけど、果たして使えるのかどうか。
- ウインドウハンドルを元にしてウインドウサイズを取得するなら、USER32.DLL の GetWindowRect() を使えばいい。
- 多重起動を抑止するためには、KERNEL32.DLL の CreateMutex() を使えばいい。
となると、Ruby から .dll を使う何かしらが必要になるわけだけど…。ググったところ、win32api とか、DL とか、fiddle とか、ffi とか、そのあたりを使うことになりそうだなと分かった。
ただ、Ruby 1.8時代は win32api が使われていたけれど、Ruby 1.9 になると DL を使うことになって、Ruby 2.0以降は fiddle を使うことになっているようで…。
_DLとWin32APIとFiddle - mirichiの日記
win32api も DL も fiddle も互換性が無いそうで、Ruby 1.8 + win32api を勉強しても、Ruby 1.9 + DL を勉強しても、後々一切何の役にも立たないあたりがちょっともやもやする。
もしかして、ffi を使えば、互換性云々で悩まなくて済んだりしないのでは…? Ruby 1.9.3 p551 で使える ffi のバージョンは… ffi 1.9.14 が最終版だろうか。
_ffi | RubyGems.org
gem install ffi -v "1.9.14"
一応、上記の指定で、ffi-1.9.14-x86-mingw32.gem が落ちてきてインストールはできた。実際に動くかどうかは試してないけど…。
ただ、Ruby 1.8.7 p330 i386-mswin32版で同じことをしたら、cl.exe が無いと言われてビルドに失敗した。x86-mingw32 のバイナリを利用するわけにはいかないのかな…。
以下を打ったらインストールできたように見えるけど、果たして使えるのかどうか。
gem install ffi -v "1.9.14" --platform x86-mingw32
◎ exe化について。 :
exe化は、ocra を使うことになるだろうか。Ruby 1.8.7 や Ruby 1.9.3 で使えるのかどうか疑問だったけど、調べた感じでは使えそうな感じだった。Ruby 1.8 / 1.9 のどちらも、以下でインストールができた。
Ruby 1.8 限定だけど、exerb も使えるかもしれない。ただ、画像ファイルや *.so 等を1つの .exe にまとめる方法がよく分からなかった…。
※ 2022/08/19追記。Ruby 1.8.7 は ocra 1.3.1 が、Ruby 1.9.3 は ocra 1.3.10 が利用できる最終版の模様。
gem install ocra
Ruby 1.8 限定だけど、exerb も使えるかもしれない。ただ、画像ファイルや *.so 等を1つの .exe にまとめる方法がよく分からなかった…。
※ 2022/08/19追記。Ruby 1.8.7 は ocra 1.3.1 が、Ruby 1.9.3 は ocra 1.3.10 が利用できる最終版の模様。
◎ 余談。gemについて。 :
Ruby 1.8 / 1.9 は、インストールした直後から gem が使える状態になっていたのかどうか、ちょっと記憶が怪しい…。
Ruby 2.3.3 の時は、Rubygems をアップデートしないと gem が使えなかった、とメモしてあったので、ひょっとすると昔の自分は、何かそのへんやっていたかもしれないなと。
_Windows10上で Ruby 2.3.3 をインストールしようとして少しハマった
_Download RubyGems | RubyGems.org
ただ、手元の環境では gem のバージョンが古くても一応使えているように見える…。gem -v と打ってバージョンを確認したところ以下が入っていた。
まあ、gem を使って何かしらをインストールしようとしてエラーが出たら、その時に Rubygems のアップデートを検討すればいいだけの話かも。
Ruby 2.3.3 の時は、Rubygems をアップデートしないと gem が使えなかった、とメモしてあったので、ひょっとすると昔の自分は、何かそのへんやっていたかもしれないなと。
_Windows10上で Ruby 2.3.3 をインストールしようとして少しハマった
_Download RubyGems | RubyGems.org
ただ、手元の環境では gem のバージョンが古くても一応使えているように見える…。gem -v と打ってバージョンを確認したところ以下が入っていた。
- ActiveScriptRuby 1.8.7 (2010-12-23 patchlevel 330) [i386-mswin32] : gem 1.3.7
- Ruby 1.9.3 p551 (2014-11-13) [i386-mingw32] : gem 1.8.29
まあ、gem を使って何かしらをインストールしようとしてエラーが出たら、その時に Rubygems のアップデートを検討すればいいだけの話かも。
◎ 余談。Ruby 2.x.x + Ruby/SDL について。 :
Windows10 + Ruby 2.x.x 上で Ruby/SDL をインストールする方法は自分も分からないです。
たしかどこかで、DevKit(MSYS2)内にSDL関連パッケージ群を事前にインストールした上で作業すればビルドできるかも、みたいな話を見かけた記憶もあるけど。それどうやってインストールするんや…てな状態で。一応、以下のようなパッケージがあるらしいけど…。
_Base Package: mingw-w64-SDL - MSYS2 Packages
_Base Package: mingw-w64-SDL_image - MSYS2 Packages
_Base Package: mingw-w64-SDL_gfx - MSYS2 Packages
_Base Package: mingw-w64-SDL_net - MSYS2 Packages
_Base Package: mingw-w64-SDL_ttf - MSYS2 Packages
_Base Package: mingw-w64-SDL_mixer - MSYS2 Packages
これが Debian Linux や Ubuntu Linux なら、ruby-sdl というパッケージが用意されているので、sudo apt install ruby-sdl であっさりインストールできるのだけど。
_Ubuntu - jammy の ruby-sdl パッケージに関する詳細
_Debian -- bullseye の ruby-sdl パッケージに関する詳細
ちなみに、gosu という、これまた SDL を使ったゲーム制作用ライブラリは、Windows10 x64 21H2 + Ruby 2.6.10 p210 i386-mingw32 (32bit) 上でも、gem install gosu で gosu-1.4.3.gem が落ちてきて、ビルドしてインストールできてるのですが…。
たしかどこかで、DevKit(MSYS2)内にSDL関連パッケージ群を事前にインストールした上で作業すればビルドできるかも、みたいな話を見かけた記憶もあるけど。それどうやってインストールするんや…てな状態で。一応、以下のようなパッケージがあるらしいけど…。
_Base Package: mingw-w64-SDL - MSYS2 Packages
_Base Package: mingw-w64-SDL_image - MSYS2 Packages
_Base Package: mingw-w64-SDL_gfx - MSYS2 Packages
_Base Package: mingw-w64-SDL_net - MSYS2 Packages
_Base Package: mingw-w64-SDL_ttf - MSYS2 Packages
_Base Package: mingw-w64-SDL_mixer - MSYS2 Packages
これが Debian Linux や Ubuntu Linux なら、ruby-sdl というパッケージが用意されているので、sudo apt install ruby-sdl であっさりインストールできるのだけど。
_Ubuntu - jammy の ruby-sdl パッケージに関する詳細
_Debian -- bullseye の ruby-sdl パッケージに関する詳細
ちなみに、gosu という、これまた SDL を使ったゲーム制作用ライブラリは、Windows10 x64 21H2 + Ruby 2.6.10 p210 i386-mingw32 (32bit) 上でも、gem install gosu で gosu-1.4.3.gem が落ちてきて、ビルドしてインストールできてるのですが…。
[ ツッコむ ]
2022/08/16(火) [n年前の日記]
#1 [ruby] Ruby + ffi の動作確認をした
Windows10 x64 21H2上で、Ruby + ffi の動作確認をしてみた。ffi というのは、Ruby から .dll や .so の中にある関数を呼び出せるようになる外部ライブラリ。という説明で合ってるのかどうか…。
_ffi | RubyGems.org
_ffiの全バージョン履歴 | RubyGems.org
_ffi | RubyGems.org
_ffiの全バージョン履歴 | RubyGems.org
◎ 動作確認に使ったソース。 :
以下で紹介されていたソースを使わせてもらって動作確認した。user32.dll と MessageBoxA() を使って、メッセージボックスを表示するだけのスクリプト。
_winapi - What are the Ruby Win32API Parameters | How do I pass a null pointer? - Stack Overflow
_02_messagebox_ffi.rb
_winapi - What are the Ruby Win32API Parameters | How do I pass a null pointer? - Stack Overflow
_02_messagebox_ffi.rb
require "rubygems" require 'ffi' module Win32 extend FFI::Library ffi_lib "user32" attach_function :messageBox, :MessageBoxA, [:pointer, :string, :string, :long], :int end rc = Win32.messageBox(nil, "Hello World", "MessageBoxA by FFI", 0x40) puts rc
◎ 動作確認環境とインストール手順。 :
Ruby 2.6.10 p210 i386-mingw32 の場合。
Ruby 1.9.3 p551 i386-mingw32 の場合。
Ruby 1.8.7 p330 i386-mswin32、Ruby 1.8.7 p330 i386-mingw32 の場合。
Ruby 1.8 までは require "rubygems" を記述しないと、rubygems でインストールしたライブラリを理由できなかったけれど。 Ruby 1.9 以降は require "rubygems" を記述しなくても動くことを忘れていて、「動かない」「動かない」と何時間も悩んでしまった…。
gem install ffiffi 1.15.5 x86-mingw32 がインストールされた。動作した。
Ruby 1.9.3 p551 i386-mingw32 の場合。
gem install ffi -v 1.9.14Ruby 1.8/1.9 に対応している ffi の最終版は 1.9.14 らしい。-v 1.9.14 でバージョンを指定してインストール。x86-mingw32版がインストールされた。動作した。
Ruby 1.8.7 p330 i386-mswin32、Ruby 1.8.7 p330 i386-mingw32 の場合。
gem install ffi -v 1.9.14 --platform x86-mingw32動作した。ただ、注意点がある。require "ffi" をする前に、require "rubygems" を記述すること。
Ruby 1.8 までは require "rubygems" を記述しないと、rubygems でインストールしたライブラリを理由できなかったけれど。 Ruby 1.9 以降は require "rubygems" を記述しなくても動くことを忘れていて、「動かない」「動かない」と何時間も悩んでしまった…。
◎ 試行錯誤の記録。 :
Ruby 1.8.7 p330 i386-mswin32 に以下でインストールしてみたが、これは動かなかった。
ビルドしないとダメなのかと、RubyInstaller版の Ruby 1.8.7 p330 i386-mingw32 + DevKit をインストールして、以下を打ったらビルドが通ったが、しかしこれも動かない。
動作していたらしきバージョンをググって探した。
_Windows XPにwatir-webdriverをインストールする | メモログ
1.0.9 なら動いていたらしい…?
_Version 1.0.7 doesn't work with ruby versions > 1.8.7 - Issue #341 - ffi/ffi
1.0.7 も動かないと言われてるな…。
ふと、突然思い出した。Ruby 1.8 時代は、rubygems でインストールしたライブラリを使う場合、何かおまじないが必要だった気がする。
ググってみた。require "rubygems" を最初に書いておかないとダメだったのでは。
require "ffi" の前に追加。
あっさり動いた…。
Ruby 1.8.7 p330 mingw32 + ffi 1.9.14 x86-mingw32 も動いた。
Ruby 1.8.7 p330 mswin32 + ffi 1.9.14 x86-mingw32 も動いた。
そういうオチか…。トホホ。
gem install ffi -v 1.9.14 --platform x86-mingw32
02_messagebox_ffi.rb:12:in `require': no such file to load -- ffi (LoadError) from 02_messagebox_ffi.rb:12
ビルドしないとダメなのかと、RubyInstaller版の Ruby 1.8.7 p330 i386-mingw32 + DevKit をインストールして、以下を打ったらビルドが通ったが、しかしこれも動かない。
gem install ffi -v 1.9.14 --platform ruby
動作していたらしきバージョンをググって探した。
_Windows XPにwatir-webdriverをインストールする | メモログ
1.0.9 なら動いていたらしい…?
gem install ffi -v 1.0.9動かなかった。
_Version 1.0.7 doesn't work with ruby versions > 1.8.7 - Issue #341 - ffi/ffi
1.0.7 も動かないと言われてるな…。
ふと、突然思い出した。Ruby 1.8 時代は、rubygems でインストールしたライブラリを使う場合、何かおまじないが必要だった気がする。
ググってみた。require "rubygems" を最初に書いておかないとダメだったのでは。
require "ffi" の前に追加。
require "rubygems" require "ffi"
あっさり動いた…。
Ruby 1.8.7 p330 mingw32 + ffi 1.9.14 x86-mingw32 も動いた。
Ruby 1.8.7 p330 mswin32 + ffi 1.9.14 x86-mingw32 も動いた。
そういうオチか…。トホホ。
◎ 余談。win32apiバージョン。 :
Windows10 x64 21H2 + Ruby 1.8.7 p330 mswin32版上で、win32api を使ってメッセージボックスを表示する例も試してみた。
_01_messagebox_win32api.rb
これも動作してくれた。
_01_messagebox_win32api.rb
require "Win32API" msgbox = Win32API.new('user32', 'MessageBoxA', %w(p p p i), 'i') msgbox.call(0, "Hello World.", "MessageBoxA", 0x40)
これも動作してくれた。
[ ツッコむ ]
2022/08/17(水) [n年前の日記]
#1 [ruby][windows] Rubyスクリプトの多重起動を抑止する処理
Windows10 x64 21H2 + Ruby 1.8 / 1.9 / 2.6 で、Rubyスクリプトの多重起動を抑止できないか試してみた。
Windowsの場合、kernel32.dll の CreateMutex() を使えば、多重起動してるかどうかを判別できるらしい。
動作確認に使った Ruby のバージョンは以下。
とりあえず、Ruby 1.8 / 1.9 の場合は win32api を使っても警告が出なかったので、Ruby 1.8 / 1.9 なら win32api を使ってこの手の処理を書けばよさそうだなと…。
また、ffi を使えば Ruby 1.8 - 2.x でも同じ記述ができるのではと期待していたけれど、そんなことはなかった。最近の ffi じゃないと記述が通らない・正常動作しない場合もあるのだなと。そうなると、Ruby 1.8 / 1.9 で、あえて ffi を使う理由は無さそうな気がする。win32api を使ったほうが悩まなくて済む。
Windowsの場合、kernel32.dll の CreateMutex() を使えば、多重起動してるかどうかを判別できるらしい。
動作確認に使った Ruby のバージョンは以下。
- Windows10 x64 21H2
- Ruby 1.8.7 p330 i386-mswin32 (ActiveScriptRuby)
- Ruby 1.9.3 p551 i386-mingw32 (RubyInstaller)
- Ruby 2.6.10 p210 i386-mingw32 (RubyInstaller)
◎ win32apiを利用して実現。 :
Ruby 1.8 時代に標準で添付されていた win32api を使って試してみた。
環境は以下。
処理内容は、1秒毎に数値を 0 〜 9 まで出力する。また、起動時に、既にどこかで同じスクリプトが動いてたら即座に終了する。
_03_mutex_win32api.rb
環境は以下。
- Windows10 x64 21H2
- Ruby 1.8.7 p330 i386-mswin32
- Ruby 1.9.3 p551 i386-mingw32
処理内容は、1秒毎に数値を 0 〜 9 まで出力する。また、起動時に、既にどこかで同じスクリプトが動いてたら即座に終了する。
_03_mutex_win32api.rb
require "Win32API" create_mutex = Win32API.new("kernel32", "CreateMutex", "llp", "l") release_mutex = Win32API.new("kernel32", "ReleaseMutex", "l", "l") close_handle = Win32API.new("kernel32", "CloseHandle", "l", "l") get_last_error = Win32API.new("kernel32", "GetLastError", "", "l") MUTEX_NAME = "ruby_win32api_mutex_sample" ERROR_ALREADY_EXISTS = 183 mutex = create_mutex.call(0, 1, MUTEX_NAME) err = get_last_error.call() puts "Mutex : #{mutex}" puts "GetLastError : #{err}" if mutex == 0 or err == ERROR_ALREADY_EXISTS puts "already exists" exit else 10.times do |i| puts i sleep 1 end end if mutex != 0 release_mutex.call(mutex) close_handle.call(mutex) end
- CreateMutex() を呼んだ直後に、GetLastError() でエラーコードを調べて、ERROR_ALREADY_EXISTS = 183 が出ていたら既にスクリプトがどこかで起動してる。
- Mutex が確保できていたら、処理終了時に ReleaseMutex() と CloseHandle() を呼んでおく。
◎ ffiを利用して実現。 :
外部ライブラリ ffi を利用して同じことが実現できないか試してみた。ffi のインストール方法は昨日のメモを参照のこと。
_Ruby + ffi の動作確認をした
動作した環境は、Windows10 x64 21H2 + Ruby 2.6.10 p210 i386-mingw32 + ffi 1.15.5 x86-mingw32。
残念ながら、Ruby 1.8 / 1.9 (+ ffi 1.9.14)では動作しなかった。
_04_mutex_ffi.rb
動作する状態にするまで、かなりハマった…。
問題その1。ffi を使った場合、何故か CreateMutex() が呼べなかった。指定しても「そんな関数は無い」と言われてしまう。CreateMutexA() か CreateMutexW() にしたら呼び出すことができた。ちなみに、CreateMutexW() の呼び出し方は、win32-mutex のソースを参考にさせてもらった。
_win32-mutex/mutex.rb at main - chef/win32-mutex
問題その2.kernel32.dll の GetLastError() を呼んでも、それらしい値が全く返ってこなくて悩んでしまった…。何度試しても、どんな状況でも、必ず 0 になってしまう。
そのあたり、以下のページにヒントがあった。
_Add API.last_error #55 - Github Lab
Ruby から Win32 API を呼ぶと、GetLastError() の値がリセットされてしまう場合があるそうで。そんな時のために、ffi は FFI::LastError.winapi_error というものを用意してあって、そこに GetLastError() の本来の値が格納されているらしい。
_Method: FFI::LastError.winapi_error - Documentation for ffi/ffi (master)
そんなわけで、FFI::LastError.winapi_error を使ってエラーコードを取得したら期待通りの動作になった。
ただ、Ruby 2.6 にインストールした ffi 1.15.5 には、FFI::LastError.winapi_error が用意されていたのだけど。Ruby 1.8 / 1.9 にインストールした ffi 1.9.14 にはメソッドが用意されてなくてエラーになってしまう。
昔は winapi_error ではなく win_error という名前だった、という情報にも辿り着いたのだけど。win_error に書き換えても、やはりエラーが出る。
_Rename to winapi_error - ffi/ffi@4ba55c5
どうやらどこかのバージョンの時点で win_error が追加されたけど、それは Ruby 1.8 / 1.9 でも動作してくれる ffi 1.9.14 には実装されていないようだなと…。そんなわけで、Ruby 1.8 / 1.9 + ffi 1.9.14 では、CreateMutex() を使う方法は分からなかった。
_Ruby + ffi の動作確認をした
動作した環境は、Windows10 x64 21H2 + Ruby 2.6.10 p210 i386-mingw32 + ffi 1.15.5 x86-mingw32。
残念ながら、Ruby 1.8 / 1.9 (+ ffi 1.9.14)では動作しなかった。
_04_mutex_ffi.rb
require "rubygems" require "ffi" module WinKernel extend FFI::Library ffi_lib :kernel32 ffi_convention :stdcall attach_function :CreateMutexW, [:long, :bool, :pointer], :ulong attach_function :ReleaseMutex, [:ulong], :bool attach_function :CloseHandle, [:ulong], :long # attach_function :GetLastError, [], :ulong end mutex_name = "rubyffimutexsample" ERROR_ALREADY_EXISTS = 183 mutex = WinKernel.CreateMutexW(0, false, mutex_name.encode("UTF-16LE")) # Add API.last_error #55 - Github Lab # https://githublab.com/repository/issues/cosmo0920/win32-api/55 # # GetLastError() is reset when calling Win32 API from Ruby. # FFI::LastError.winapi_error" is a good choice. begin # err = WinKernel.GetLastError # err = FFI::LastError.error err = FFI::LastError.winapi_error rescue => e puts "Error : This version of ffi does not support FFI::LastError.winapi_error." if mutex != 0 WinKernel.ReleaseMutex(mutex) WininKernel.CloseHandle(mutex) end exit end puts "Mutex : #{mutex}" puts "GetLastError : #{err}" if mutex == 0 or err == ERROR_ALREADY_EXISTS puts "already exists" exit else 10.times do |i| puts i sleep 1 end end if mutex != 0 WinKernel.ReleaseMutex(mutex) WinKernel.CloseHandle(mutex) end
動作する状態にするまで、かなりハマった…。
問題その1。ffi を使った場合、何故か CreateMutex() が呼べなかった。指定しても「そんな関数は無い」と言われてしまう。CreateMutexA() か CreateMutexW() にしたら呼び出すことができた。ちなみに、CreateMutexW() の呼び出し方は、win32-mutex のソースを参考にさせてもらった。
_win32-mutex/mutex.rb at main - chef/win32-mutex
問題その2.kernel32.dll の GetLastError() を呼んでも、それらしい値が全く返ってこなくて悩んでしまった…。何度試しても、どんな状況でも、必ず 0 になってしまう。
そのあたり、以下のページにヒントがあった。
_Add API.last_error #55 - Github Lab
Ruby から Win32 API を呼ぶと、GetLastError() の値がリセットされてしまう場合があるそうで。そんな時のために、ffi は FFI::LastError.winapi_error というものを用意してあって、そこに GetLastError() の本来の値が格納されているらしい。
_Method: FFI::LastError.winapi_error - Documentation for ffi/ffi (master)
そんなわけで、FFI::LastError.winapi_error を使ってエラーコードを取得したら期待通りの動作になった。
ただ、Ruby 2.6 にインストールした ffi 1.15.5 には、FFI::LastError.winapi_error が用意されていたのだけど。Ruby 1.8 / 1.9 にインストールした ffi 1.9.14 にはメソッドが用意されてなくてエラーになってしまう。
昔は winapi_error ではなく win_error という名前だった、という情報にも辿り着いたのだけど。win_error に書き換えても、やはりエラーが出る。
_Rename to winapi_error - ffi/ffi@4ba55c5
どうやらどこかのバージョンの時点で win_error が追加されたけど、それは Ruby 1.8 / 1.9 でも動作してくれる ffi 1.9.14 には実装されていないようだなと…。そんなわけで、Ruby 1.8 / 1.9 + ffi 1.9.14 では、CreateMutex() を使う方法は分からなかった。
◎ DL と fiddle を利用して実現。 :
Ruby 1.9.3 限定の記述になるけれど、当時標準で添付されていた DL と fiddle を使って実現してみた。
動作確認環境は、Windows10 x64 21H2 + Ruby 1.9.3 p551 i386-mingw32。Ruby 1.8 や Ruby 2.6 では、「DL なんて無い」と言われて動かなかった。
_05_mutex_dl.rb
Ruby 1.9.3 の fiddle にも、ffi と同様に、Fiddle::win32_last_error というそれらしいメソッドがあるのだけれど。何故か DL+ fiddle の組み合わせの場合は、kernel32.dll の GetLastError() を呼ぶほうが正しい値が返るようだった。
また、この場合は、CreateMutexA() や CreateMutexW() ではなく、CreateMutex() を呼んでも通る模様。
動作確認環境は、Windows10 x64 21H2 + Ruby 1.9.3 p551 i386-mingw32。Ruby 1.8 や Ruby 2.6 では、「DL なんて無い」と言われて動かなかった。
_05_mutex_dl.rb
require "dl" require "fiddle" include Fiddle libc = DL.dlopen("kernel32.dll") create_mutex = Fiddle::Function.new(libc["CreateMutex"], [TYPE_LONG, TYPE_LONG, TYPE_VOIDP], TYPE_LONG) release_mutex = Fiddle::Function.new(libc["ReleaseMutex"], [TYPE_LONG], TYPE_LONG) close_handle = Fiddle::Function.new(libc["CloseHandle"], [TYPE_LONG], TYPE_LONG) get_last_error = Fiddle::Function.new(libc["GetLastError"], [], TYPE_LONG) mutex_name = "rubydlfiddlemutexsample" ERROR_ALREADY_EXISTS = 183 mutex = create_mutex.call(0, 1, mutex_name) # err = Fiddle::win32_last_error err = get_last_error.call() puts "Mutex : #{mutex}" puts "GetLastError : #{err}" if mutex == 0 or err == ERROR_ALREADY_EXISTS puts "already exists" exit else 10.times do |i| puts i sleep 1 end end if mutex != 0 release_mutex.call(mutex) close_handle.call(mutex) end
Ruby 1.9.3 の fiddle にも、ffi と同様に、Fiddle::win32_last_error というそれらしいメソッドがあるのだけれど。何故か DL+ fiddle の組み合わせの場合は、kernel32.dll の GetLastError() を呼ぶほうが正しい値が返るようだった。
また、この場合は、CreateMutexA() や CreateMutexW() ではなく、CreateMutex() を呼んでも通る模様。
◎ ハマりそうなポイントのまとめ。 :
- CreateMutex の名前で呼べる場合と、CreateMutexA か CreateMutexW を呼ばないといけない場合がある。
- GetLastError() を呼んで正常動作する場合と、ffi や fiddle で別途用意されたエラーコード取得メソッドを呼ばないと正常動作しない場合がある。
とりあえず、Ruby 1.8 / 1.9 の場合は win32api を使っても警告が出なかったので、Ruby 1.8 / 1.9 なら win32api を使ってこの手の処理を書けばよさそうだなと…。
また、ffi を使えば Ruby 1.8 - 2.x でも同じ記述ができるのではと期待していたけれど、そんなことはなかった。最近の ffi じゃないと記述が通らない・正常動作しない場合もあるのだなと。そうなると、Ruby 1.8 / 1.9 で、あえて ffi を使う理由は無さそうな気がする。win32api を使ったほうが悩まなくて済む。
◎ 参考ページ。 :
[ ツッコむ ]
#2 [ruby][windows] Rubyスクリプトでウインドウサイズを取得したい
Windows10 x64 21H2 + Ruby 1.8.7 p330 i386-mswin32 で、Windows上のウインドウのサイズを取得したい。
win32api を利用して試してみた。
_06_getwindowrect_win32api.rb
ruby 06_getwindowrect_win32api.rb ウインドウハンドル、で実行できる。ウインドウハンドルは10進数で記述。
win32api を利用して試してみた。
_06_getwindowrect_win32api.rb
require "Win32API" if ARGV.size != 1 fn = File.basename(__FILE__) puts "Usage:\n ruby #{fn} WINDOWHANDLE" exit end hwnd = ARGV[0].to_i puts "Window Handle = #{hwnd}" get_window_rect = Win32API.new("user32", "GetWindowRect", "lp", "i") buf = "\0" * 256 get_window_rect.call(hwnd, buf) x0, y0, x1, y1 = buf.unpack("l4") w = x1 - x0 h = y1 - y0 puts "(x0, y0) = (#{x0}, #{y0})" puts "(x1, y1) = (#{x1}, #{y1})" puts "(w, h) = #{w}, #{h}"
ruby 06_getwindowrect_win32api.rb ウインドウハンドル、で実行できる。ウインドウハンドルは10進数で記述。
◎ ウインドウハンドルを調べるツールについて。 :
ウインドウハンドルを調べる作業は、以下のツールを使わせてもらった。
_WinExplorer v1.30
Windows10 x64 21H2上で、WinExplorer 1.30 を利用できた。
あるいは、Spy++ を利用することもできそう。Visual Studio 2019 と一緒に入ってる場合もあるらしい。
_spy++を使ってクラス名を調べる方法 | 自動マクロプログラミング
_【Visual Studio 2019】Spy++の場所 - buralog
_WinExplorer v1.30
Windows10 x64 21H2上で、WinExplorer 1.30 を利用できた。
あるいは、Spy++ を利用することもできそう。Visual Studio 2019 と一緒に入ってる場合もあるらしい。
_spy++を使ってクラス名を調べる方法 | 自動マクロプログラミング
_【Visual Studio 2019】Spy++の場所 - buralog
[ ツッコむ ]
#3 [comic] 漫画を読んでた
弟が帰省した際に持ってきてくれた漫画単行本を消化。
「ジョジョリオン」荒木飛呂彦著。全27巻を読み終えた。よく分からないけど、これはパラレルワールド作品と言う認識でいいのだろうか…。それにしても、27巻は長い…。疲れた…。
ところどころ読み辛いコマ割りがあって、結構読み飛ばしてしまった気もする。設定も意味不明というか、なんでそうなるのと首を捻る部分がいくつかあって…。でもまあ、元々そういう作家性だし…。
「ジョジョリオン」荒木飛呂彦著。全27巻を読み終えた。よく分からないけど、これはパラレルワールド作品と言う認識でいいのだろうか…。それにしても、27巻は長い…。疲れた…。
ところどころ読み辛いコマ割りがあって、結構読み飛ばしてしまった気もする。設定も意味不明というか、なんでそうなるのと首を捻る部分がいくつかあって…。でもまあ、元々そういう作家性だし…。
[ ツッコむ ]
2022/08/18(木) [n年前の日記]
#1 [ruby] exerbと格闘中
Ruby 1.8.7 p330 と Ruby/SDL (rubysdl) 1.3.1.1、2.1.1.1 を使って、Windows用のスクリーンセーバを作れないものかと実験しているところだけど、exe化するあたりでハマっているところ。
一応、ocra 1.3.1 を使って exe化はできたけど、ocra は動作に必要なファイルを一時ディレクトリに解凍して動く仕組みなので、予想はしていたけど結構起動が遅いわけで。そこでふと、exerb ならどうだろうと手を出してみたのだけど、これが上手く行かない…。
ちなみに、puts "Hello World" だけの Rubyスクリプトを exerb でexe化したら、ちゃんと動作する exe になった。Ruby の基本機能だけを使ったスクリプトならそこそこ動くということなのかな…。
実験環境は以下。
一応、ocra 1.3.1 を使って exe化はできたけど、ocra は動作に必要なファイルを一時ディレクトリに解凍して動く仕組みなので、予想はしていたけど結構起動が遅いわけで。そこでふと、exerb ならどうだろうと手を出してみたのだけど、これが上手く行かない…。
ちなみに、puts "Hello World" だけの Rubyスクリプトを exerb でexe化したら、ちゃんと動作する exe になった。Ruby の基本機能だけを使ったスクリプトならそこそこ動くということなのかな…。
実験環境は以下。
- Windows10 x64 21H2
- Ruby 1.8.7 p330 i386-mswin32 (ActiveScriptRuby) + rubysdl 2.1.1.1
- Ruby 1.8.7 p330 i386-mingw32 (RubyInstaller) + rubysdl 1.3.1.1
◎ 分かったこと。 :
- Ruby/SDL を使ったスクリプトを exerb で exe にする場合、.exy にSDL関連の .dll を追記してやる必要がある。
- rubysdl 2.1.1.1 は、どうやらdllのファイル名が長過ぎるようで、exerb ではexe化できなかった。
- rubysdl 1.3.1.1 は、dllファイル名の長さの問題は回避できそうだが、pngの読み込みができない exe になった。
- rubysdl 1.3.1.1 を利用して、かつ、画像は bmp のみに限定したら、動作する exe が生成された。
- しかし、exe を他のPCに持っていったら動かなかった…。
◎ 試行錯誤の記録。その1。 :
まず、Ruby 1.8.7 p330 mswin32 + rubysdl 2.1.1 の環境で、exerb 5.3.0 を使って exeファイルを作成してみた。
実行したところ、SDL関連のdllが読めないと言ってきた。
exerb にファイル構成等を指示するために、.exyファイルを作成してみる。
.exy内には、SDL関連dllが記述されてなかった。
ocra を使った時の出力結果を頼りにして、SDl関連dll を .exy に追記してみる。ちなみに、自分の環境では、Ruby 1.8.7 を C:/Ruby/Ruby187p330mswin32/ にインストールしてある。
.exy の一番最後(fileブロック内)に以下を追記。
exerb に .exy を指定してexeを生成。
しかし、出来上がった exeファイルを実行すると、以下のエラーが出る。
以下のスレッドが関係してそうだけど…。
_Re: exerb fails (exerb-eng:0059) - Exerb - OSDN
以下も関係あるだろうか。
_Exerb Project
何故かは分からないけど、exeファイル名は17文字までという制限があるらしい。一応、生成exeファイル名は17文字以下にしてみたのだけど、もしかして利用するdllファイル名も17文字以下でなければならないということだろうか。だとしたら、打つ手は無さそう…。
exerb rbsdlsav.rb.exe が生成された。
実行したところ、SDL関連のdllが読めないと言ってきた。
exerb にファイル構成等を指示するために、.exyファイルを作成してみる。
mkexy rbsdlsav.rb
.exy内には、SDL関連dllが記述されてなかった。
ocra を使った時の出力結果を頼りにして、SDl関連dll を .exy に追記してみる。ちなみに、自分の環境では、Ruby 1.8.7 を C:/Ruby/Ruby187p330mswin32/ にインストールしてある。
> ocra --version Ocra 1.3.1 > ocra --windows scrsavrubysdl.rb res === Loading script to check dependencies === Building scrsavrubysdl.exe === Adding user-supplied source files === Adding ruby executable rubyw.exe === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/SDL_image.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/SDL.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/SDL_mixer.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/SGE.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/smpeg.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/SDL_ttf.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/libfreetype-6.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/zlib1.dll === Adding detected DLL C:/Ruby/Ruby187p330mswin32/bin/libpng12-0.dll === Adding library files === Compressing 3601505 bytes === Finished building scrsavrubysdl.exe (1187787 bytes)
.exy の一番最後(fileブロック内)に以下を追記。
sdl.so: file: C:/Ruby/Ruby187p330mswin32/lib/ruby/site_ruby/1.8/i386-msvcrt/sdl.so type: extension-library SDL_image.dll: file: C:/Ruby/Ruby187p330mswin32/bin/SDL_image.dll type: dynamic-library SDL.dll: file: C:/Ruby/Ruby187p330mswin32/bin/SDL.dll type: dynamic-library SDL_mixer.dll: file: C:/Ruby/Ruby187p330mswin32/bin/SDL_mixer.dll type: dynamic-library SGE.dll: file: C:/Ruby/Ruby187p330mswin32/bin/SGE.dll type: dynamic-library smpeg.dll: file: C:/Ruby/Ruby187p330mswin32/bin/smpeg.dll type: dynamic-library SDL_ttf.dll: file: C:/Ruby/Ruby187p330mswin32/bin/SDL_ttf.dll type: dynamic-library libfreetype-6.dll: file: C:/Ruby/Ruby187p330mswin32/bin/libfreetype-6.dll type: dynamic-library zlib1.dll: file: C:/Ruby/Ruby187p330mswin32/bin/zlib1.dll type: dynamic-library libpng12-0.dll: file: C:/Ruby/Ruby187p330mswin32/bin/libpng12-0.dll type: dynamic-library
exerb に .exy を指定してexeを生成。
exerb rbsdlsav.exy
しかし、出来上がった exeファイルを実行すると、以下のエラーが出る。
> rbsdlsav.exe sdl.rb:17:in `require': Couldn't modify DLL's name in the import table. The name of the executable file is too long. (LoadError) from sdl.rb:17
以下のスレッドが関係してそうだけど…。
_Re: exerb fails (exerb-eng:0059) - Exerb - OSDN
以下も関係あるだろうか。
_Exerb Project
> Q12. The error message "Fail to modify the import table. exe file name is too long." has appeared.
> A12. This message means that the exe file name must be written in 17 characters.
何故かは分からないけど、exeファイル名は17文字までという制限があるらしい。一応、生成exeファイル名は17文字以下にしてみたのだけど、もしかして利用するdllファイル名も17文字以下でなければならないということだろうか。だとしたら、打つ手は無さそう…。
◎ 試行錯誤の記録。その2。 :
Ruby/SDL (rubysdl) 2.1.1.1 を使っているスクリプトを、exerb を使って exe化するのは、ファイル名文字数の関係でどうやら難しい気配がしてきたわけだけど。
これが Ruby/SDL (rubysdl) 1.3.1 ならどうだろうか。rubysdl 1.3.1 なら、多少は dllファイル名が違っているはず。
Ruby 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1 で試してみた。
ocra 1.3.1 の出力結果は以下。
.exy を作成。
生成された .exy を修正。
core を cui から gui に変更。
SDL関連dllの記述を追記。
exerb でexe化。
exeファイルが生成された。しかし、exe を実行すると、png関連のモジュールが見つからないと言ってきた。
Ruby/SDL でpng画像を利用しようとすると、このエラーに遭遇してしまうのだろう…。であればと、png画像を利用することを諦めて、bmp画像を使うことにしたら、上記のエラーが出てこなくなって、ようやく動作する exe が生成された。
と思ったがダメだった。他のPCに exe をコピーして実行すると例外が発生する。
exerb で変換作業をしたPC上なら動くが、他のPCでは動かないexeになってしまった。何故。
※ 2022/08/19追記。他のPCでexeを動かした際のエラーダイアログも載せておく。

試しに、Ruby 1.8.7 p330 i386-mswin32 (ActiveScriptRuby) + rubysdl 1.3.1 にしてexeに変換してみたけれど、症状変わらず。やはり他のPCにexeを持っていくとエラーが出る。mingw32版 Ruby ではなく、mswin32版 Ruby なら動いてくれるかと期待したけどダメだった。
exerb に渡した .exy は以下。
_rbsdlsav2.exy
これが Ruby/SDL (rubysdl) 1.3.1 ならどうだろうか。rubysdl 1.3.1 なら、多少は dllファイル名が違っているはず。
Ruby 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1 で試してみた。
ocra 1.3.1 の出力結果は以下。
> ruby -v ruby 1.8.7 (2010-12-23 patchlevel 330) [i386-mingw32] > ruby -e "require 'sdl'; puts SDL::VERSION" 1.3.1 > ocra --version Ocra 1.3.1 > ocra --windows rbsdlsav.rb res === Loading script to check dependencies === Building rbsdlsav.exe === Adding user-supplied source files === Adding ruby executable rubyw.exe === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/libiconv-2.dll === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/SDL_mixer.dll === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/SDL.dll === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/SGE.dll === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/SDL_image.dll === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/SDL_ttf.dll === Adding detected DLL C:/Ruby/Ruby187p330mingw32/bin/smpeg.dll === Adding library files === Compressing 4309809 bytes === Finished building rbsdlsav.exe (1644661 bytes)
.exy を作成。
mkexy rbsdlsav2.rb
生成された .exy を修正。
core を cui から gui に変更。
core: gui
SDL関連dllの記述を追記。
zlib.so: file: C:/Ruby/Ruby187p330mingw32/lib/ruby/1.8/i386-mingw32/zlib.so type: extension-library SDL.dll: file: C:/Ruby/Ruby187p330mingw32/bin/SDL.dll type: dynamic-library SGE.dll: file: C:/Ruby/Ruby187p330mingw32/bin/SGE.dll type: dynamic-library SDL_mixer.dll: file: C:/Ruby/Ruby187p330mingw32/bin/SDL_mixer.dll type: dynamic-library SDL_image.dll: file: C:/Ruby/Ruby187p330mingw32/bin/SDL_image.dll type: dynamic-library SDL_ttf.dll: file: C:/Ruby/Ruby187p330mingw32/bin/SDL_ttf.dll type: dynamic-library smpeg.dll: file: C:/Ruby/Ruby187p330mingw32/bin/smpeg.dll type: dynamic-library zlib1.dll: file: C:/Ruby/Ruby187p330mingw32/bin/zlib1.dll type: dynamic-library libpng12.dll: file: C:/Ruby/Ruby187p330mingw32/bin/libpng12.dll type: dynamic-library libiconv-2.dll: file: C:/Ruby/Ruby187p330mingw32/bin/libiconv-2.dll type: dynamic-library libtiff.dll: file: C:/Ruby/Ruby187p330mingw32/bin/libtiff.dll type: dynamic-library jpeg.dll: file: C:/Ruby/Ruby187p330mingw32/bin/jpeg.dll type: dynamic-library libcharset-1.dll: file: C:/Ruby/Ruby187p330mingw32/bin/libcharset-1.dll type: dynamic-library ogg.dll: file: C:/Ruby/Ruby187p330mingw32/bin/ogg.dll type: dynamic-library vorbis.dll: file: C:/Ruby/Ruby187p330mingw32/bin/vorbis.dll type: dynamic-library vorbisfile.dll: file: C:/Ruby/Ruby187p330mingw32/bin/vorbisfile.dll type: dynamic-library
exerb でexe化。
exerb rbsdlsav2.exy
exeファイルが生成された。しかし、exe を実行すると、png関連のモジュールが見つからないと言ってきた。
> rbsdlsav2.exe rbsdlsav2.rb:197:in `loadFromIO': Couldn't load image from IO: Failed loading png_create_info_struct: 指定されたモジュールが見つかりません。 (SDL::Error) from rbsdlsav2.rb:197:in `get_surface_image_from_base64' from rbsdlsav2.rb:75:in `draw_fullscreen' from rbsdlsav2.rb:235
Ruby/SDL でpng画像を利用しようとすると、このエラーに遭遇してしまうのだろう…。であればと、png画像を利用することを諦めて、bmp画像を使うことにしたら、上記のエラーが出てこなくなって、ようやく動作する exe が生成された。
と思ったがダメだった。他のPCに exe をコピーして実行すると例外が発生する。
プログラムで例外が発生しました。詳細は下記の通りです。 タイプ: ExerbRuntime::Error メッセージ: Win32API Error #126 --- 指定されたモジュールが見つかりません。 バックトーレス: sdl.rb:17:in 'require' sdl.rb:17sdl.rb の 17行目には、require 'sdl.so' と書いてある。sdl.so を読み込むことができていないということだろうか…。
exerb で変換作業をしたPC上なら動くが、他のPCでは動かないexeになってしまった。何故。
※ 2022/08/19追記。他のPCでexeを動かした際のエラーダイアログも載せておく。

試しに、Ruby 1.8.7 p330 i386-mswin32 (ActiveScriptRuby) + rubysdl 1.3.1 にしてexeに変換してみたけれど、症状変わらず。やはり他のPCにexeを持っていくとエラーが出る。mingw32版 Ruby ではなく、mswin32版 Ruby なら動いてくれるかと期待したけどダメだった。
exerb に渡した .exy は以下。
_rbsdlsav2.exy
◎ 余談。exerb と DATA。 :
exerb で exe化した exe を動かしたところ、以下のエラーが出た。
スクリプトの最後につけた base64文字列を読み取れずにエラーを出している模様。
_DATAオブジェクトがきかない (exerb-dev:0477) - Exerb - OSDN
_Re: DATAオブジェクトがきかない(再送) (exerb-dev:0481) - Exerb - OSDN
_Re: DATAオブジェクトがきかない(再送) (exerb-dev:0485) - Exerb - OSDN
exerb は DATA云々に対応してなかったというオチらしい。
exerb はデータファイルを同梱させるのが難しそうなので、わざわざ画像ファイルのデータもスクリプト内に収めようと手を入れてみたのだけど、こんな罠があるとは…。
スクリプトの最後、__END__ 以下に記述していた base64文字列を、スクリプトの途中で配列定数として用意して利用するように書き換えたところ、一応動いてくれた。
> rbsdlsav.exe rbsdlsav.rb:150:in `get_surface_image_from_base64': uninitialized constant DATA (NameError) from rbsdlsav.rb:35:in `draw_fullscreen' from rbsdlsav.rb:192
スクリプトの最後につけた base64文字列を読み取れずにエラーを出している模様。
_DATAオブジェクトがきかない (exerb-dev:0477) - Exerb - OSDN
_Re: DATAオブジェクトがきかない(再送) (exerb-dev:0481) - Exerb - OSDN
_Re: DATAオブジェクトがきかない(再送) (exerb-dev:0485) - Exerb - OSDN
exerb は DATA云々に対応してなかったというオチらしい。
exerb はデータファイルを同梱させるのが難しそうなので、わざわざ画像ファイルのデータもスクリプト内に収めようと手を入れてみたのだけど、こんな罠があるとは…。
スクリプトの最後、__END__ 以下に記述していた base64文字列を、スクリプトの途中で配列定数として用意して利用するように書き換えたところ、一応動いてくれた。
[ ツッコむ ]
2022/08/19(金) [n年前の日記]
#1 [ruby][windows] Ruby/SDLでスクリーンセーバを作成
Windows10 x64 21H2上で、Ruby 1.8.7, 1.9.3 + Ruby/SDL (rubysdl) 2.1.1.1 を使って、Windows用のスクリーンセーバ(の雛形)を作成してみた。
動作させている様子は以下。解像度が低いけど雰囲気ぐらいは伝わるかと。
Windows用スクリーンセーバに求められる仕様については、pygame でスクリーンセーバを作った際にメモしたソレが多少は参考になるかと。
_Windows用スクリーンセーバについてのおさらい。
動作させている様子は以下。解像度が低いけど雰囲気ぐらいは伝わるかと。
Windows用スクリーンセーバに求められる仕様については、pygame でスクリーンセーバを作った際にメモしたソレが多少は参考になるかと。
_Windows用スクリーンセーバについてのおさらい。
◎ 動作確認環境。 :
動作確認環境は以下。
セットアップファイル名も一応列挙。
_ActiveScriptRuby and Other packages
_Download Archives - RubyInstaller
Ruby は32bit版を利用。
Ruby は32bit版を利用。
大事なことなので2回言いました。
Ruby/SDLのバイナリが32bitなので、64bit版のRubyに入れて使おうとしても動かないです。たぶん。
ちなみに、Windows用のRuby/SDLは Ruby 1.8 / 1.9用しか存在しないので、Ruby 2.0以降でこういったことはできないと思われます。
利用できるRubyのバージョンや、Ruby/SDL のインストール方法は、以下を参照のこと。
_Ruby/SDLでスクリーンセーバを作れないものか
_Ruby/SDLをインストールしようとしてハマった
- Windows10 x64 21H2
- RubyInstaller 1.9.3 p551 i386-mingw32 + rubysdl 2.1.1.1 + ocra 1.3.10
- ActiveScriptRuby 1.8.7 p330 i386-mswin32 + rubysdl 2.1.1.1 + ocra 1.3.1
- RyubyInstaller 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1.1 + ocra 1.3.1
セットアップファイル名も一応列挙。
- rubyinstaller-1.9.3-p551.exe
- rubysdl-2.1.1.1-mswin32-1.9.1-p243.zip
- ActiveRuby.msi
- rubysdl-2.1.1.1-mswin32-1.8.7-p174.zip
- rubyinstaller-1.8.7-p330.exe
- rubysdl-1.3.1.1-mswin32-1.8.6-p36.zip
_ActiveScriptRuby and Other packages
_Download Archives - RubyInstaller
Ruby は32bit版を利用。
Ruby は32bit版を利用。
大事なことなので2回言いました。
Ruby/SDLのバイナリが32bitなので、64bit版のRubyに入れて使おうとしても動かないです。たぶん。
ちなみに、Windows用のRuby/SDLは Ruby 1.8 / 1.9用しか存在しないので、Ruby 2.0以降でこういったことはできないと思われます。
利用できるRubyのバージョンや、Ruby/SDL のインストール方法は、以下を参照のこと。
_Ruby/SDLでスクリーンセーバを作れないものか
_Ruby/SDLをインストールしようとしてハマった
◎ ソース。 :
ソースは以下。処理内容は、画面内をRubyのロゴ画像が跳ね回るだけの処理。
_rbsdlsav.rb
使用画像は以下。.rb と同じ階層に res というフォルダを作って、その中に入れておく。
_ruby_logo.png
_rbsdlsav.rb
require "sdl" require "Win32API" APPLI_NAME = "Screensaver sample by using Ruby/SDL" APPLIT_VER = "0.0.1" IMG_FILE = "res/ruby_logo.png" MUTEX_NAME = { "None" => "screensaver_rubysdl_sample_fullscreen", "fullscreen" => "screensaver_rubysdl_sample_fullscreen", "config" => "screensaver_rubysdl_sample_config", "preview" => "screensaver_rubysdl_sample_preview", } ERROR_ALREADY_EXISTS = 183 # Draw fullscreen def draw_fullscreen SDL.init(SDL::INIT_VIDEO) screen = SDL::Screen.open(0, 0, 0, SDL::FULLSCREEN | SDL::HWSURFACE | SDL::DOUBLEBUF) img = SDL::Surface.load(get_resource_path(IMG_FILE)).display_format x, y = screen.w / 2, screen.h / 2 dx, dy = screen.w / (60.0 * 3), screen.h / (60.0 * 4) count = 0 looping = true SDL::Mouse.hide # hide mouse cursor # main loop while looping while event = SDL::Event2.poll case event when SDL::Event2::Quit looping = false when SDL::Event2::KeyUp looping = false when SDL::Event2::MouseMotion if count >= 120 rx = event.xrel ry = event.yrel r = 10 looping = false if (rx * rx + ry * ry >= r * r) end end end # update sprite position x += dx y += dy dx *= -1 if (x <= 0 or x >= screen.w - img.w) dy *= -1 if (y <= 0 or y >= screen.h - img.h) count += 1 screen.fill_rect(0, 0, screen.w, screen.h, [0, 0, 0]) # fill bg screen.put(img, x, y) # draw sprite screen.flip # update screen SDL.delay(16) # wait end SDL::Mouse.show # show mouse cursor end # Draw preview def draw_preview(hwnd) if hwnd > 0 # /p xxxxxx scrw, scrh = get_window_size(hwnd) SDL.putenv("SDL_VIDEODRIVER=windib") SDL.putenv("SDL_WINDOWID=#{hwnd}") else # /p 0 scrw, scrh = 152, 112 end SDL.init(SDL::INIT_VIDEO) screen = SDL::Screen.open(scrw, scrh, 0, SDL::SWSURFACE) img = SDL::Surface.load(get_resource_path(IMG_FILE)).display_format # main loop frames = 180 * 5 frames.times do |t| while event = SDL::Event2.poll case event when SDL::Event2::Quit return when SDL::Event2::KeyDown return if event.sym == SDL::Key::ESCAPE end end x = (screen.w - img.w) / 2 y = 12 * Math.sin((t * 4) * Math::PI / 180.0) + ((screen.h - img.h) / 2) screen.fill_rect(0, 0, screen.w, screen.h, [235, 108, 0]) # fill bg screen.fill_rect(8, 8, screen.w - 16, screen.h - 16, [255, 128, 0]) # fill bg screen.put(img, x, y) screen.flip SDL.delay(16) end end def draw_config s = "#{APPLI_NAME}\nVer. #{APPLIT_VER}" title = "Information" msg_box(s, title) end def get_resource_path(filepath) # return File.dirname(__FILE__) + "/" + filepath return File.join(File.dirname(__FILE__), filepath) end # Display messagebox dialog def msg_box(msg, title) msgbox = Win32API.new("user32", "MessageBoxA", %w(p p p i), "i") msgbox.call(0, msg, title, 0x40) end # Get window size def get_window_size(hwnd) get_window_rect = Win32API.new("user32", "GetWindowRect", "lp", "i") buf = "\0" * 256 get_window_rect.call(hwnd, buf) x0, y0, x1, y1 = buf.unpack("l4") w, h = (x1 - x0), (y1 - y0) return w, h end # parse command line option kind = "None" hwnd = 0 if ARGV.size >= 1 s = ARGV[0].downcase.slice(0..1) case s when "/s" kind = "fullscreen" when "/c" kind = "config" when "/p" kind = "preview" hwnd = ARGV[1].to_i end end # Mutex create_mutex = Win32API.new("kernel32", "CreateMutex", "llp", "l") get_last_error = Win32API.new("kernel32", "GetLastError", "", "l") release_mutex = Win32API.new("kernel32", "ReleaseMutex", "l", "l") close_handle = Win32API.new("kernel32", "CloseHandle", "l", "l") mutex = create_mutex.call(0, 1, MUTEX_NAME[kind]) err = get_last_error.call() exit if (mutex == 0 or err == ERROR_ALREADY_EXISTS) # main job case kind when "config" draw_config when "preview" draw_preview(hwnd) else # fullscreen draw_fullscreen end if mutex != 0 release_mutex.call(mutex) close_handle.call(mutex) end exit
- draw_fullscreen() が、フルスクリーン表示モードの処理。
- draw_config() が、設定画面モードの処理。
- draw_preview() が、プレビューモードの処理。
- Mutex 云々が、多重起動抑止処理。
使用画像は以下。.rb と同じ階層に res というフォルダを作って、その中に入れておく。
_ruby_logo.png
◎ テストで実行。 :
実行は以下。
ruby rbsdlsav.rb /s ruby rbsdlsav.rb /c ruby rbsdlsav.rb /p 0 ruby rbsdlsav.rb
- /s : フルスクリーン表示で実行。
- /c : 設定画面を表示。ここではアプリ名をメッセージボックスで表示するだけ。
- /p 0 : プレビューモード。スクリーンセーバ設定画面を表示した際に、プレビュー窓内に表示されるモード。ウインドウハンドルとして 0 を指定すると、小さいウインドウを表示してテスト描画する。本番では0より大きい数値(10進数)が渡されるはず。
- コマンドラインオプション無し : フルスクリーン表示で実行するようにしておいた。
◎ exe化する。 :
Windows用スクリーンセーバのファイル拡張子は .scr だけど、実体は .exe ファイル。.exe を .scr にリネームして、スクリーンセーバとして利用する。
今回、Rubyスクリプトのexe化には ocra を利用した。
ocra のインストールは以下。
以下で exe ファイルが生成される。
今回、Rubyスクリプトのexe化には ocra を利用した。
- Ruby 1.8.7 の場合、ocra 1.3.1 が利用できる。ocra 1.3.2 以降はエラーが出る。
- RUby 1.9.3 の場合、ocra 1.3.10 が利用できる。ocra 1.3.11 はエラーが出る。
ocra のインストールは以下。
# Ruby 1.8.7 gem install ocra -v 1.3.1 # Ruby 1.9.3 gem install ocra -v 1.3.10
以下で exe ファイルが生成される。
ocra --windows rbsdlsav.rb res
- --windows : GUIアプリとして実行する指定。
- *.rb : 変換したいスクリプトファイル名。
- res : 画像ファイル等が入ってるフォルダ。*.rb と一緒にフォルダ名を列挙すると exe に同梱される。
◎ スクリーンセーバとして利用する。 :
.exe が生成されたら、.scr にリネームする。以下の場所に .scr をコピーすれば、スクリーンセーバとして利用できるようになる。
今回作ったスクリーンセーバは、32bit版 Ruby で作ったので、C:\Windows\SysWOW64\ 以下にコピーして動作確認した。
- Windows が64bit版、スクリーンセーバが32bit版のプログラムなら、C:\Windows\SysWOW64\ 以下に .scr をコピーする。
- Windows が64bit版、スクリーンセーバが64bit版のプログラムなら、C:\Windows\System32\ 以下に .scr をコピーする。
- Windows が32bit版なら、C:\Windows\System32\ 以下に .scr をコピーする。
今回作ったスクリーンセーバは、32bit版 Ruby で作ったので、C:\Windows\SysWOW64\ 以下にコピーして動作確認した。
◎ exerbではexe化できない。 :
[ ツッコむ ]
#2 [ruby][windows] exerbでRuby/SDLのスクリプトをexe化したかったけど無理だった
Ruby/SDL 1.3.1.1 を使ったRubyスクリプトを exerb 5.3.0 でexeに変換したかったけど、ちょっと無理っぽい気配になってきた。いや、無理。これたぶん無理。
環境は、Windows10 x64 21H2 + Ruby 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1.1 + exerb 5.3.0。ちなみに、動作確認に使ってる別PCも Windows10 x64 21H2。
環境は、Windows10 x64 21H2 + Ruby 1.8.7 p330 i386-mingw32 + rubysdl 1.3.1.1 + exerb 5.3.0。ちなみに、動作確認に使ってる別PCも Windows10 x64 21H2。
◎ 症状。 :
exerbで生成されたexeは、変換作業に用いたPC上では動作するけれど、別PC上では動作しなかった。


◎ 検証に使ったソース。 :
以下は、exerb で exe化できないか試した際のRubyスクリプトソース。及び、exerb に渡す .exyファイル。Ruby/SDLを使ったWindows用スクリーンセーバ(の雛形)で、画面の中をRubyロゴ画像が跳ね回る処理をしている。
_rbsdlsav2.rb
_rbsdlsav2.exy
ちなみに、上記のスクリプト rbsdlsav2.rb は画面に画像を描画しているけれど、画像ファイルをbase64に変換してスクリプト内に含めてある版なので、別途画像ファイルを必要としない。
また、中に含めた画像ファイルは png ではなく bmp にしてある。png にすると、exe実行時、何故か以下のエラーが出てしまうので…。
作業手順その他については、以下も参照のこと。
_試行錯誤の記録。その2 - exerbと格闘中 - mieki256's diary
_rbsdlsav2.rb
_rbsdlsav2.exy
ちなみに、上記のスクリプト rbsdlsav2.rb は画面に画像を描画しているけれど、画像ファイルをbase64に変換してスクリプト内に含めてある版なので、別途画像ファイルを必要としない。
また、中に含めた画像ファイルは png ではなく bmp にしてある。png にすると、exe実行時、何故か以下のエラーが出てしまうので…。
> rbsdlsav2.exe rbsdlsav2.rb:197:in `loadFromIO': Couldn't load image from IO: Failed loading png_create_info_struct: 指定されたモジュールが見つかりません。 (SDL::Error) from rbsdlsav2.rb:197:in `get_surface_image_from_base64' from rbsdlsav2.rb:75:in `draw_fullscreen' from rbsdlsav2.rb:235
作業手順その他については、以下も参照のこと。
_試行錯誤の記録。その2 - exerbと格闘中 - mieki256's diary
◎ 調査に使ったツール。 :
ググったところ、Dependencies というツールを使えば、*.so や *.dll が必要としている *.dll 等を調べることができると知った。
_lucasg/Dependencies: A rewrite of the old legacy software "depends.exe" in C# for Windows devs to troubleshoot dll load dependencies issues.
このツールを導入して、sdl.so や sdl*.dll を開いて、どの .dll が要求されているのか調べていった。
_lucasg/Dependencies: A rewrite of the old legacy software "depends.exe" in C# for Windows devs to troubleshoot dll load dependencies issues.
このツールを導入して、sdl.so や sdl*.dll を開いて、どの .dll が要求されているのか調べていった。
◎ 原因その1。 :
不具合が発生した原因は2点ある。
原因その1。変換作業に使ったWindows10機には、C:\Windows\SysWOW64\ 以下に、何故かSDL関連のDLLがごっそりほとんど入っていた。おそらく、何かしらのアプリをインストールした際、知らない間に、SysWOW64\以下にSDL関連DLLがコピーされてしまったのだろう…。
そのため、Rubyスクリプトをexe化したソレを実行した際、そのファイルに足りてないDLLがあったとしても、SysWOW64\ 以下から足りてないDLLを呼び出してしまって、故に exe が動いてしまっていた。
しかし、別PCの SysWOW64\ 以下には、SDL関連DLLが全く入ってなかったので、足りてないDLLに遭遇したらどこからも呼び出すことができず、「指定されたモジュールが見つかりません」とエラーを出していた。
原因その1。変換作業に使ったWindows10機には、C:\Windows\SysWOW64\ 以下に、何故かSDL関連のDLLがごっそりほとんど入っていた。おそらく、何かしらのアプリをインストールした際、知らない間に、SysWOW64\以下にSDL関連DLLがコピーされてしまったのだろう…。
そのため、Rubyスクリプトをexe化したソレを実行した際、そのファイルに足りてないDLLがあったとしても、SysWOW64\ 以下から足りてないDLLを呼び出してしまって、故に exe が動いてしまっていた。
しかし、別PCの SysWOW64\ 以下には、SDL関連DLLが全く入ってなかったので、足りてないDLLに遭遇したらどこからも呼び出すことができず、「指定されたモジュールが見つかりません」とエラーを出していた。
◎ 原因その2。 :
原因その2.今回足りてなかったファイルは、libfreetype-6.dll だった。sdl.so が SDL_ttf.dll を要求して、その SDL_ttf.dll が libfreetype-6.dll を要求していた。
ところが、そもそも、Ruby/SDL 1.3.1.1 (rubysdl-1.3.1.1-mswin32-1.8.6-p36.zip) には、libfreetype-6.dll が同梱されていなかった…。当然、Ruby 1.8.7 のインストールフォルダにもコピーされていないので、フォルダを眺めてSDL関連DLLを全て列挙したつもりが、libfreetype-6.dll だけ指定が抜けている状態になってしまった。
これがまた困ったことに、libfreetype-6.dll について .exy 内で記述を追記して exe を生成しても、正常に動作する exe にはならない。
と言うのも、exerb には、「各種ファイル名は17文字まで」という謎の制限があるようで、libfreetype-6.dll はその制限に引っ掛かってしまう。
生成した exe を実行すると、「Couldn't modify DLL's name in the import table. The name of the executable file is too long.」とエラーメッセージを出す。「ファイル名が長過ぎて扱えない」と言われているのだと思う。
余談。libfreetype-6.dll は、Ruby/SDL 2.1.1.1 の zip (rubysdl-2.1.1.1-mswin32-1.8.7-p174.zip) の中にあるものを利用してみた。
まあ、Ruby/SDL 1.3.1.1 と 2.1.1.1 のどちらを使うかと言えば、フツーは 2.1.1.1 を使うだろうから…。「Ruby/SDL 1.3.1.1 の zip はファイルが不足している」という罠にハマることは無いだろうなと…。
ところが、そもそも、Ruby/SDL 1.3.1.1 (rubysdl-1.3.1.1-mswin32-1.8.6-p36.zip) には、libfreetype-6.dll が同梱されていなかった…。当然、Ruby 1.8.7 のインストールフォルダにもコピーされていないので、フォルダを眺めてSDL関連DLLを全て列挙したつもりが、libfreetype-6.dll だけ指定が抜けている状態になってしまった。
これがまた困ったことに、libfreetype-6.dll について .exy 内で記述を追記して exe を生成しても、正常に動作する exe にはならない。
と言うのも、exerb には、「各種ファイル名は17文字まで」という謎の制限があるようで、libfreetype-6.dll はその制限に引っ掛かってしまう。
生成した exe を実行すると、「Couldn't modify DLL's name in the import table. The name of the executable file is too long.」とエラーメッセージを出す。「ファイル名が長過ぎて扱えない」と言われているのだと思う。
余談。libfreetype-6.dll は、Ruby/SDL 2.1.1.1 の zip (rubysdl-2.1.1.1-mswin32-1.8.7-p174.zip) の中にあるものを利用してみた。
まあ、Ruby/SDL 1.3.1.1 と 2.1.1.1 のどちらを使うかと言えば、フツーは 2.1.1.1 を使うだろうから…。「Ruby/SDL 1.3.1.1 の zip はファイルが不足している」という罠にハマることは無いだろうなと…。
◎ 対策。 :
exerb に .exy を通じて、「各DLLを内包しろ」と指定すると、17文字を超えるファイル名を扱えなくてエラーを出すわけだけど。
しかし、それらのDLLが、exeファイルとは別ファイルとして同梱されている状態なら、17文字制限は絡んでこないので、exeもちゃんと動作してくれる。
つまり、対策としては…。
ただ、今回は、スクリーンセーバを作りたいという話なので…。できれば、1つのexeファイルで動いてくれるほうがありがたいわけで…。
しかし、それらのDLLが、exeファイルとは別ファイルとして同梱されている状態なら、17文字制限は絡んでこないので、exeもちゃんと動作してくれる。
つまり、対策としては…。
- mkexy で生成した .exy に対して、DLL関連を追記して、どうにか1つのexeにしよう、などと考えてはいけない。
- 必要な DLL は、exe と同階層にまとめて置いておくことを心掛ける。
ただ、今回は、スクリーンセーバを作りたいという話なので…。できれば、1つのexeファイルで動いてくれるほうがありがたいわけで…。
◎ 結論。 :
他の用途ならともかく、Ruby/SDLを使ってWindows用スクリーンセーバを作りたいなら、exerbは諦めてocraを使いましょう。という話になるのかなと。ocraで作ったexeは起動が遅いけど、その分簡単にexe化できるメリットもあるし…。
そもそも、exerb は Ruby 1.8 までしか対応してないので…。今時使われる Ruby は、Ruby 2.x、もしかすると Ruby 3.x だったりするはず。フツーは exerb を使いたくても使えないだろうから、今更な話ではあるなと…。今回、Ruby/SDL を Windows上で使いたいが故に、あえて Ruby 1.8 を使っただけなので…。「せっかく Ruby 1.8 を使うなら exerb もイケるかな」と欲を出しちゃって大変なことになったという…。
そもそも、exerb は Ruby 1.8 までしか対応してないので…。今時使われる Ruby は、Ruby 2.x、もしかすると Ruby 3.x だったりするはず。フツーは exerb を使いたくても使えないだろうから、今更な話ではあるなと…。今回、Ruby/SDL を Windows上で使いたいが故に、あえて Ruby 1.8 を使っただけなので…。「せっかく Ruby 1.8 を使うなら exerb もイケるかな」と欲を出しちゃって大変なことになったという…。
◎ 2022/08/20追記。 :
Windows上で Ruby/SDL 1.3.1.1 を利用する際に必要になるdllを一応列挙しておきます。
Ruby 2.1.1.1 の場合は、またちょっと違ってくるかと…。rubysdl-2.1.1.1-mswin32-1.8.7-p174.zip 内の dll/ の中には以下が入っているようだけど…。
Ruby/SDL 1.3.1.1 と比べて、以下のファイルがリネーム or 追加されている模様。
jpeg.dll libcharset-1.dll libfreetype-6.dll libiconv-2.dll libpng12.dll libtiff.dll ogg.dll SDL.dll SDL_image.dll SDL_mixer.dll SDL_ttf.dll SGE.dll smpeg.dll vorbis.dll vorbisfile.dll zlib1.dll
Ruby 2.1.1.1 の場合は、またちょっと違ってくるかと…。rubysdl-2.1.1.1-mswin32-1.8.7-p174.zip 内の dll/ の中には以下が入っているようだけど…。
jpeg.dll libcharset-1.dll libfreetype-6.dll libiconv-2.dll libogg-0.dll libpng12-0.dll libtiff-3.dll libvorbis-0.dll libvorbisfile-3.dll mikmod.dll SDL.dll SDL_image.dll SDL_mixer.dll SDL_ttf.dll SGE.dll smpeg.dll zlib1.dll
Ruby/SDL 1.3.1.1 と比べて、以下のファイルがリネーム or 追加されている模様。
libogg-0.dll libpng12-0.dll libtiff-3.dll libvorbis-0.dll libvorbisfile-3.dll mikmod.dll
[ ツッコむ ]
2022/08/20(土) [n年前の日記]
#1 [ruby] Rubyスクリプトに画像ファイルを含める
Ruby/SDL を使ったスクリプト内に、png画像を含めてしまいたい。
exerbを使って Rubyスクリプトをexe化する際、画像ファイルもRubyスクリプト内に含めてしまったほうが都合がいいのかなと思ってしまったので、そういうことができるかどうか試してみた。
まあ、Ruby/SDLを使用しているスクリプトを、exerbでexe化するのは _色々問題があって無理だった のだけど。せっかく実験したので、一応メモしておく。
動作確認環境は以下。
昔、似たようなことをやった記憶がある。日記を検索してみたら、DXRuby を使ってそういうことをしていた。
_Rubyスクリプトの中に画像を仕込むソレ
要するに、png画像を base64文字列に変換してスクリプト内に記述しておけば目的は果たせそう。
_Base64 - Wikipedia
exerbを使って Rubyスクリプトをexe化する際、画像ファイルもRubyスクリプト内に含めてしまったほうが都合がいいのかなと思ってしまったので、そういうことができるかどうか試してみた。
まあ、Ruby/SDLを使用しているスクリプトを、exerbでexe化するのは _色々問題があって無理だった のだけど。せっかく実験したので、一応メモしておく。
動作確認環境は以下。
- Windows10 x64 21H2
- Ruby 1.8.7 p330 i386-mingw32 + Ruby/SDL 1.3.1.1
- Ruby 1.9.3 p330 i386-mingw32 + Ruby/SDL 2.1.1.1
昔、似たようなことをやった記憶がある。日記を検索してみたら、DXRuby を使ってそういうことをしていた。
_Rubyスクリプトの中に画像を仕込むソレ
要するに、png画像を base64文字列に変換してスクリプト内に記述しておけば目的は果たせそう。
_Base64 - Wikipedia
◎ base64への変換スクリプト。 :
png画像等をbase64に変換するスクリプトは以下。
_image2base64.rb
注意点。Ruby 1.9.3 なら動いたけれど、Ruby 1.8.7 では動かなかった。Ruby 1.8.7 には File.binread() が無かった。
実行は以下。hoge.png を読み込んで、tmp.txt に base64 の形で出力する例。
これで得られた base64文字列を、Rubyスクリプトのどこかにコピペしてしまえばいい。
_image2base64.rb
infile = ARGV[0] bin_data = File.binread(infile) base64_text = [bin_data].pack("m") puts base64_text
注意点。Ruby 1.9.3 なら動いたけれど、Ruby 1.8.7 では動かなかった。Ruby 1.8.7 には File.binread() が無かった。
実行は以下。hoge.png を読み込んで、tmp.txt に base64 の形で出力する例。
ruby image2base64.rb hoge.png > tmp.txt
これで得られた base64文字列を、Rubyスクリプトのどこかにコピペしてしまえばいい。
◎ base64を読み出して使うスクリプト。 :
base64文字列を読み出して、Ruby/SDLのウインドウに描画してみる。
_03_imgdisp_rubysdl.rb
実行すると以下のようなウインドウが開く。画像を描画できている。
少し解説。
base64文字列を読み込んで、バイナリデータに変換して、それを StringIO に渡している部分は以下。
StringIO というのは、文字列をファイル(IOオブジェクト)のような感覚で利用できるオブジェクトらしい。
_class StringIO (Ruby 1.8.7)
StringIO を渡して、Ruby/SDL で利用できる画像データ(Surface) に変換している部分は以下。
一般的に、Ruby/SDL で画像ファイルを読み込むなら SDL::Surface.load(filename) を使うけれど、SDL::Surface.loadFromIO(io) を使えば Ruby のIOオブジェクトから画像を読み込める。
_Ruby/SDL Reference Manual (2.x)
_03_imgdisp_rubysdl.rb
require "sdl" require "stringio" SCRW, SCRH = 512, 288 # SDL init SDL.init(SDL::INIT_EVERYTHING) screen = SDL::Screen.open(SCRW, SCRH, 0, SDL::SWSURFACE) # read base64 text base64_text = "" DATA.each { |l| base64_text += l.chomp } bin_data_io = StringIO.new(base64_text.unpack("m")[0], "rb") # load image from StringIO img = SDL::Surface.loadFromIO(bin_data_io).display_format # main loop loop do while event = SDL::Event2.poll case event when SDL::Event2::Quit exit when SDL::Event2::KeyDown exit if event.sym == SDL::Key::ESCAPE end end screen.fill_rect(0, 0, SCRW, SCRH, [0, 100, 200]) # fill bg screen.put(img, 32, 32) # draw sprite screen.update_rect(0, 0, 0, 0) # update screen SDL.delay(16.6) # wait end __END__ iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAYFBMVEUA/wB3 CQKFBwSPCAGIDgCaCAWkBgGlCQ6dDgC0DACpEAC0Dw+pFhvIFgTJHB+gLSbF KRzGNC3fLzHFS0bXRz7cX1i8bmvldW/hf3rNlZLnlpHlsa7zwr/v1NP46ej/ //9Nzsg7AAAAAXRSTlMAQObYZgAABZ5JREFUWIW9V9uWmzoMnWCMARPAgRAI GP7/L7sl2eB0pqs9L0deTNqZ7ItkWcDX1/8Qx3Hs2/qen2OLsHd7L/4TfKfl iWB43Nv73VZFqbVS/4b223t+ze/Ng+H1HPu2rWxR61J1Kv8H9DqNd4nXtlEO rq2qqi600pnKsttfrK+v6n7Ga/fveXBVTQQlMsiz2+3PDCjbOlTVXRbHduyL I/26RAYqy2/ZnwmOY5ttn6ARMzZj6YwpjS61yXJk0P2JIWhRXB6eOxjWTmvd YA8axQ5+Jjj8ZOsa2d7THIaDYp2Ab5ijQxbdTwz72hG6/s3Dk7ph25Yh4FXG dfgJ39S1MHw4mA9043uen070wZD9tJN+qVP908P97efn6/kcR+eCPtXgG4Gf 6qhfX/6B77eV0OPYu75H/uiD7IcqEr4U7FUD5hn21+jGHtH2lSP/tx8ciP7p 4J7UcPWCbulnb5Xo/0bgp0L0P2tAMfr13le8bG9tZdXt9o1gX8r6w0H0gM/l mCBM6MrSspHhA0/6CfqqQ+28d6xv46qFIemDA/tflqKfsMhaDy+6uCr8qyrA oEFwjZXdd2VZftTg1K8mf0xBtyZsXdQFQqd9tE/Af6uBLIejPABd1cRAaMLz qU4KoDXwxXc01oJDVFhgBKepE6mRbtlFsHYlM9Snh8hU1xMO8kowaMK34LmT Mn11AH6FbyQ1ONfgcYx9TnhOPOLBoE6CNcvxyzKxEDzUdQc8TvKkC9KXHCLD SbDfcpUrcljWSS/Qp1n33SOmgvWBFw+Ez3QkWEgfOZQfHigIv23rus7soPxw oCPBPrEfzkE8lKxf1mbBNEfM78lorl9SAxUJDk8zFlXQbCHxQPgZd6YXopH6 a3Xu4kWwdFISYYh1KAi/0RTicHH/JCQDJkCFG5pwuVLRg9jo1n0N6Of4dCXr U6Di8KxPAu+caTgHYgBHXlIVCD+OQ1gyCaN4qEAg2DBrGky5jHeCFlkY/P52 Iy+Ovgz6gUAnBIOzvYlZMIcuJ78vND8lWqzUwC27MgDBCw77jhkkj7Jbjv0N VAw8ldxNrAAbyBMC/3rCgyv5XsHfmVa6vQGF+UeXpc/iNIB5nKuEYH+/4KF3 TWDolh2kMn/OOYRpdhXglufkwAjByk8vxMB16JA99K/pdy596fMumsIwwfze V/bg4KFb+PBOpGtF+7x0yP+W5eQAeCEYn34XD8O0eHoKwNk1BU/ASqYYppmp TClo7AA7AD4QYOTRndv7ndE4vh3tZIEphglKP4WlSPTzXFljLeP7ceZnh32X k49nAKOlG4owB/mqC6kA62MBzwT9ox89YzfPJ3/F0W2kz2TKnUtf+ghrWyFo +2YCcnuvcvLnuTFGRwbqqyKETvQTB31fNm/GhpM/oDo6zm/pPsoGeVAHfndQ Wd0MjJSzPw5sABPyZMh5WBRFfssTB61txUFpbPN8nZPj2VAKwYM4YASR5Ek0 LeO/bN1oa92ImUFPMI/RmbMEcQZlUn9ldOJAB4IKataaUdAP6mjST6so+pk2 xhoVq5A17YMJ6LvWmHZ8PHpao0kchAN44a05HajgAE999ADsCI/o28aEbVSX fo4JRp2HulkdixAc0LMveeapg2WjAZXqq4LwzID5KZyRgPGIMLlME2uQ4HVQ l/nUZKmDJjgw+GuP73zgM64/44M+hyOGWycEX4KnvWwZfzZyrL8y9tJ/8HLc CIFAh5OjWeTagzjAzupFfTA8WrwvNKcDeX5nnybdA3KA6iXeWZ/D4f40hpt7 7Fcw0JT5cHDt3ice0eQuEOD9RWUNyoU5luwBM3DPqRwecUJc6xKGsRniAw4X m+5LxeXgPEO5Svqf/qc6sOFe94gZIKDRZbg7JnhNO0WXCp8cHV1fP4XMWy14 JW8kcf39HVco8BamGsPvQ/+G4PgFlvOHReRcvJgAAAAASUVORK5CYII=
実行すると以下のようなウインドウが開く。画像を描画できている。
ruby 03_imgdisp_rubysdl.rb
少し解説。
- このスクリプトは、「__END__」と書かれた行の下に、画像ファイルを変換したbase64文字列を列挙している。
- Ruby は、「__END__」と書いてある行より下に書かれた行に対して、「DATA」というシンボルでアクセスすることができる。
base64文字列を読み込んで、バイナリデータに変換して、それを StringIO に渡している部分は以下。
base64_text = "" DATA.each { |l| base64_text += l.chomp } bin_data_io = StringIO.new(base64_text.unpack("m")[0], "rb")
StringIO というのは、文字列をファイル(IOオブジェクト)のような感覚で利用できるオブジェクトらしい。
_class StringIO (Ruby 1.8.7)
StringIO を渡して、Ruby/SDL で利用できる画像データ(Surface) に変換している部分は以下。
img = SDL::Surface.loadFromIO(bin_data_io).display_format
一般的に、Ruby/SDL で画像ファイルを読み込むなら SDL::Surface.load(filename) を使うけれど、SDL::Surface.loadFromIO(io) を使えば Ruby のIOオブジェクトから画像を読み込める。
_Ruby/SDL Reference Manual (2.x)
◎ 別の書き方。 :
「__END__」以下の行を読み込もうとすると1種類のデータしか読み込めなかったりするし、exerb が DATAシンボルに非対応だったりもするので、ちょっと違う書き方をしてみた。
_04_imgdisp_rubysdl_2.rb
base64 をバイナリ化するあたりは、ちょっとだけ記述が減ってくれた。
_04_imgdisp_rubysdl_2.rb
require "sdl" require "stringio" SCRW, SCRH = 512, 288 BASE64_STRINGS = [ "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAYFBMVEUA/wB3", "CQKFBwSPCAGIDgCaCAWkBgGlCQ6dDgC0DACpEAC0Dw+pFhvIFgTJHB+gLSbF", "KRzGNC3fLzHFS0bXRz7cX1i8bmvldW/hf3rNlZLnlpHlsa7zwr/v1NP46ej/", "//9Nzsg7AAAAAXRSTlMAQObYZgAABZ5JREFUWIW9V9uWmzoMnWCMARPAgRAI", "GP7/L7sl2eB0pqs9L0deTNqZ7ItkWcDX1/8Qx3Hs2/qen2OLsHd7L/4TfKfl", "iWB43Nv73VZFqbVS/4b223t+ze/Ng+H1HPu2rWxR61J1Kv8H9DqNd4nXtlEO", "rq2qqi600pnKsttfrK+v6n7Ga/fveXBVTQQlMsiz2+3PDCjbOlTVXRbHduyL", "I/26RAYqy2/ZnwmOY5ttn6ARMzZj6YwpjS61yXJk0P2JIWhRXB6eOxjWTmvd", "YA8axQ5+Jjj8ZOsa2d7THIaDYp2Ab5ijQxbdTwz72hG6/s3Dk7ph25Yh4FXG", "dfgJ39S1MHw4mA9043uen070wZD9tJN+qVP908P97efn6/kcR+eCPtXgG4Gf", "6qhfX/6B77eV0OPYu75H/uiD7IcqEr4U7FUD5hn21+jGHtH2lSP/tx8ciP7p", "4J7UcPWCbulnb5Xo/0bgp0L0P2tAMfr13le8bG9tZdXt9o1gX8r6w0H0gM/l", "mCBM6MrSspHhA0/6CfqqQ+28d6xv46qFIemDA/tflqKfsMhaDy+6uCr8qyrA", "oEFwjZXdd2VZftTg1K8mf0xBtyZsXdQFQqd9tE/Af6uBLIejPABd1cRAaMLz", "qU4KoDXwxXc01oJDVFhgBKepE6mRbtlFsHYlM9Snh8hU1xMO8kowaMK34LmT", "Mn11AH6FbyQ1ONfgcYx9TnhOPOLBoE6CNcvxyzKxEDzUdQc8TvKkC9KXHCLD", "SbDfcpUrcljWSS/Qp1n33SOmgvWBFw+Ez3QkWEgfOZQfHigIv23rus7soPxw", "oCPBPrEfzkE8lKxf1mbBNEfM78lorl9SAxUJDk8zFlXQbCHxQPgZd6YXopH6", "a3Xu4kWwdFISYYh1KAi/0RTicHH/JCQDJkCFG5pwuVLRg9jo1n0N6Of4dCXr", "U6Di8KxPAu+caTgHYgBHXlIVCD+OQ1gyCaN4qEAg2DBrGky5jHeCFlkY/P52", "Iy+Ovgz6gUAnBIOzvYlZMIcuJ78vND8lWqzUwC27MgDBCw77jhkkj7Jbjv0N", "VAw8ldxNrAAbyBMC/3rCgyv5XsHfmVa6vQGF+UeXpc/iNIB5nKuEYH+/4KF3", "TWDolh2kMn/OOYRpdhXglufkwAjByk8vxMB16JA99K/pdy596fMumsIwwfze", "V/bg4KFb+PBOpGtF+7x0yP+W5eQAeCEYn34XD8O0eHoKwNk1BU/ASqYYppmp", "TClo7AA7AD4QYOTRndv7ndE4vh3tZIEphglKP4WlSPTzXFljLeP7ceZnh32X", "k49nAKOlG4owB/mqC6kA62MBzwT9ox89YzfPJ3/F0W2kz2TKnUtf+ghrWyFo", "+2YCcnuvcvLnuTFGRwbqqyKETvQTB31fNm/GhpM/oDo6zm/pPsoGeVAHfndQ", "Wd0MjJSzPw5sABPyZMh5WBRFfssTB61txUFpbPN8nZPj2VAKwYM4YASR5Ek0", "LeO/bN1oa92ImUFPMI/RmbMEcQZlUn9ldOJAB4IKataaUdAP6mjST6so+pk2", "xhoVq5A17YMJ6LvWmHZ8PHpao0kchAN44a05HajgAE999ADsCI/o28aEbVSX", "fo4JRp2HulkdixAc0LMveeapg2WjAZXqq4LwzID5KZyRgPGIMLlME2uQ4HVQ", "l/nUZKmDJjgw+GuP73zgM64/44M+hyOGWycEX4KnvWwZfzZyrL8y9tJ/8HLc", "CIFAh5OjWeTagzjAzupFfTA8WrwvNKcDeX5nnybdA3KA6iXeWZ/D4f40hpt7", "7Fcw0JT5cHDt3ice0eQuEOD9RWUNyoU5luwBM3DPqRwecUJc6xKGsRniAw4X", "m+5LxeXgPEO5Svqf/qc6sOFe94gZIKDRZbg7JnhNO0WXCp8cHV1fP4XMWy14", "JW8kcf39HVco8BamGsPvQ/+G4PgFlvOHReRcvJgAAAAASUVORK5CYII=", ] # SDL init SDL.init(SDL::INIT_EVERYTHING) screen = SDL::Screen.open(SCRW, SCRH, 0, SDL::SWSURFACE) # read base64 text base64_text = BASE64_STRINGS.join bin_data_io = StringIO.new(base64_text.unpack("m")[0], "rb") # load image from StringIO img = SDL::Surface.loadFromIO(bin_data_io).display_format # main loop loop do while event = SDL::Event2.poll case event when SDL::Event2::Quit exit when SDL::Event2::KeyDown exit if event.sym == SDL::Key::ESCAPE end end screen.fill_rect(0, 0, SCRW, SCRH, [0, 100, 200]) # fill bg screen.put(img, 32, 32) # draw sprite screen.update_rect(0, 0, 0, 0) # update screen SDL.delay(16.6) # wait end
base64 をバイナリ化するあたりは、ちょっとだけ記述が減ってくれた。
base64_text = BASE64_STRINGS.join bin_data_io = StringIO.new(base64_text.unpack("m")[0], "rb")
[ ツッコむ ]
#2 [zatta] スクリーンセーバ作成に対する憧れ
自分語り。思考メモ。スクリーンセーバにまつわるアレコレを脈絡なくメモ。
ここ最近、スクリーンセーバ作成についてアレコレ実験しているけれど。「ちょwwwおまwww今時スクリーンセーバ作成ってwww」と笑われそうだなと不安になりながら作業メモを書き残してる状態で。
でも、仕方ない。スクリーンセーバ作成というお題は、自分の中ではずっと気になってるお題でして…。
大昔、Windows95 が普及し始めて、CD-ROMに入ったデスクトップアクセサリー云々が店頭販売されてた当時。「スクリーンセーバって面白いな。見ていて楽しい」「自分も作れないものかなあ」と気になったのですが。しかし、作り方がさっぱり分からない。一体何をどうすればいいんだろう。そんなもやもやが、喉に刺さった魚の小骨のように自分の中ではずっと残っていて。プログラマーとしてはフツーに悔しいじゃないですか。作り方の概要すら知らないなんて…。少なくとも自分は、その状態ってちょっと恥ずかしいし情けない状態だなと感じてしまって。そこにはプログラマーとしての敗北感があるわけですよ。
あれから四半世紀。さすがに今なら簡単に作れるようになってるんじゃないかと、時々思い出したように関連情報をググってみたりして。仕様その他がなんとなく分かってきたような気分になってきたので、こうして実験を始めちゃったという。まあ、使った言語やライブラリはアレだけど、一応作れることは分かってきて、多少は気分的にスッキリ…。
いや、スッキリしてない。四半世紀も経ってるのに、まだこんなに作成が難しいのかと、正直ガッカリした面もあるなと。
もっと簡単に作れないものかなあ…。
ここ最近、スクリーンセーバ作成についてアレコレ実験しているけれど。「ちょwwwおまwww今時スクリーンセーバ作成ってwww」と笑われそうだなと不安になりながら作業メモを書き残してる状態で。
でも、仕方ない。スクリーンセーバ作成というお題は、自分の中ではずっと気になってるお題でして…。
大昔、Windows95 が普及し始めて、CD-ROMに入ったデスクトップアクセサリー云々が店頭販売されてた当時。「スクリーンセーバって面白いな。見ていて楽しい」「自分も作れないものかなあ」と気になったのですが。しかし、作り方がさっぱり分からない。一体何をどうすればいいんだろう。そんなもやもやが、喉に刺さった魚の小骨のように自分の中ではずっと残っていて。プログラマーとしてはフツーに悔しいじゃないですか。作り方の概要すら知らないなんて…。少なくとも自分は、その状態ってちょっと恥ずかしいし情けない状態だなと感じてしまって。そこにはプログラマーとしての敗北感があるわけですよ。
あれから四半世紀。さすがに今なら簡単に作れるようになってるんじゃないかと、時々思い出したように関連情報をググってみたりして。仕様その他がなんとなく分かってきたような気分になってきたので、こうして実験を始めちゃったという。まあ、使った言語やライブラリはアレだけど、一応作れることは分かってきて、多少は気分的にスッキリ…。
いや、スッキリしてない。四半世紀も経ってるのに、まだこんなに作成が難しいのかと、正直ガッカリした面もあるなと。
もっと簡単に作れないものかなあ…。
◎ 余談。 :
スクリーンセーバ作成の民主化だの、スクリーンセーバはCGアートだの、普段考えてたことをメモしてたのだけど、ふと日記を検索したらまるっきり同じことを以前メモしてあった…。
_スクリーンセーバの作り方を調べてる
_スクリーンセーバの作り方を調べてる
[ ツッコむ ]
2022/08/21(日) [n年前の日記]
#1 [pc] 親父さん用PCの無線LAN子機を交換
親父さんから、PCがネットに接続できないと不具合報告。
確認してみたところ、バスパワー(ACアダプタ無し)の4ポートUSB2.0ハブ経由で接続した無線LAN子機、ELECOM WDC-433DU2HBK が反応しない。
_ワイヤレスアダプター 433M - WDC-433DU2HBK
USBハブのLEDは光っているので、その先にUSB機器が接続されていると認識されている状態なのだろうけど。無線LAN子機内のLEDがいつまで経っても光らない。本来はチカチカと青く光るはずなのだけど。
とうとう壊れたのかもしれないなと、自分の部屋で埃を被っていた無線LAN子機、IO-DATA WN-G300UA を発掘して交換してみた。
_WN-G300UA | Wi-Fi(無線LAN)子機 | IODATA アイ・オー・データ機器
特に何もしなくても認識されて、無線LANルータと接続できたのだけど、一応念のためにコントロールパネル経由で WDC-433DU2HBK のドライバをアンインストールしてから、WN-G300UA のドライバ、wng300ua112.exe をインストールしておいた。しばらくこれで様子を見てもらおう…。
確認してみたところ、バスパワー(ACアダプタ無し)の4ポートUSB2.0ハブ経由で接続した無線LAN子機、ELECOM WDC-433DU2HBK が反応しない。
_ワイヤレスアダプター 433M - WDC-433DU2HBK
USBハブのLEDは光っているので、その先にUSB機器が接続されていると認識されている状態なのだろうけど。無線LAN子機内のLEDがいつまで経っても光らない。本来はチカチカと青く光るはずなのだけど。
とうとう壊れたのかもしれないなと、自分の部屋で埃を被っていた無線LAN子機、IO-DATA WN-G300UA を発掘して交換してみた。
_WN-G300UA | Wi-Fi(無線LAN)子機 | IODATA アイ・オー・データ機器
特に何もしなくても認識されて、無線LANルータと接続できたのだけど、一応念のためにコントロールパネル経由で WDC-433DU2HBK のドライバをアンインストールしてから、WN-G300UA のドライバ、wng300ua112.exe をインストールしておいた。しばらくこれで様子を見てもらおう…。
◎ 懸念事項。 :
ググったところ、各無線LAN子機に使われているチップは以下の模様。
_ELECOM WDC-433DU2H 11ac Wireless Adapter USB Drivers - Download Device Drivers - DriverAgent.com - DriverAgent.com
_帰ってきた、無精ヒゲの一応日記: WDC-433DU2H のドライバ
しかし、ウチでは、RTL8192CU を使った無線LAN子機はどうも不調で…。そのうち親父さんから「ネットに繋がらないぞ」と文句が出るかもしれない。万が一、新しく入手することを考えて、使用チップを把握しつつ候補を探しておくか…。
- IO-DATA WN-G300UA のチップは、Realtek RTL8192CU。
- ELECOM WDC-433DU2HBK のチップは、Realtek RTL8811AU。
_ELECOM WDC-433DU2H 11ac Wireless Adapter USB Drivers - Download Device Drivers - DriverAgent.com - DriverAgent.com
_帰ってきた、無精ヒゲの一応日記: WDC-433DU2H のドライバ
しかし、ウチでは、RTL8192CU を使った無線LAN子機はどうも不調で…。そのうち親父さんから「ネットに繋がらないぞ」と文句が出るかもしれない。万が一、新しく入手することを考えて、使用チップを把握しつつ候補を探しておくか…。
◎ 古い無線LAN子機は生きてた。 :
WDC-433DU2H は壊れたのかと思ったけれど、自分のサブPC(Athlon5350機)に繋いでみたところ、LEDが光った。ドライバ(WDC-433DU2HBK_Win_1030.25.0701.2017.zip)をインストールしたらルータとも接続できた。
どうやら親父さん用PCと接触不良を起こしてただけっぽいな…。親父さんはUSBハブ周りをとにかく雑に扱う人だから…。
それとも電力不足だろうか。と言っても、親父さん用PCのUSBハブには、無線LAN子機の他に、ワイヤレスマウス受信機、プリンタケーブルしか差してないのだけどな…。プリンタケーブルはそれほど電流を流さないだろうし、無線LAN子機 + ワイヤレスマウス受信機なら動きそうなものだけど。
どうやら親父さん用PCと接触不良を起こしてただけっぽいな…。親父さんはUSBハブ周りをとにかく雑に扱う人だから…。
それとも電力不足だろうか。と言っても、親父さん用PCのUSBハブには、無線LAN子機の他に、ワイヤレスマウス受信機、プリンタケーブルしか差してないのだけどな…。プリンタケーブルはそれほど電流を流さないだろうし、無線LAN子機 + ワイヤレスマウス受信機なら動きそうなものだけど。
◎ 簡易USBスタンドモドキを自作。 :
以前100円ショップで購入したアレコレを利用して、簡易ではあるけれどUSBスタンドモドキを自作した。親父さん用PCが置いてあるPCデスクの柱に貼り付けておいた。これで無線LAN子機の固定と向き調整ができるはず。
材料は以下。
_ダイソー コードクリップ 4P のパーツレビュー | ヴェルファイア(【ShiN】) | みんカラ
_セリアの超強力マグネット | 100均マグネットの活用術10選
_タピオカ用のストローはどこで買える?これでおうちでもタピ活できちゃう?|mamagirl [ママガール]
※ 2022/09/19追記。
写真を撮っておいたので一応アップロード。

材料は以下。
- ダイソー コードクリップ 4P
- Seria 超強力マグネット 4P
- ダイソー ビッグストロー 口径12mm
- USB延長ケーブル
- 両面テープ
_ダイソー コードクリップ 4P のパーツレビュー | ヴェルファイア(【ShiN】) | みんカラ
_セリアの超強力マグネット | 100均マグネットの活用術10選
_タピオカ用のストローはどこで買える?これでおうちでもタピ活できちゃう?|mamagirl [ママガール]
※ 2022/09/19追記。
写真を撮っておいたので一応アップロード。

[ ツッコむ ]
2022/08/22(月) [n年前の日記]
#1 [prog][windows] msys2 + MinGW-w64でスクリーンセーバをビルドしようとしてハマった
以下で紹介されている、MinGW用のスクリーンセーバサンプルをビルドしてみたくなった。環境は Windows10 x64 21H2。
_よしいずの雑記帳 スクリーンセーバーの作り方
以下の2つのファイルをコピペして作成。文字コードは UTF-8N にした。(※ 2022/08/23追記。文字コードはSJISにしたほうが良かったのかもしれない。)
_scrnsave.c
_resource.rc
また、Makefile も作成した。
_Makefile
結論を先に書いておくけど、msys2 + MinGW-w64 ではダメ。MinGW でビルドしましょう…。
_よしいずの雑記帳 スクリーンセーバーの作り方
以下の2つのファイルをコピペして作成。文字コードは UTF-8N にした。(※ 2022/08/23追記。文字コードはSJISにしたほうが良かったのかもしれない。)
_scrnsave.c
_resource.rc
また、Makefile も作成した。
_Makefile
screensv.scr: screensv.o resource.o gcc screensv.o resource.o -lscrnsave -mwindows -o screensv.scr screensv.o: screensv.c gcc -c screensv.c -o screensv.o resource.o: resource.rc windres resource.rc -o resource.o .PHONY: clean clean: rm -f *.scr rm -f *.o
結論を先に書いておくけど、msys2 + MinGW-w64 ではダメ。MinGW でビルドしましょう…。
◎ msys2 + MinGW-w64ではスクリーンセーバをビルドできない :
msys2+ MinGW-w64 でビルドしようとしたら失敗した。screensv.o と resource.o は生成されたけど、リンクエラーが出る。
スクリーンセーバ作成に関係してくるファイルは以下だろうか…。ちなみに自分の環境では msys2 を C:\msys64\ にインストールしてある。
scrnsave.h はそれらしい内容が書かれているように見えるけれど。libscrnsav*.a は、どちらもファイルサイズが990Byte と異様に小さい上に、全く同じサイズというのが気になる。
余談。巷の関連記事では、スクリーンセーバの作成には scrnsave.h と scrnsave.lib が必要と書いてあるのだけど、それは Microsoft Visual C++用の話のようで。MinGW の場合は libscrnsave.a か libscrnsavw.a が必要になるらしい。最初、そのことが分からなくて、scrnsave.lib を探して「無いな…MinGWでは作れないのかな…?」と勘違いしてた。
さておき。msys2 + MinGW-w64 でリンクエラーが出る原因についてググっていたら、以下の話を見かけた。
_Re: [Mingw-w64-public] [mingw-w64:discussion] No symbols in libscrnsave.a; source file scrnsave.c c | MinGW-w64 - for 32 and 64 bit Windows
_MinGW-w64 - for 32 and 64 bit Windows / Discussion / Help: No symbols in libscrnsave.a; source file scrnsave.c commented out
_MinGW-w64 - for 32 and 64 bit Windows / Bugs / #351 Scrnsave lib empty
おそらくだけど、「libscrnsav*.a の元になる scrnsave.c が全部コメントアウトされているけれどこれってどういうこと?」と言っているのだろう。ナニソレ、マジかよ。
MinGW-w64のソースコードであろう、mingw-w64-v10.0.0.zip をDLして解凍して中を覗いてみた。
_MinGW-w64 - for 32 and 64 bit Windows download | SourceForge.net
以下がスクリーンセーバ関連のファイルだろうか。
scrnsave.c を開いて確認してみたら、たしかに #if 0 - #endif で全内容がコメントアウトされていた。これではリンクできるわけが無い…。なんでこんなことをしたんだ…。
アレかな。メンテナの方々が「今時スクリーンセーバなんて作るやつ居ねえだろ」と勝手に決めつけてやらかしたのかな…と邪推。まあ、MinGW から fork して MinGW-w64 を作り始めた時に、まずは全体のビルドが通る状態にすることを優先していただろうし。そのまま放置されたのかも…。
そんなわけで、msys2 + MinGW-w64 では、スクリーンセーバをビルドすることはできないと分かった。
※ 2022/08/23追記。github にも scrnsave.c があった。1行目から #if 0 があることが分かるかと。
_mingw-w64/scrnsave.c at master - Alexpux/mingw-w64
$ gcc --version gcc.exe (Rev1, Built by MSYS2 project) 12.2.0 Copyright (C) 2022 Free Software Foundation, Inc. ... $ windres --version GNU windres (GNU Binutils) 2.39 Copyright (C) 2022 Free Software Foundation, Inc. ... $ make --version GNU Make 4.3 このプログラムは x86_64-pc-msys 用にビルドされました Copyright (C) 1988-2020 Free Software Foundation, Inc. ...
$ make gcc -c screensv.c -o screensv.o windres resource.rc -o resource.o gcc screensv.o resource.o -lscrnsave -mwindows -o screensv.scr C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: screensv.o:screensv.c:(.text+0x229): undefined reference to `DefScreenSaverProc' C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o): in function `main': C:/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crt0_c.c:18: undefined reference to `WinMain' collect2.exe: error: ld returned 1 exit status make: *** [Makefile:2: screensv.scr] エラー 1
スクリーンセーバ作成に関係してくるファイルは以下だろうか…。ちなみに自分の環境では msys2 を C:\msys64\ にインストールしてある。
- C:\msys64\mingw64\include\scrnsave.h
- C:\msys64\mingw64\lib\libscrnsave.a
- C:\msys64\mingw64\lib\libscrnsavw.a
scrnsave.h はそれらしい内容が書かれているように見えるけれど。libscrnsav*.a は、どちらもファイルサイズが990Byte と異様に小さい上に、全く同じサイズというのが気になる。
余談。巷の関連記事では、スクリーンセーバの作成には scrnsave.h と scrnsave.lib が必要と書いてあるのだけど、それは Microsoft Visual C++用の話のようで。MinGW の場合は libscrnsave.a か libscrnsavw.a が必要になるらしい。最初、そのことが分からなくて、scrnsave.lib を探して「無いな…MinGWでは作れないのかな…?」と勘違いしてた。
さておき。msys2 + MinGW-w64 でリンクエラーが出る原因についてググっていたら、以下の話を見かけた。
_Re: [Mingw-w64-public] [mingw-w64:discussion] No symbols in libscrnsave.a; source file scrnsave.c c | MinGW-w64 - for 32 and 64 bit Windows
_MinGW-w64 - for 32 and 64 bit Windows / Discussion / Help: No symbols in libscrnsave.a; source file scrnsave.c commented out
_MinGW-w64 - for 32 and 64 bit Windows / Bugs / #351 Scrnsave lib empty
おそらくだけど、「libscrnsav*.a の元になる scrnsave.c が全部コメントアウトされているけれどこれってどういうこと?」と言っているのだろう。ナニソレ、マジかよ。
MinGW-w64のソースコードであろう、mingw-w64-v10.0.0.zip をDLして解凍して中を覗いてみた。
_MinGW-w64 - for 32 and 64 bit Windows download | SourceForge.net
以下がスクリーンセーバ関連のファイルだろうか。
- mingw-w64-v10.0.0\mingw-w64-crt\libsrc\scrnsave.c
- mingw-w64-v10.0.0\mingw-w64-headers\include\scrnsave.h
scrnsave.c を開いて確認してみたら、たしかに #if 0 - #endif で全内容がコメントアウトされていた。これではリンクできるわけが無い…。なんでこんなことをしたんだ…。
アレかな。メンテナの方々が「今時スクリーンセーバなんて作るやつ居ねえだろ」と勝手に決めつけてやらかしたのかな…と邪推。まあ、MinGW から fork して MinGW-w64 を作り始めた時に、まずは全体のビルドが通る状態にすることを優先していただろうし。そのまま放置されたのかも…。
そんなわけで、msys2 + MinGW-w64 では、スクリーンセーバをビルドすることはできないと分かった。
※ 2022/08/23追記。github にも scrnsave.c があった。1行目から #if 0 があることが分かるかと。
_mingw-w64/scrnsave.c at master - Alexpux/mingw-w64
◎ MinGWでビルド :
msys2 + MinGW-w64 ではなく、MinGW + msys でビルドを試してみた。
必要なファイルは以下だろうか。ちなみに自分の環境では、MinGW を D:\MinGW\ にインストールしてある。
すんなりビルドが通った…。
出来上がった screensv.scr を右クリック → Test、を選んだら、画面一杯に丸が描画され続けた。また、マウスを動かしたら終了した。
MinGW でビルドした .exe や .scr は32bit版だろうから…。Windows10 64bit版を使っているので、C:\Windows\SysWOW64\ 以下に screensv.scr をコピーして動作確認した。正常に動作してくれた。
ただ、スクリーンセーバ設定画面で、「設定」をクリックすると、文字化けしたメッセージが表示されてしまう。
> gcc --version gcc (MinGW.org GCC Build-2) 9.2.0 Copyright (C) 2019 Free Software Foundation, Inc. ... > windres --version GNU windres (GNU Binutils) 2.32 Copyright (C) 2019 Free Software Foundation, Inc. ... > make --version GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. ...
必要なファイルは以下だろうか。ちなみに自分の環境では、MinGW を D:\MinGW\ にインストールしてある。
- D:\MinGW\include\scrnsave.h
- D:\MinGW\lib\libscrnsave.a
- D:\MinGW\lib\libscrnsavw.a
すんなりビルドが通った…。
出来上がった screensv.scr を右クリック → Test、を選んだら、画面一杯に丸が描画され続けた。また、マウスを動かしたら終了した。
MinGW でビルドした .exe や .scr は32bit版だろうから…。Windows10 64bit版を使っているので、C:\Windows\SysWOW64\ 以下に screensv.scr をコピーして動作確認した。正常に動作してくれた。
ただ、スクリーンセーバ設定画面で、「設定」をクリックすると、文字化けしたメッセージが表示されてしまう。
[ ツッコむ ]
2022/08/23(火) [n年前の日記]
#1 [prog][windows] Windows10にMinGWをインストールし直した
Windows10 x64 21H2上で、MinGW + msys (msys2 + MinGW-w64に非ず)を再インストールしてみた。インストール直後の状態で、スクリーンセーバをビルドできるのかどうかが気になったので…。
MinGW (MinGW が正しい表記らしい) + msys というのは、Windows上で、CコンパイラやC++コンパイラを使えるようにしてくれるツール群、という説明で合っているのだろうか。とにかく、コイツをインストールすれば、gcc という有名なCコンパイラが無料で使えるようになる。
_MinGW - Wikipedia
MinGW は32bit版プログラムの生成しか対応してないので、今時は msys2 + MinGW-w64 という、64bit対応の環境が使われることが多いのだけど。スクリーンセーバの作成をしたいとなると、MinGW-w64 はそのへん対応してないので、MinGW を使うしかなく。 *1
さておき。せっかくなので、2022/08時点でのインストール手順をメモ。そのうち自分も手順を絶対に忘れそうなので…。
MinGW (MinGW が正しい表記らしい) + msys というのは、Windows上で、CコンパイラやC++コンパイラを使えるようにしてくれるツール群、という説明で合っているのだろうか。とにかく、コイツをインストールすれば、gcc という有名なCコンパイラが無料で使えるようになる。
_MinGW - Wikipedia
MinGW は32bit版プログラムの生成しか対応してないので、今時は msys2 + MinGW-w64 という、64bit対応の環境が使われることが多いのだけど。スクリーンセーバの作成をしたいとなると、MinGW-w64 はそのへん対応してないので、MinGW を使うしかなく。 *1
さておき。せっかくなので、2022/08時点でのインストール手順をメモ。そのうち自分も手順を絶対に忘れそうなので…。
◎ インストーラを入手。 :
MinGW の入手先は、昔は SourceForgeだったけど、今は OSDN になっているのだとか。まあ、細かい各パッケージは SourceForge からダウンロードしているっぽいけれど。
_MinGW - Minimalist GNU for Windows Project Top Page - OSDN
mingw-get-setup.exe というファイルをダウンロードする。
_MinGW - Minimalist GNU for Windows Project Top Page - OSDN
mingw-get-setup.exe というファイルをダウンロードする。
◎ インストーラを実行。 :
mingw-get-setup.exe を実行する。以下の画面が出てくる。
MinGW のインストールディレクトリその他を指定。
その後のインストール作業に必要になるプログラムがいくつかダウンロードされる。
MinGW関係の色々なパッケージを選んでダウンロードできる画面が出てくる。
チェックボックスを左クリックするとメニューが出てくる。
その他のインストールするジャンルを選択する。
チェックをつけ終わったら、変更を指示。

パッケージのダウンロード画面が出てくる。
色々なパッケージがダウンロードされていく。
ダウンロードが終わると、パッケージが展開される。
ちなみに、左のほうで「All Packages」を選べば、全パッケージの一覧が表示される。
こんな感じの流れで、パッケージのインストールは終了。
- 「Install」をクリックして先に進む。
MinGW のインストールディレクトリその他を指定。
- デフォルトでは C:\MinGW になってる。
- 右のほうの「Change」をクリックすれば、インストール場所を変更できる。自分は D:\MinGW に変更した。
- 下のほうにあるチェックボックスは、「スタートメニューに登録するか」「デスクトップにショートカットファイルを作るか」といったオプション設定。
その後のインストール作業に必要になるプログラムがいくつかダウンロードされる。
- ダウンロードが終わったら「Continue」をクリック。
MinGW関係の色々なパッケージを選んでダウンロードできる画面が出てくる。
- 左のほうで「Basic Setup」を選択。
- 右のほうで、「mingw-developer-toolkit-bin」の頭のチェックボックスを左クリック。
チェックボックスを左クリックするとメニューが出てくる。
- 「Mark for Installation」を選んで、インストールを指示。
その他のインストールするジャンルを選択する。
- おそらくだけど、ada、fortran、objc は、使う場面がほとんど無いと思う。それ以外は全部チェックを入れる感じでいいかと。もちろん、「いいや、俺は (ada|fortran|Objective-C) をバリバリ使うんだ」という人はインストールすればよろしいかと。
チェックをつけ終わったら、変更を指示。

- 「Installation」→「Apply Chnages」を選択。
パッケージのダウンロード画面が出てくる。
- 「Apply」をクリック。
色々なパッケージがダウンロードされていく。
ダウンロードが終わると、パッケージが展開される。
- 「Close」をクリックして閉じる。
- 左のほうにあるチェックボックスを入れれば、次回からはダウンロードと展開が終わったら自動で閉じてくれるようになるらしい。自分は試してないけれど…。
ちなみに、左のほうで「All Packages」を選べば、全パッケージの一覧が表示される。
こんな感じの流れで、パッケージのインストールは終了。
◎ パスを通す。 :
インストールは終わったけれど、この状態では MinGW の各ツールにパスが通って無いので使えない。
えてしてその手の解説記事では、Windowsの環境変数 PATH に、以下の2つのディレクトリを追加していくけれど…。
自分も昔はそうしていたけど、他の開発関連ツール群と衝突してハマったことがあるので、それ以来、以下のようなbatファイルを作って、MinGW を使いたい時だけ環境変数を変更して利用している。
_mingw.bat
やっていることは、環境変数 PATH の一番最初に MinGW と msys のパスを追加したり、C/C++ 関係のヘッダファイルやライブラリファイルの場所を環境変数に指定しているだけ。
例えば、DOS窓を開いて mingw.bat を実行すれば、その後は mingw が使えるようになる。以下を眺めれば、mingw をインストールした場所にパスが通って各ツールが使えているのが分かるかと…。
えてしてその手の解説記事では、Windowsの環境変数 PATH に、以下の2つのディレクトリを追加していくけれど…。
- MinGWインストールフォルダ\bin
- MinGWインストールフォルダ\msys\1.0\bin
自分も昔はそうしていたけど、他の開発関連ツール群と衝突してハマったことがあるので、それ以来、以下のようなbatファイルを作って、MinGW を使いたい時だけ環境変数を変更して利用している。
_mingw.bat
@echo off @rem MinGW,MSYS enable @rem Please set your MinGW install Directory. set MINGW_PATH=C:\MinGW set MSYS_PATH=%MINGW_PATH%\\msys\1.0 set MINGWADDPATH=%MINGW_PATH%\bin;%MSYS_PATH%\bin set PATH=%MINGWADDPATH%;%PATH% set C_INCLUDE_PATH=%MINGW_PATH%\include set CPLUS_INCLUDE_PATH=%MINGW_PATH%\include set LIBRARY_PATH=%MINGW_PATH%\lib echo MinGW enable. echo add path %MINGWADDPATH%
- set MINGW_PATH=C:\MinGW の行を、自分の環境に合わせて変更すること。
やっていることは、環境変数 PATH の一番最初に MinGW と msys のパスを追加したり、C/C++ 関係のヘッダファイルやライブラリファイルの場所を環境変数に指定しているだけ。
例えば、DOS窓を開いて mingw.bat を実行すれば、その後は mingw が使えるようになる。以下を眺めれば、mingw をインストールした場所にパスが通って各ツールが使えているのが分かるかと…。
> mingw MinGW enable. add path D:\MinGW\bin;D:\MinGW\\msys\1.0\bin > which which "D:\MinGW\msys\1.0\bin\which" "D:\home\bin\which.cmd" > which gcc "D:\MinGW\bin\gcc.exe" > which make "D:\MinGW\msys\1.0\bin\make.exe" > gcc --version gcc (MinGW.org GCC Build-2) 9.2.0 Copyright (C) 2019 Free Software Foundation, Inc. ... > make --version GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. ... This program built for i686-pc-msys
◎ コンパイルができるか動作確認。 :
gcc を使ってコンパイルができるか動作確認してみる。
以下のCソースファイルをコンパイルする。「Hello world!」と出力するだけのプログラム。
_hello.c
gcc でコンパイル。
ということで、gcc でコンパイルすることができた。MinGW のインストールは成功。
以下のCソースファイルをコンパイルする。「Hello world!」と出力するだけのプログラム。
_hello.c
#include <stdio.h> int main(void) { printf("Hello world!\n"); return 0; }
gcc でコンパイル。
> gcc hello.c > a.exe Hello world! > gcc hello.c -o hello.exe > hello.exe Hello world!
- gcc hoge.c で、hoge.c がコンパイルされて a.exe という実行ファイルができる。
- gcc hoge.c -o hoge.exe で、hoge.c がコンパイルされて、-o で指定したファイル名、hoge.exe という実行ファイルができる。
ということで、gcc でコンパイルすることができた。MinGW のインストールは成功。
*1: MinGW-w64 の libscrnsave.a、libscrnsavw.a は、ファイルはあるけど中身が空っぽなのです…。
[ ツッコむ ]
#2 [prog] スクリーンセーバのビルドの実験中
Windows10 x64 21H2 + MinGW (msys2 + MinGW-w64に非ず)で、スクリーンセーバのビルドができるかどうか実験中。
以下のページで紹介されているサンプルを利用させてもらって実験しているところ。ありがたや。
_よしいずの雑記帳 スクリーンセーバーの作り方
動作させてみた様子は以下。
以下のページで紹介されているサンプルを利用させてもらって実験しているところ。ありがたや。
_よしいずの雑記帳 スクリーンセーバーの作り方
動作させてみた様子は以下。
◎ メッセージが文字化けする件。 :
ビルドはできたものの、「設定」ボタンをクリックすると文字化けしているメッセージが表示されてしまう。改善できないかと試していて結構ハマってしまった。
てっきり resource.rc 内のメッセージが表示されているものと思い込んで色々試してみたのだけど、いくら試してもメッセージが変わらない・修正できなくて悩んでしまった。
_Resource Hacker
原因は自分の勘違いだった。設定ボタンをクリックして表示されるメッセージは、screensv.c 内で記述されていた。resource.rc は関係無かった…。screensv.c 側を変更したら、あっさりダイアログ内のテキストも変わってくれた。しかしそうなると、resource.rc 内の記述はどこで使われるのだろう…。
てっきり resource.rc 内のメッセージが表示されているものと思い込んで色々試してみたのだけど、いくら試してもメッセージが変わらない・修正できなくて悩んでしまった。
- UTF-8Nで保存してた resource.rc をSJISで保存し直したり。
- テキストを英数字で記述し直したり。
- Windows10がどこかにリソースをキャッシュして、そっちが表示され続けているのではと疑って、クリーンアップ機能その他を利用してみたり。
- Resource Hacker というツールを導入して、.scr の中に入っているリソースを確認してみたり。
- ファイル名を変更してみたり。
_Resource Hacker
原因は自分の勘違いだった。設定ボタンをクリックして表示されるメッセージは、screensv.c 内で記述されていた。resource.rc は関係無かった…。screensv.c 側を変更したら、あっさりダイアログ内のテキストも変わってくれた。しかしそうなると、resource.rc 内の記述はどこで使われるのだろう…。
◎ 結局こうなった。 :
手元では、以下の3つのファイルを同じ場所に置いて make と打てば .scr (mgscrsav.scr) が得られる状態になった。
_mgscrsav.c
_resource.rc
_Makefile
ただ、出来上がった mgscrsav.scr を右クリックして「Test」や「構成」を選んでも、以下のダイアログが表示されて実行できなくなってしまった…。

どうやら、作業フォルダを、シンボリックリンクを使って、別の場所にあるように見せかけつつ作業してたのがマズかったようだなと…。オリジナルのフォルダに移動してから、.scrを右クリック→「Test」「構成」を選んだら動作してくれた。
32bit版プログラムをWindows10 64bit上で動かしている状態なので、C:\Windows\SysWOW64\ 以下に .scr をコピーして動作確認。動いてくれた。
_mgscrsav.c
_resource.rc
_Makefile
ただ、出来上がった mgscrsav.scr を右クリックして「Test」や「構成」を選んでも、以下のダイアログが表示されて実行できなくなってしまった…。

ファイルを開くことができません インターネット セキュリティ設定のために、1つ以上のファイルを開くことができませんでした。
どうやら、作業フォルダを、シンボリックリンクを使って、別の場所にあるように見せかけつつ作業してたのがマズかったようだなと…。オリジナルのフォルダに移動してから、.scrを右クリック→「Test」「構成」を選んだら動作してくれた。
- 「Test」を選ぶ : フルスクリーン表示
- 「構成」を選ぶ : 設定ダイアログが表示される状態。
32bit版プログラムをWindows10 64bit上で動かしている状態なので、C:\Windows\SysWOW64\ 以下に .scr をコピーして動作確認。動いてくれた。
- Windows 64bit版、スクリーンセーバが32bit版なら、C:\Windows\SysWOW64\ 以下に .scr をコピー。
- Windows 64bit版、スクリーンセーバが64bit版なら、C:\Windows\System32\ 以下に .scr をコピー。
- Windows 32bit版なら、C:\Windows\System32\ 以下に .scr をコピー。
[ ツッコむ ]
2022/08/24(水) [n年前の日記]
#1 [prog][windows] Borland C++ Compilerを試用
Windows10 x64 21H2上で、Borland C++ Compiler 7.3 と 5.5 (以下、bccと記述)を試用してみた。教育目的、あるいは個人利用の範囲なら無料で利用できるらしい。
◎ セットアップファイルの入手。 :
セットアップファイル名は以下。
_ホーム - Embarcadero
_C++ Compiler - Free Tool - Embarcadero
7.3 の入手は、公式サイトの上のほうにある「無料版」にマウスカーソルを合わせるとメニューが出てくるので、「C++ Compiler」をクリック。「今すぐダウンロード」をクリックすると入手ページが開かれる。入手にはメールアドレスその他の入力が必要。
- bcc 7.3 (Borland C++ Compiler 7.3) : BCC102.zip
- bcc 5.5 (Borland C++ Compiler 5.5) : freecommandlinetools2.exe
_ホーム - Embarcadero
_C++ Compiler - Free Tool - Embarcadero
7.3 の入手は、公式サイトの上のほうにある「無料版」にマウスカーソルを合わせるとメニューが出てくるので、「C++ Compiler」をクリック。「今すぐダウンロード」をクリックすると入手ページが開かれる。入手にはメールアドレスその他の入力が必要。
◎ インストール。 :
bcc 7.3 (BCC102.zip) は、解凍して任意のフォルダに置いておけば良さそう。
bcc 5.5 (freecommandlinetools2.exe) は、実行するとインストーラが起動する。
bcc 7.3 は、インストールフォルダ\bin\ に PATH を通せば利用できるけど、bcc 5.5 は、2つほどファイルを作成する必要がある模様。bcc32.cfg と ilink32.cfg を作らないといけない。
_bcc32.cfg
_ilink32.cfg
bcc 5.5 (freecommandlinetools2.exe) は、実行するとインストーラが起動する。
bcc 7.3 は、インストールフォルダ\bin\ に PATH を通せば利用できるけど、bcc 5.5 は、2つほどファイルを作成する必要がある模様。bcc32.cfg と ilink32.cfg を作らないといけない。
- インストールフォルダ\bin\bcc32.cfg
- インストールフォルダ\bin\ilink32.cfg
_bcc32.cfg
-I"D:\Dev\borland\bcc55\Include" -L"D:\Dev\borland\bcc55\Lib"
_ilink32.cfg
-L"D:\Dev\borland\bcc55\Lib"
◎ パスを通す。 :
どちらも、環境変数 PATH に、インストールフォルダ\bin\ を追加して使うことになる。
Windowsの環境変数にあらかじめ追加登録しておいてもいいけれど、自分の場合、以下のようなbatファイルを作成して使うことにした。PATHが通ってる場所に各batを置いておいて、DOS窓で、bcc か bcc55 を実行すれば、PATHの先頭に bccインストールフォルダ\bin が追加されて、bcc が使えるようになる。
_bcc.bat
_bcc55.bat
Windowsの環境変数にあらかじめ追加登録しておいてもいいけれど、自分の場合、以下のようなbatファイルを作成して使うことにした。PATHが通ってる場所に各batを置いておいて、DOS窓で、bcc か bcc55 を実行すれば、PATHの先頭に bccインストールフォルダ\bin が追加されて、bcc が使えるようになる。
_bcc.bat
@echo off @set BCCPATH=D:\Dev\BCC102\bin set PATH=%BCCPATH%;%PATH% @echo enable BCC 7.3 (Borland C/C++ Compiler 7.3) @echo Add Path [%BCCPATH%]
_bcc55.bat
@echo off @set BCCPATH=D:\Dev\borland\bcc55\Bin set PATH=%BCCPATH%;%PATH% @echo enable BCC 5.5 (Borland C/C++ Compiler 5.5) @echo Add Path [%BCCPATH%]
◎ コンパイルできるか確認。 :
以下のファイルを作成。
_testhello.c
bcc 7.3 なら、以下でコンパイル。
bcc 5.5 なら、以下でコンパイル。
testhello.exe と testhello.tds というファイルが作成された。testhello.exe を実行すると、「Hello.」と表示された。コンパイルはできた。
_testhello.c
#include <stdio.h> int main(void) { printf("Hello.\n"); return 0; }
bcc 7.3 なら、以下でコンパイル。
bcc32c testhello.c
bcc 5.5 なら、以下でコンパイル。
bcc32 testhello.c
testhello.exe と testhello.tds というファイルが作成された。testhello.exe を実行すると、「Hello.」と表示された。コンパイルはできた。
◎ 参考ページ。 :
_C/C++ 開発環境のインストール(BCC)
_USBメモリ活用講座【実践編・Borland C++開発環境ポータブル化】
_Borland C++ Compiler 5.5日本語版のインストールの方法
_プログラミングを始める前に
_Borland C++ Compiler のインストール
_Embacardero C++ コンパイラとは
_USBメモリ活用講座【実践編・Borland C++開発環境ポータブル化】
_Borland C++ Compiler 5.5日本語版のインストールの方法
_プログラミングを始める前に
_Borland C++ Compiler のインストール
_Embacardero C++ コンパイラとは
◎ 2022/08/26追記。 :
上記の作業で初期設定は済んだものと思っていたけど、まだ足りてなかった。
bcc 5.5 のリソースコンパイラ、brc32.exe を利用しようとした際、以下のエラーが出る。ちなみに、bcc32.exeではなくて brc32.exe であることに注意。どこかしらで質問しても、皆さん、bcc32 と見間違えて変な回答をしている…。
brc32 -h と打ってみると、ヘルプメッセージが出てくる。
-ipath で include のパスを指定できる、と書いてあるが、その下、-x をつけると INCLUDE という環境変数を無効化できる、とも書いてある。
つまり、環境変数 INCLUDE に、ヘッダーファイルのある場所(bcc5.5インストールフォルダ\Include)を指定すればいいのだろう…。
自分の場合、bcc55.bat というbatファイルを実行すれば bcc 5.5 が使えるようにしてあるので、batファイルを修正して、環境変数 INCLUDE も設定するようにしておいた。
_bcc55.bat
参考ページ。
_BRC32 - リソース シェル - RAD Studio
_■ - Humanity
bcc 5.5 のリソースコンパイラ、brc32.exe を利用しようとした際、以下のエラーが出る。ちなみに、bcc32.exeではなくて brc32.exe であることに注意。どこかしらで質問しても、皆さん、bcc32 と見間違えて変な回答をしている…。
brc32 main.rc scrnsvr.scr Borland Resource Compiler Version 5.40 Copyright (c) 1990, 1999 Inprise Corporation. All rights reserved. Error main.rc 1 11: Cannot open file: windows.h「windows.h を開けなかった」と言っている。brc32 は、ヘッダーファイル(*.h)の置いてある場所を知らないのだろう…。
brc32 -h と打ってみると、ヘルプメッセージが出てくる。
> brc32 -h Syntax: brc32 [options ...] filename options marked with a '*' are on by default -r compile only. Do not bind resources -fofilename set output res filename -fefilename set output exe filename -v verbose -ipath set include path -x ignore INCLUDE environment variable -dname[=string] define #define -32 * build 32-bit Windows compatible res/exe files -16 build 16-bit Windows compatible res/exe files -Vd.d Mark the .exe file with Windows version provided (4.0 is the default) -31 Provided for downward compatibility (build 16-bit res/exe files) -w32 Provided for downward compatibility (build 32-bit res/exe files) -k do not create fastload area (16 bit only) -t (ignored, for compatibility) -? or -h display this message
-ipath で include のパスを指定できる、と書いてあるが、その下、-x をつけると INCLUDE という環境変数を無効化できる、とも書いてある。
つまり、環境変数 INCLUDE に、ヘッダーファイルのある場所(bcc5.5インストールフォルダ\Include)を指定すればいいのだろう…。
set INCLUDE=(bcc5.5インストールフォルダ)\Include
自分の場合、bcc55.bat というbatファイルを実行すれば bcc 5.5 が使えるようにしてあるので、batファイルを修正して、環境変数 INCLUDE も設定するようにしておいた。
_bcc55.bat
@echo off set BCCPATH=D:\Dev\borland\bcc55 set BCCBINPATH=%BCCPATH%\Bin set PATH=%BCCBINPATH%;%PATH% set INCLUDE=%BCCPATH%\Include echo enable BCC 5.5 (Borland C/C++ Compiler 5.5) echo Add Path [%BCCBINPATH%] echo Set Env INCLUDE=%INCLUDE%
参考ページ。
_BRC32 - リソース シェル - RAD Studio
_■ - Humanity
[ ツッコむ ]
2022/08/25(木) [n年前の日記]
#1 [xyzzy] xyzzyで行結合
xyzzy 0.2.2.253 で行結合(行連結?)をしたい。ちなみに、環境は Windows10 x64 21H2。
今まで、以下で紹介されてる「セレクションを行結合(join-line)」を便利に使わせてもらっていたのだけど。
_xyzzy の音 - 編集

各行を隙間なくピッチリ結合するのがちょっとアレで。各行にスペース1文字を入れつつ結合したい…。
そんなわけで、少しだけ処理を追加して動作を変えた版を、自分用の siteinit.l に追加しておいた、とメモ。
2回目の正規表現置換時に、各行の先頭に空白1文字を入れて、3回目の正規表現で行頭の空白文字を消去してるだけ。
余談。自分の環境では、C-z j と C-z k に割り当てておいた。
今まで、以下で紹介されてる「セレクションを行結合(join-line)」を便利に使わせてもらっていたのだけど。
_xyzzy の音 - 編集

各行を隙間なくピッチリ結合するのがちょっとアレで。各行にスペース1文字を入れつつ結合したい…。
そんなわけで、少しだけ処理を追加して動作を変えた版を、自分用の siteinit.l に追加しておいた、とメモ。
(defun join-line-with-space () (interactive) (when (get-selection-type) (selection-start-end (start end) (narrow-to-region start end) (goto-char (point-min)) (replace-buffer "^[ \t]*\\(.*?\\)[ \t]*" "\\1" :regexp t) (goto-char (point-min)) (replace-buffer "^\\(.*?\\)\n" " \\1" :regexp t) (goto-char (point-min)) (replace-buffer "^[ \t]*" "" :regexp t) (widen))))
2回目の正規表現置換時に、各行の先頭に空白1文字を入れて、3回目の正規表現で行頭の空白文字を消去してるだけ。
余談。自分の環境では、C-z j と C-z k に割り当てておいた。
(global-set-key '(#\C-z #\j) 'join-line) (global-set-key '(#\C-z #\k) 'join-line-with-space)
◎ 似たような機能が既にあった。 :
上記の内容を ~/.xyzzy or siteinit.l に追加した後で、関連情報をググっていて気が付いた。emacs系エディタには、最初から delete-indentation という機能があるそうで…。M-^ に割り当てられている模様。
現在行を上の行に連結していく機能で、連結時に空白1文字も入れてくれる。キーをポンポンポンと押していくだけで上記と同じ結果を得られてしまう。
わざわざ関数を作らなくても良かったのだな…。
現在行を上の行に連結していく機能で、連結時に空白1文字も入れてくれる。キーをポンポンポンと押していくだけで上記と同じ結果を得られてしまう。
わざわざ関数を作らなくても良かったのだな…。
◎ サクラエディタの場合。 :
サクラエディタの場合、外部マクロを追加することで delete-indentation と同じ処理ができる。
_カーソル行を、上の行と行連結 - DeleteIndentation.js - Macro/投稿/198 - SakuraEditorWiki
_Macro/カテゴリ - SakuraEditorWiki
他にも気になるマクロを目にした。導入させてもらおう…。ありがたや。
_カーソル上の連続するスペース・タブを削除 - DeleteHorizontalSpace.js - Macro/投稿/197 - SakuraEditorWiki
_カーソル上の連続するスペース・タブをスペース1個にする - JustOneSpace.js - Macro/投稿/199 - SakuraEditorWiki
_カーソル行を、上の行と行連結 - DeleteIndentation.js - Macro/投稿/198 - SakuraEditorWiki
_Macro/カテゴリ - SakuraEditorWiki
他にも気になるマクロを目にした。導入させてもらおう…。ありがたや。
_カーソル上の連続するスペース・タブを削除 - DeleteHorizontalSpace.js - Macro/投稿/197 - SakuraEditorWiki
_カーソル上の連続するスペース・タブをスペース1個にする - JustOneSpace.js - Macro/投稿/199 - SakuraEditorWiki
[ ツッコむ ]
2022/08/26(金) [n年前の日記]
#1 [prog][windows] bccでスクリーンセーバのビルドを試しているところ
bcc 5.5 (Borland C++ Compiler) を使って、スクリーンセーバのサンプルソースのビルドができるか試しているところ。
以下のページで VC++、bcc、lcc でビルドできるサンプルが3つほど紹介されているので、手元の環境でもビルドできるのか確認中。
_Winのスクリーンセーバーを作ってみる
ちなみに、上記ページにも書かれているけれど、bcc には scrnsave.h、scrnsave.lib が付属していないので、そのままだとスクリーンセーバのサンプル群がビルドできない。上記ページには、MinGW版のソースを元にして作成された scrnsave.h、scrnsave.c、scrnsave.lib も紹介されているので、それを利用させてもらうことになる。ありがたや。
以下のページで VC++、bcc、lcc でビルドできるサンプルが3つほど紹介されているので、手元の環境でもビルドできるのか確認中。
_Winのスクリーンセーバーを作ってみる
ちなみに、上記ページにも書かれているけれど、bcc には scrnsave.h、scrnsave.lib が付属していないので、そのままだとスクリーンセーバのサンプル群がビルドできない。上記ページには、MinGW版のソースを元にして作成された scrnsave.h、scrnsave.c、scrnsave.lib も紹介されているので、それを利用させてもらうことになる。ありがたや。
◎ ハマった点。brc32がエラーを出す。 :
bcc 5.5 には、brc32.exe というリソースコンパイラが入っていて、ソレを使って .exe や .scr にリソースを埋め込むことになる。以下は例。
ちなみに、bcc32 じゃなくて brc32 であることに注意。brc32 がエラーを出すという質問に対して、bcc32 の設定方法を回答してる事例を一体何度見たことか…。違うんや…。bcc32 じゃなくて brc32 の話や…。
さておき。brc32.exe にヘッダーファイルの場所(〜\Include)を教えてやらないと、「windows.h が開けない」とエラーで出てしまう。
解決策としては、brc32 に -i"(bcc5.5インストールフォルダ)\Include" といった感じでヘッダーファイルの場所を伝えてやるか、環境変数 INCLUDE にヘッダーファイルの場所を設定してから brc32 を呼び出す。
ただ、bcc だけを使ってビルドすることが前提なら、*.rc の中に #include <windows.h> を含めなくても良いという話も見かけた。他のコンパイラを使う場合は、#include <windows.h> が必要になるのだとか。
brc32 main.rc scrnsvr.scr
ちなみに、bcc32 じゃなくて brc32 であることに注意。brc32 がエラーを出すという質問に対して、bcc32 の設定方法を回答してる事例を一体何度見たことか…。違うんや…。bcc32 じゃなくて brc32 の話や…。
さておき。brc32.exe にヘッダーファイルの場所(〜\Include)を教えてやらないと、「windows.h が開けない」とエラーで出てしまう。
Error main.rc 1 11: Cannot open file: windows.h
解決策としては、brc32 に -i"(bcc5.5インストールフォルダ)\Include" といった感じでヘッダーファイルの場所を伝えてやるか、環境変数 INCLUDE にヘッダーファイルの場所を設定してから brc32 を呼び出す。
set INCLUDE=(bcc5.5インストールフォルダ)\Include
ただ、bcc だけを使ってビルドすることが前提なら、*.rc の中に #include <windows.h> を含めなくても良いという話も見かけた。他のコンパイラを使う場合は、#include <windows.h> が必要になるのだとか。
[ ツッコむ ]
2022/08/27(土) [n年前の日記]
#1 [prog][windows] MinGWでWindowsアプリがビルドできそうか実験中
Windows10 x64 21H2 + MinGW (gcc 9.2.0) で、Windowsアプリがビルドできそうか実験中。
以下の解説ページを眺めながら、1つ1つサンプルをビルドできるかチェックしてるところ。
_WIN32 SDK Programming
ちなみに、gcc でコンパイルする際は以下。
以下の解説ページを眺めながら、1つ1つサンプルをビルドできるかチェックしてるところ。
_WIN32 SDK Programming
ちなみに、gcc でコンパイルする際は以下。
gcc hoge.c -o hoge.exe -mwindows-mwindows をつけることで、Windowsアプリであることを指定してるらしい。これをつけないとリンクエラーになる場面もあった。
[ ツッコむ ]
2022/08/28(日) [n年前の日記]
#1 [prog][windows] MinGWでWindowsアプリがビルドできそうか実験中その2
Windows10 x64 21H2 + MinGW (gcc 9.2.0) で、Windowsアプリがビルドできそうか実験中。
昨日と同様に、以下の解説ページを眺めながら、1つ1つサンプルをビルドできるかチェックしてるところ。
_WIN32 SDK Programming
gcc でコンパイルして *.o を作成するのは以下。
リソースをコンパイルする時は以下。
メインプログラムの *.o と、リソースファイル *.o をリンクして、実行形式を作成する。
昨日と同様に、以下の解説ページを眺めながら、1つ1つサンプルをビルドできるかチェックしてるところ。
_WIN32 SDK Programming
gcc でコンパイルして *.o を作成するのは以下。
gcc -c hoge.choge.o が生成される。
リソースをコンパイルする時は以下。
windres hoge_res.rc hoge_res.oこれで、リソースファイル hoge_res.rc がコンパイルされて、hoge_res.o が生成される。
メインプログラムの *.o と、リソースファイル *.o をリンクして、実行形式を作成する。
gcc hoge.o hoge_res.o -o hoge.exe -mwindows-mwindows をつけることで、Windowsアプリであることを指定する。
[ ツッコむ ]
2022/08/29(月) [n年前の日記]
#1 [pc] USBハブが壊れたかもしれない
メインPCに接続している、手持ちのUSB2.0ハブが壊れたっぽい。
裏面には、BUFFALO BSH4A02シリーズと書いてある。USB2.0。4ポート。バスパワー/セルフパワー両対応。個別スイッチ付き。色が白いので、型番は BUFFALO BSH4A02WH だろうと思われる。
裏面の4つのゴム足を剥がすとプラスネジが見えた。ネジを外したら開けることができた。
基板を眺めてみたけれど、これといっておかしなところはないような…。ハンダクラックとやらがあるのではと注視してみたけれど、それらしいものは見当たらない。ただ、小さな電解コンデンサがいくつかついているので、容量抜け?とやらになってる可能性はあるかもしれない。
蓋を閉めて、もう一度PCに接続してみた。USB接続のテンキーを繋いでみたら反応した。単に接触不良だったのか…。
と思ったが、ワイヤレスマウスの受信機を差したら、それは動かなかった。以前は動いていたのに。電流不足だろうか。だとすると、やはりコンデンサ関係…?
何にせよ、不調なUSBハブは怖くて使えないなと。HDDを繋いでファイルコピーや移動に失敗したら目も当てられない。
- LEDは点くのだけど、接続した各種USB機器が全く反応しない。
- USBケーブルの抜き差し等をしてみたけれど効果無し。
裏面には、BUFFALO BSH4A02シリーズと書いてある。USB2.0。4ポート。バスパワー/セルフパワー両対応。個別スイッチ付き。色が白いので、型番は BUFFALO BSH4A02WH だろうと思われる。
裏面の4つのゴム足を剥がすとプラスネジが見えた。ネジを外したら開けることができた。
基板を眺めてみたけれど、これといっておかしなところはないような…。ハンダクラックとやらがあるのではと注視してみたけれど、それらしいものは見当たらない。ただ、小さな電解コンデンサがいくつかついているので、容量抜け?とやらになってる可能性はあるかもしれない。
蓋を閉めて、もう一度PCに接続してみた。USB接続のテンキーを繋いでみたら反応した。単に接触不良だったのか…。
と思ったが、ワイヤレスマウスの受信機を差したら、それは動かなかった。以前は動いていたのに。電流不足だろうか。だとすると、やはりコンデンサ関係…?
何にせよ、不調なUSBハブは怖くて使えないなと。HDDを繋いでファイルコピーや移動に失敗したら目も当てられない。
◎ 代替製品が市場から無くなっていた。 :
代替製品をググって探してみたけれど、個別スイッチ付きのUSB2.0ハブという製品ジャンルは、もう市場から無くなっていた。いや、まだELECOMから1製品だけ出ているけれど。昔はBUFFALOやサンワサプライも販売していたのだけどな…。
ヨドバシのサイトで物色したら、ケーブルが50cm以上のUSBハブもほとんど見当たらないことに気づいた。どれも10cmや15cmの短いものばかり。ノートPCに接続することが前提の製品群だろうか。
ひょっとして、USB3.0が主流になっていて、USB2.0ハブが疎まれてるのだろうか…。
考えてみたら、個別スイッチ付きは便利だけど、スイッチを切り替えるたびに、多少はUSBハブに衝撃が加わるはずなので…。場合によってはハンダが剥がれてしまったりするかもしれないと思えてきた。スイッチ付きじゃない製品を使ったほうがいいのだろうか。どうなんだろう。
ヨドバシのサイトで物色したら、ケーブルが50cm以上のUSBハブもほとんど見当たらないことに気づいた。どれも10cmや15cmの短いものばかり。ノートPCに接続することが前提の製品群だろうか。
ひょっとして、USB3.0が主流になっていて、USB2.0ハブが疎まれてるのだろうか…。
考えてみたら、個別スイッチ付きは便利だけど、スイッチを切り替えるたびに、多少はUSBハブに衝撃が加わるはずなので…。場合によってはハンダが剥がれてしまったりするかもしれないと思えてきた。スイッチ付きじゃない製品を使ったほうがいいのだろうか。どうなんだろう。
[ ツッコむ ]
2022/08/30(火) [n年前の日記]
#1 [prog][windows] Geany 1.38 x64をインストールした
Windows10 x64 21H2上で、Geany 1.38 x64 をインストールした。今までインストールしてたのは Geany 1.36 x86。
_Home | Geany
_Geany - Wikipedia
Geany は、Linux や Windows で利用できるプログラミング向けのエディタ。GTKを利用して作られている。
Geany は、ずっと 32bit版(x86)が提供されていたけれど、1.38 は 64bit版(x64) を提供するように変わったようだなと…。
ところで、Geany Windows版は、1.30 以降、英数字を打ち込むと何故か半角カタカナになる(文字化けする)というバグがあって、そのバグに遭遇しないバージョンを探すのが少々面倒だった。ただ、少なくとも、Geany 1.38 x64 は英数字を打っても半角カタカナにならないように見えた。これなら使えそう。
ちなみに、以前動作確認をした際は、Geany 1.31、1.33、1.37.1 がバグってた。Geany 1.29以前や 1.36 では問題が起きなかった。
_mieki256's diary - Windows10の復旧作業中
_mieki256's diary - Geany 1.36 をインストール
_mieki256's diary - Lua用のエディタとしてGeanyを導入してみたり
何かしらを入力するウィジェット上で、打った文字が半角カタカナになるバグは、GTK 32bit版の特定バージョンで発生するバグだった。これは想像だけど、Geany が 64bit版になって、GTK 64bit版を使うようになったことで、そのバグに遭遇しなくて済むようになったのかもしれない。というのも、GTK を使っている Inkscape や GIMP でも32bt版を使うと同種のバグに遭遇したけれど、64bit版を使うと問題が起きなかったりしたので…。
_Home | Geany
_Geany - Wikipedia
Geany は、Linux や Windows で利用できるプログラミング向けのエディタ。GTKを利用して作られている。
Geany は、ずっと 32bit版(x86)が提供されていたけれど、1.38 は 64bit版(x64) を提供するように変わったようだなと…。
ところで、Geany Windows版は、1.30 以降、英数字を打ち込むと何故か半角カタカナになる(文字化けする)というバグがあって、そのバグに遭遇しないバージョンを探すのが少々面倒だった。ただ、少なくとも、Geany 1.38 x64 は英数字を打っても半角カタカナにならないように見えた。これなら使えそう。
ちなみに、以前動作確認をした際は、Geany 1.31、1.33、1.37.1 がバグってた。Geany 1.29以前や 1.36 では問題が起きなかった。
_mieki256's diary - Windows10の復旧作業中
_mieki256's diary - Geany 1.36 をインストール
_mieki256's diary - Lua用のエディタとしてGeanyを導入してみたり
何かしらを入力するウィジェット上で、打った文字が半角カタカナになるバグは、GTK 32bit版の特定バージョンで発生するバグだった。これは想像だけど、Geany が 64bit版になって、GTK 64bit版を使うようになったことで、そのバグに遭遇しなくて済むようになったのかもしれない。というのも、GTK を使っている Inkscape や GIMP でも32bt版を使うと同種のバグに遭遇したけれど、64bit版を使うと問題が起きなかったりしたので…。
◎ セットアップファイルその他をメモ。 :
入手して実行したセットアップファイルは以下。
同梱されている GTK は、GTK 3.24.30 と表示されていた。


- geany-1.38_setup.exe
- geany-plugins-1.38_setup.exe。
同梱されている GTK は、GTK 3.24.30 と表示されていた。


◎ 余談。エディタの公式サイトのデザインについて。 :
思考メモ。
Wikipedia日本語版の Geany のページを眺めたら、「Windows上のエディタ、NoteTabやConTEXTと似たような機能を提供している。」という一文が目に入った。なんやそのエディタ。聞いたことないな。
ググってみたら、ちょっと妙な印象を受けた。片方は有償エディタじゃないか…。そして、どちらも、公式サイトのデザインがめっちゃ怪しい…。いかにも商売感丸出しのデザイン…。
フツーこの手のエディタの公式サイトって、もうちょっと古いセンスというか、良く言えば質実剛健、悪く言えばプログラマーらしく色気ゼロなデザインでまとめてあるものでは…。「草原でお姉さんが『サイコー!』と叫んでますよー」「美人なお姉さんやイケメンお兄さんが爽やかな笑顔でオフィスで仕事してますよー」的写真をエディタの公式サイトに載せたりしないだろう…。いやはやコレは胡散臭い。(<偏見)
ちなみに、Wikipedia英語版のGeanyのページには、それらのエディタについての記述はなかった。ますます怪しい。
これ、もしかして、中国企業あたりが宣伝目的でこういう一文をこっそりあちこちに追加してるのではないか…? Wikipediaって結構汚染されてる…? などと一瞬疑ってしまったのだけど。編集履歴を確認したら、2009/07/14に初めてページが作られた時点で、それらのエディタについても最初から記述があった。つい最近追加された記述じゃなかったのね…。開発してるのもスイスの会社っぽいし。単に自分がそれらのエディタについて知らなかっただけか…。関連ページをググったら対応OSが Windows Me と書いてあるページも見かけたので、かなり昔から存在していたエディタだった模様。
それはさておき。どうして自分はあの手の写真を目にすると「怪しい」「胡散臭い」と感じてしまうのだろう…。「だったらどんな写真なら安心するのか」と問われたら…。エディタのスクリーンショットがバーンと載ってると安心するかなあ…。欲を言えば、GIFアニメか動画で、補完がバンバン効いてたり、置換処理がサクサクできる様子を見せられたら、「おっ。これは便利そう」とうっかり興味を持ってしまうかもしれない。そもそも、いかにもモデルさんっぽいお兄さんお姉さん達の写真を見せられたところで、その○○の何が優れているのかさっぱり分からない…。いやまあ、単に売り込みたい層が違うだけだろうけど…。
思考メモです。オチはないです。
Wikipedia日本語版の Geany のページを眺めたら、「Windows上のエディタ、NoteTabやConTEXTと似たような機能を提供している。」という一文が目に入った。なんやそのエディタ。聞いたことないな。
ググってみたら、ちょっと妙な印象を受けた。片方は有償エディタじゃないか…。そして、どちらも、公式サイトのデザインがめっちゃ怪しい…。いかにも商売感丸出しのデザイン…。
フツーこの手のエディタの公式サイトって、もうちょっと古いセンスというか、良く言えば質実剛健、悪く言えばプログラマーらしく色気ゼロなデザインでまとめてあるものでは…。「草原でお姉さんが『サイコー!』と叫んでますよー」「美人なお姉さんやイケメンお兄さんが爽やかな笑顔でオフィスで仕事してますよー」的写真をエディタの公式サイトに載せたりしないだろう…。いやはやコレは胡散臭い。(<偏見)
ちなみに、Wikipedia英語版のGeanyのページには、それらのエディタについての記述はなかった。ますます怪しい。
これ、もしかして、中国企業あたりが宣伝目的でこういう一文をこっそりあちこちに追加してるのではないか…? Wikipediaって結構汚染されてる…? などと一瞬疑ってしまったのだけど。編集履歴を確認したら、2009/07/14に初めてページが作られた時点で、それらのエディタについても最初から記述があった。つい最近追加された記述じゃなかったのね…。開発してるのもスイスの会社っぽいし。単に自分がそれらのエディタについて知らなかっただけか…。関連ページをググったら対応OSが Windows Me と書いてあるページも見かけたので、かなり昔から存在していたエディタだった模様。
それはさておき。どうして自分はあの手の写真を目にすると「怪しい」「胡散臭い」と感じてしまうのだろう…。「だったらどんな写真なら安心するのか」と問われたら…。エディタのスクリーンショットがバーンと載ってると安心するかなあ…。欲を言えば、GIFアニメか動画で、補完がバンバン効いてたり、置換処理がサクサクできる様子を見せられたら、「おっ。これは便利そう」とうっかり興味を持ってしまうかもしれない。そもそも、いかにもモデルさんっぽいお兄さんお姉さん達の写真を見せられたところで、その○○の何が優れているのかさっぱり分からない…。いやまあ、単に売り込みたい層が違うだけだろうけど…。
思考メモです。オチはないです。
[ ツッコむ ]
#2 [lua][prog] Luaでタートルグラフィック
Geany を触っているうちに、昔、こういった見た目のエディタ上で、Lua だか Python だかを使ってグラフィックっぽいものを描画できるか試していた記憶が蘇ってきた。アレは一体何を使って作業していたのだったか…。何だったっけ…。
色々なエディタ/IDEを起動して確認しているうちに思い出した。Geany じゃなくて、Lua用の軽量IDE、ZeroBrane Studio 1.90 だった。
_ZeroBrane Studio - Lua IDE/editor/debugger for Windows, Mac OSX, and Linux
Python じゃなくて、Lua を使ってタートルグラフィックスを試してたのだな…。
_Drawing trees with turtles - ZeroBrane
日記を検索してもそのあたりメモしてなかったようなので、今回一応メモしとく。
色々なエディタ/IDEを起動して確認しているうちに思い出した。Geany じゃなくて、Lua用の軽量IDE、ZeroBrane Studio 1.90 だった。
_ZeroBrane Studio - Lua IDE/editor/debugger for Windows, Mac OSX, and Linux
Python じゃなくて、Lua を使ってタートルグラフィックスを試してたのだな…。
_Drawing trees with turtles - ZeroBrane
日記を検索してもそのあたりメモしてなかったようなので、今回一応メモしとく。
◎ サンプルの動かし方。 :
(ZeroBrane Studioインストールフォルダ)\myprograms\welcome.lua を開くと、各種サンプルを試すことができる。
welcome.lua の中には、Lua言語のコメント行として、Markdown で各種説明や各サンプルへのリンクが書かれている。この Markdown は ZeroBrane Studio が解析して表示してくれるようで、例えば、リンクはリンクとしてちゃんと機能するようになってる。素晴らしい。
welcome.lua の中の、「Demos」の「Turtle graphics demo」をマウスクリックすると、myprograms\turtle-samples\demo.lua が開かれて、Luaを用いたタートルグラフィックを試すことができる。
実行は F6 キー。もしくはツールバー上の「>>」(Execute the current project/file)をクリック。
一応、動作してる様子をキャプチャしてみた。解像度が低いからアレだけど、雰囲気ぐらいは伝わるかと…。
myprograms\turtle-samples\ の中に、タートルグラフィックのサンプルが入っているので、それぞれ開いて実行してみるのもいいかもしれない。
welcome.lua の中には、Lua言語のコメント行として、Markdown で各種説明や各サンプルへのリンクが書かれている。この Markdown は ZeroBrane Studio が解析して表示してくれるようで、例えば、リンクはリンクとしてちゃんと機能するようになってる。素晴らしい。
welcome.lua の中の、「Demos」の「Turtle graphics demo」をマウスクリックすると、myprograms\turtle-samples\demo.lua が開かれて、Luaを用いたタートルグラフィックを試すことができる。
実行は F6 キー。もしくはツールバー上の「>>」(Execute the current project/file)をクリック。
一応、動作してる様子をキャプチャしてみた。解像度が低いからアレだけど、雰囲気ぐらいは伝わるかと…。
myprograms\turtle-samples\ の中に、タートルグラフィックのサンプルが入っているので、それぞれ開いて実行してみるのもいいかもしれない。
◎ 余談。Pythonでタートルグラフィックス。 :
ZeroBrane Studio + Lua とは別の話だけど。Python を使えばタートルグラフィックスを試すこともできる。
_turtle グラフィックスに挑戦してみよう | Python学習講座
_亀に訊け:Pythonの亀グラフィックス
_お絵かきで Python を学ぶ(タートルグラフィックス) - Qiita
_python学習の入り口に。turtle(たーとる)でCGを描こう。
「python turtle」でググれば解説ページがたくさん出てくるので参考になるかと。
_turtle グラフィックスに挑戦してみよう | Python学習講座
_亀に訊け:Pythonの亀グラフィックス
_お絵かきで Python を学ぶ(タートルグラフィックス) - Qiita
_python学習の入り口に。turtle(たーとる)でCGを描こう。
「python turtle」でググれば解説ページがたくさん出てくるので参考になるかと。
[ ツッコむ ]
2022/08/31(水) [n年前の日記]
#1 [prog][windows] MinGWでWindowsアプリがビルドできそうか実験中その3
Windows10 x64 21H2 + MinGW (gcc 9.2.0) で、Windowsアプリがビルドできそうか実験中。
以下のサイトで紹介されているサンプルを、MinGW (gcc) でビルドできるか試していた。
_図形の描画 | WINAPI入門〜bituse〜
_ペンとブラシの作成 | WINAPI入門〜bituse〜
_ビットマップ画像の表示 | WINAPI入門〜bituse〜
_WINAPI入門 〜bituse〜
Windows APIとやらを使って四角を描いたりするサンプルなら、gcc でもビルドできることは確認できていたのだけど。bitmapファイル(.bmp)をリソースファイルに含めた場合にビルドできるかどうかが不安だったわけで。ただ、試してみたらスンナリとビルドできたし、ウインドウ上で画像が描画された。
ちなみに、gcc を使ってコンパイルする際の指定は以下。
リソースファイルを含まない場合。hoge.c から hoge.exe が生成される。
リソースファイルを含む場合。hoge.c から hoge.o を、hoge_res.rc から hoge_res.o を生成して、.o 群をリンクして hoge.exe を生成する。
念のために書いておくけど、hoge とか fuga とか piyo とか foo とか bar ってのは、メタ構文変数というヤツで…。
_知識の枝 hoge foo barとは何か
_メタ構文変数(hoge / foo)とは - 意味をわかりやすく - IT用語辞典 e-Words
_メタ構文変数 - Wikipedia
以下のサイトで紹介されているサンプルを、MinGW (gcc) でビルドできるか試していた。
_図形の描画 | WINAPI入門〜bituse〜
_ペンとブラシの作成 | WINAPI入門〜bituse〜
_ビットマップ画像の表示 | WINAPI入門〜bituse〜
_WINAPI入門 〜bituse〜
Windows APIとやらを使って四角を描いたりするサンプルなら、gcc でもビルドできることは確認できていたのだけど。bitmapファイル(.bmp)をリソースファイルに含めた場合にビルドできるかどうかが不安だったわけで。ただ、試してみたらスンナリとビルドできたし、ウインドウ上で画像が描画された。
ちなみに、gcc を使ってコンパイルする際の指定は以下。
リソースファイルを含まない場合。hoge.c から hoge.exe が生成される。
gcc hoge.c -o hoge.exe -mwindows
リソースファイルを含む場合。hoge.c から hoge.o を、hoge_res.rc から hoge_res.o を生成して、.o 群をリンクして hoge.exe を生成する。
gcc -c hoge.c windres hoge_res.rc hoge_res.o gcc hoge.o hoge_res.o -o hoge.exe -mwindows
念のために書いておくけど、hoge とか fuga とか piyo とか foo とか bar ってのは、メタ構文変数というヤツで…。
_知識の枝 hoge foo barとは何か
_メタ構文変数(hoge / foo)とは - 意味をわかりやすく - IT用語辞典 e-Words
_メタ構文変数 - Wikipedia
[ ツッコむ ]
#2 [pc] USB2.0ハブ UH-2314NWを購入
ヤマダ電機須賀川店でUSB2.0ハブを購入してきた。製品は、ナカバヤシ株式会社 Digio2 UH-2314NW (UH-2314Nシリーズ)。USB2.0、4port、バスパワー。ケーブル長120cm。W22 x D66 x H19mm。パッケージ裏面には、各ポート最大消費電流100mAまで、と書いてある。税込929円。保証期間6ヶ月。
今まで使ってた USB2.0ハブ、BUFFALO BSH4A02WH が不調なので、代替品として買ってきた。ちなみに、BSH4A02WH は、今まで使えていたワイヤレスマウス受信機が使えなくなる不具合が起きていた。今回購入した UH-2314NW に、その受信機を繋いだところ、スンナリ動作してくれた。
今まで使ってた USB2.0ハブ、BUFFALO BSH4A02WH が不調なので、代替品として買ってきた。ちなみに、BSH4A02WH は、今まで使えていたワイヤレスマウス受信機が使えなくなる不具合が起きていた。今回購入した UH-2314NW に、その受信機を繋いだところ、スンナリ動作してくれた。
[ ツッコむ ]
以上、31 日分です。
Ruby 2.6.10 x86-mingw32 + cairo 1.17.x x86-mingw32 + gtk2 3.4.3 x86-mingw32 + popplerで動かしています!
いつの間にかmsysのgccが上がってそのままではRuby/Gtk2 gemでインストール時にビルドに失敗しますね。
上記の環境では、gcc 7.xだとRuby/Gtk2(3.4.3)すんなり入りました!