macOSでVMWare Fusionを起動後ウィンドウを非表示にしてヘッドレスにする
Command + Option + Shift + Esc でウインドウを非表示にできる。 ヘッドレス起動ができたらいいのに。
オペ端用壁紙を作った
オペレーション専用の端末用の壁紙を作りました。
ここから先は危険地帯、という気概を持って望みたい端末の壁紙にどうぞ。 個人利用の範囲内であればご自由に使っていただいて構いません。
高解像度なので一部を切り取ったりしてもお洒落に決まると思います。 実際、2枚目は1枚目をトリミングしてフィルタをかけたものです。
サビの割に文字が綺麗すぎたりツッコミどころはありますが、文字の部分の構造が複雑で作り直す気概が湧かないのでとりあえずこんな感じで。 Adobe Illustratorで作成しました。
素材は https://www.beiz.jp のものを使わせていただきました。 高解像度による素材の質感がとても高品質です。
Rust で hello-world の減量
Go でビルドサイズの比較をした流れで今度は Rust で hello-world を書いてバイナリサイズの比較をしてみます。
% rustc --version rustc 1.48.0 (7eac88abb 2020-11-16)
println!
一般的な hello-world の書き方です。
fn main() { println!("Hello, world!") }
println!
を使うために特に import は不要なんですね。
io
use std::io; use std::io::prelude::*; fn main() -> io::Result<()> { io::stdout().write(b"Hello, world!")?; Ok(()) }
Result
をハンドリングすべしとか、ちょっと面倒ですね。
参考: https://doc.rust-lang.org/std/io/index.html
syscall
syscall
的なパッケージを使ってみたかったのですが、難しくてまだできていません。
それから syscall
パッケージで使われている ams!
や llvm_asm!
がまだ stable な機能ではないらしく nightly ビルドでのみ利用可能になっています。
参考: https://blog.rust-lang.org/inside-rust/2020/06/08/new-inline-asm.html
バイナリサイズ比較
rustc
でビルドしてサイズを比べてみます。
最適化オプションについては Codegen options
を参照してください。
- 何も指定しない
-O
(opt-level=2
)を指定opt-level=z
(サイズ削減優先)を指定
を比較したところ以下のようになりました。() 内は upx -9
を適用した容量です。
default | opt-level=2 | opt-level=z | |
---|---|---|---|
println |
370K (132K) | 369K (132K) | 370K (132K) |
io |
376K (136K) | 373K (136K) | 373K (136K) |
Goの2MB程度のバイナリに比べるとかなり小さめですね。
シンプルなプログラムのためか、あまり最適化オプションによる差異はありませんでした。
println
と io
を比べると import
しているためか io
の方が容量が大きめです。
Go で hello-world の減量
Go言語ってビルドサイズが大きいですよね。
バイナリサイズを削減する ldflags
や upx
について、どの程度の減量が見込めるのか確認してみます。
ついでに標準ライブラリへの理解を兼ねて hello-world の書き方もいくつかのパターンで比べてみます。
% go version go version go1.15.5 darwin/amd64
fmt
一般的な hello-world の書き方です。
func main() { fmt.Print("Hello, world!") }
いつも使っている表現ですが fmt
パッケージで標準出力するのにちょっと違和感を持ってました。
os
func main() { os.Stdout.WriteString("Hello, world!") }
fmt
に比べて、こちらの方が標準出力している感がありますね。
でも fmt
の方が色んな型を渡せて便利なので fmt
を使っちゃいますね。
syscall
func main() { syscall.Write(syscall.Stdout, []byte("Hello, world!")) }
使ったことはありませんでしたが、意外とシンプルに書けました。
syscall.Stdout
は uintptr
型の変数で os.File
型の os.Stdout
とは別物です。
バイナリサイズ比較
ビルドしてサイズを比べてみます。
- ldflags を指定して
go build -ldflags="-s -w"
するパターン - ビルドしたバイナリに
upx -9
を適用したパターン - その両方を適用したパターン
を比較したところ以下のようになりました。
default | ldflags | upx | ldflags+upx | |
---|---|---|---|---|
fmt |
2.0M | 1.6M | 1.1M | 620K |
os |
1.5M | 1.1M | 840K | 448K |
syscall |
1.2M | 915K | 708K | 376K |
syscall
が一番小さくなりましたが、それでも何も工夫しなければ1MBを超えています。
御膳立て部分のコードが大きそうですね。
ldflags と upx はそれぞれバイナリサイズを半分程度まで削減できるみたいです。 両方使うと元の大きさの 1/3 程度の小ささになりました。 まだ何をやっているツールか把握していませんが、upx すごいですね。
本当は各ライブラリの表現を部分的に引っ張ってきてより低級な表現に展開する、みたいなことをしたかったのですが、internal
なパッケージをユーザ側で利用することができず諦めました。
おまけ
ディレクトリを跨いだファイルサイズの比較に tree -h が便利
tree -h
でファイルサイズを表示しつつツリー表示してくれます。
--du
オプションをつけると du
的にフォルダの合計サイズも表示してくれます。
% tree --du -h . ├── [ 303] build.sh ├── [5.3M] fmt │ ├── [2.0M] hello-world │ ├── [1.6M] hello-world.ldflags │ ├── [620K] hello-world.ldflags.upx │ ├── [1.1M] hello-world.upx │ └── [ 176] main.go ├── [3.8M] os │ ├── [1.5M] hello-world │ ├── [1.1M] hello-world.ldflags │ ├── [448K] hello-world.ldflags.upx │ ├── [840K] hello-world.upx │ └── [ 83] main.go └── [3.2M] syscall ├── [1.2M] hello-world ├── [915K] hello-world.ldflags ├── [376K] hello-world.ldflags.upx ├── [708K] hello-world.upx └── [ 104] main.go 12M used in 3 directories, 16 files
続き: Karabiner-Elements と Swift で background のアプリに特定のキーストロークを送る
ターミナルやエディタでコーディングしつつ、参考書を表示しているKindleのページ送りをしたかったのが動機です。 表題で言っている background のアプリとは最前面(foremost)ではないウィンドウで動作しているアプリを指しています。
上の記事で試したのは、指定したアプリ向けにキーストロークを送る AppleScript を Karabiner-Elements で特定のキーにより実行する、というものでした。しかし AppleScript の限界なのか background のアプリにキーストロークを送ることはできず、実際には一瞬ターゲットのアプリを foremost に持ってきてキーを送り foremost を元のアプリに戻すという処理しか書けませんでした。
Swift を使えばターゲットのアプリを activate することなく特定のアプリにキーストロークを送れるようだったので、使用感向上のため実装してみました。
Swift スクリプトの用意
アプリにキーストロークを送る send-key-to-app.swift
を用意します。
AppleScript よりこちらの方が好きです。
import Foundation import AppKit let args = CommandLine.arguments if args.count != 3 { print("\(args[0]) [APP_NAME] [KEY_CODE]") print("APP_NAME: should be an executable file name(not a path)") print("KEY_CODE: a number such as 123(LEFT), 124(RIGHT), ...") exit(1) } let appName = args[1] let keyCode = CGKeyCode(args[2]) ?? 0 let src = CGEventSource(stateID: CGEventSourceStateID.hidSystemState) func sendKeyStroke(pid: pid_t, keyCode: CGKeyCode) { let keyUp = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: true) keyUp?.postToPid(pid) let keyDown = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: false) keyDown?.postToPid(pid) } func getPidByName(executableFileName: String) -> Optional<pid_t> { let apps = NSWorkspace.shared.runningApplications for a in apps { print(a.bundleIdentifier) if a.executableURL?.lastPathComponent == executableFileName { return a.processIdentifier } } return nil } if let pid = getPidByName(executableFileName: appName) { sendKeyStroke(pid: pid, keyCode: keyCode) } else { print("executable '\(appName)' not found in user processes") }
始めて Swift を書きましたが、Optional
型と if let
のコードが気持ちいいですね。
値を wrap した Optional の扱い方はちょっと Rust っぽいです。
Karabiner-Elements に設定する前に以下のように動作確認をしておきます。
swift send-key-to-app.swift Kindle 124
これで background で動いている Kindle のページが進めば成功です。 AppleScript のものと比べるとウィンドウ切り替えのチカチカ感が減ってとても快適です。
Karabiner-Elements の設定
冒頭で紹介した以前の記事と shell_command
以外は同じです。
適当に Command + 1
でページ送り、Command + 2
でページバックするようにしています。
{ "profiles": [ { "complex_modifications": { "rules": [ { "description": "Kindleのページ移動", "manipulators": [ { "from": { "key_code": "1", "modifiers": { "mandatory": ["command"], "optional": ["any"] } }, "to": [{"shell_command": "swift /path/to/send-key-to-app.swift Kindle 123"}], "type": "basic" }, { "from": { "key_code": "2", "modifiers": { "mandatory": ["command"], "optional": ["any"] } }, "to": [{"shell_command": "swift /path/to/send-key-to-app.swift Kindle 124"}], "type": "basic" } ] },
更新したら「Restart Karabiner-Elements」をすれば早速反映されます。
この使い方でもいいのですが、現状ページ送りごとに毎回 swift インタプリターが実行されているので、気になる場合はビルドすると良さそうですね。
余裕があったので Swift をビルドする
swift package build --type executable
でコマンド用のプロジェクトが簡単に作成できたので作っておきました。
https://github.com/asa-taka/send-key-to-app
心なしかページ遷移を繰り返してもiMacのファンが鳴らなくなった気がします。
Karabiner-Elements と AppleScript で background のアプリに特定のキーストロークを送る
ターミナルやエディタでコーディングしつつ、参考書を表示しているKindleのページ送りをしたかったのが動機です。 表題で言っている background のアプリとは最前面(foremost)ではないウィンドウで動作しているアプリを指しています。
指定したアプリ向けにキーストロークを送る AppleScript を Karabiner-Elements で特定のキーにより実行することで実現します。 「background のアプリに」と言いつつ実際にやっていることは、一瞬 foremost に持ってきてキーを送って元のアプリに戻す、という処理です。
AppleScript の用意
アプリにキーストロークを送る send-key.applescript
を用意します。
AppleScript を書くのは初めてですが自然言語志向すぎて胸焼けしそうですね。
on run argv if (count of argv) < 2 then log "send-key [TARGET_APP] [KEY_CODE]" log "KEY_CODEs: 123(LEFT), 124(RIGHT), ..." error number -1721 end if set targetApp to (item 1 of argv as text) set keyCode to (item 2 of argv as number) set currentApp to (path to frontmost application as text) activate application targetApp tell application "System Events" to key code keyCode activate application currentApp end run
シンプルに書きたい人は変数を埋め込んで以下のようにしてもOKです。
set currentApp to (path to frontmost application as text) activate application "Kindle" tell application "System Events" to key code 123 activate application currentApp
事前に以下のように動作確認をしておきます。osascript
は AppleScript をシェルから起動するためのものらしいです。
osascript send-key.applescript Kindle 124
これで Kindle のページが進んで元のアプリが frontmost になっていれば成功です。
background のアプリに直接キーストロークを送る仕組みが見つからなかったので、 一度ターゲットのアプリを activate してからキーストロークを送り元のアプリを再び activate しています。 なので一瞬ウィンドウがチカチカします。ホームメイド感がありますね。
Karabiner-Elements の設定
Karabiner-Elements に前述のスクリプトを起動するキーの設定をします。
デフォルトだと ~/.config/karabiner
みたいです。
以下のような感じで profiles[].complex_modifications.rules
に設定を追加します。
全体は長いので、今回関係のないフィールドは省略しています。
設定の肝は from
のキーに対して shell_command
で任意のコマンドを実行可能な点です。
これができるだけでも Karabiner の用途はかなり広がりますね。
ここでは適当に Command + 1
でページ送り、Command + 2
でページバックするようにしています。
{ "profiles": [ { "complex_modifications": { "rules": [ { "description": "Kindleのページ移動", "manipulators": [ { "from": { "key_code": "1", "modifiers": { "mandatory": ["command"], "optional": ["any"] } }, "to": [{"shell_command": "osascript /path/to/send-key.applescript Kindle 123"}], "type": "basic" }, { "from": { "key_code": "2", "modifiers": { "mandatory": ["command"], "optional": ["any"] } }, "to": [{"shell_command": "osascript /path/to/send-key.applescript Kindle 124"}], "type": "basic" } ] },
設定ファイルの構造については Karabiner-Element のドキュメント をご覧ください。
更新したら「Restart Karabiner-Elements」をすれば早速反映されます。
サボったところ
Karabiner-Element の設定をいい感じにしたらいけるかもしれませんが、調べるのが面倒で放置している箇所です。
快適にコードを書きたいというのが当初の目的なので、時間はコーディングに割くべきと、多少の不具合には目をつぶっています(正当化)。
Go の struct と interface で Embedding
Effective Go の Embedding の内容を試してみます。
Go では embedding を利用して継承のようなことができますが、struct と interface の違いが今ひとつ理解できていなかったため、実際にコードを書いてコンパイラに怒られながら、どういう違いがあるのか試していきたいと思います。
- 確認方法
- interface を interface に埋め込む
- struct を struct に埋め込む
- struct を interface に埋め込む(不可)
- interface を struct に埋め込む
確認方法
こんな感じで embedding を利用して BaseMethod
と SuperMethod
を実装するパターンを色々見ていきます。
type baseInterface interface { BaseMethod() string } type superInterface interface { baseInterface SuperMethod() string }
実装の確認用兼、interface を満たしているかの確認用の関数を用意します。
func printSuper(name string, v superInterface) { fmt.Println(name+".BaseMethod ->", v.BaseMethod()) fmt.Println(name+".SuperMethod ->", v.SuperMethod()) }
interface を interface に埋め込む
type baseInterface interface { BaseMethod() string } // 一応 baseInterface の方の実装も作っておく type implOfBaseInterface struct{} func (implOfBaseInterface) BaseMethod() string { return "implOfBaseInterface.BaseMethod" } type superInterface interface { baseInterface SuperMethod() string } type implOfSuperInterface struct{} // interface を interface に埋め込んだ場合 BaseMethod の定義は必要 func (i implOfSuperInterface) BaseMethod() string { return "implOfSuperInterface.BaseMethod" } func (implOfSuperInterface) SuperMethod() string { return "implOfSuperInterface.SuperMethod" }
impleOfSuperInterface
側でも BaseMethod
の実装が必要になります。
実装を参照する先も無いので当たり前と言えば当たり前ですね。
func main() { printSuper("implOfSuperInterface", implOfSuperInterface{}) }
してみると
implOfSuperInterface.BaseMethod -> implOfSuperInterface.BaseMethod implOfSuperInterface.SuperMethod -> implOfSuperInterface.SuperMethod
これも impleOfSuperInterface
に実装したものがそのまま呼ばれて、当たり前の結果ですね。
このパターンの埋め込みをするメリットとしては、interface 側のメソッドの定義を省略できることくらいでしょうか。
struct を struct に埋め込む
type baseStruct struct{} func (b baseStruct) BaseMethod() string { return "baseStruct.BaseMethod" } type superStruct struct { baseStruct } // この場合 BaseMethod は省略できるが実装してもいい // func (s superStruct) BaseMethod() string { // // 明示的に baseStruct.BaseMethod を呼ぶこともできるし(あまり意味なさそう) // return s.baseStruct.BaseMethod() // // 他の処理を定義してもいい // return "superStruct.BaseMethod" // } func (s superStruct) SuperMethod() string { return "superStruct.SuperMethod" }
struct を struct に埋め込んだ場合は superStruct.BaseMethod
の実装を省略できるみたいです。
func main() { printSuper("superStruct", superStruct{}) }
してみると
superStruct.BaseMethod -> baseStruct.BaseMethod superStruct.SuperMethod -> superStruct.SuperMethod
superStruct.BaseMethod
で baseStruct.BaseMethod
が呼ばれていますね。
struct を interface に埋め込む(不可)
type structEmbeddedInterface interface { baseStruct SuperMethod() } type implOfStructEmbeddedInterface struct{} func (implOfStructEmbeddedInterface) SuperMethod() { fmt.Println("structEmbeddedInterface.SuperMethod") }
これは不可能なパターンで、コンパイル時に interface contains embedded non-interface baseStruct
と怒られます。
interface を struct に埋め込む
type interfaceEmbeddedStruct struct { baseInterface } func (interfaceEmbeddedStruct) SuperMethod() string { return "interfaceEmbeddedStruct.SuperMethod" }
この場合は interfaceEmbeddedStruct.BaseMethod
の実装をしなくてもコンパイルは通ります。しかし、ランタイムで panic: runtime error: invalid memory address or nil pointer dereference
というエラーになります。一番タチが悪いですね。
このパターンの活用事例は悩ましいですが、考察されているページがあったので時間のある時に読みたいと思います。 https://horizoon.jp/post/2019/03/16/go_embedded_interface/
今回触って改めて、このGo言語における embedding は手を動かさないと理解できないなと思いました。単純にパターンが多いので網羅しづらいのと、網羅されても今度は長大になるため読み解く時間がないので、手元で試した方が速いなと…