どらめも

なんでもないことをかきます

One2miniとかいうキーボード買ったよ

まえがき

最近apple wireless keyboardにコーヒーをこぼして破壊したのでキーボードを買いました。
候補としてファンクションキーが独立していないものを使ってみたかったのとusキーボードを使ってみたくなったのでarchissのProgresTouch Tinyを買おうと思ってアキバ散策にでかけました。
とりあえず友人に相談したところ、試打は絶対にしたほうがいいということだったので試打をしてみました。メカニカルが初めてということもあり、試打してみたところ、静音赤軸がしっくり来ました。
ところでアキバのどの店に行ってもProgresTouch Tinyの静音赤軸は売っておらず、amazonにも在庫はありませんでした。(一応メルカリに中古は売ってました)
その中でツクモさんに言って色々見てたところ、このOne2miniが目に留まりました。ナニコレカッコイイ。しかも静音赤軸でキーキャップは2色成形とスペックも申し分なしです。まあ合わなかったらツクモの交換保障で適当なの買えばいいやということで買ってみました。交換保証、高額キーボード購入に相性ぴったりですね。

f:id:doradorasuki:20200803013544j:plain
オレンジのエンターキー好き

使ってみて

ネットで軽くググったところによるとフォートナイトやる人に人気のあるキーボードらしいです。 私自身1万円↑のキーボードを使うのは初めてなのでレビューにバイアスが入ってそうですがすごい使いやすいです。打っている感じも気持ちいいですしがたつきもないです。
ところでこいつは60%キーボードの部類なので矢印キーがなかったりファンクションキーがなかったりします。その代わりにfnキー+何らかのキーでいろいろな機能が動くようになっています。
ちなみに私はデフォルトからcapslockの位置にfnキー、右下のfnとctrlを入れ替えて使っています。このキーボード矢印の配列が←↑↓→の順にfn+jiklなのでデフォルトだとカーソルが遠いのとそもそも手の移動がめんどくさいのでcapslockの位置にfnキーを持ってきたのは個人的にファインプレーだと思っています。左手小指+いろいろでカーソル移動とかf12とかできるのでかなり良いです。また、このキーボード、キーボード側にキースワップを覚えさせることができるのでos切り替えerの人も安心して使えます。欠点としてはマニュアルが英語と中国語しかないこととvimをたまに使うときにカーソルの向きが異なるのでごっちゃになりがちなくらいでしょうか。(どっちかに統一すればいい話ですが)

あとがき

カニカルキーボード初めて使いましたが、キーボードに課金する人の気持ちがなんとなくわかった気がします。似たような系列のhhkbなんかも機会があれば使ってみたいですね。
ところでキーボードを光らせる需要やっぱわかんないですね....(LED常時offで使ってます)

その他(自分用)

macのスペースの左右で英語、かな切り替えが好きなのでusキーボード用にwindowsではalt-ime-ahkubuntuではFcitxの設定をいじる+「xcape -e '#64=Muhenkan;#108=Henkan_Mode'」で運用しています。 ←なんでubuntuこれでいいのかイマイチ理解できん。。。

ProjectDIVA専用コントローラを自作する

まえがき

大学で電子情報機器学(BDM(ビックリドッキリメカ))っていう電子機器作ったら単位上げるぜ!!って授業があり、今度初音ミクprojectDIVA MEGA39sがでるので、学科民のぺちとDIVAコンを自作することにしました。それ前受注生産してたくね?って話ですが、なんと35k+taxです(は????????????switchより高いけど?????????)ということでお金がないので泣く泣く自作することになりました........
ところで、DIVAコンといえば最初に出てくるのが松尾ぽるさんの自作本ですけど、これ売ってたのC94でboothにもpdf含めてもう在庫とかなかったんですよね。そこで動画を頼りに頑張ることになりました。始めた時点での経験値はarduino使ったことないけど?とかpsocってなに??って感じでした。(試されたググり力)今回はその記事になります。
P.S.
時は1月2日、大本営発表があり、pdfが無料公開されました。対戦ありがとうございました。ここから下はただの駄文になったのでブラウザバックをおすすめします。

f:id:doradorasuki:20200108113700p:plain
きりたんはいいぞ

結局

完成しませんでした........
多分1から電子部分やり直すか凍結ですね......理由は後述します。

振り返り

とりあえず出来事を時系列に沿って振り返りたいと思います。

  • 10月下旬
    DIVAコンを作ることが決まる(DMより) ↓
    電子工作無知なのでサーベイの開始

    草案の作成

    f:id:doradorasuki:20200504113021j:plain
    DIVAコン原案

    この頃はgimxを用いてswitch/ps4に対してコントローラの信号を出そうと考えていました。arduino leonardoを使うとキーボードとかの出力を出すことができますスゴイ。

  • 11月下旬
    とりあえず触ってみなきゃということでタッチセンサ部分に使おうと思っていたpsoc4100sを購入しました。

    f:id:doradorasuki:20200504114618j:plain
    psoc4100sの実験をしてる図

    この時点でひとつのちに大きな影響を与えるミスをしました。psocへの配線としてピンヘッダ+ジャンパーケーブルを採用してしまったことです。デバッグ簡単になるしこれでいいかな〜と思いつきでやりましたが......やめた方がいいです。ゲームコントローラとしては最悪ですぐ抜けます、あと認識したりしなかったりするのでケーブル直にはんだ付けしましょう!! 他にいい方法あったら知りたいのでぜひDM等で教えてください。

  • 11月下旬~12月上旬
    学科でポケモンが流行る
    関係なさそうですがかなり重要です。 学科民のこんなツイートが飛び込んできました。


    ここから学科ではポケモン自動化ブームが起こることになります。(は?)詳細は伏せますが、switchのあるコントローラをハックしてarduinoからその信号を出せるようにしたライブラリがあり、arduino(HID機能持ち)をまるでswitchのコントローラのように動作させることが可能です。つまり、Aボタンを押したn秒後にBボタンを押すなどあらかじめ操作を組み込むことも可能です。
    これにより、DIVAコンのgimx部分が削れました。遅延問題を心配していたのでこれは大きな知見でした。遊びって大事ですね〜

  • 12月14日
    psocarduino leonardoの使い方をある程度理解したので某nithmのエミュレーターを使って遊んでみました。

    ここで感じたのが某nithmのノーツはスライド部分と画面が1対1に対応していてプログラム的にも組みやすいなあということです。(ほんとか)DIVAさんフリック、しかも2つ同時とかがあるのでどうしようと考えていたところでした。

  • 12月25日
    世間的にはクリスマスですが僕は相変わらず引きこもってコントローラ格闘していました。

    f:id:doradorasuki:20200504120013j:plain
    試作その1

    この画像のおかしいところわかるでしょうか?
    アルミニウムにははんだがつきません
    いやしらんがな(無知)ほんとにこれで実際にアルミと線をはんだ付けしようと試みたので笑い者もいいところです。

  • 12月29日
    この日はコミケに行くつもりがなかったのでお家で作業です。

    銅テープって高いですね。2kくらいしました。やっとスライダーができました、締め切りまであと2週間弱です。だからジャンパーケーブルを使うなよ...

  • 1月10日


    ジャンパーケーブル等が原因?でいくつか無反応箇所がみられましたが、(そこをうまく回避して)発表会は乗り切ったので単位もしっかり優がきました。
    ここでは触れてませんがtetris99とかでも動作確認は取れたので、とりあえずα版完成ということにしました。
    ここからテスト期間に入ったのでコントローラはあまり触っていません。

  • 春休み
    とりあえず無反応をどうにかしないことにはスライド処理は実装不可なので線を一回配線し直したり(ピンヘッダははんだ付けしてしまったのでとれない)しましたが、Aを直したらBが抜けるみたいな感じで全部治し切るのは無理でした....ピンヘッダ+ジャンパーケーブルはやめましょうね。

  • Now
    某ウィルスのせいで春休みに帰省したきり東京に戻れず、DIVAコンの話が風化しそうだったので現状の整理、知見の確認も兼ねてブログを一度書いてしまうことにしました。

知見まとめ

自分のため、そしてこれから電子工作とか始める方のために今回詰まったポイントをまとめておきます。

  • ジャンパーケーブル、ワニクリップはデバッグにとどめておく
    授業中も口を酸っぱくしてこのことは言われていましたが、いうて大丈夫だろう(どうして)と思って作業を進めていましたが、これらを使っているといざ完成を目指そうとするときに確実に後悔するので早い段階で使うのをやめた方がいいです。特に音楽ゲームのコントローラとか作る場合はなおさらです。筐体への振動負荷は自分の思っているよりも何倍もすごいです。なんでも抜けます。

  • arduino IDEは安定板を使う
    それはそうなのですが、何を血迷ったか僕はarduino nightlyを使っていてシリアルモニタが頻繁にフリーズする問題と格闘することになっていました。(安定板使ったら治りました)

  • usb-ttlケーブルはちゃんとしたものを使う
    usb-ttlケーブルの格安品を使ったら毎回windowsを起動するたびにドライバの当て直しをするはめになってストレスが結構ありました。少額なのでケチらないようにしましょう。
    ところでpsocデバッグとしてttlケーブルでで出力をteratermごしに見ていたのですが、usbからいい感じに情報とる方法がドキュメント読んだ感じある気がしたのでやり方知ってる方いたらぜひ教えてほしいです。

  • アルミニウムにははんだはつきません
    それは、そう

  • スイッチのプルアップとプルダウンはしっかり考慮する
    知識としては持っていましたが、実際やるのを忘れてなんで動かないんだろう...を最初の方結構やっていたので気を付けましょう。あと、スイッチに関してはオムロンサイトが役立ちます。

    psoc creatorについて

    ここから以下はpsoc creator(psoc関連のIDE)に関する忘備録なので興味のない人は読み飛ばしてくださって結構です。
    psoc関連はなぜかドキュメントがかなり分散していてとてもやりにくかったので忘備録として詰まったところを書いておきます。

  • 基本
    TopDesign.cyschで使いたいモジュールを配置し、buttonsample.cydwrでモジュールに対してピンを割り振り、main.cに実行プログラムを書いていきます。この3つがあればとりあえず動きます。

  • buildと書き込みは別
    書き込みの時に自動buildしてくれそうな雰囲気を醸し出していますが、このIDEにおいてbuildと書き込みは別物です(少なくともデフォルトでは)

  • guiと実際のソースコードの関連について
    このIDEでは使っているとわかると思いますが、TopDesign.cyschで使いたいモジュール(タッチセンサのCapSenseやUART用のモジュールなど)をgui的に追加し、buildすると、そのモジュール用の関数が自動生成されてmain.cからそのモジュールの操作関数を呼べるようになります。これどこかドキュメントに書いてあるんですかね、見つからなかったのですが()
    関数の命名規則として、

<モジュールの名前>_<ドキュメントに書いてある関数>  

となります。もう一度言います、書き込みではなくビルドしないと予測変換にも出てこないしその関数は使えないので気を付けましょう。初心者殺しすぎひん...?

f:id:doradorasuki:20200504132259j:plain
参考画像

  • CapsenseTunerにおいて使用不可になる問題
    突然connectをするとエラーが出るようになり(エラーメッセージ的になんらかのidが書き換わっているらしい(画像ないです))その後そのプロジェクトでは一生Tunerが使用不可になります。
    未解決問題かつ深刻なので知見大募集しています。スクショはそのうち追加します...

  • Capsenseについて
    Sensing Elementsの数をいじると検出する数を変更できます。(Capsenseをダブルクリックすると出てくる)物理センサ1つに対して10検出とかもできますが、バグるので1物理センサに対して1センサにした方がいい気がします。

あとがき

今回初めての電子工作ということで色々調べながらやることになって、結果的に完成はしなかったのですがコントローラ自作に関することをいろいろ知れたのはよかったです。
今回の敗因としては経験者の言うことを素直に聞かなかったことですね。もうジャンパーケーブルはこりごりです。再開するとしたらpsocの基盤がだいぶやられてる気がする?ので基盤の買いなおしからかなと思っています。(ところでarduino leonardoって最初からジャンパーケーブルさすようになってますがあれどうやって固定するんですかね)
あと家にbeatmaniaの専コンとか転がってるのでそれの改造とかもしたいなあと思っています。
ここまでお読みいただきありがとうございました!

有用?リンク集

Ubuntuでログインループになった話

まえがき

最近再起動してないし一回しとくかポチーしたところログインループに陥ったので自分への備忘録、同じようなミスをした人のために記事にしとこうと思います。
環境↓

DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"

結論

ログインループにハマっている人は長い記事を呼んでいる暇はないと思うので結論を書いておきます。
PATHがバグってコマンドのシンボリックリンクが全て無効になることでguiが立ち上がらなくなっていた
Ubuntu(Linux全般かもしれません)にはhomeディレクトリに.xsession-errorsというgui関連のエラーファイルが存在します。そこを見てみると

/usr/sbin/lightdm-session: 行 33: cat: コマンドが見つかりません
/usr/sbin/lightdm-session: 行 34: truncate: コマンドが見つかりません
/usr/sbin/lightdm-session: 行 106: ls: コマンドが見つかりません
/usr/lib/gnome-session/run-systemd-session: 7: /usr/lib/gnome-session/run-systemd-session: systemctl: not found

と書いてありました。コマンドが機能していませんね。
わからない方のために補足しておくと、コマンドは一部のものを除いてシンボリックリンクと呼ばれる参照のようなものを用いて実装されています。
例えばよく使うコマンドだと

ls

は本来

/bin/ls  

なのです。

cat /bin/ls  

とかするとバイナリが中に入っているのがわかると思います。
長くなりましたが、これによりPATH関係が壊れていることが予想されるので、.bashrcで自分の追加したヤバそうなPATHを削除すると正常に起動するようになりました。詳しい原因などは後述します。
guiが上手くロードされない(cuiubuntuなら大丈夫でログインもできる)人はとりあえずエラーを読むといいと思います。(参考)cuiにはctrl+alt+fn(nはpcによって異なる)で入れるので参考までに。

ぐぐってみた

とりあえず知識が何もない状態だったのでひたすらググってみました。(pcが2台あると便利ですね)その中で原因として上がったのが

  • nvidiaのドライバ問題←8割これ
  • lightdmがバグっている

が多かったですね。 今回ubuntuを動かしていたpcはノートでgpuは積んでいなかったので、その時点でnvidiaの可能性は消えました(ひええ〜)
ところでlightdmとはなんでしょうか私は知りませんでした。ログインとかのguiを作ってくれるやつらしいです。(参考)何もわからずにlightdmのセットアップコマンドを打ちましたが、治りませんでした。(それはそう)ちなみにこの過程でいろんな設定データ(IMEとか)も吹き飛ばしちゃいました、あーあ。

再起動前何をしたのか?

復旧するために可能な限り再起動前に自分のやった原因となりそうなものをを整理してみました。

  • fishをつかってみた(.bashrcにexec fishを記入)
  • PATHを追加した
  • sudo apt update(流石に原因ではなさそう)

linuxでハマった経験が少ないので手探りで色々実験していくことにしました。

原因の特定と解決

上に上げた原因を一つずつ解決していくことにしました。

  • fishを解除する。
exec fish  

を.bashrcから削除しました。すると...?
f:id:doradorasuki:20200324171927j:plain
文字化けとコマンドバグりが起きました。文字化けしているので読みにくいですが多分シンボリックリンクが死んでいます。
とりあえず文字化けを直しましょう。(参考)注意ですが、vim等のコマンドも死んでいるので/usr/bin/vimとかで指定してやりましょう。
ここで一つ疑問が生まれました。fishだとコマンド生きてたのにbashに戻したら全部死んでる...?
原因として考えられるのはfishとbashでは見ているPATH用のファイルが違うからです。ここでまちがってもfishのせいで壊れたとか考えちゃだめですよ。(戒め)

  • PATH関連ファイル

.bashrcにこんなふうに書いてありました(完)

export PATH=$HOME/.nodebrew/current/bin

おかしいところがわかりますか...?この状態で

source .bashrc

をすると、PATH(echo $PATHを実行)が

PATH=$HOME/.nodebrew/current/bin

になりますそりゃ壊れますよね。PATHを追加するのではなく、PATHそのものを書き換えてしまっています。(幸いexportはしていなかったためfishの方ではPATHが壊れることはなかったようです。)exportの意味がわからない人は調べましょう。(参考)
ちゃんと

export PATH=$HOME/.nodebrew/current/bin:$PATH

してやりましょう。

まとめ

今回のループと対処が大変だった原因をまとめると、

  • PATHを誤って壊した
  • fishでそれを隠蔽してしまいわかりにくくなった
  • ubuntu関係の知識の欠落

という感じですね。エラーメッセージはちゃんと読みましょうね〜あと、安易にネットの知らないコマンドコピペして再インストールとかしないほうがいいです(IMEの再設定めんどくさかった...)
それではよきubuntuライフを〜

2020年なので

まえがき

あけましておめでとうございます、実家に帰り日付変わる前に睡眠して+健康+になっているどらです。
TLをみてると抱負をブログとかに書き起こしている人が結構いていいなーって思ったので年始で意識の高い今のうちに私も書いていきます。

2020年の抱負

院試に受かって卒業する

一番やばそうなのがこれですね。学問に対する地力が他の人に比べてないのを3年間大学生活を送ってきて嫌という程痛感しているので真面目に頑張りたいと思います。春休み時間決めて勉強するメイツを募集しているのでよかったらDMとかください。(ところで院試1週間前にマジカルミライあるらしいですね)

本をたくさん読む

最近本を100冊読んだ的なエントリで読書体力の話があってそういえば我に返ってみると大学に入ってから明らかに本を読まなくなったことに気づきました。これは普通に偏りすぎでアウトなのでいろんな分野の本を今年は少しでも毎日時間を捻出して読むようにしたいと思います。今までこれ読んでよかったとかいう本があればお待ちしております。

関数型言語を1つやる

今年理情の講義を受けてみてOcamlとかで例を話されるんですけどわかんね〜っていうのをずっと繰り返し、目を背けてきたので春休みにでも関数型を一つ入門してみようと思いました。いままで触ったことなかったしね。直近としてはAtCoderとかといてとりあえずかけるようになろうかなあとか考えてます。

サーバーとかたてたりバックエンドとかやる(つまりそっちの言語を何かやる)

これは純粋な興味ですね。去年MikuARとかもやってて感じたのがサーバー立ててそこと通信したら機能もっと増やせるのになーとかでした。適当に記事読んでも何もわからんって感じだったのでサーバー周りとかラズパイでも使ってやってみたいです。あと単純に目標として大学にいる間にとりあえずコンピュータのいろんな分野を触っておこうというのがあるので。

AtCoder水色後半(1400〜)

周りの人黄色ばっかでコワイ...ってなっていますが、私的には競技プログラミングに生活を捧げる覚悟はないので1400を目指していきます。(水色付近単振動はやめたいしいい加減ABC-Eは解けるようになりたいので...)まああと水色後半まで地力つければ流石に緑落ちはないでしょうという魂胆(甘いかな)ですね。

C++Pythonドキュメント読んで使える機能増やす

何も考えずにプログラミングをしてると使える機能だけ使って新しい機能は増えていかないので、今年は能動的にドキュメント読んだりチュートリアルやったりして言語仕様を理解して使える機能を増やしていきたいと思ってます。できることが増えればコードとかも短くなるしわかりやすくなるはず...?競プロしててかつ言語仕様強いマンたまに見かけるけどすごいなあって思いながらいつもみてます。

インプット、アウトプットを意識的に心がける

これは私の悪い週間なのですが、ハッシュタグとかで進捗管理しようとすると秒でめんどくさくなる傾向があります。しかし、言語化できるまで理解することは大事なことなので今年はめんどくさがらずに頑張りたいと思います。はてなブログとかもあるし。あと、前の項でも書きましたが、本、ドキュメント、チュートリアル等のインプットを今年は意識的やっていきたいですね。(Unityのチュートリアルとかも)使える武器は増やしておきたいので。

大規模ソフトウェアを手探るpart2をする

去年大学の実験で大規模ソフトウェアを手探る、というものがあり私たちの班ではGuake Terminalというソフトウェアのソースコードを読み、いくつかの機能を追加しました。(詳しくは他の記事参照)その中でOSSが以前より身近なものに感じられるようになりました。その経験を生かして今年もOSSのコードを読んだりするのはやっていきたいと思います。(大きいコードになれる練習にもなりそうですし)

Cコンパイラをしあげる

今関数定義で止まっているので頑張ります....

その他願望とか

  • OculusとPCかってVRChatやりて〜今年はやるぞ〜
  • MikuARの開発継続などのミク開発
  • なんとなくtwitterみるのをやめなさーい、twitterなんとなくするならゲームした方がまだマシ(電車内とか)

    最後に

    今年も頑張っていきましょう!

音ゲー行脚ヾ(●_ゝ-*)イクゾ!!

まえがき

ライザのアトリエでレベリングしたのにセーブせずに終了して泣いているドラです。 みなさん行脚してますか〜?音ゲーやってるの東京都だけとか千葉神奈川だけとかになってませんか〜??
今回は多分大方の音ゲーで搭載されている全国行脚の機能について語りたいと思います。この記事はB4UTAdventCallendar #Advent4UTの12月21日の分です。
この記事では僕が行脚するときの基軸にしているchunithmを中心に書いていきますが、あしからず。(ちなみにchunithmは比較的設置台数多めな気がするので行脚うめとしておすすめです)

行脚なんでするの??

楽しいからですね。あと、称号とかももらえるし。真面目な話をすると僕は18切符旅行が好きなのですが、どうしても18切符旅行だと目的がないと美味しいもの食べて観光地行ってで終わりがちです。そこでゲーセンという目的地があるととても地図を見るしルート考えるしなによりたくさん歩くので(ときにはチャリを借りたり)行った街の散策とかがたくさんできます。行脚がなかったら絶対に歩かなかった都道府県、場所に行けるので個人的に爆アドです。(ゲームセンター求めて深夜ハイクしたりも...)そして、これが私の行脚状況です。東日本が埋まってる感じです(?)f:id:doradorasuki:20191204132846j:plain

基本的な行脚戦略

閉店の相次ぐゲームセンター界隈、どこに行けばゲーセンが有るのか、というのは非常に厄介な問題ですね。基本的な解としては県庁所在地駅付近に目をつけるのが正解です。これには2つの理由があり、
* 県庁所在地は人が集まりやすいため、ゲーセンが立地している可能性が高い
* 18切符旅という性質上後の工程も考慮しできるだけ大きな駅で乗り降りをしていきたい
これで大体の都道府県では上手く行きます。

さあ,目的地に向かって移動開始だ!とあなたは意気込んでいるところかもしれないが,ちょっと待ってほしい。一つだけ先に忠告しておくことがある.それは県庁所在地駅前がシャッター街と化している都道府県、車社会の存在だ。

(私の地元の話をすると福井県とかもそうですね)1こういうところだと駅前に人が来ないので必然的に駅前にゲームセンターもありません、、、、さてさてこういうときにどうするべきなのか、こういうときに私なりに取っている戦略としては
1. その都道府県の他の駅の近くにゲーセンがないか洗ってみる
2. 県庁所在地にありがちなレンタサイクルについて調べてみる(ex.石川県金沢駅)
3. 頑張って歩く(新潟県直江津駅-上越レジャーランボウル)
私的な基準ですがあまり旅程に影響を与え過ぎたくないため、徒歩は片道2km弱に収めたいなあってのがあります。そういうときにレンタサイクルは倍は進めるので、レンタサイクルが存在してかつ手続きが容易な場合は利用をおすすめします。頑張って歩くのも楽しいですが、1時間に1本の電車に間に合わせないといけない(は?)とかが発生しがちなのでメンタル的にきついですね。

少し邪道?な行脚戦略

理想的には全ての都道府県楽しみながら行脚を進めるべきですが、現実的にはそうもいかないところがあります。特に18切符だと線路の都合上行きづらい都道府県とかあります。そういう時どうするか、県境のゲームセンターを探しましょう。例を示すと福島県から山形県米沢市に少しだけ足を伸ばしたり(日本海側から攻めるのは新潟県も広いので結構大変です)、湘南新宿ラインで東北から帰っているときに小山駅水戸線に乗り換えて一瞬結城駅(茨城県)にいくとかですね(そんなこと言ってる割には茨城県埋まってなくないですか?????←それは言わない約束だよ、お兄ちゃん2)(独立国家イバラキへの到達は困難....)

その他行脚Tips

  • 開店時間の早いゲームセンターを考えてみる
    18切符の性質上始発で出るのがデフォルトみたいなところがありますが(本当に????)もちろんそんな早い時間にゲームセンターはやっていないのでその時間にその都道府県を通過した時点でそこが埋まるのは遠い未来になります。しかしながら、次いつ来るかわからない場所は埋めていきたいところですよね。そういうときのための開店時間の早いゲームセンターです。東京都は条例かなんかで10時固定?らしいですけど近場で有名なのは8時からやってるイミグランデ日吉とかですね。こういうゲーセンが実は全国にもちらほらあります。ここではその一例を紹介したいと思います。
    • ラウンドワン梅田
      高速バス御用達のラウワン梅田ですね。午前6時からやっているの最高です。
    • ラウンドワン札幌すすきの店
      JR札幌駅から歩いていけるラウンドワンです。近くにネカフェも有るのでネカフェ朝早く追い出された後とかに行きやすいですね。
    • 石川、福井のレジャーランド
      金沢駅から自転車で行けるのでgood
  • ロッカーとか使うといいかも 帰省の時とかの大荷物を持って数km往復するのはとても疲れるし時間かかるので行脚は用法を守って正しく服用しましょう....(本を大量に抱えて数km往復して死んだ顔)
    ロッカーとか使ったほうが満足度、疲労度ともに大きく改善すると思います。

あとがき

  • maimaiがdx以降にあわせて全国行脚がリセットされたらしいですね。chunithmでもやられたらと思うと怖くて夜も眠れないのでSEGAさんどうかよろしくお願いします.......
  • いい加減茨城埋めて東日本終わり!を名乗りたいので誰か茨城に行く手頃な理由をください
  • 最近waccaがようやく鳥取に導入され全国行脚可能になったらしいです笑

  1. icpc模擬国内の廻小宮のパロディです。

  2. 冴えカノfineはいいぞ

ARでミクさんを召喚!@みくみくわぁるど

まえがき

この記事は、 ツナガルダイガク Advent Calendar 2019の16日目の記事です。
ツナガルダイガクのひとははじめまして、どらめも読んだことが有る人はお久しぶりです。私はボカロ好きで集まった団体(サークルですらない)であるNo Miku No Lifeとしてみくみくわぁるどという企画で駒場祭にてARミクさんアプリをつくって展示したのでそのことについてアドカレを書こうと思います。技術的なお話に関しては他の記事で書くかもしれないし書かないかもしれないです。

f:id:doradorasuki:20191204105314p:plain
捕捉ありがとうございます!

みくみくわぁるどについて

企画の構成員が各自勝手にミクさん関連のものを作って展示した(ほかにXRの体験とか)り、一生ボカロの動画をプロジェクタで流していたり各自持ち込んだグッズや同人誌を展示したり来た人にスケッチブックに自由にイラストを書いてもらったりしました。→公式アカウント

何で作ろうと思ったか

まずそれみくちゃあんじゃん。
それは言わない約束ですよ。(知らない方のために補足してきますがみくちゃっていうミクさんの写真とか取れるスゴイアプリがあります。→公式さん)2014年くらいから存在してるのすごすぎて、、、
気を取り直して、
・技術書展でjyukoさんのARがわかる本という本を購入しARに興味を持った。←Unity初学でもとてもわかり易いのでとてもおすすめ!!
・ソロでなんとかなりそう(ぼっち開発なので)
MMDとかUnityとか使ってみたかった
・ミクさん現実に出したいじゃん!!!!!!!
・ツーショットとかとりたいじゃん!!!!!
とかが主な理由です。ちなみにUnityで開発したのは初めてです。C#も初めてでした。(Javaは多少書いたこと有る感じかなって感じです)
防備録的なものも作ってるのでこれからやってみよーって人はぜひ(露骨な宣伝)

どんなものができたか


告知ツイート的にはこんな感じです。#みくみくわぁるど とかでtwitterサーチすると結構出てくると思いますが、駒場祭のときにはミクさんが周りの明るさに対応して明るくなったり写真を取るときには表示されている平面が消えるようになったりUIが改善されたりしています。

↑当日の様子(twitterより引用)
機能としては
・dancemode
・picturemode
があります。
dancemodeは基準マーカー上(jump for joyの画像)でミクさんが曲に合わせて踊ってくれ、2台のデバイスを用いて同期を取って同時に踊っている様子を見ることもできます。
picturemodeは任意の場所(平面に限る)にミクさんを召喚してピースするミクさんと写真を撮れるモードです。どちらのモードも写真を取ってairdrop等で共有できる機能をつけた他いろんなモデルに対応しています。
楽曲やモデルやモーションをお借りしているので公開はできません。(それはそう) (マジミラとかでツーショを撮るのに個人的に使おうかなって感じなので見つけて話しかけてくれれば、、)

感想

・MMD4Mechanim(Unity用のMMDインポートライブラリ)多機能すぎてむずかしい!!! ・駒場祭ですごく喜んでくれる人がいて嬉しかった
・AR楽しいね
・よくわかんなくても、ものができてしまうUnityさんとARFoundation(ARライブラリ)さんすごすぎ

あとがき

うちの大学、学園祭でいままではアニメとか同人的なものはあってもボカロにフォーカスしたものはなかったのでそういう空間づくりに携われてよかったなあと思いました。来年とかが決まっている団体ではないのですが(B3ばっかなので)、個人的にミクさんプロダクトの開発とかUnityは楽しいのでやっていきたいという感じです。また、サークルはないですけどマジミラとかボマスとかボカロイベントは行く人なので、ツナガルダイガクの人とかボカロ好きな人は仲良くしていただけるとありがたいです。

GuakeTerminalを手探ってみる

前書き

この記事はEEIC3年のAセメスター実験「大規模ソフトウェアを手探る」にてレポート課題としてdoradorasukiKotaro7750shugo256によって書かれているものです。この実験はオープンソースソフトウェアのソースコードを手探った上で何らかの機能を追加しようという実験です。今回私達のグループでは筆者の一人が日頃から愛用しているLinuxのターミナルエミュレータであるGuakeについて手探ってみました。3人の共同執筆記事ということを念頭に置いて読んでください。

環境

Ubuntu 18.04.3 LTS

課題設定

まずはじめにGitHub上のissueを各自で読み、欲しい機能を考えました。 その結果、このissueでリクエストされている機能を追加してみようということになりました。 この機能を簡潔に説明すると、タブをピンどめしてGuake自体を終了してもタブの状態を保存できるというものです。 少し調べてみたところ、標準でカレントディレクトリの情報くらいは保存して復元できるようになっており、その機能を拡張するようにすれば良さそうだと思ったのでこの課題を実装してみることにしました。

使用した内部的な機能

GuakeにはTerminalとやりとりするための様々な関数が実装されていてそれに乗っかることで様々な挙動をさせることが可能です。ここでは今回使った関数などを中心に紹介したいと思います。

ターミナルでコマンドを実行する

# guake/terminal.py

class GuakeTerminal(Vte.Terminal):
    ...
    def execute_command(self, command):
        if command[-1] != '\n':
            command += "\n"
        self.feed_child(command)

terminal.pyGuakeTerminalクラスのインスタンスメソッドです。Guake内ではタブごとにこのクラスが作られているのですが、execute_commandメソッドを実行することでターミナルで実行可能なコマンドを実行させることが可能です。

ちなみにfeed_childは親クラスであるVte.Terminalのメソッドであり、これは引数に与えたコマンドをターミナル上であたかもユーザーが実行したかのように(表示にも反映)して実行するという動きをします。Vte.TerminalGtkのターミナルエミュレート用のクラスとして用意されているもののようです。

VteTerminalのシグナルをキャッチして関数を実行する

# guake/terminal.py

class GuakeTerminal(Vte.Terminal):
    def __init__(self, guake):
        ...
        self.connect(<シグナル名>, <動作させたい関数>)
        ...

Vte.Terminalインスタンスメソッドconnectを用いると、自前のメソッドとVteTerminalのシグナルを結びつけることができます。今回は最終的には使わずに実装できましたが、キーボード入力やディレクトリ移動などを取得したいときなどに便利だ思います。ちなみに使っていた頃は上のようにGuakeTerminalクラスのコンストラクタで呼び出していました。

追加機能その1:開いているタブのコマンド実行履歴をバックアップする

現状と目標

ターミナルに与えたコマンドを保存する機能自体は多くのシェルの機能として実装されています。例えばBashであればコマンドは実行するたびにキャッシュされ、Bashが終了するタイミングで.bash_historyというファイルに書き込まれます。 しかしこの機能では、下図のように別々のターミナルで実行したコマンドも区別されずに同一のファイルに保存されてしまうので、複数のターミナルのhistoryがごちゃ混ぜになってしまうことがよくあります。
ターミナルごとにhistoryが混ざらずに残ってくれると嬉しいなあということで、今回はGuakeの機能としてタブごとのコマンドのバックアップ機能を作ることとしました。具体的にはGuakeの各タブに対してユニークなhistoryファイルを割り当て(下図)、そこにそのタブでコマンドが叩かれるたびにその内容を書き込んでいくというもので、Guakeが終了したとしても次回起動時に復帰できるということを目標としました。

実装

上記の設計から気付くとは思いますが、今回の実装は基本的にBash依存となっています。 というのも、historyを保存するhistoryコマンドがBashのビルトイン関数であり、他のシェルを使っている場合にはそもそも保存されないからです。 一応シェル依存にならないように他の機構を考えはした(後述)のですが、どれもあまり美しくなかったのでこの機構を採用しました。

実装は大きく分けて以下の2つに大別されるのでそれぞれ説明していきます。

コマンド履歴保存

Guakeにはもともと備わっている機能として、タブが何個開いていて、どのディレクトリにいるのかという情報をファイルに保存するというものがあり、Guakeを開いた際にファイルからタブ情報を読み込むことで復元しています。このファイルを拡張してコマンド履歴の保存ファイルの場所も保存する設計にしました。

タブとテキストファイルを紐付ける

上述のタブ情報はXDG_CONFIG_HOME/session.json(環境変数で指定されています)に保存されています。ここに以下のように新たにhistoryというkeyを追加し、そのvalueをコマンド履歴保存用のテキストファイルへのパスとすることで各タブとそのテキストファイルを紐付けるようにしました。

{
    "schema_version": 2,
    "timestamp": 1572598653,
    "workspace": {
        "0": [
            [
                {
                    "panes": [
                        {
                            "type": "term",
                            "directory": "/home/denjo/.config/guake",
                            "history": "/home/denjo/.config/guake/tabs/HoNYAJD7Y5.txt"
                        }
                    ],
                    "label": "denjo@DJ00059: ~/.config/guake",
                    "custom_label_set": false
                }
            ]
        ]
    }
}

具体的には、下のようにGuakeTerminalクラスのインスタンス変数としてhistory_pathを用意し、ここにhistoryファイルへのパスを持っておくようにしました。ちなみにcreate_random_file_name()はXDG_CONFIG_HOME/guake/tabs/以下のランダムなテキストファイル名を生成する関数です。

# guake/terminal.py

class GuakeTerminal(Vte.Terminal):
    def __init__(self, guake):
        ...
        self.history_path = self.create_random_file_name()
        ...

ここで定義したファイル名history_pathは、タブごとの情報をsession.jsonに書き込める形式に変換して返す関数であるsave_box_layout()を拡張することで実際にファイルに書き込むようにしています。

# guake/boxes.py
    def save_box_layout(self, box, panes: list):
       ...
            directory = box.terminal.get_current_directory()
            panes.append({'type': btype, 'directory': directory,'history': str(box.terminal.history_path)})

boxはメンバにそれぞれのターミナルの情報を持っており、そこから上述のhistory_pathを取得して、'history'のvalueとして追記することでsession.jsonに書き込みました。

タブのコマンド履歴をテキストファイルに書き込む

どうやらnotebook.pyTerminalNotebookクラスのインスタンスメソッドnew_page_with_focusが、タブ生成時に呼ばれる関数であるということがpdbなどを用いることで判明したので、実装としてはこの関数のreturnの手前に下のような2行を書き加えるだけです。

# guake/notebook.py

class TerminalNotebook(Gtk.Notebook):
    ...
    def new_page_with_focus(...):
        ...
        command = 'PROMPT_COMMAND=$PROMPT_COMMAND"\n history -w {}"'.format(terminal.history_path)
        terminal.execute_command_and_reset_output(command)

        return box, page_num, terminal

まず、terminal.execute_command_and_reset_output(command)についてterminalGuakeTerminalクラスのインスタンスであり、execute_command_and_reset_outputメソッドについては後述しますが、前述のexecute_commandメソッドを用いてコマンドを実行した上でエスケープシーケンスを用いて出力を削除する関数です。これがないとユーザーの画面に堂々とhistory -wコマンドが表示されてしまい、流石にカッコ悪いです。

この関数に渡すcommandは、PROMPT_COMMANDという環境変数に対してhistoryを書き込むコマンドhistory -w [filename]を書き加えるという処理です。PROMPT_COMMANDBash特有の機能であり、この変数に書かれたコマンドが、プロンプトが表示される直前(=コマンドの実行終了直後)に実行されるというものです。

historyコマンドおよび環境変数PROMPT_COMMANDはともにBash依存の機能ではありますが、これらと似たような機能はzshなど他のシェルにおいても備わっている(と信じている)ので、少しの変更で対応できるのではないかと踏んでいます。

エスケープシーケンスについて

上述のexecute_command_and_reset_outputメソッドはGuakeTerminalクラスのメソッドとして以下のように実装しました。

# guake/terminal.py

class GuakeTerminal(Vte.Terminal):
    ...
    def execute_command(self, command):
        ...
    
    def execute_command_and_reset_output(self, command):
        command = ' ' + command
        command += '\n echo -en "\e[0;0H\e[0J"'
        self.execute_command(command)

ここでは実行したいコマンドの後にechoコマンドを加えるということをしています。-eエスケープシーケンスを有効にし、-nは出力後に改行を入れないというオプションです。\e[0;0Hでターミナルの先頭に移動し、\e[0Jで画面の出力のクリアを行うので、この二つの呪文によりターミナル画面のリセットが実現できます。

なお、上述のPROMPT_COMMANDのセットはターミナル起動時にしか行わないので、ターミナル使用中に画面がリセットされてしまうということはありません。またシェルの画面リセットのコマンドとしてresetclearがありますが、これらはともに不採用となりました。resetは画面以外の色々なこともリセットしている(要出典)ようで実行に数秒かかってしまい、clearはぱっと見画面がリセットされたように見えるだけで上にスクロールすると元の画面が消えずに残ってしまっていたからです。

ちなみにcommandの先頭にスペースを加えているのは、このコマンドをターミナルのhistoryに反映させないための裏技です。

コマンド履歴復帰

Guakeにもともと備わっているタブ情報復元処理が私達がやりたい処理のタイミングと内容が似ているため、タブ情報の復帰のタイミングでhistory -rコマンドを実行する設計にしました。

タブ情報の復帰はguake_app.pyGuakeクラスのインスタンスメソッドrestore_tabssession.jsonから読みだすという形で行われているようです。大きなメソッドなので実装は割愛しますが、この関数の中でsession.jsonhistoryに書かれたfilepathを読み、history -r <filepath>を各タブで実行することでコマンド履歴の復元を実現している。

ボツ案

上で書いた機構以外にもいくつか考えた機構があるので紹介していこうと思います。

入力を検知してコマンドを抽出し保存する

"使用した内部的な機能"の節で説明したように、Vte.Terminalクラスを用いればターミナルの出す様々なシグナルを利用できます。その一つにテキストが入力されたときに発火されるというもの(“commit”)があります。

このシグナルを受け取り、入力された文字を取得することでコマンドを取得できると考えました。 しかし、このイベントの発火条件がテキストの状態が変わったときとなっているため、Delete周りの処理が煩雑になると考え、この機構はボツとなりました。

Enterキーを押されたらhistoryコマンドを実行する

前述のVteTerminalのシグナルの中に画面に描画されている文字が変更されたときに発火されるもの(“text-inserted”)があります。

このシグナルを使うと、VteTerminalに対して送ろうとしている文字列を取得することが可能であるため、Enterキーが押されたときにhistoryコマンドを実行した上でそのhistoryコマンドの描画をエスケープシーケンスを用いて消そうと考えました。しかしながら、エスケープシーケンスがうまく動作させることができずこの機構はボツとなりました。

画面に表示されている文字列からコマンドを抽出する

Vte.Terminalクラスではget_textというメソッドが提供されていて、これを用いるとプロンプトを含めたターミナルに表示されているテキスト全体をstringとして取得することができます。

この関数を上述の手法と同様にして、Enterキーが押されるたびに呼び出して最後の行だけを取り出せば、[プロンプト]+[直近の実行コマンド]という文字列が得られます。したがってプロンプトの文字列を保持しておけば、実行コマンドのみを取り出せるので、これをhistoryファイルに一行ずつ書き込めばコマンド実行ごとにhistoryが更新されることになります。

シグナルを用いればcdでプロンプトの長さが変わるタイミングも検知できたので、このアプローチは一応成功しました。しかし、プロンプトの長さを保持するやり方は無理矢理感満載で美しくないこと、そして前述のPROMPT_COMMANDといううってつけの機能の存在に気づいたことによりボツとなりました。

シェルの標準入力からコマンドを取ってくる

標準入力からコマンドを取得すること自体は可能でしたが、コマンドを投げつけること(改行コードの送信)がなぜかできませんでした。

追加機能その2:複数のタブで同時にコマンドを実行できるようにする

現状と目標

少し講義の時間が余ったので教授に複数タブで同じコマンドを同時実行できたらsshとかするときに便利だよねっていうことで同時実行するコマンドを作ることにしました。 現状同じことをやろうとするとコピペする、スクリプトを組んで実行するなどの方法が考えられますが、もっと手軽に実行できるようにするために専用のUIを作ってそこから実行させるように設計しました。

実装

挙動としては、 1. Ctrl+Shift+sでコマンド実行のためのウィンドウを開き、 2. 実行するコマンドを入力し、 3. 実行するタブを選択し、 4. Enter又はクリックで実行される

としました。

ウィンドウクラスの作成

ウィンドウはGtkを用いて下のように作成しました。

チェックボックスを外したものに関してはコマンドが実行されません。

実装方法としてはGtkのサンプルコードをもとにclassの生成を行いました。

class MultiExecWindow(Gtk.Window):

    def __init__(self,guake):
        super(MultiExecWindow, self).__init__()

        self.guake=guake
        self.init_ui()
        self.text=""

    def init_ui(self):    

        grid = Gtk.Grid()
        execBtn = Gtk.Button(label="execute")
        execBtn.connect("clicked", self.on_button_clicked)
        grid.attach(execBtn, 3, 0, 1, 1)
        grid.set_column_spacing(5)
        self.add(grid)        

        entry = Gtk.Entry()
        entry.connect("key-release-event", self.on_key_release)

        grid.attach(entry, 0, 0, 3, 1)

        self.label = Gtk.Label("")
        self.label.set_width_chars(10)

        grid.attach(self.label, 1, 0.5, 1, 1)

        self.terminal_checkbutton = []
        self.terminal_name = {}
        for t in self.guake.notebook_manager.iter_terminals():
            cur_directory = t.get_current_directory()
            if cur_directory in self.terminal_name:
                self.terminal_name[cur_directory] = self.terminal_name[cur_directory] + 1
                checkBtn = Gtk.CheckButton(label= (cur_directory + ":[" + str(self.terminal_name[t.get_current_directory()])) + "]" )
            else:
                self.terminal_name[cur_directory] = 0
                checkBtn = Gtk.CheckButton(label=cur_directory)
            grid.attach_next_to(checkBtn,None,3,1,1)
            checkBtn.set_active(True)
            self.terminal_checkbutton.append({"button":checkBtn,"uuid":t.get_uuid()})

        self.set_border_width(5)

        self.set_title("Exexute in all terminals")
        self.set_default_size(300, 50)
        self.connect("destroy", self.on_finished)

    def on_key_release(self, widget,ev,data=None):
        self.text=widget.get_text()
        #エンターキーを回収
        if ev.keyval==Gdk.KEY_Return:
            self.on_button_clicked(widget)

    def on_button_clicked(self, widget):
        selected_terminal_uuid = self.select_terminal()
        self.guake.exec_multi(self.text,selected_terminal_uuid)
        self.on_finished(None)

    def on_finished(self,widget):
        self.hide()
        self.guake.show()
    
    def select_terminal(self):
        selected_terminal_uuid = []
        for checkBtn in self.terminal_checkbutton:
            if checkBtn["button"].get_active():
                selected_terminal_uuid.append(checkBtn["uuid"])

        return selected_terminal_uuid

このクラスを用いてウィンドウを作るのですが、その処理をguake_app.py内のGuake class内で呼び出しました。

def exec_multi_window(self):
        win = MultiExecWindow(self)
        self.hide()
        win.show_all()

Guake classには設定が呼び出されたりする時用にGuakeを閉じたり開いたりするメソッドが実装されていたため、そのメソッドを用いて設定と同じようなウィンドウ遷移をするようにしました。また、コマンドの複数タブにおける実行に関してはiter_terminalsというタブの情報を取得するものが存在していたため、これを流用し、execute_commandを用いて実行しました。

def exec_multi(self,command,selected_terminal_uuid):
        for t in self.notebook_manager.iter_terminals():
            if t.get_uuid() in selected_terminal_uuid:
                t.execute_command(command)

ショートカットの作成

guake/data/org.guake.gschema.xmlにショートカットの一覧が書いてあったのでそこに追加したいショートカットを追記すればうまく行くと思い追記します。

        <key name="exec-multi-window" type="s">
            <default>'&lt;Control&gt;&lt;Shift&gt;s'</default>
            <summary>execute in multi window</summary>
            <description>Accelerator to active function that exectute in multi window.</description>
        </key>

しかし、このままだとうまくショートカットが読み込まれません。どうやら、xmlファイルをコンパイルしたものを読んでいるらしいのでコンパイルしているコードを探します。

#guake/guake_app.py


class Guake(SimpleGladeApp):
    def __init__(self):
        ...
        
        try:
            #try_to_compile_glib_schemas()
            schema_source = load_schema()
        except GLib.Error:  # pylint: disable=catching-non-exception
            log.exception("Unable to load the GLib schema, try to compile it")
            try_to_compile_glib_schemas()
            schema_source = load_schema()
        self.settings = Settings(schema_source)

このように、Guakeクラスでスキーマコンパイルしようとしていることがわかるので、一度コンパイル済みスキーマファイルを削除することで例外を出し、再コンパイルさせます。

ショートカットのスキーマへの登録はできたので、次はショートカット時のイベントハンドラを登録していきます。

関連しているであろうコードはどこにあるのか探してみると、guake/keybindgins.pyというお誂え向けのファイルがあったのでそこを探してみると、

#guake/keybindings.py
class Keybindings():
    def __init__(self, guake):
        ...
        # Setup local keys
        keys = [
            'toggle-fullscreen', 'new-tab', 'new-tab-home', 'close-tab','save-tabs','exec-multi-window' 'rename-current-tab',
            ...
        ]
       
        for key in keys:
            guake.settings.keybindingsLocal.onChangedValue(key, self.reload_accelerators)
            self.reload_accelerators()
            
    ...
    def reload_accelerators(self, *args):
        ...
        self.load_accelerators()

    def load_accelerators(self):
        ...
        key, mask = Gtk.accelerator_parse(getk('new-tab'))
        if key > 0:
            self.accel_group.connect(key, mask, Gtk.AccelFlags.VISIBLE, self.guake.accel_add)


Keybindingsクラスのコンストラクタで、リストの要素をハンドラと結びつけていることがわかります。例えば上記のnew-tabという要素に対しては、Guakeクラスのaccel_add関数をハンドラとしていることがわかります。 このことから、xmlファイルに書いた名前を要素に追加して、それをハンドラに登録すればうまく行きそうなことが読み取れるので、追記します。(すでに上のリストにはexec_multi_windowという要素を追記してあります)

そして、load_accelerators関数にaccel_exec_multi関数というハンドラを結びつけるように追記します。

accel_exec_multi関数は、Guakeクラスのメンバ関数として実装します。

    def accel_exec_multi(self, *args):
        self.exec_multi_window()
        return True
    ...    
    def exec_multi_window(self):
        win = MultiExecWindow(self)
        self.hide()
        win.show_all()

結果として、Ctrl+Shift+sを押すことで作成したウィンドウが出るようになりました。

感想

  • 日頃から常用しているGuake Terminalのコードを実際に読み、コードを改変、追加することで機能を継いできたという経験ができとても刺激的でした。愛着もわいたのでばりばりGuakeを使っていきたいと思います。
  • 規模はそこまででもなかったが、このくらいの規模のソフトウェアをデバッグして、機能を追加するという経験はしたことがなかったので楽しかったです。
  • 全体を理解するのが困難である大きなプログラムに対して、必要な情報を見つけ出して所望の機能を実現するようにいじる、ということを通して、自分の中での既存のソフトウェアの中を除いて見ることへのハードルがかなり下がりました。

    参考

  • VteTerminal リファレンス
  • エスケープシーケンスチートシート