Codeer.Friendly で WPF の ContextMenu を取得する方法
Codeer.Friendly とは
Friendly は 株式会社 Codeer が開発した Windows の GUI を自動テストするためのライブラリです。
DLLインジェクションという方法で自動化するため、アプリケーションの内部のコードも操作でき、他の自動化ツールではできないような細やかな操作が可能となるため重宝しています。ただ、privateメソッドすら実行可能というある意味恐ろしいツールなので用法・用量を守って正しく使わないとえらいことになります。
Friendly の使い方については他の方のブログを見ていただくとして、今回は ContextMenu の取得で手間取ったので備忘録として記事を書きます。
ContextMenu の取得
ContextMenu を VisualTree() を使って取り出そうとして取り出せません。 Friendly では VisualTreeWithPopup() というのが別に用意されていてこちらを使えばContextMenuを取り出せます。 なぜ別々のメソッドになっているのかは謎ですが、これでいけます。
Popup を取得するときは VisualTree() でも VisualTreeWithPopup() でも取り出せるので好きなほうを使いましょう。
例
var process = Process.Start(@"C:\dev\source\repos\WpfApp3\WpfApp3\bin\Debug\net8.0-windows\WpfApp3.exe"); Thread.Sleep(1000); var app = new WindowsAppFriend(process); var shell = app.WaitForIdentifyFromTypeFullName("WpfApp3.MainWindow"); var contextMenus = shell.VisualTreeWithPopup().ByType("System.Windows.Controls.ContextMenu"); var contextMenu = contextMenus[0]; var menuitems = contextMenu.VisualTree().ByType("System.Windows.Controls.MenuItem"); var menuitem = new WPFMenuItem(menuitems[2]); menuitem.EmulateClick();
動的にコンポーネントを切り替える方法
親コンポーネントのオブジェクトの状態によって子コンポーネントを動的に切り替える。かつ、親コンポーネントのマウスイベントから子コンポーネントのメソッドを呼び出す。
useRecoil を使うのがミソか。
import { useEffect, useState, forwardRef, useRef, useImperativeHandle, Children, } from "react"; import { useRecoilState } from "recoil"; import { flagState, positionState } from "./state.jsx"; const Klass1 = forwardRef((props, ref) => { const [position, setPosition] = useRecoilState(positionState); useImperativeHandle(ref, () => ({ updatePosition, })); const updatePosition = (event) => { setPosition({ x: event.clientX, y: event.clientY, }); }; return <>{props.children}</>; }); const Klass2 = forwardRef((props, ref) => { const [position, setPosition] = useRecoilState(positionState); useImperativeHandle(ref, () => ({ updatePosition, })); const updatePosition = (event) => { setPosition((prev) => { return { x: prev.x + 1, y: prev.y + 1, }; }); }; return <>{props.children}</>; }); const Factory = ({ flag, children, childRef }) => { if (flag) { return <Klass1 ref={childRef}>{children}</Klass1>; } else { return <Klass2 ref={childRef}>{children}</Klass2>; } }; const MyComponents = () => { const [flag, setFlag] = useRecoilState(flagState); const [position, setPostion] = useRecoilState(positionState); const childRef = useRef(); const style = { width: 120, height: 120, backgroundColor: "green", }; return ( <Factory flag={flag} childRef={childRef}> <div id="my_components" style={style} onMouseMove={(event) => childRef.current.updatePosition(event)} > <label> x={position.x}, y={position.y} </label> </div> </Factory> ); }; export default MyComponents;
WindowsでKernel#spawnで作った子プロセスを親プロセスが死ぬときに同時に死んでもらう
WindowsだとRubyのspawnメソッドでプロセスを作成すると親プロセスからは切り離されて、親プロセスが終了しても残り続けます。
これが便利な時もありますが、私のユースケースでは不便に働くことがありました。なので、今回は親子共々死んでもらいます。
以前に、「アプリケーションが終了するときに、子プロセスも終了させる方法」ということで、Go言語で実装した記事を投稿しました。
こちらの焼き増し記事となります。ご了承を。
Ruby コード
module Kernel32 require 'fiddle' require 'fiddle/import' require 'fiddle/types' extend Fiddle::Importer dlload 'kernel32.dll' include Fiddle::Win32Types typealias 'ULONG_PTR', 'ULONG*' typealias 'LONGLONG', 'double' typealias 'SIZE_T', 'ULONG_PTR' typealias 'ULONGLONG', 'unsigned long long' typealias 'LPVOID', '*void' SecurityAttributes = ([ "DWORD nLength", "LPVOID lpSecurityDescriptor", "BOOL bInheritHandle", ]) typealias 'LPSECURITY_ATTRIBUTES', '*SecurityAttributes' LARGE_INTEGER = struct([ "LONGLONG QuadPart" ]) IoCounters = struct([ "ULONGLONG ReadOperationCount", "ULONGLONG WriteOperationCount", "ULONGLONG OtherOperationCount", "ULONGLONG ReadTransferCount", "ULONGLONG WriteTransferCount", "ULONGLONG OtherTransferCount", ]) JobObjectBasicLimitInformation = struct([ { PerProcessUserTimeLimit: LARGE_INTEGER}, { PerJobUserTimeLimit: LARGE_INTEGER}, 'DWORD LimitFlags', 'SIZE_T MinimumWorkingSetSize', 'SIZE_T MaximumWorkingSetSize', 'DWORD ActiveProcessLimit', 'ULONG_PTR Affinity', 'DWORD PriorityClass', 'DWORD SchedulingClass' ]) JobObjectExtendedLimitInformation = struct([ { BasicLimitInformation: JobObjectBasicLimitInformation}, { IoInfo: IoCounters }, 'SIZE_T ProcessMemoryLimit', 'SIZE_T JobMemoryLimit', 'SIZE_T PeakProcessMemoryUsed', 'SIZE_T PeakJobMemoryUsed', ]) JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000 extern 'HANDLE CreateJobObjectA(LPSECURITY_ATTRIBUTES, LPCSTR)' extern 'HANDLE OpenProcess(DWORD, BOOL, DWORD)' extern 'BOOL SetInformationJobObject(HANDLE, int, LPVOID, DWORD)' extern 'BOOL AssignProcessToJobObject(HANDLE, HANDLE)' extern 'BOOL CloseHandle(HANDLE)' end class JobObject PROCESS_SET_QUOTA = 0x0100 PROCESS_TERMINATE = 0x0001 JobObjectExtendedLimitInformation = 9 def initialize @job = Kernel32.CreateJobObjectA(0, 0) info = Kernel32::JobObjectExtendedLimitInformation.malloc info.BasicLimitInformation.LimitFlags = Kernel32::JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE Kernel32.SetInformationJobObject( @job, JobObjectExtendedLimitInformation, info, Fiddle::Importer.sizeof(Kernel32::JobObjectExtendedLimitInformation) ) end def register(pid) hProcess = Kernel32.OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, 0, pid) Kernel32.AssignProcessToJobObject(@job, hProcess) Kernel32.CloseHandle(hProcess) end end job_obj = JobObject.new pids = [] 3.times do |i| pid = spawn("ruby.exe -e 'loop { p #{i}; sleep 1}'") job_obj.register(pid) end sleep 5
上記コードを実行すると、rubyのプロセスが3つ起動します。これらプロセスは無限ループで動作しているので通常はCTRL-Cで殺すか、taskkill等で殺すかしないと終了しません。ですが今回は親プロセスが5秒後に終了すると同時に終了します。
苦労した点
Goの場合は、すでにWin32APIのライブラリがあり、それを使えば比較的簡単に実装できましたが、Rubyの場合はそのようなライブラリがなくAPIの定義からする必要がありました。
Rubyには fiddle という ffi を行うための標準ライブラリがあり、それを用いてAPI定義を作成していくのですが、如何せん fiddle の情報が少なく「こういった場合はどうやって定義するんだろう?」という状況が結構起こった感覚があります。
例えば、Windowsでは LONGLONGという型がありますが、定義は以下のようになっているようです。
#if !defined(_M_IX86) typedef __int64 LONGLONG; #else typedef double LONGLONG; #endif
なので、今動かしているプラットフォームに依存しているということなのですが、これを Ruby で表現する方法がわかりませんでした。おそらく私の環境では double で定義しておけば問題ないだろう。ということで今回のケースでは LONGLONG は double で定義しました。
また、入れ子になっている構造体の定義もどのように定義すればいいか、公式のドキュメントからはわかりませんでした。(ちゃんと読み込んだら書いてあるのかもですが、私は見つけられませんでした)
じゃぁ、どうやって定義したかというと、GithubのPullRequestに例が載っていたのでその通りに実装して動くかどうか試して、動いたのでこれが正解かな?という感じで定義しました。
これはドキュメントにコントリビュートするチャンスかもしれませんね!!
制限事項
上記のコード例ははじめは notepad.exe で書いていたのですが、なぜか一つだけプロセスが残るという問題がありました。これははじめのメモ帳だけプロセスが二つ起動するため、pidが一つだけJobObjectに登録されないことが問題と考えていますが、それをどう解決できるのかがわからず今回での対応は保留とさせていただきました。どなたかわかる方いらっしゃったら教えてください!!
まとめ
親プロセスが死んだときに子プロセスも死ぬようにJobObjectに登録するRubyプログラムを書いてみました。Fiddle を使ったことがなかったので使い方等含めて勉強になったかなと思います。できればもう書きたくないですが、もし書くことがあればこの記事を参考にできると思うで、未来の自分へのメモ書きということでここに記しておきます。
以上!!
【読書感想ブログ】Go言語プログラミングエッセンス - mattn / 技術評論社
Go言語といえば mattnさん、mattnさんといえばGo言語。それくらいには mattnさんが書かれるGo言語の書籍には信頼がありますが、そんな mattnさんが新たに出版された書籍が「Go言語プログラミングエッセンス」です。しかも単著。これは買うしかないですよね。そして読むしかないですよね。なので読んでみた感想です。
はじめのほうには Go言語がなぜ作られたのか、どういう言語なのかというところ、または文法的なところが書かれていました。mattnさんはブログもされていて、たまにポストされるんですが、私でもわかるような書き口で書いてくださっていて助かるな~といつも思っているんですが、その書き口がこの書籍にも反映されていて、内容がスラスラ入ってくる感じになってます。
この書籍のいいところは、具体的なコードが多く書かれていて手を動かしながら理解できる点が良いと思いました。また、簡単なWebアプリケーションやCLI題材によく使われるパッケージの紹介等もされていてよかったです。
あとは、Go言語に限らないテクニックなんじゃないかな?と思う部分もあり、たとえば「推測するな計測せよ」なんていう思想はほかの言語でも通用しそうですよね。そういったプログラミングをする上での大事な考え方も学べて良い本だなぁ~と思いました。
個人的に今後に役立ちそうだなぁと思った部分の抜き書き
go get でバージョン指定
go get github.com/mattn/go-runewidth@v0.0.12
のようにするとバージョンを指定して go get できる
最新バージョンにバグがあったり、破壊的な変更がされていたりする時にこういう対応をするとよい
vendor ディレクトリ
go.mod と同じディレクトリにあるvendorというディレクトリに置かれたモジュールのソースコードは優先して参照されます
以下のコマンドを実行すると、go.mod と go.sum に書かれたモジュールが一括で vendor ディレクトリにダウンロードできます
$ go mod vendor
おまじない
おまじないとして次のようにインターフェースIの型を持つ変数_に型fooのポインタ型nilを代入しておくことで、わざわざコンパイルをしなくても、IDEがエラーを検知して間違いに気づくことができます
recoverの使いどころ
通常、意図しないpanicが起きうるということは、そのプログラムにはバグがあることを意味するため、不用意にrecoverを呼び出すべきではありません。プログラムは落ちるべくして落ちたほうがよく、それを止めるべきではありません。
Explicit is better than implicit, Simple is betetr than complex
明示的であることは暗黙的であることよりも優れている。単純であることは複雑であることよりも優れている
Goにおける日付時刻の書式
「1月2日3時4分5秒2006年」と連番になっており、おおよそ表4.1のように理解すると覚えやすくなっています。
書式 | 連番 | 意味 |
---|---|---|
1 | 1 | 月 |
01 | 1 | 月のゼロ埋め |
2 | 2 | 日 |
02 | 2 | 日のゼロ埋め |
3 | 3 | 時 |
03 | 3 | 時のゼロ埋め |
15 | 3 | 時のゼロ埋め(24時間表記) |
4 | 4 | 分 |
04 | 4 | 分のゼロ埋め |
5 | 5 | 秒 |
05 | 5 | 秒のゼロ埋め |
2006 | 6 | 年 |
+007 | 7 | タイムゾーン |
JST | - | タイムゾーン |
timeパッケージに標準で用意されている書式
取り扱いづらそうに見えますが、多くの場合はtimeパッケージに標準で用意されている固定の書式名を使うことができます
書式名 | フォーマット |
---|---|
Layout | 01/02 03:04:05PM '06 -0700 |
ANSIC | Mon Jan \_2 15:04:05 2006 |
UnixDate | Mon Jan \_2 15:04:05 MST 2006 |
RubyDate | Mon Jan 02 15:04:05 -0700 2006 |
RFC822 | 02 Jan 06 15:04 MST |
RFC822Z | 02 Jan 06 15:04 -0700 |
RFC850 | Monday, 02-Jan-06 15:04:05 MST |
RFC1123 | Mon, 02 Jan 2006 15:04:05 MST |
RFC1123Z | Mon, 02 Jan 2006 15:04:05 -0700 |
RFC3339 | 2006-01-02T15:04:05Z07:00 |
RFC3339Nano | 2006-01-02T15:04:05.999999999Z07:00 |
Kitchen | 3:04PM |
Stamp | Jan \_2 15:04:05 |
StampMilli | Jan \_2 15:04:05.000 |
StampMicro | Jan \_2 15:04:05.000000 |
StampNano | Jan \_2 15:04:05.000000000 |
DateTime | 2006-01-02 15:04:05 |
DateOnly | 2006-01-02 |
TimeOnly | 15:04:05 |
time.ParseDuration
d, err := time.ParseDuration("3s")
3秒であれば time.Second * 3 と書くことができます。この 3s や 4m といった記法は、ユーザーから経過時間を指定してもらう際にとても便利で、例えばUNIXの sleep コマンドにも採用されています
context.Context
context.Context は習慣的に関数の第一引数として渡され、仮想の関数に引き渡されます。
どこに書くかいつも悩んでいたのでこういう慣習を知れたのはよかった
tag付ビルド
これらのソースコードは -tags cat フラグを付けてビルドすると cat.go が有効になり、dog.go は無効になります。
$ go build -tags cat
Functional Options Pattern
Server struct に公開フィールドとして Timeout や Logger を足したり、SetTimeout や SetLogger メソッドを追加したりすることはできます。しかし、利用者にタイムアウト値やロガーを任意のタイミングで変更させたくはありません。できれば New で指定した初期値から変更させないように制限したいですね。
Golang Functional Options Pattern | Golang Cafe
DDDとの親和性高そう
Method Value
少し混乱するかもしれませんが、Goのメソッド呼び出しは、第一引数にレシーバに持った関数の呼び出しと同義であるといえます。
type I int func (i I) Add(n int) I { return i + I(n) } func main() { n = 1 fmt.Println(n.Add(2)) fmt.Println(I.Add(n, 2)) }
I.Add(n, 2)
なんて呼び出しができるということを初めて知った
goroutine
goroutine は「ファイル I/O やネットワーク通信でブロックしている間にもランタイムが選んだ CPUコアで別の処理を平行して行うことで単純な並列処理よりも効率的に処理を行う」を主題にしているランタイムの機能です
これを知っているとゴルーチンをむやみやたらに使うことも減りそう
channel by range
ch := make(chan []byte) for _, b := range ch { }
channel を for で使えるの初めて知った
テストのパッケージ
テストをする際には、パッケージ名の指定に2つの方法があります。
- テスト対象と同じパッケージ名称でテストコードを書く
- テスト対象とは別のパッケージ名称でテストコードを書く
前者はパッケージ名称が同じですので、private関数もテストすることができます。一方、後者はあえて別名を付けることにより、privateな変数や関数にアクセスできないテストを書くことがるため、このパッケージの使用者と同じ実装をサンプルコードとして明示することができます
t.Skip / t.Skipf
テストによっては特定のOSでは実施できないものもあります。そういった場合には t.Skip または t.Skipf を使うことができます。
t.Short
-short を付けて実行した場合には testing.Short() が true を返します。各テストで -short のときにはスキップしたい場合には、t.SkipNow() を呼び出します。
t.Parallel
テストを並列実行したい場合に使うらしい
もう一つの注意点は、Table Driven Tests を平行で実行する際にはループの中で test変数を束縛することです。
for _, test := range tests { test := test // ← ★ t.Run(test.name, func(t *testing.T) { t.Parallel() got := Add(test.lhs, test.rhs) if got != test.want { t.Errorf("%v: want %v, but %v:", test.name, test.want, got) } } }
★部分の処理を入れないと、タイミングによってはすべてのテストが tests に含まれる最後の項目を参照してしまいます。
これは、今後注意しなくてもよくなるかも?なぜなら、loopvar という機能が今後入る可能性があるから。そのあたりも、mattnさんが Forkwell の Youtubeで話されていた。
Go言語プログラミングエッセンス - Forkwell Library#27 - YouTube
Goの標準外のパッケージ
パッケージ | 説明 |
---|---|
golang.org/x/arch | マシンアーキテクチャ |
golang.org/x/crypto | 暗号化 |
golang.org/x/exp | 実験的 |
golang.org/x/image | 画像 |
golang.org/x/mod | Go モジュール |
golang.org/x/net | ネットワーク |
golang.org/x/oauth2 | OAuth2 |
golang.org/x/sync | 非同期 |
golang.org/x/sys | システムコール |
golang.org/x/term | 端末 |
golang.org/x/text | テキスト |
golang.org/x/time | 時間 |
golang.org/x/tools | ツール |
golang.org/x/xerrors | エラー |
flag.Duration / flag.DurationVar
第四章で time.Duration を紹介しましたが、これを flag として受け取ることができます
flag.Text / flag.TextVar
encoding.TextMarshaler は MarshalTextを、encoding.TextUnmarshaler は UnmarshalText メソッドを持ったインターフェースです。
type TestMarshaler interface { MarshalText() (text []byte, err error) } type TextUnmarshaler interface { UnmarshalText(text []byte) error }
これらインターフェースを実装した型を flag パッケージで扱うことができます。
独自の型をテキストでパースできるのはよさそう
echo.FormFieldBinder / echo.CustomFunc()
echo でフォームデータを型にバインドするときに便利に使えるメソッドがあるみたい。
フォーム以外にも、クエリ文字列なんかもあるみたいで便利そう。
また、プリミティブな型以外にもバインドできる echo.CustomFunc()
っていうのがあってこれも便利そうだった。
この書籍では inputタグでPOSTされる時刻フォーマットをパースする目的で使われていた。
私もこの問題に直面したことがあって、その時は別の方法で対処したんだけど、今後はこちらを使ったほうが見やすいコードになりそうだなと思った。
テンプレート処理
echo でテンプレートを扱いには Render メソッドを持った実装を e.Renderer に設定する必要があります。
e.Renderer = &Template { templates: template.Must(template.New(""). Funcs(template.FuncMap{ "FormatDateTime": formatDateTime, }).ParseFS(templates, "templates/*")), }
こうすることで、echoからは以下のようにindexテンプレートにvalueを渡し、FormatDateTime関数を呼び出せるようになります。
return c.Render(http.StatusBadRequest, "index", value)
{{FormatDateTime .}}
echo にテンプレート渡せるのも知らんかったし、テンプレートにカスタム関数を追加できるのも知らんかった。
モジュールのメジャーバージョンアップ
ただし、go get -u は、モジュールのメジャーバージョンアップは行いません。メジャーバージョンアップをするには以下を実行します
$ go mod edit -replace=モジュール名@v=モジュール名@v
ent/ent
Ent (ent/ent) は Facebook 社が開発しているORMです。
今度何かDB関連の個人プロジェクトを作成するときに使ってみようかな。
まとめ
Go言語プログラミングエッセンスの読書感想ブログを書きました。
信頼の mattnさんの書籍で、予想通り良書でした。
Go言語のチュートリアルを一通りやって、次に読む書籍としてよいかと思いますので皆さんも読んでみてはいかがでしょうか?
webview/webiew を使って Linux上でフルスクリーンで表示する
動機
シングルアプリケーションをkioskモードで動かしたい。
そうすることで、ラズパイとかでアプリ作って・・・みたいなことがやりやすそう。
flutter-elinux でもいいんだけど、dart 覚えるの大変そう。
Go でできるなら Go でやりたい。
webview/webview とかでそれっぽいことできないかなぁ~?
昔は webview/webview でも Fullscreen() メソッドがあったけどなぜか最新ではなくなってる。
最新でも Fullscreen() したい!!
そんな感じの動機。
WebKitGtk
webview/webview は linux の場合、 Windowの表示に Gtk3 を使っているみたい。
webview/webview.go at 899018ad0e5cc22a18cd734393ccae4d55e3b2b4 · webview/webview · GitHub
さっきも書いた通り、Webviewにはウィンドウを操作するメソッドは生えてない。あるのはウィンドウへのポインタを返す Window() メソッドがあるのみ。そのポインタを使って勝手に良しなにしてくださいな。ということのようだ。
gotk3/gotk3
Go で Gtk3 を操作するためのライブラリに gotk3/gotk3 というのがある。
GitHub - gotk3/gotk3: Go bindings for GTK3
こいつには Fullscreen() のメソッドがある。
gtk package - github.com/gotk3/gotk3/gtk - Go Packages
なので、このライブラリに webview から取得したポイントを使って Window を作ればフルスクリーンにできるはず。
gotk3/gtk/window.go at master · gotk3/gotk3 · GitHub
ここら辺を見ると、自前で gtk.Window を作れそうだったので、それをベースにコードを書いてみた。
それが以下のコード
コード
package main import ( "github.com/webview/webview" "github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/glib" "unsafe" ) func fullScreen(handle unsafe.Pointer) { obj := glib.Take(handle) w := >k.Window { gtk.Bin { gtk.Container { gtk.Widget { glib.InitiallyUnowned { obj }, }, }, }, } w.Fullscreen() } func main() { w := webview.New(false) defer w.Destroy() fullScreen(w.Window()) w.SetHtml("Thanks for using webview!") w.Run() }
weston を使った フルスクリーンのデモ
weston.ini の [autolaunch] の path に ビルドしたバイナリのフルパスを指定して weston を起動すると以下のように表示されるはず。
まとめ
webview/webview を使って Linux上でフルスクリーンで表示してみました。
ここでも webview の Fullscreen 表示の議論がなされているけど、まだ Open ステータスなので、今のところは 自前で Fullscreen する方法でやっていくしかなさそう。
flutter-elinux を起動するなら [shell] に書くより [autolaunch] に書いたほうが早く起動する
weston を何も指定せずに起動すると、weston-desktop-shell が起動します。
そうじゃなくて、自前のアプリを動かしたい場合は weston.ini の shell セクション の client エントリに実行ファイルのパスを指定してあげると、それが起動します。
flutter-elinux のアプリを起動したい場合は引数を指定する必要があり、clientエントリには引数は指定できないため シェルスクリプトを別途書いて、そいつを clientエントリで呼び出すようにしてあげると起動させることができます。
[shell] client=/home/bamchoh/run.sh
こんな感じ。
でも結構起動が遅くて、5~10秒かかったりする。
でもさっき気づいたのが、 shellセクションじゃなくて autolaunch セクションに記述するほうほう。 こっちに指定したら即座に起動してびっくりした。
[autolaunch] path=/home/bamchoh/run.sh
こうすると、weston-desktop-shell が起動した直後に所定のアプリが即座に起動します。 一瞬デスクトップっぽいのが見えたりするけど、それが気にならないならこっちのほうが早く起動できる。
まとめ
weston-desktop-shell が起動するのが気にならない人は [autolaunch] セクションを使ってみよう!
weston.ini で画面サイズを指定する方法
weston.ini に設定しているのに画面サイズが変わらないぞ?
weston.ini に 以下のように設定しても画面サイズが変わらなかった。
[output] name=X1 mode=1280x768
nameの設定がキモだったようです。
weston.ini(5) — Arch manual pages
manページを見ると、nameはちゃんと決められた値にしないといけないようです。
Setting HDMI Resolution in Weston - Display
このサイトに記載しているサポートしている解像度ファイル /sys/class/drm/card0-HDMI-A-1/modes
は 今使っているディスプレイ事にあるようで私の場合だと /sys/class/drm/
のしたに card0-Virtual-1
~ card0-Virtual-8
があった。
上記のページでは HDMI-A-1
を name
に使っていることから、 Virtual-1
を設定すればよいみたいだったのでそのようにしてみる。
[output] name=Virtual-1 mode=1280x768
この設定にするとちゃんと解像度が設定された。
まとめ
weston で解像度を設定するときは [output] セクションの mode
プロパティに解像度を WIDTHxHEIGHT のフォーマットで記述し、 name
には /sys/class/drm/
以下にあるファイルから所定のフォーマットに沿った形で指定すること。