Bluetooth キーボードのためにまたCapsLockを消し去ったり

今まではcapslock殺しはxmodmapに任せていて、それで不満とかなかった。
しかし、bluetooth キーボードが混沌を運んできた。Amazon二千円で。

接続が切れるたびに(再接続になるたびに)xmodmapされる前の状態に戻る!!!
発狂しかけたので設定方法を調べました。

CapsLock

xkbで設定しろよ。ってことがわかった。
それでこれをどこで設定するかという話になるのだけれど、色々なところで書けるみたいでどれが一番スマートか分かりかねました。

capslockを殺すだけなら /usr/share/X11/xorg.conf.d/10-evdev.conf なりのevdevの設定で

Section "InputClass"
        Identifier "evdev keyboard catchall"
        MatchIsKeyboard "on"
        MatchDevicePath "/dev/input/event*"
        Driver "evdev"
        Option "XkbLayout" "us"
        Option "XkbOptions" "ctrl:nocaps"
EndSection

するのが手っ取り早そう。

ついでにリマッピング

最近出回っているbluetoothキーボードは、Escがなかったりしますので更にリマップしたくなるでしょう。私の場合は、EscがXF86HomePageとかが割り当たってたのでリマップします。私の場合、どうせ使わないので潰す方向でいきます。

Debian 6 squeeze のキーマップ設定 (One Foot) でxkbの適用順番が解説されています。

まず、xevでキーコードを調べる。xevでキーコードが取れない場合はHow to get keycodes for xmodmap?を見るとよろしい。

KeyRelease event, serial 32, synthetic NO, window 0x1a00001,
    root 0x9d, subw 0x0, time 1509250, (-532,286), root:(623,829),
    state 0x0, keycode 180 (keysym 0xff1b, XF86HomePage), same_screen YES,
    XKeysymToKeycode returns keycode: 9
    XLookupString gives 1 bytes: (1b) ""
    XFilterEvent returns: False

keycode 180
次に、setxkbmap -print -vして適応されているルールを調べる。

keycodes:   evdev+aliases(qwerty)
types:      complete
compat:     complete
symbols:    pc+us+inet(evdev)+ctrl(nocaps)
geometry:   pc(pc104)
xkb_keymap {
    xkb_keycodes  { include "evdev+aliases(qwerty)"  };
    xkb_types     { include "complete"   };
    xkb_compat    { include "complete"   };
    xkb_symbols   { include "pc+us+inet(evdev)+ctrl(nocaps)" };
    xkb_geometry  { include "pc(pc104)"  };
};
  1. X11/xkb/keycodes/evdev
  2. X11/xkb/keycodes/aliasesqwertyセクション

の順で適用さている。 キーコード180は、X11/xkb/keycodes/evdev

    <I180> = 180;   // #define KEY_HOMEPAGE            172

<I180>に割り当てられているので、今度は<I180>を追う。(ちなみにここで、<ESC> = 180とすると、元々のEscapeキーであるkeycode 9がどこにも割り当てられてない状態になってしまう。)

今度は、X11/xkb/symbols/以下で<I180>が適応されている部分を探す。 さっきのsetxkbmap -print -vをみてみる。

  1. X11/xkb/symbols/pc
  2. X11/xkb/symbols/us
  3. X11/xkb/symbols/inetのevdevセクション
  4. X11/xorg.conf.d/10-evdev.confで指定したctrl:nocpas

の順で適用さているのがわかる。ここで- 日本語キーボードを英語配列風にする (@tmtms のメモ)の方法でリマップしても<I180>はX11/xkb/symbols/inetで割り当てられているのでダメダ。

考えられた方法は、

  1. X11/xkb/symbols/inetを呼び出さないようにルールを書き換える。そうすれば、もカスタマイズしたように動くので、そのカスタマイズしたsymbolsをX11/xorg.conf.d/10-evdevのXkbLayoutで指定する。
  2. X11/xkb/symbols/inetを直接書き換える。

1番の場合、どのルールが適用されているか調べる必要があるのだけれど、私のX は、--with_default_xkb_rules=evdev を指定してビルドされてるので、X11/xkb/rules/evdevを書き換えればいい。…のだけれどあんまり書き換えたくない。

2番の場合、…もあんまり書き換えたくない。

…結局は、2番で。うーん。

あとはxkbの設定についてArch Wiki とか見ればいいのだと思う。 X KeyBoard extension(Arch Wiki)

理想としては再接続かかるたびにxmodmap走らせたいよねー。ユーザーごとに設定したいよねー。xmodmapは指定の仕方によってはトグル操作になって嬉しくないのかも知れないけど、キーコードに直接割り当てるだけなら大丈夫そう。これはまたそのうちできたらできたで…。

その方法がわかるまでにストレスで死にそうなので、今はこうしてるってメモでした。 もっといい方法知りたい。

be composed entirely out of 〜

ちょっと意味がわからなかったので教えてもらったメモ

be composed out ofbe composed ofは同じ意味。
be made ofbe made out of も同じ意味。
be made ofbe made fromの違いを習った遠き日に、もしかしたら教えてくれていたのかもしれいなぁ。

Example:
Water is composed entirely out of hydrogen and oxygen.

out が入ることで否定のような気がして混乱したけど、これはもう慣用句なので慣れましょう、自分。 H2Oはentirely out of hydrogen and oxygen だけど、窒素が0.01%入ってたとすると、それはcomposed entirely out of じゃない。

composed entirely of omposed out of の方をよく使うらしい。

composed of系は、made fromにもmade ofにもなる(?)。それとも、くくり方が違うのかな?ちょっと誰か教えて欲しい…。be composed ofを書きなおせとかいう問題は解く予定ないけど。

Stack Exchangeには、English Language Learnersってのがあるということも教えてもらったので活用していきたい所存。文法用語を英語でなんて言うのか全然知らないということに気づけました。

SDL2.0 on Android 導入

すごくいいカンジのチュートリアル(HowTo: SDL on Android part 1 – Setup)があったので、翻訳してみました!(許可いただけました)。SDL2.0のリリースおめでとうございます!!

モバイル開発を始めたいと思ってたんだ。けど、どうやったらいいんだ?

まずとりあえずそこにある選択肢は、iOS(iPod,iPhone,iPad),Windows Phone(7,8),BlackBerry(BlackBerry, PlayBook)、そしてAndroid(市場のほとんど他のデバイス)、このどのプラットフォームを選ぶかだ。
僕の気持ちは、Androidの方に傾いていたんだ。Androidはほかのプラットフォームより少しはオープンだからね。それに、Kindle Fire HDが気になっていたんだ。そういうわけで、心を決めてKindle Fire HD 7"を購入した。これはかっちりとしたタブレットだけど、僕が作りたいのはゲームだ!

僕の経験と積みあがったコードはSDLのものだった。しかも都合のいいことにSDLの開発チームはモバイル環境でいい感じに動くように新しいSDLを開発してるっていうじゃないか。

もうそこに飛び込んでみるしかないよね。

取り組み型チュートリアルスタイルで僕が取り掛かったことや書きだしたコードを吐き出していこうと思う。

Android開発環境を作ろう

まず、最初のステップはAndroidの開発環境を整えることだ。

Androidでの開発は通常Javaで行われる。だけど僕達はこれからCとC++で書くためにJava Native Interface(JNI)を使う。開発環境を作る手順はそれほどヒドくないけど忍耐強さが必要かもしれないよ。

ただ単に公式ドキュメント(SDL/README-android.txt)に従っていけばいいんだ。
developer.android.comには導入方法と、ツール群のダウンロードホスト、設定方法などがある。

ADTには、SDKEclipseが含まれていて、これだけで何でもできるようになっている。
僕は普段はCode::Blocksを使っているんだけど、Eclipseを試してみるのもそんなにたいしたことではない。デバイスによっては更にすこし特別な設定が必要かも知れない、例えばKindle Fire HDとか。Amazonに詳細な情報がある。

どっちにしても、JDK(Java Development Kit)をセットアップする必要がある。公式の導入ではJDK 6を推奨していたと思う。ここで言えるのは OSが何bitで、JDKEclipseのバージョンがいくつかをはっきりさせておく必要があるってことかな。
Windows上で64-bit(x64)のものを(苛つく前に)動かすことができなかったので、32-bit(x86)のJDKと32-bitの ADT bundleを使うことにするよ。Linux上では問題は起きなかった。

セットアップできたら、Eclipseを開いてみよう。
そして、Window > Android SDK Manager を開こう。ここで、ターゲットにしたいAndroidバージョンのために必要な全ての構成部品をダウンロード・インストールできる。

まずとりあえず、Android SDK ToolsとAndroid SDK Platform-toolsは選ぶ必要がある。
Kindle Fire HDAndroid 4.0.3 (API level 15)なので、そのカテゴリを展開してSDK Platform, ARM EABI v7a System Image (エミュレータのため)を選ぶ。Kindle Fireは他にもいくつかのパッケージを必要とする(USB driver, device definitions, Android Support Libr/ary, etc. – 詳しくはthe Amazon page)。

これで、完全な Java開発環境 for Androidが君の手に入った!
すべてのJavaを使う人たちにバンザーイ! But I want more…

Setting up SDL2

SDL2.0をはじめる一番いい方法はMercurial(hg)を使うことだと思う。MercurialSDL開発に使われているバージョン管理ソフトだ(もちろんそれ以外のプロジェクトにも使われてる)。
しばらくごとに更新されるスナップショットもあるけど、hgを使うほうがいいと思う。(追記:SDL 2.0.0が公式リリースされました!!ここからダウンロードしてこれるよ。mercurialリポジトリも最新のbug fixのために使われてるのでどっちでも好きな方で。)
Mercurialを使えば周辺ライブラリも最新状態に保てる(SDL_image,SDL_ttf,SDL_mixer,etc.)。

まずMercurialを使えるようにして、SDLのtreeをcloneする:

hg clone http://hg.libsdl.org/SDL

もしもTortoiseHGを使ってるならCloneコマンドを使ってURL(http://hg.libsdl.org/SDL)を入れればいい。
SDL/README.androidAndroid portのための記述がある。

SDLはあなたの書いたネイティブコードを読み込むためにJavaのあれこれに気をかけるくれる、それにプロジェクトの雛形でまで備えている(とても使いやすい!)。 けど、それ以外にもまだ全てをリンクするために少しはしないといけないこともある。(それは、また後で)

Projectを作る

まずProjectのディレクトリを作る。これは
SDLフォルダ内のandroid-projectディレクトリをProjectを置きたいところにコピーして、好きな名前(プロジェクト名)にリネームして完成。
つまり、コピーしたディレクトリがProjectのディレクトリになる。それから、SDLディレクトリ全体のコピー(もしくはシンボリックリンク)をProject内のjniディレクトリの中に入れる。

コマンドラインでやるならこんな感じ:

cp -r SDL/android-project workspace/
mv workspace/android-project workspace/myProject
cd workspace/myProject/jni
ln -s ../../../../SDL ./
※jni下に置くSDLは、configureしていないクリーンな状態である必要がある。
  • 'jni'ディレクトリは、コンパイルとリンクするために必要な全てのライブラリを入れるところ。
  • 'src'ディレクトリは、書いたソースコード自体をいれるところ。

このjniディレクトリと、その下にあるsrcディレクトリがプロジェクトの中で一番大切な部分てことだ。

それらのディレクトリ以外にも、下の3つのファイルも重要

  • AndroidManifest.xmlAndroidシステムに向けてあなたのappの情報を記述してる。
  • jni/Application.mk は特殊な環境設定を
  • jni/src/Android.mk はNDKにコードをどうbuildするかを伝える。

Your Very Own Source

いよいよネーティブコードを書き始める時だ。

"Hello World"のためのソースファイルを作ってみよう。
jni/src/main.c(main.cpp もcoolだね)
そして次に
jni/src/Android.mkを開こう
LOCAL_SRC_FILES下のYourSourceHere.cをmain.c(とか名付けた名前に)変える。ここまでくれば、ndk-build(Windowsならndk-build.cmd)を走らせてうまくやれているかどうか確かめよう。

Projectのベースディレクトリ(頂点にあるディレクトリ)で

ndk-build

ndk-buildが見当たらない場合

NDK付属してきてるのでandroid-ndk-r8eなりにPATHが通ってるか確認してみて。
もう、NDKはダウンロードしてきたよね?
NDKディレクトリ(android-ndk-r8eとか)をPATH環境変数に追加しとけば楽だよ。さもなくば、ndk-buildへのfull pathを記述しないといけない。Windowsでは、コントロールパネル > System > Advanced System Settings > Environment Variables ..(セミコロンを使って要素を分けて…、ぶっ壊さないように慎重にね)

ndk-buildコマンドはSDLをコンパイルしてトラブルを起こさない(perfect world)!

ggkuronさんが試したときには、SDLがiconvを使うので、自前で準備する必要がありました。jni/SDL/includeにiconv.hを入れられればOK。

今のコードの結果を今書いているシステム(訳:エディタ?PC?) ではそのまま走らせることはできない、…のでプロジェクトの骨組みとかコードが大丈夫そうかどうかしっかり確認して、Eclipseで読み込もう。
File > New > Project...
Android > Android Project from Existing Code
を選んで、あなたのProjectディレクトリの頂点がどこにあるかを教え、それからFinishをクリック。
”Copy projects into workspace"オプションがあるだろうけど、僕にとってはややこしいといえる結果をもたらしたからたぶんすべきじゃないと思うんだ。

それが終わったら、Eclipse上にいい感じでProjectができてるはずさ。
緑色の"Run"ボタンを押してエミュレータや接続されたデバイス上でのテストしよう。

An Example Program

SDL 2.0の完全なチュートリアルをここに書こうとは思っていない。だから、ここにはデモを置いとこう。コードはここ

これは、まったく全て正しく動いたとしたらスクリーン上に何か画像を表示するだけのプログラムだ。

そのために画像を読み込む必要があるんだけど…。
Androidでは全てのデバイス上に読み込まれるデータを.apkにパックしないといけないので少しめんどくさい。それでも、SDL2にはいい機能がある。

どんなデータでも、Project内のassetsディレクトリに置くとSDLSDL_RWopsを使うとそこを見に行く。そうやってどういう類のデータ読み込める、だけどSDLにはもっといい機能がある。

SDL_LoadBMP()もassetsディレクトリを見にいくんだ。そのおかげでなんの苦痛なしに画像をロードできる。これはまさに今回のmain.c内で使える、動くはずだ。bmpファイルをassetsディレクトリに入れるのを忘れないでね。必要ならこれ使ってよ:

f:id:ggkuron:20130816093430j:plain

Important Settings in AndroidManifest.xml

動くようになったところで、もしかしたらProjectに名前を付けたいと思ったかも知れない。うん、もし違うProjectを作りたくなったらどっちみちユニークな名前が必要なんだ。やってみよう。

まず、ベースディレクトリにあるAndroidManifest.xmlから設定を始めよう。
AndroidManifest.xmlはappがAndroidシステムのためにProjectについて記述したものだ。appの名前と、特定バージョン向けの設定、パーミッション設定がここにある。Androidは特定のデバイスのための設定を判別して動かすこともできるのだ。

まず、個々のプロジェクトパッケージを判別させる。

AndroidManifest.xmlの上の方に、"org.libsdl.app"というpackage nameがあると思う。それをあなたのチームとプロジェクトを表すものに変える。(ドメインを持ってるなら、それを逆順にしたものを使うといいよ)

package名はappごとにユニークである必要がある。デバイスには同じpackage名のappは1つしか入れらない。GooglePlay上にも同じpackageのappは1つだけしか存在しないようになっている。

AndroidManifest.xmlの下の方に動いて、次に進もう

<activity android:name="SDLActivity"

"SDLActivity"をあなたの好きなように変えて(class名になる)。これは本当に好きなように(漢字とかはだめだよ)。 僕は、"MyTest1"にしてみるよ。
そうすると、同名のJava activity を作る必要がある。
sampleが見たいなら、SDL/README-android.txtを(ちょっと僕のバージョンと違う):

package com.dinomage.test1;
import org.libsdl.app.SDLActivity;
/*
 * A sample wrapper class that just calls SDLActivity
 */
  public class MyTest1 extends SDLActivity { }

このように、
packageと書いてる行をあなたのpackage名に
class name をあなたのclass名に変える。

これを、src/com/dinomage/test1/MyTest1.javaというファイルにして保存。

src/org/libsdl/app/SDLActivity.javaはそのまま。

AndroidManifest.xmlで、もうひとつの大切なのはactivity settingsの記述だ。ここに、

android:configChanges="orientation"

このattributeを追加すると、orientation change eventがクラッシュすることなく動くようになる。(すぐに直される類のbugかもしれない)

そして最後に、リソースの参照先が@string/app_nameのように記述されてる。
これは、resディレクトリ(res/values/strings.xml for @string)から引き出される。弄ることもできる。

Android.mkって何だ?

Android.mkはあなたのappのbuild processをコントロールするために使われる。手短にいうと、Makefileだ。
ここに、コンパイルフラグとかライブラリディレクトリとかincludeディレクトリが必要なら書き込む。

ひとつtipを。このようにすると、全てのソースファイルを引き込める。
jni/src/Android.mk

LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \
$(patsubst $(LOCAL_PATH)/%, %, $(wildcard $(LOCAL_PATH)/src/*.cpp)) \
$(patsubst $(LOCAL_PATH)/%, %, $(wildcard $(LOCAL_PATH)/src/*.c))

Application.mk

Application.mk はbuild環境のための設定が書かれている。僕のはこうなってる:

 APP_STL := gnustl_static APP_ABI := armeabi x86 

これで C++STLを使えるようになって、appをひとつはARM向けに、もうひとつはx86向けに2つ同時に構築できるようになる。
たぶん、デフォルトではarmeabi-v7a(NEONとThumb2サポート)も記述さているだろうが、僕が走らせてみたところAndroid4.0でbugを踏みぬいた。―armeabiとarmeabi-v7aを両方入れたところ、動的に上手くリンクされなかった。結果、荒れた。

Running ndk-build

ndk-buildはAndroidの"make"コマンドだ。
Projectのベースディレクトリで走らせると、ネイティブコードをすべてコンパイルして必要なバイナリを吐き出してくれる。
コマンドラインからndk-buildを走らせるか、Eclipseの外部ツールとして設定するといい。
adbを使って(コマンドラインを通してか、Eclipseの"Run"もしくは"Run As... Android application")でappをデバイスやエミュレータに に届けられる。バイナリはapkにラップされる。

もし、全部やり遂げたら、教えてくれ!

勝手追記

C++11を使う

TOOLCHAINでGCC4.7とかを使えるようにしておく

Application.mkに

NDK_TOOLCHAIN_VERSION=4.7

Android.mkに

LOCAL_CFLAGS += -std=c++11

std::to_stringが使えなかった思ひde

GLFWでのキーボード入力

GLFWでキーボード状態を取得する方法の日本語情報がないような気がしたので,ユーザーマニュアルを簡単に訳してみました.

(*追記 GLFWのバージョンは2.??で、ユーザーマニュアルのURIは見失ってライセンス確認できてないです。ゴラァしてくれると引っ込めます。)

GLFWが提供する3つの方法

  1. 個々のキーのそのときの状態を調べる
  2. あるキーの状態が変わったの受け取るコールバック関数
  3. 文字を受け取るコールバック関数

何をしたいかによって,どの方法が好まれるかは異なります. 2と3の方法の違いは,シフトなどの修飾キーに影響されるかどうかというところ.文字のコールバックでは,カーソルキーや修飾キーを受け取れない.

1.キー状態を調べる

ある瞬間のキーが押されてるかどうかをチェックする

int glfwGetKey( int key );

引数keyでどのキーを調べたいかを指定します.キーの指定はISO 8859-1で.返り値はキーが押されていたならGLFW_PRESS(すなわち 1),押されてなかったならGLFW_RELEASE(すなわち 0).

glfwGetKeyしてから次のglfwGetKeyまでにキーの状態が変わったか知りたいときの方法

(処理落ちしたときには,glfwGetKeyが呼ばれるまでにユーザーがキーを放してしまう!)

glfwEnable( GLFW_STICKY_KEYS ); 

これが有効になっていると,glfwGetKeyでチェックされるまでキーは開放されない.

glfwDisable( GLFW_STICKY_KEYS );

現在押されてない全てのキーが解放される,glfwGetKeyにチェックされるのを待たずに解放されたキー入力は即座に開放状態に取り変わり,入力を受け取れない. デフォルトではGLFW_STICKY_KEYSは無効になっている. Sticky key(粘りキー判定)はとても有効だけど,危険もはらんでる.それはglfwGetKeyでチェックされないまま長いこと"pressed"になったままになっている場合だ.典型例は,メニューとゲームの2つの構成を持つプログラムでメニューでの入力を残したままゲームセクションへ遷移したときなどである.これを避けるためには,セクションを離れるときには必ずSticky keyを無効化すること.

2.コールバック関数

これは,glfwGetKeyの代替となる.

void glfwSetKeyCallback( GLFWkeyfun cbfun );

引数funはコールバック関数へのポインタです.コールバック関数は2つの整数型の引数をとる.第1引数はキー識別子,第2引数はGLFW_PRESS か GLFW_RELEASEの状態.もし,コールバック関数を登録したくないならfun=NULL指定できる.

コールバック関数を使えば,キーが押されたことや開放されたことを逃すことはないだろう.必要なイベントが発生したかを調べる代わりに必要なイベントにだけ反応すればいいのだ.

3.文字入力のためのコールバック関数

キーボードを文字入力デバイスとして使うなら,文字入力コールバック関数を使えばいい.

void glfwSetCharCallback( GLFWcharfun cbfun );

funはコールバック関数へのポインタだ.コールバック関数の第1引数はユニコードコードポイント.第2引数はGLFW_PRESS か GLFW_RELEASE.これも,fun=NULLにできる.

その他

キーリピート
glfwEnable( GLFW_KEY_REPEAT );

これで,キーリピートできる.

システムキーの無効化

ALT+TABなどのOSが提供するイベントを無効化したいとき.

glfwDisable( GLFW_SYSTEM_KEYS );