本物の golang を... 本物の Gopher を、お見せしますよ。

# <i class="fa fa-gavel" style="font-size:1em;"></i> 事の始まり

それはD言語くんへの嫉妬から始まった。

[D言語くんの繁殖についての仮説](http://qiita.com/gobori/items/821e6d8219033c4f68a8)
![](https://qiita-image-store.s3.amazonaws.com/0/100657/5ea43b29-d022-0b0c-3794-831100ab5703.gif)

[lsを間違えてdlと入力してしまったときに、D言語くんが通り過ぎるコマンド](http://qiita.com/ueshita/items/06befb8790736c7294ae)

![](https://qiita-image-store.s3.amazonaws.com/0/51482/2fb3b54f-1688-6cc3-d707-9180deb6c52b.gif)

くっ悔しい...

そしてショボいAAで2番煎じ。
[lsを間違えてgolと入力してしまったときに、Gopherが通り過ぎるコマンド書いた。](https://twitter.com/mattn_jp/status/676781384615124992)

![](http://i.imgur.com/lPF8Gtd.gif)

**こんなはずじゃない！**
**僕らの Gopher 君はこんなチープじゃない！**

> 明日来てください。本物の Gopher をお見せしますよ。
&mdash; mattn (@mattn_jp) [December 15, 2015](https://twitter.com/mattn_jp/status/676788392051380224)

(本当はもうちょっと前から作ってましたが)

# <i class="fa fa-gavel" style="font-size:1em;"></i> 本物のGopher君とは

そして今回、これを公開するに至った。

https://github.com/mattn/gopher

まずはこれをご覧頂きたい。

![Gopher君](http://i.imgur.com/9Xf0BTM.gif)

**！！！！！！！！！！！！１**

![20110824233828.jpg](https://qiita-image-store.s3.amazonaws.com/0/665/71661c0c-ebfc-fb45-e1b1-f71f28ee1648.jpeg)


**こいつ・・・　動くぞ・・・**

golang で書いたデスクトップマスコットです。起動すると画面のタスクバーの上をウロウロします。たまに方向転換したりジャンプしたりします。かわいいですね！
プログラムは .NET 等を使わない Windows のネイティブアプリ、Windows API を叩いています。**CGOを一切使用していない Full Pure Golang**です。

# <i class="fa fa-gavel" style="font-size:1em;"></i> どうやって動いているのか

gopher コマンドは data フォルダにある png ファイルをassetsバイナリとしてexeファイルに埋め込んでいます。ですのでexeファイル1つあればどこでも起動します。またランタイムも必要としません。起動直後にこのassetsのpngファイルを読み取り、RGBAから透明度を得てウィンドウのリージョンを作成しています。

```go
	files := []string{
		"data/out01.png",
		"data/out02.png",
		"data/out03.png",
		"data/waiting.png",
	}

	// make scene 1, 2, 3, and schene waiting
	for i, fname := range files {
		img[i], err = loadImage(fname)
		if err != nil {
			return nil, err
		}
		hBitmap[i], err = hBitmapFromImage(img[i])
		if err != nil {
			return nil, err
		}

		// create region for window
		hRgn[i] = winapi.CreateRectRgn(0, 0, 0, 0)
		for y := img[i].Bounds().Min.Y; y != img[i].Bounds().Max.Y; y++ {
			for x := img[i].Bounds().Min.X; x != img[i].Bounds().Max.X; x++ {
				_, _, _, a := img[i].At(x, y).RGBA()
				// combine transparent colors
				if a > 0 {
					mask := winapi.CreateRectRgn(int32(x), int32(y), int32(x+1), int32(y+1))
					winapi.CombineRgn(hRgn[i], mask, hRgn[i], winapi.RGN_OR)
					winapi.DeleteObject(winapi.HGDIOBJ(mask))
				}
			}
		}
	}
```

使っているコマは以下の3つ

![out01.png](https://qiita-image-store.s3.amazonaws.com/0/665/e44c3fbc-af63-c555-1941-e39040bb385d.png) ![out02.png](https://qiita-image-store.s3.amazonaws.com/0/665/d61ec1d2-3565-d932-c444-310edcc35cf7.png) ![out03.png](https://qiita-image-store.s3.amazonaws.com/0/665/b783461e-b642-efd8-3037-e44096062f1b.png)

GIMPを使ってチマチマと手と足を切り取っては回転させ、色を補完しながら仕上げました。またオリジナル画像の周りの輪郭がボケていて透明部分が切り出し辛くだいぶん苦労しました。おそらく今回作った成果物の中で一番時間を掛けてしまったかもしれません。

これらの画像を Windows のタイマーイベントを使って、各アニメーションモードに従った座標の移動とウィンドウリージョンの適用を行っています。
尚、golang は標準でビルドするとコンソールアプリが生成されますが今回はコンソールを持たない Windows 実行ファイルです。お手数ですが、go get ではなくリポジトリ上で mingw32-make を実行して下さい。go build に `-ldflags="-H windowsgui"` というオプションが付与され、コンソールの無いexeファイルが生成されます。
また Makefile を見てもらえると分かる通り、mingw のリソースコンパイラを使い、exeファイルにアイコンを付与しています。go build はカレントディレクトリに syso という拡張子のオブジェクトファイルを置いておくと一緒にリンクしてくれるので go ファイルのビルド前に syso ファイルを生成しておきます(この動作は各プラットフォームで同じです)。


```mf
IMAGES = $(wildcard data/*)

all : gopher.exe

gopher.exe : main.go bindata.go Makefile gopher.syso
	go build -ldflags="-H windowsgui"

bindata.go : $(IMAGES) main.go
	go-bindata data

gopher.syso : gopher.rc
	windres gopher.rc gopher.syso

clean :
	-rm *.syso *.exe
```

以下、この gopher コマンドを使用して説明しますので、パスの通ったディレクトリに gopher.exe をコピーしておいて下さい。

# <i class="fa fa-gavel" style="font-size:1em;"></i> これだけじゃないぞ！

コマンド引数に `-sl` を付けると、「ls コマンドを間違って sl にタイプミスした時用の sl モード」になります。Gopher君が画面を**倍速で**走り抜けます。

![SLモード](http://i.imgur.com/BgiIAj9.gif)

**速い！** そして **デカい！**

# <i class="fa fa-cubes" style="font-size:1em;"></i> いっぱい起動したらどうなんの？

Windows アプリなのでコンソールを取りません。いっぱい起動するとどうなるか確かめたくなりますよね。ではお見せしましょう。

![いっぱい](http://i.imgur.com/xt550tv.gif)

**うわぁーーーーーーーーっ！(小並感)**

画面が豊かになりましたね。
いっぱい起動してウザくなる人も若干ながら居るかと思いますが、どうかタスクマネージャで強制終了といった可哀想な事はしないで下さい。リポジトリに同梱している `gopherc` というクライアントソフトを使い、以下の様に実行して下さい。

```
gopherc -x
```

画面上の Gopher が一括で終了します。この gopherc コマンドは gopher プロセスと WM_COPYDATA によるプロセス間通信を行い、外部から Gopher の挙動の幾つかを変えられる様になっています。

そしてこの gopherc コマンドにはもう一つ秘密があるのです。

# <i class="fa fa-cubes" style="font-size:1em;"></i> おしゃべりGopher君

```
gopherc -m Hello
```

`-m` フラグを指定して実行するとGopher君が吹きだしを表示してくれます。

![](http://i.imgur.com/DTIBM9W.gif)

**なにそれすごい！(よく分からない)**

さらに

```
gopherc -j
```

でGopher君がロングジャンプしてくれます。

![](http://i.imgur.com/OKqbF7n.gif)

**すごいすごいよ！(さらによく分からない)**

# <i class="fa fa-gavel" style="font-size:1em;"></i> Vim でおしゃべり Gopher

リポジトリの misc/vim に Vim plugin を同梱してあります。

```
:HeyGopher おなかすいた
```
と実行するとGopher君があなたの気持ちを代弁してくれます。

![heygopher.png](http://i.imgur.com/K9h25F5.png)

**これで深夜の勤務でも寂しくない！**

# <i class="fa fa-gavel" style="font-size:1em;"></i> ニュース読み上げGopher君

実は `gopherc` コマンドは全て gopher パッケージに構築されたプロセス間通信を行う Gopher API を使って実装されています。

```goz
gopherObj.Message("Hello!", "http://www.google.com")
```

上記の様に実行すると、Gopher君が「Hello」と表示し、クリックするとブラウザでGoogleが開きます。応用例ですが、リポジトリに gopherfeed というコマンドを用意しました。gopher コマンドを実行してデスクトップをウロウロさせた後、`gopherfeed` コマンドを実行してみて下さい。

![ニュース読み上げGopher](http://i.imgur.com/UEdmHYI.png)

Gopher君がニュースを読んでくれ、クリックすると RSS フィードの URL がブラウザで開きます。

**これは便利！？(おそらく気のせい)**

# <i class="fa fa-gavel" style="font-size:1em;"></i> リアルタイムチャットで Gopher君

もう一つ応用例をお見せします。リポジトリに chat というコマンドがあります。Websocket を使ったチャットサーバです。Websocket はクライアントからの接続イベントをフックする事が出来る為、そこで Gopher君を起動させてやる事が出来ます。もちろん接続が切れたら Gopher君を消します。
上記のメッセージ Gopher API をチャットシステムと連携すれば...

![chat.png](http://i.imgur.com/PMVBSJ2.png)

Gopher君がリアルタイムに会話するチャットシステムの出来上がり！**誰かが JOIN してくるとその度に Gopher君が増え**、数が多いとブラウザでテキストを打つのも一苦労です！

**なにそれ怖い！**

# <i class="fa fa-gavel" style="font-size:1em;"></i> まとめ

本物の Gopher は如何でしたか。皆さんのデスクトップにもぜひ1匹常駐させてみては如何でしょうか。

※ gopher の画像は Renee French さんによって作成された[画像](https://golang.org/doc/gopher/gophercolor.png)を使用させて頂きました。

**追記: Linux版を[公開](https://github.com/mattn/xgopher)しました(Go言語じゃないです)。**