Emacs 29.1 の What's New ピックアップ
Emacs 29.1のWhat'sの紹介記事を見て、自分的に気になったものを並べる。
split-root-window-below と split-root-window-right
ウィンドウ分割は C-x 2
で今のウィンドウを上下に分割、 C-x 3
で左右に分割なんだけど、例えばすでに左右に分割された状態で、その2つのウィンドウの下にウィンドウを入れようとしたときのコマンドがなかった。この例の場合についていえば、右のウィンドウを一度消してからc-x 2(上下分割), c-x 3(左右分割) とかなんとかしたりして、面倒だなと思っていた。必要になるのはたまにだけど。
そういうときに、今回追加された split-root-window-below
(C-x w 2
) とか split-root-window-right
(C-x w 3
) が使える。便利。
Emoji
emoji-list
とか emoji-search
とかで絵文字を挿入できるようになった。たぶん使わないけど、ためしておーとなった。
tree-sitter
使ってみたい。あとで時間あるときにためす。
pixel-scroll-precision-mode
普通のアプリケーションのように?なめらかなスクロールができるようになる。ためしてみたけどちょっとスクロール重くなったり固まったりして、そもそもマウスでスクロールする習慣ないからデフォルトで有効にはしないかな。
キーバインド、キーマップ設定関数
Use 'keymap-set' instead of 'define-key'. Use 'keymap-global-set' instead of 'global-set-key'. Use 'keymap-local-set' instead of 'local-set-key'. Use 'keymap-global-unset' instead of 'global-unset-key'. Use 'keymap-local-unset' instead of 'local-unset-key'. Use 'keymap-substitute' instead of 'substitute-key-definition'. Use 'keymap-set-after' instead of 'define-key-after'. Use 'keymap-lookup' instead of 'lookup-key' and 'key-binding'. Use 'keymap-local-lookup' instead of 'local-key-binding'. Use 'keymap-global-lookup' instead of 'global-key-binding'.
define-key
や global-set-key
などに対応するあたらしい関数。今までの関数は使えなくなる訳ではないけれど、新しい方を推奨していくとのこと。参照記事にもあるけど、古い名前に馴染んでしまっているよなあ。まあleafとかuse-packageとか使っているとあまり気にする必要ないか。
Proced
proced-modeというのがあるのを今回知った。ためしてみてpsっぽい一覧が見えておーとなったけど、使わないかな..
Tramp
/docker:<コンテナ名>
で動いているコンテナの中のファイルにアクセスできるようになった。コンテナ内のファイルをemacsでじっくり編集したくなることはあまりないけど、覚えておいてもいいかな。
Project
project-find-file
や project-kill-buffers
など。project-find-file は Emacs 25のころからあったらしい。projectile を入れて projectile-find-file
とか projectile-kill-buffers
をよく使っているけど、それの代わりになるのかな。ivyとかその他パッケージがprojectile利用していそうなのでprojectileは消せないかな。それなら今のままでいいかな?
scratch-buffer
scratch buffer がなければ作るコマンド。これ欲しかった。たまにscratch buffer消しちゃったときに、やっぱり手頃なバッファーが欲しくなって scratchバッファーの作りかたを何度か検索した(そしてない事に驚いた)。Lisp評価できる必要ないし、適当な名前でバッファー作ればいいんだけども。initial-major-mode
でscratch bufferのモード指定できたのか。fundamentalとかにしておくかな。
package-upgrade-all
大抵 list-package -> U -> x -> y と流れでやっているのを、ショートカットできる。何がアップデートされるのか分からないのはちょっと微妙。
M-SPC
(cycle-spacing
)
知らなかった。たくさんのスペースを1つにしたいときは多々あるので、覚えよう。記事にある M-^
(delete-indentation
) も知らなかったけど、これも覚えよう。
restart-emacs
あるなら使うかもしれない。パッケージアップデートしたときとかゴミみたいなバッファーたくさんあって掃除したいときとか、再起動はたまにする。
duplicate-line
と duplicate-dwim
C-a C-SPC C-n M-w C-y
が染みついているのでいまさらという感じもあるが、よく使うものでもあるのでどこかに割り当てようかな。kill-ringが汚れないのもよい。
Global font size change (C-M + スクロールもしくは C-x C-M-=
, C-x C-M--
)
フォントサイズを変えたいときは大抵バッファーごとではなくてすべてのバッファーで変えたいのでうれしい。
ctrl + スクロール もしくは C-x C-=
, C-x C--
でバッファー単位のフォントサイズ変更をしたときに、行番号とかtab-barのタブとかmode-lineとかの大きさが変わらなくて気持ち悪かったんだけど、こっちのフォントサイズ変更だと全部変わるのもうれしい。
Emacs is now capable of editing files with very long lines
たまにながーい行のあるファイルを扱うときにEmacsがおっっっそくなって心を無にすることがあるので、改善はうれしい。
Ubuntu 20.04 を Wireguard サーバー(ゲートウェイ)にする
Ubuntu 20.04をWireguard サーバーにし、さらにGWとして使えるように設定する。IPv6は設定しない。
OSの設定をする
ルーティングしてもらうために /etc/sysctl.conf の以下の行のコメントを外す。
net.ipv4.ip_forward=1
Wireguardをインストールする
$ sudo apt update $ sudo apt install -y wiregaurd $ sudo reboot
Wireguardを設定する
Wireguardの設定は、wg-quickでOS起動/停止時に有効化/無効化する方針で。
鍵ペアを作る (/etc/wireguard/{privatekey,publickey})。
$ sudo sh -c 'cd /etc/wireguard; wg genkey | tee privatekey | wg pubkey > publickey' $ sudo chmod 600 /etc/wireguard/privatekey
wg-quick 用の設定ファイルを作成する。 xxx.conf の xxx がWireguardのインターフェース名として使われる。ここでは wg0 を使う。PrivateKeyには作成した privatekey の内容を転記する。IPアドレスやポート番号は適当に。PostUp/PreDownの第1引数 %i はWireguardのインターフェース名に置換される。第2引数は listen port、第3引数は wireguard用のセグメントを指定する。PeerにIPを手動で割り当てるのちょっとめんどい。
$ sudo vi /etc/wireguard/wg0.conf [Interface] SaveConfig = false Address = 10.0.1.1/24 ListenPort = 47474 PrivateKey = xxxxxx PostUp = /etc/wireguard/postup.sh %i 47474 10.0.1.0/24 PreDown = /etc/wireguard/predown.sh %i 47474 10.0.1.0/24 [Peer] # hogehoge PC PublicKey = abcabc AllowedIPs = 10.0.1.2/32 [Peer] # fugafuga PC PublicKey = xyzxyz AllowedIPs = 10.0.1.3/32
下はPeer側の設定例。PeerのPeerはサーバー。AllowedIPsを 0.0.0.0/0 としデフォルトゲートウェイをWireguardサーバーに向ける。ロケーションによってDNSレスポンスが変わる事もあるらしいので、Wireguardサーバーに近いDNSサーバーを使いたい場合は指定する。
[Interface] PrivateKey = zzzzzz Address = 10.0.1.2/32 DNS = xx.xx.xx.xx [Peer] PublicKey = abcabc AllowedIPs = 0.0.0.0/0 Endpoint = xx.xx.xx.xx:47474 $ sudo chmod 600 /etc/wireguard/wg0.conf
wireguardを有効/無効にするときにfirewallの設定を追加/削除したいので、 postup.sh/predown.sh に書いておく。ポートを開く他に、FORWARDチェインの設定(ufw route allow ...) とIPマスカレードの設定(iptables -t nat ...)が必須。
[/etc/wireguard/postup.sh] WG_IF=$1 PORT=$2 TUN_SEG=$3 GW_IF=$(ip -j route list default | python3 -c 'import sys,json; print(json.load(sys.stdin)[0]["dev"])') ufw allow in on $GW_IF to any port $PORT proto udp ufw allow in on $WG_IF ufw route allow in on $WG_IF -s $TUN_SEG out on $GW_IF iptables -t nat -I POSTROUTING -o $GW_IF -s $TUN_SEG -j MASQUERADE ufw status verbose
[/etc/wireguard/predown.sh] WG_IF=$1 PORT=$2 TUN_SEG=$3 GW_IF=$(ip -j route list default | python3 -c 'import sys,json; print(json.load(sys.stdin)[0]["dev"])') ufw delete allow in on $GW_IF to any port $PORT proto udp ufw delete allow in on $WG_IF ufw route delete allow in on $WG_IF -s $TUN_SEG out on $GW_IF iptables -t nat -D POSTROUTING -o $GW_IF -s $TUN_SEG -j MASQUERADE ufw status verbose
$ sudo chmod 755 /etc/wireguard/post{up,down}.sh
Wireguardを有効化する
$ sudo systemctl enable --now wg-quick@wg0.service
Wireguard、他の状態を確認する
$ systemctl status wg-quick@wg0.service $ journalctl -u wg-quick@wg0.service $ sudo wg show wg0
ついでに他の設定も確認する
$ sudo ufw status verbose $ sudo iptables -t nat -nvL $ sudo sysctl -a | grep ip_forward
補足
上では混ぜて書いているが、Wireguardの設定の他に、Linuxをルーター(GW)として動作させるためには、以下の設定を忘れないように。
kpartx -a xxx で追加したデバイスのソースイメージを探す
kpartx -a *.img
でイメージファイルの各パーティションをアタッチした時に、デファイスファイルのどれがどれに対応しているか調べた。環境はUbuntu 20.04。
ためしに kpartx -a
してみる。
### ディスクを作る $ dd if=/dev/zero of=/tmp/test.img bs=1 count=0 seek=1G ### 適当にパーティションを作る $ sgdisk -a1 -n1:34:2047 -t1:EF02 -n2:2048:+256M -t2:EF00 -n3:0:0:+100% -t3:8300 /tmp/test.img $ sudo sgdisk -p /tmp/test.img Disk /tmp/test.img: 2097152 sectors, 1024.0 MiB Sector size (logical): 512 bytes Disk identifier (GUID): BAC9F0D5-63A5-411B-9F48-E06CBB18E58B Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 2097118 Partitions will be aligned on 2-sector boundaries Total free space is 0 sectors (0 bytes) Number Start (sector) End (sector) Size Code Name 1 34 2047 1007.0 KiB EF02 2 2048 526335 256.0 MiB EF00 3 526336 2097118 767.0 MiB 8300 $ sudo kpartx -av /tmp/test.img add map loop0p1 (253:0): 0 2014 linear 7:0 34 add map loop0p2 (253:1): 0 524288 linear 7:0 2048 add map loop0p3 (253:2): 0 1570783 linear 7:0 526336 $ ll /dev/mapper/ total 0 drwxr-xr-x 2 root root 120 Jul 22 17:28 ./ drwxr-xr-x 16 root root 4200 Jul 22 17:28 ../ crw------- 1 root root 10, 236 Apr 23 14:53 control lrwxrwxrwx 1 root root 7 Jul 22 17:28 loop0p1 -> ../dm-0 lrwxrwxrwx 1 root root 7 Jul 22 17:28 loop0p2 -> ../dm-1 lrwxrwxrwx 1 root root 7 Jul 22 17:28 loop0p3 -> ../dm-2
名前からして /dev/mapper/loop0p{1..3}
の親は /dev/loop0
であると推測できるが、一応確認してみる。
kpartx
で接続された各パーティションは、device mapper によってマップされている。マッピングの様子は、たとえば dmsetup ls --tree
で表示できる。
$ sudo dmsetup ls --tree loop0p3 (253:2) └─ (7:0) loop0p2 (253:1) └─ (7:0) loop0p1 (253:0) └─ (7:0)
253:x や 7:0 はデバイスナンバーといい、major:minor の組で表わされる。 dev 下にあるデバイスファイルを ls -l
で表示すると、オーナー/グループと日付の間に major, minor の形で表示される。たとえば dmsetup ls --tree
の結果の loop0p3 を見ると、loop0p3 (dm-2) 自体は 253:2
で、親は 7:0
であるという。これは下の ls -l の結果と照合できる。
$ ls -l /dev/loop* /dev/mapper/* /dev/dm-* brw-rw---- 1 root disk 253, 0 Jul 22 17:28 /dev/dm-0 brw-rw---- 1 root disk 253, 1 Jul 22 17:28 /dev/dm-1 brw-rw---- 1 root disk 253, 2 Jul 22 17:28 /dev/dm-2 crw-rw---- 1 root disk 10, 237 Jul 22 06:10 /dev/loop-control brw-rw---- 1 root disk 7, 0 Jul 22 17:28 /dev/loop0 brw-rw---- 1 root disk 7, 1 Jul 22 07:57 /dev/loop1 brw-rw---- 1 root disk 7, 10 Jul 20 15:34 /dev/loop10 brw-rw---- 1 root disk 7, 11 Jul 21 06:45 /dev/loop11 brw-rw---- 1 root disk 7, 2 Jul 22 07:57 /dev/loop2 brw-rw---- 1 root disk 7, 3 Jul 22 07:56 /dev/loop3 brw-rw---- 1 root disk 7, 4 May 17 16:33 /dev/loop4 brw-rw---- 1 root disk 7, 5 Jun 21 20:28 /dev/loop5 brw-rw---- 1 root disk 7, 6 May 6 09:28 /dev/loop6 brw-rw---- 1 root disk 7, 7 Jun 14 15:28 /dev/loop7 brw-rw---- 1 root disk 7, 8 Jun 29 23:38 /dev/loop8 brw-rw---- 1 root disk 7, 9 Jul 22 07:57 /dev/loop9 crw------- 1 root root 10, 236 Apr 23 14:53 /dev/mapper/control lrwxrwxrwx 1 root root 7 Jul 22 17:28 /dev/mapper/loop0p1 -> ../dm-0 lrwxrwxrwx 1 root root 7 Jul 22 17:28 /dev/mapper/loop0p2 -> ../dm-1 lrwxrwxrwx 1 root root 7 Jul 22 17:28 /dev/mapper/loop0p3 -> ../dm-2
ループデバイスにアタッチされているものは、losetup --list
コマンドで確認できる。
$ losetup --list /dev/loop0 NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC /dev/loop0 0 0 0 0 /tmp/test.img 0 512
まとめると、今回の例では、 /tmp/test.img
は /dev/loop0 (7:0)
にアタッチされており、その中のパーティションが /dev/mapper/loop0p{1..3}
にアタッチされている事がわかる。
kpartx -a
でアタッチしたパーティションは kpartx -d
でデタッチできる
$ sudo kpartx -dv /tmp/test.img del devmap : loop0p3 del devmap : loop0p2 del devmap : loop0p1 loop deleted : /dev/loop0
万が一、kpartx -a
を何度もしたりアタッチしたファイルが何だったか忘れたなどの場合には、dmsetup
, losetup
を使って状況を確認、個別に削除できる。
補足
デバイスのmajor番号は、/proc/device
に一覧になっている。又、デバイス種ごとに番号は重複してもよいらしい。
$ egrep '^([A-C]| *[7] )' /proc/devices Character devices: 7 vcs Block devices: 7 loop
参考URL
- デバイスのmajor, minor numberについて https://www.ibm.com/docs/en/linux-on-systems?topic=hdaa-names-nodes-numbers
- Device mapperのわかりやすい解説 http://lc.linux.or.jp/lc2009/slide/T-02-slide.pdf
vmdkをdockerコンテナ内でマウントする
Ubuntu cloud imageの初期状態のディスク内容を見たかったので、vmdkを直接マウントすることにした。ホストにいろいろインストールするのは避けたいので、dockerコンテナ内でやることにした。guestmount
コマンドを使う。ホストはUbuntu 18.04。
Ubuntu cloud-imageダウンロード
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.ova $ tar tvf focal-server-cloudimg-amd64.ova -rw-r--r-- root/root 10760 2021-05-03 17:59 ubuntu-focal-20.04-cloudimg.ovf -rw-r--r-- root/root 213 2021-05-03 17:59 ubuntu-focal-20.04-cloudimg.mf -rw-r--r-- root/root 539142656 2021-05-03 17:59 ubuntu-focal-20.04-cloudimg.vmdk $ tar xf focal-server-cloudimg-amd64.ova ubuntu-focal-20.04-cloudimg.vmdk
コンテナ起動
fuseが必要になるので、/dev/fuseをマウントすると共に、必要な権限を追加する(cap-add, security-opt) (ref)$ docker run -it --rm -v $(pwd)/ubuntu-focal-20.04-cloudimg.vmdk:/tmp/ubuntu-focal-20.04-cloudimg.vmdk \ --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \ ubuntu:20.04 bash
パッケージインストール
ref# apt update && \ DEBIAN_FRONTEND=noninteractive apt install \ --no-install-recommends -y \ libguestfs-tools qemu-utils linux-image-generic
マウント
guestmount
は少し時間かかる。# guestmount -a /tmp/ubuntu-focal-20.04-cloudimg.vmdk -i --ro /mnt # ll /mnt total 88 drwxr-xr-x 19 root root 4096 May 3 21:45 ./ drwxr-xr-x 1 root root 4096 Jun 17 15:32 ../ lrwxrwxrwx 1 root root 7 May 3 21:39 bin -> usr/bin/ drwxr-xr-x 4 root root 4096 May 3 21:45 boot/ drwxr-xr-x 5 root root 4096 May 3 21:42 dev/ drwxr-xr-x 91 root root 4096 May 3 21:45 etc/ drwxr-xr-x 2 root root 4096 Apr 15 2020 home/ lrwxrwxrwx 1 root root 7 May 3 21:39 lib -> usr/lib/ lrwxrwxrwx 1 root root 9 May 3 21:39 lib32 -> usr/lib32/ lrwxrwxrwx 1 root root 9 May 3 21:39 lib64 -> usr/lib64/ lrwxrwxrwx 1 root root 10 May 3 21:39 libx32 -> usr/libx32/ drwx------ 2 root root 16384 May 3 21:45 lost+found/ drwxr-xr-x 2 root root 4096 May 3 21:39 media/ drwxr-xr-x 2 root root 4096 May 3 21:39 mnt/ drwxr-xr-x 2 root root 4096 May 3 21:39 opt/ drwxr-xr-x 2 root root 4096 Apr 15 2020 proc/ drwx------ 2 root root 4096 May 3 21:42 root/ drwxr-xr-x 3 root root 4096 May 3 21:42 run/ lrwxrwxrwx 1 root root 8 May 3 21:39 sbin -> usr/sbin/ drwxr-xr-x 6 root root 4096 May 3 21:42 snap/ drwxr-xr-x 2 root root 4096 May 3 21:39 srv/ drwxr-xr-x 2 root root 4096 Apr 15 2020 sys/ drwxrwxrwt 2 root root 4096 May 3 21:42 tmp/ drwxr-xr-x 15 root root 4096 May 3 21:41 usr/ drwxr-xr-x 13 root root 4096 May 3 21:42 var/
見れた!
docker stop や docker-compose downが遅いとき
docker-compose downがなんか遅いなーと思っていたら、entrypoint.sh
の書き方がよくなかった。 exec your-command
としないと、 SIGTERM
シグナルがプロセスに届かず、タイムアウト(10秒)を待って SIGKILL
される。
確認
例としてflaskアプリをgunicornで動かしてみる。
ファイルの準備
Dockerfile
from python:3.9-slim RUN pip install flask gunicorn COPY app.py /
app.py
from flask import Flask app = Flask(__name__) @app.route("/") def konnnichiha(): return "<p>Konnnichiha!</p>"
entrypoint.sh
#!/bin/bash gunicorn -w 1 -b 0.0.0.0:5000 app:app
entrypoint-exec.sh
#!/bin/bash exec gunicorn -w 1 -b 0.0.0.0:5000 app:app
テスト
コンテナを実行する。
$ chmod 755 entrypoint* $ docker build -t flask-test . $ docker run -d --name test-flask\ -v $(pwd)/entrypoint.sh:/entrypoint.sh\ --entrypoint '/entrypoint.sh'\ test-flask $ docker run -d --name test-flask-exec\ -v $(pwd)/entrypoint-exec.sh:/entrypoint.sh\ --entrypoint '/entrypoint.sh'\ test-flask
exec版はentrypoint.shがいなくなっている。
$ docker top test-flask UID PID PPID C STIME TTY TIME CMD root 935376 935355 0 23:26 ? 00:00:00 /bin/bash /entrypoint.sh root 935446 935376 0 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app root 935448 935446 0 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app $ docker top test-flask-exec UID PID PPID C STIME TTY TIME CMD root 935610 935591 2 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app root 935658 935610 1 23:26 ? 00:00:00 /usr/local/bin/python /usr/local/bin/gunicorn -w 1 -b 0.0.0.0:5000 app:app
終了してみると、execなしはタイムアウト待機分の10秒余計にかかる。
$ time docker stop test-flask test-flask real 0m10.228s user 0m0.019s sys 0m0.019s $ time docker stop test-flask-exec test-flask-exec real 0m0.323s user 0m0.020s sys 0m0.020s
execありはシグナルを受け取ってgracefulに終了している。
$ docker logs test-flask [2021-05-20 03:26:16 +0000] [7] [INFO] Starting gunicorn 20.1.0 [2021-05-20 03:26:16 +0000] [7] [INFO] Listening at: http://0.0.0.0:5000 (7) [2021-05-20 03:26:16 +0000] [7] [INFO] Using worker: sync [2021-05-20 03:26:16 +0000] [8] [INFO] Booting worker with pid: 8 $ docker logs test-flask-exec [2021-05-20 03:26:22 +0000] [1] [INFO] Starting gunicorn 20.1.0 [2021-05-20 03:26:22 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1) [2021-05-20 03:26:22 +0000] [1] [INFO] Using worker: sync [2021-05-20 03:26:22 +0000] [8] [INFO] Booting worker with pid: 8 [2021-05-20 03:27:02 +0000] [1] [INFO] Handling signal: term [2021-05-20 03:27:02 +0000] [8] [INFO] Worker exiting (pid: 8) [2021-05-20 03:27:02 +0000] [1] [INFO] Shutting down: Master
掃除。
$ docker rm test-flask test-flask-exec
しくみ
docker stopやdocker-compose downは、SIGTERM
を送ってデフォルトの10秒 (ref https://docs.docker.com/engine/reference/commandline/stop/) たっても終了しない場合は、 SIGKILL
が送信される。
PID 1が bash entrypoint.sh
だと、シグナルが子プロセスに届かない。 exec command
で command
を実行すると command
が PID 1になるので、シグナルを受け取れる。
exec command
は command
がシェルを置き換える。man bashより:
exec [-cl] [-a name] [command [arguments]] If command is specified, it replaces the shell. No new process is created. The arguments become the arguments to command. (略)
なお、シグナルを受け取った後でシェルで他の処理をしたいときは、exec
ではなく trap
する
ref: https://docs.docker.com/engine/reference/builder/#entrypoint
他にもコンテナの終了が遅いときは、CMD/ENTRYPOINT
の書き方とか、そもそも SIGTERM
では終了しないプロセスとか、いくつか原因になりそうな事がある。
ref: https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop
M1 MacでKensingtonのマウスでダブルクリックできない問題が直った(完全には直ってはいなかった)
追記(2021-06-19): KensingtonWorks 2.2.10 on macOS 11.4 で完全に直ったっぽい。やったー
KensingtonのSlimBladeを使っているのだけど、M1 Macにつなぐとダブルクリックできない症状があった。どんなに早くダブルクリックしても、少しゆっくりダブルクリックしても、ただのクリックになってしまう。OSのダブルクリックスピードの設定を調整しても変わらない。ドライバである KensingtonWorks をアンインストールすればダブルクリックできるようになるので、KensingtonWorks(とmacOSの相性)の問題と思われる。
ドライバを入れないと2ボタンマウスになってしまい、上の方の2つのボタンが機能しなくなってしまうので、何かしらのドライバを入れたい。SteerMouse を使ってみたけれど、画面をつかんでスクロールする機能(Smart Scrollのgrab scroll)と相性が悪く断念した。SteerMouseにもgrab scrollの機能があるけれど、左右ボタン同時押しの設定と同時には使えなかった。grab scrollをよく使うので、ダブルクリックする時だけ隣に置いてあるトラックパッドを使う事にした。
先日バージョン2.2.8が出てインストールしてみたらダブルクリックできるようになっていた。アンインストーラーを実行しても残るゴミまできれいにしてからインストールした。もしかしたらそのせいかもしれない。
実は2.2.6が出た時にも同じことをして、その時もダブルクリックはできるようになったけど、ドラッグできなくなる問題が発生した。なのでその時は2.2.5に戻した。ちなみにドラッグできなかった時は、マウスボタンを押した際には何もイベントが発行されず、ボタンを上げた時にボタン押下とボタン離すイベントが発行されていた。それではドラッグできないね。
その後出た2.2.7は、面倒だったのでアンインストールせずインストーラーを実行しただけだったんだけど、特に挙動に変化はなかった(ダブルクリックできない)。
そして、2.2.8でボタンに設定を割り当てていったら、2.2.6の時と同じようにドラッグできず、ミドルクリックもできなくなった。もしかしたら2.2.6の時から挙動は変わっていないかもしれない。リリースノートにも2.2.7以降それ系の修正について記載はない。
完全?にアンインストールする手順
なぜドライバが残るのか、、そしてドライバが残っている状態ではKensingtonのマウスが完全に使えなくなったので(カーソルが動かない)、別のメーカーのマウスが手元にないと辛い。キーボードだけでもなんとかはなるけど。
- KensingtonWorks Uninstaller.app でアンインストールする
Terminalで
systemextensionsctl list
を実行すると、com.kensington.tbwDKDriver が残っている。ただ再起動するだけでは消えない。% systemextensionsctl list 1 extension(s) --- com.apple.system_extension.driver_extension enabled active teamID bundleID (version) name [state] * * 293UQF7R4S com.kensington.tbwDKDriver (2.1.16/2.1.16) KensingtonWorks DriverKit driver 11 [activated enabled]
System Integrity Protection (SIP)を無効にする
- 電源を落とす
- 電源ボタン長押しで復旧モードで起動する
復旧モードでTerminalを開き、SIPを無効にする
$ csrutil disable
再起動
ドライバを消す
% systemextensionsctl uninstall 293UQF7R4S com.kensington.tbwDKDriver Success % systemextensionsctl list 1 extension(s) --- com.apple.system_extension.driver_extension enabled active teamID bundleID (version) name [state] * * 293UQF7R4S com.kensington.tbwDKDriver (2.1.16/2.1.16) KensingtonWorks DriverKit driver 11 [terminated waiting to uninstall on reboot]
kensingtonと名前のつくものを探して消す。設定系の他にも、/Application 下に .Kensington*.app というような名前のアプリケーションが残っていて気持ちわるい。
% sudo find /Applications /Library ~/Library -iname '*kensington*' 2>/dev/null | egrep -v 'Safari|CrashReporter' ... % sudo rm -rf ...
SIPを有効にする(復旧モードで
csrutil enable
する)
インストール
きれいに削除できたら、KensingtonWorksを普通にインストールする。インストーラを実行すると再起動を促されるので再起動する。再起動後設定してみたらミドルクリックできなかったが、もう一度再起動したらミドルクリックできるようになった。
ボタン同時押しの設定はしない方がいい
2.2.8 (おそらく2.2.6から) ボタン同時押しの設定をすると、ボタン単押しが機能しなくなる(機能する場合もある)。
自分としては、トラックボールに欲しい機能の優先順位は
- ドラッグやgrab scroll
- ショートカット(
Cmd+W
とCmd+[
) (同時押しに割り当て) - ダブルクリック
の順なので、2.2.5 に戻してダブルクリックを諦めるというのがよい。だけどまた綺麗にインストールしなおすのが面倒なので、とりあえず同時押しには何も割り当てない設定でしばらく過ごす事にする。ちなみにSmart Scrollのgrab scrollにはMiddle Click (button 3)を割り当てている。
まとめ
M1 MacにKensingtonWorksを入れるとダブルクリックできなくなる問題は、おそらくバージョン2.2.6(2021-01-20リリース)で直った。直らない時は残ったゴミを完全に消してからインストールするとよいかも。そしてバージョン2.2.8(2021-03-03リリース)では、ボタン同時押しのアクションを設定すると、そのボタンを片方だけ押したときのアクションが効かなくなる事がある(おそらくバージョン 2.2.6の頃から)。
もしかしたら今後のmacOSのバージョンアップで直る可能性もなくはない? 今のバージョンはmacOS Big Sur 11.2.3。
AlfredのWorkflowでWebサイトのDOMを操作したりする
とあるサイトの検索フォームをAlfredから直接実行したかった。検索用のAPIが用意されていないので、ページのformからsubmitしないといけない。
偶然、AppleScriptからSafariのページでJavaScriptを実行できる事を知ったので、以下のような流れで実現できると思い、調査開始。
- Alfredで検索文字列をAppleScriptに渡す
- AppleScriptで、
- Safariに検索ページを表示
do JavaScript
でDOM操作(formに入力してsubmit)
今回はじめてAppleScriptに挑戦し独特な文法に困惑していたところ、JavaScript (Java Script for Automation; JXA) でも書ける事がわかって、そちらに避難した。プログラミングが初めてならApple Scriptの方が英語の文法そのままなので分かりやすいのかもしれないけど、、AppleScriptはまた次の機会に。
Alfredで検索文字列をApple Scriptに渡す
Templates > Essentials > Keyword to Script を選び、名前やキーワードを適当に設定する。ScriptのLanguageに /usr/bin/osascript (JS)
を選ぶ。
スクリプト
Workflowの前ステップから渡ってきたキーワードを {query}
経由で受け取って処理を実行する。以下は例としてWikipediaのトップページから検索するようになっている。
function run(){ let url = 'https://en.wikipedia.org/wiki/Main_Page' let word = "{query}" let app = Application("Safari") app.includeStandardAdditions = true app.activate() let tab = loadUrlNewTab(app, url) waitPageLoad(app, tab) searchWord(app, tab, word) } function loadUrlNewTab(app, url){ let curWin = app.windows[0] curWin.tabs.push(app.Tab()) curWin.currentTab = curWin.tabs[-1] curWin.currentTab.url = url return curWin.currentTab } function waitPageLoad(app, tab){ ret = false do { ret = app.doJavaScript( "(function(){return document.readyState == 'complete'})()", { in: tab } ) delay(0.2) }while(ret==false) } function searchWord(app, tab, word){ app.doJavaScript( "(function(){\ let form = document.getElementById('searchform');\ let input = document.getElementById('searchInput');\ input.value = '" + word + "';\ form.submit();\ })()", {in: tab} ) }
作るときは、Script Editorで実行(Cmd + R
)して動きを確認しながら書いていった。
loadUrlNewTab
はもう少し簡潔に書けるような気がするけど、とりあえず愚直に。
waitPageLoad
は、これがないとページ読み込み完了より先にDOMを読みに行ってしまうので必要。
searchWord
中の getElementById
は、ページに合わせて適当に修正する。
使い方
キーワード スペース 検索語句
-> Enter
でタブが開いて、検索結果が表示される。
参考
ところでosascriptで使えるAppleScriptもJavaScriptも、あまり流行っていないのか情報が少なくて苦労した。流行りの言葉だと、どうやって書くんだろうと思って調べると、大抵StackOverflowなどでやりたい事が既に質問されていたりするのだけど、それがない。ただし大変ありがたいことに、情報をまとめてすばらしい解説ページを作ってくれている方々がいたおかげで、AppleScript / JXA がどんな感じかなんとなく掴む事ができた。ありがたやありがたや。