pig's diary

何でも忘れるので万年初心者ね

systemd-networkd + wpa_supplicant でワイヤレスネットワーク接続

netctl で繋いでいたが、別のマシンで systemd-networkd での接続が比較的スムーズに行ったので、母艦の ArchLinux も systemd-networkd で管理することにした。netctl で接続したときはツールがいろいろ出てきて(wifi-menu, wpa_supplicant)、正直どうやって繋がっているのか分かっておらず、接続が切れたときにパニックに陥りすぐ復旧できなかったというのも systemd-networkd に揃える目的の一つ。

基本的に systemd-networkd - ArchWiki に従って進めることでうまくいった。

2010年くらいの Mac miniパーティションを切って Arch Linux を入れている。Wi-Fi インターフェース(手元だと wlp2s0b1)は認識されているのが前提。

$ ip link
...
2: wlp2s0b1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
...

まず netctl 時代に作った設定ファイルを消す。systemd-networkd が netctl で作った設定ファイルを使うことも可能のようだが、前述の理由と、Arch Wiki にある方法が最小設定だろうということで、wpa_supplicant に置き換えていく。以下で全部だったと思うが確証はなく、実際は find /etc/systemd/ してネットワーク接続に関係しそうなサービスを全て消していった。

# rm /etc/netctl/wlp2s0*
# systemctl disable netctl-auto@wlp2s0
# systemctl disable 'netctl@wlp2s0b1\x2dSSID.service'

再起動してネットに繋がらないことを確認。次に WPA supplicant - ArchWiki に沿って Wi-Fi 接続設定ファイルを作る。

# cat /etc/wpa_supplicant/wpa_supplicant-wlp2s0b1.conf
ctrl_interface=/run/wpa_supplicant
update_config=1

update_config=1 を追加すると wpa_cli による example.conf への変更が保存されるようになります。

とのこと。このあと wpa_supplicant-wlp2s0b1.conf に Wi-Fi SSID とパスコードが保存される。

# wpa_cli

ここでやることは Wiki にある内容とほぼ同じだったので割愛する。こちらを参照のこと。

WPA supplicant - ArchWiki

しかし私の場合初めに list_networks するとこれから繋ぐ SSID が登録されてあった。netctl も中で同じことをしていたのだろうか? 全て消し、 add_network set_network 0 ssid "MYSSID" set_network 0 psk "passphrase" enable_network 0 しなおした。save_config すると先ほどの wpa_supplicant-wlp2s0b1.conf に保存される。続いて起動時に接続を確立するようサービスを設定。

# systemctl start wpa_supplicant@wlp2s0b1.service
# systemctl enable wpa_supplicant@wlp2s0b1.service

Wi-Fi が利用できる状態になったところで、systemd-networkd がそれを利用しネット接続するための設定をする。Match さえすればファイル名はなんでも良いよう。

$ cat /etc/systemd/network/wlp2s0b1.network
[Match]
Name=wlp2s0b1

[Network]
DHCP=ipv4

全てのオプションは man systemd.network で見られる。

私の場合は再起動が必要だった。ネットに繋がれば成功。

IndexedDB調査

IndexedDBの動きを見てみた。特徴とおぼしき点を列挙する。

  • DBバージョンとマイグレーションのための機構を提供
  • JSONで表現できる型しか保存できない
    • functionが生えてるとこける
  • 主キー、もしくはマイグレーション(onupgradeneeded)で予めindexを作っておいたフィールドでしか検索できない
  • 検索は範囲(range)という形式でのみ行える
  • getAll でレコードの配列を取得する方法と、openCursorで1件1件取得する方法がある
  • offsetで取れない
    • cursor.advance(n)で何とかするしかなさそう

以下は半年後全部忘れちゃった自分がすぐに思い出せる用gist:

gist.github.com

参考 developer.mozilla.org

`pointer-events:none;` で作る視覚効果

知らなかった。こういうのがある。

  pointer-events:none;

stackoverflow.com

IE11+。全ブラウザが実装してる。使えそうだ。

Can I use... Support tables for HTML5, CSS3, etc

これとSVGを組み合わせると、ユーザーの操作を邪魔せずに、ユーザーの注意をコントロールする視覚効果を作れる。新規ユーザーへのチュートリアルや、入力補助、ヘルプなどに使えそう。

Result タブを選んでみてほしい。ユーザーを注目させるための視覚効果としてスポットを動かしている。

キーワードをハイライトできるテキストボックスをReactで作った

デモ

github.com

山場

ハイライト自体は、contenteditable+keyup+el.innerHTML.replaceですぐに動く。問題はel.innerHTML = el.innerHTML.replace(...)したあとカーソルが先頭に戻ってしまうこと。これによりユーザーが入力し続けることができず、入力装置として使い物にならない。

賢者がいた

師「この問題、好きなんだよね。けっこうやった。だいたい動いてると思うよ」。

stackoverflow.com

カーソルの位置を特定するために、テキストノードまで潜って数を数える。最終的な位置セットやもろもろのユーティリティーにrangyというライブラリを使っている。これがnpmにあってとても助かった。

npmモジュール

賢者からの返事を待ってreact-highlightableという名前でnpm publishしようと思ってる。

インターフェース

とりあえずこんな風にした。

const Highlightable = require('../react-highlightable');
const validUsers = ["ariel", "belle", "jasmine"];


ReactDOM.render(
  <div>

    <Highlightable
      className="highlightable"
      onChange={onChange}
      pairs={[
        [Highlightable.TokenPreset.URL, 'hilite hilite--link'],
        [Highlightable.TokenPreset.USER_MENTION,
            token => `hilite hilite--user hilite--user-${
                validUsers.includes(token.slice('@'.length)) ?  'valid' : 'invalid'}`],
        ['yeah|ohh', 'hilite hilite--token']
      ]}
      value='@jasmine and @belle are you still there? Visit www.google.com'
    ></Highlightable>

  </div>
)

ハイライトする文字列のパターンとclassNameまたはclassNameを作る関数をペアで渡す形式にした。

よくありそうなURL、EMAIL、USER_MENTIONはプリセットとして正規表現を用意しておいた。

使いどころ

URLハイライトとかはコメント投稿のプレビューとかに使えるかも。全ユーザー一覧をローカルに持ってることは多くないはず。サーバーに問い合わせて&ハイライトの条件を変えて再び描画、という使い方になると思う。

既知の問題

ヒットしたキーワードにカーソルが接触した状態で改行されると、contenteditableのデフォルトの動きがそうであるように、ハイライト用のスタイルを引き継いで新規行が作られてしまう。賢者のデモでも同じだった。スタイル次第では目立たないが、背景色があると結構めだつ。その後文字を入力すると問題は直る。

またハイライトしたキーワードにfontawesomeでアイコンをつけたところ、カーソル補正が壊れた。CSS擬似要素が文字カウントに影響しているようだ。こちらはなんとかなる気はする。

MutationObserver/Mutation Events は孫DOMへの操作も検知するのか

MutationObserver/Mutation Events は、孫DOMへの操作も検知するのだろうか。Chrome48.0.2564.103 とFirefox44.0.2で検証した。

※ざっくりした調査なのでご参考までに。

結論

MutationObserverは仕様にsubtreeオプションがあり、ChromeFirefoxもこれを実装している。問題なく検知する。

Mutation Events (廃止された仕様) は、Chromeで一部検知しない。Firefoxでは検知できた。

Mutation Events

廃止された仕様。使ってはいけない。Mutation events - Web developer guides | MDN

とりあえず、この3つだけを調査の対象とする。全イベント一覧はこちら

  • DOMAttrModified
  • DOMNodeInserted
  • DOMNodeRemoved

HTML

<body>
  <div>
    <div id="descendant"></div>
  </div>
  <script src="./main.js"></script>
</body>

JavaScript

'use strict';

/*
 * Mutation Events を document.body に登録する
 */
[ 'DOMAttrModified', 'DOMNodeInserted', 'DOMNodeRemoved' ]
.forEach(type => {
  document.body.addEventListener(type, e => {
    console.log('=======', e.type);
  });
});

/*
 * 孫DOMに対し、操作を加えてみる
 */
test();

function test() {
  let el = document.getElementById('descendant');
  let textNode = document.createTextNode('boooom');
  el.setAttribute('boom', 'boom');
  el.appendChild(textNode);
  setTimeout(() => el.removeChild(textNode), 800);
}

実行結果

Firefox

全てのイベントがdocument.bodyまで伝播している。

======= DOMAttrModified a.js:10:5
======= DOMNodeInserted a.js:10:5
======= DOMNodeRemoved a.js:10:5

Chrome

DOMAttrModifiedだけdocument.bodyに来ない。

======= DOMNodeInserted a.js:10 
======= DOMNodeRemoved a.js:10 

MutationObserver

MutationObserver - Web API インターフェイス | MDN

結果として、FirefoxChromeも仕様通り機能した。平和平和。

JavaScript

HTMLと、検証用test()関数は上記と同じ。MutationObserverのコードをこうした。

/*
 * Mutation Observerを登録。
 */

let mo = new MutationObserver(mutationRecords => {
  console.log('=======', mutationRecords.map(r => r.type));
});
mo.observe(document.body, {
  childList: true,
  attributes: true,
  characterData: true,
  subtree: true, /* ここポイント */
  attributeOldValue: true,
  characterDataOldValue: true
});

Firefox

======= Array [ "attributes", "childList" ] a.js:19:3
======= Array [ "childList", "characterData", "characterData" ] a.js:19:3
======= Array [ "childList" ] a.js:19:3

Chrome

======= ["attributes", "childList"] a.js:19
======= ["childList", "characterData", "characterData"] a.js:19
======= ["childList"] a.js:19

同じ結果を返していることが分かる。

参考にした記事

非推奨になった Mutation events を Mutation Observers に置き換えよう - ログろいど

urxvt が unicode 記号を見つけるのに失敗

軽量ターミナル urxvt (rxvt-unicode) でチェックマーク(✓)が出てくれなかった。

f:id:piglovesyou:20140614113358p:plain

失敗したときのフォント設定はこちら。

! ~/.Xdefaults
urxvt.font: xft:Ricty:Normal:size=14:antialias=true

✓ がでないと「ヨシッ」って気持ちになれないですよ。

rxvt FAQ によると、フォントは複数指定でき、見つかるまで順に探していくとのこと。

http://pod.tst.eu/http://cvs.schmorp.de/rxvt-unicode/doc/rxvt.7.pod#How_does_rxvt_unicode_choose_fonts

rxvt-unicode は最初にベースフォントから文字を探す。見つからなければ、次、次という感じ。持ってるフォントを予め宣言しておけば、探索も速いし rxvt-unicode と X-server のリソース消費も少なくて済む。

速そうで良い。他のターミナルがどうフォントを探すのかを知りたいところ。

チェックマークを求めて手持ちのフォントを試したのだが、この宣言で表示することができた。

! ~/.Xdefaults
urxvt.font: xft:Ricty:Normal:size=14:antialias=true, \
            xft:DejaVu Sans Mono:size=13

f:id:piglovesyou:20140614115103p:plain

ヨシッ。