Windows 10 Pro 1903(19H1) にアップデートした話

Surface Pro 5 の OS を win10pro 1903 にアップデートした。1903 は、大型アップデートである。インストール中は、何回か再起動が行われる。

良いことがあった

  • Vivaldiウィンドウ最大化→元に戻すで位置がずれなくなった
  • ファイアウォール設定でWSLのコマンド(ssh,curlなど)ごとに通信ルールを設定できるようになった

悪いこともあった

  • キーボード配列がリセットされたんでいつもの処置を施した
  • 画面の明るさ(コントラスト)が勝手に変わる現象が再発したんで対処した

詳細は以下

Vivaldiウィンドウ位置ずれ解決

いつの頃からか、Web ブラウザ Vivaldi のウィンドウの位置がおかしくなる現象が定着し、慢性的なストレスになっていた。が、今回の 1903 アップデートにより、解決した。

具体的には、通常の大きさのウィンドウを最大化し、元に戻すと、ウィンドウの位置が下にズレるという謎の挙動があった。どのくらいズレるかというと、タイトルバーの高さほどの距離だ。Vivaldi 自体のアップデートは 1 年で 5 回くらいのペースで実行していたが、その際に解決することはなかった。

Windows をアップデートすることによって、謎の挙動が起こらなくなり、今回の問題は解決した。とても良いことだ。

WSLコマンドがファイアウォールで制御可能に

WSL というのは、cmd.exebash を起動できる機能だ。黒い画面の中で linux コマンドが使える。その中には、例えば、以下のコマンドがある。

  • curl --- URL を指定して実行すると、レスポンスヘッダやボディなんかを表示できる
  • ssh --- git push する時なんかに使う

要するに、WSL には、インターネット通信を行うアプリが色々と入っているわけだ。WSL が初登場したのは 2017 年だ。今から 2 年前だ。当時は、curl 等のコマンドがインターネット通信を試みた際、その通信のみを Windows ファイアウォールにブロックしてもらうようにする方法が存在しなかった。ルールの作成がなぜか不可能だったのだ。

この問題とは、2 年間ほど付き合うことになった。WSL からのインターネット通信を「許可」するルールも「ブロック」するルールも作成できなかった。だから、通信する時だけ、手動でファイアウォールをオフにして、通信が終わったら、再度、手動でオンにするという、煩わしい作業を行っていた。

しかし、ついに、この度、Windows 10 の 1903 アップデートにより、この問題が解決した。すなわち、WSL のインターネット通信についてのファイアウォールルールを作成・適用できるようになったのである。しかも、その実態は、コマンドごとのものなのだ。つまり、curl を許可orブロックするルールを作成できたり、ssh を許可orブロックするルールを作成できたりする。とても良いことだ。

キーボード配列がリセットされてしまった…

大型アップデートでは毎度おなじみ、キーボード配列リセット。HHKB が英語配列になってしまったので、公式の設定ツール HHKBCNG.exe日本語配列に設定し直した。さらに、ChgKey.exe by (C)2002 Satoshi で Ctrl キーの入れ替えなどをやり直し。再起動。OK, 問題なし。AutoHotkey も正常に動いている。

画面の明るさ(コントラスト)が勝手に変わる機能をオフにする方法

Surface Pro 5 には、現在画面に表示している明るい色や暗い色の面積などをリアルタイムに監視して、画面のコントラストを自動で変更する機能が付いている。これは、設定アプリにある「照明が変化した場合に明るさを自動的に調整する」機能ではない。Web で調べたところ、他の Surface やノート PC にもある おせっかい機能だ。

最も簡単で安全な方法は、Intel Graphics の設定からオフにするというものである。しかし、実は、これをやるのは手間がかかる。というのも、Intel Graphics の設定画面はデフォルトでは表示できないため、Intel 公式サイトのドライバーをダウンロードしてインストールしなければならないのだ。

少し、危険なにおいのする方法もある。今回は、その方法を実行した。すなわち、regedit.exe で、レジストリの以下のキーの値をいじる。

  • HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0001\FeatureTestControl

上記のキーを、デフォルトの 9240 から 9250 へと変更して再起動する。

情報元は以下のサイトである。

How to disable adaptive contrast on the Surface Pro and Surface Book https://www.windowscentral.com/how-disable-adapative-contrast-surface

この情報は正しかった。リアルタイム自動コントラスト変更機能がオフになり、問題が解決した。良いことだ。

YouTube ライブのチャットを動画上に表示する Chrome 拡張機能

f:id:ray0mg:20181020105924p:plain

ダウンロード

YouTube ライブのチャットを動画上に表示する Vivaldi 用の拡張機能(UserJS)(以下、「この拡張機能」「本 UserJS」と呼ぶ)をつくった。そのコードについての技術的なメモを書き留めておく。

manifest.json

そもそもこの拡張機能は、「パッケージ化されていない拡張機能」である。ブラウザの設定で、拡張機能デベロッパーモードをオンにして、「読み込む」ボタン押下、manifest.json のあるフォルダを選択する。

manifest.json の中身は、以下のようになっている。

{
  "manifest_version": 2,
  "name": "UserJS",
  "version": "1.00",
  "content_scripts": [
    {
      "js": ["ytchat.js"],
      "run_at": "document_start",
      "matches": ["https://www.youtube.com/*"],
      "all_frames": true
    }
  ]
}

ここで重要なのが、"all_frames": true だ。YouTube のチャット領域は iframe 内に描画されている。all_frames:true を指定することによってのみ、本 UserJS の要である ytchat.js は iframe 内の空間でも実行されるようになる。それによって、ytchat.js は、動画上に表示するためのチャットの内容を取得することができるようになるのだ。

ytchat.js

manifest.json と同じフォルダに配置されている ytchat.js の中身は、大きく分けると 4 つのパートに分けることができる。

  • チャット内容を取得
  • 動画上に表示するチャットの表示方法を決める
  • チャットを動画上に表示
  • 本 UserJS オン・オフ切り替えボタンの表示

まずは「チャット内容を取得」について説明する。

if (location.href.startsWith("https://www.youtube.com/live_chat")) (function() {
  if (parent === self) return;
  // Program: Chat data getter
  var mo = new MutationObserver(function(records) {
    for (var rcd of records) {
      for (var nd of rcd.addedNodes) {
        var body = nd.querySelector("#message");
        if (!body) continue;
        var rawtext = "";
        for (var phrase of body.childNodes) {
          rawtext += phrase.alt || phrase.textContent;
        }
        var msg = {
          type: "chat-text",
          data: rawtext
        };
        parent.postMessage(JSON.stringify(msg), "https://www.youtube.com");
      }
    }
  });
  addEventListener("DOMContentLoaded", function() {
    mo.observe(document.querySelector("#items.yt-live-chat-item-list-renderer"),
      {childList:true});
  });
})();

本 UserJS のこの部分に限っては、iframe 内のチャット領域で実行される。

初挑戦

MutationObserver という珍しい API を使っている。近年、HTML へのノード追加・変更なんかを検出する DOMNodeInserted とかの、DOM Events の機能が、ブラウザから削除された。ゆえに、この新しい API を使うしかない。

ここで行っているのは、YouTube チャット領域を監視し、新しいチャットが書き込まれた際に、その本文だけを抜き出し、親フレームに送信することだ。

YouTube さんよぉ…

現時点の YouTube の仕様では、#items.yt-live-chat-item-list-renderer に該当する要素に対して、チャットへの新しい書き込みが、子要素として追加されていく。

YouTube の HTML には、なぜかは知らないが、同一の id を持つ要素が、大量に存在する。とんでもない仕様書違反行為である。こんな状況では document.getElementById() が意味を成さないので、#id.class で要素を泥臭く絞り込む必要があるのだ。

チャット本文取得

MutationObserver 内で for ループが二重になっているのが煩わしいが、避けられない。

rawtext += phrase.alt || phrase.textContent この部分では、チャットテキスト こんにちは あるいは、チャット絵文字 <img alt="💛" src="heart.png"> の alt を取得する。その他の要素は考慮していないが、おそらく問題はない。どんな要素の textContent も空文字か文字列である。もし、空文字ならば全く問題ない。逆に、たとえ、文字列だとしても、それはきっとチャット本文中に表示される文字列に違いないのであるから、問題ないはずなのだ。

表示

さて、次に、「動画上に表示するチャットの表示方法を決める」について。

// UI: Chat box on video
const container_id = "chatbox_on_video";
var addChatbox = function() {
  var video = document.querySelector(".html5-video-player");
  if (!video) return false;
  else if (document.getElementById(container_id)) return true;
  var container = document.createElement("pre");
  container.id = container_id;
  container.style.position = "relative";
  container.style.zIndex = "50";
  container.style.width = "0";
  container.style.opacity = "0.7";
  container.style.fontSize = "x-large";
  container.style.textShadow = "1px 1px 1px black";
  container.style.color = "white";
  video.appendChild(container);
  return true;
};
setInterval(addChatbox, 1000);

動画上に覆いかぶせるように、<pre> を relative で左上に配置している。z-index をそこそこ大きい数値にして、動画そのものに覆い隠されないようにしている。width:0 によって、動画を覆い尽くさないようにすることで、動画へのクリックイベントが動画に届くようにしている。幅ゼロではあるが、そこへ追加する文字列はきちんと表示される。

さて、次に「チャットを動画上に表示」について。

// Program: Chat render/updater
var doing = false;
addEventListener("message", function(event) {
  var container = document.getElementById(container_id);
  if (!doing || !container) return;
  if (event.origin === "https://www.youtube.com") {
    var json = JSON.parse(event.data);
    if (json.type === "chat-text") {
      var lim = 10;
      var list = container.textContent.split("\n");
      if (list.length > lim) {
        container.textContent = list.slice(-lim).join("\n");
      }
      container.textContent += json.data + "\n";
    }
  }
});

iframe 内のチャット領域から送信したチャット本文メッセージをこの部分で処理している。直近 10 件のチャット書き込みを動画上に表示する。

ON/OFF

最後に、「本 UserJS オン・オフ切り替えボタンの表示」について。

// UI: Controller (on,off)
const ctrlbtn_id = "chat_on_video_switch";
var switchUI = function(doing) {
  var container = document.getElementById(container_id);
  if (container) container.hidden = !doing;
  var btn = document.getElementById(ctrlbtn_id);
  if (btn) btn.style.opacity = 0.5 + doing;
};
var addCtrlBtn = function() {
  var dst = document.querySelector(".ytp-right-controls");
  if (!dst) return false;
  else if (document.getElementById(ctrlbtn_id)) return true;
  var imitee = dst.querySelector("button");
  if (!imitee) return false;
  var btn = imitee.cloneNode(true);
  btn.addEventListener("click", function() {
    doing = !doing;
    switchUI(doing);
  });
  btn.id = ctrlbtn_id;
  btn.style = "";
  btn.title = "chat overlay";
  btn.firstChild.innerHTML = `<text y="24" x="7" font-size="18">💭</text>`;
  dst.insertBefore(btn, dst.firstChild);
  switchUI(doing);
  return true;
};
setInterval(addCtrlBtn, 1000);

動画の下部に表示されるコントロール・ボタン部へ、独自ボタンを追加する。独自ボタンは、押すと、本 UserJS のオン・オフのスイッチが行われる。独自ボタンは、もともと YouTube にある本家ボタンをクローンしてつくっている。本家ボタンはそのアイコンの描画方法が SVG であり、それに倣って、SVG で「ふきだし」の絵文字を描画している。そうせずに生の文字列にすると、なぜか、ボタンの位置が上から 20px ほどズレて見づらく、押しづらくなる。

userjs/ytchat.js at 43cd6eef2fd8fb427a78b6eff0d0ebd659b69911 · 0mg/userjs · GitHub

Surface Pro 5 感想

Surface Pro 5 とは、2017年-2018年ごろに発売された Surface Pro のことだ。近頃、一か月間ほど使用している。

UEFI設定で本体スピーカーOFFできる

UEFI 設定画面は、電源が切ってある状態で、電源オンにする際、「音量+」ボタンを押しっぱなしにしつづけると入ることができる。

けど描画がバグることあり

設定画面には、本体カメラや本体スピーカーをオフにできる項目がある。使わないものはオフにしている。ただし、どれか 1 つでもオフにしていると、どういう因果かは不明だが、Windows サインイン画面で、描画がバグってしまうことがある。具体的には、ハイコントラスト表示に軽微な歪みを加えたようなものだ。一応、デスクトップアイコンなどは視認可能で、操作可能だが、色がめちゃくちゃで見ていられない。

この状態を解消するには、一度サインアウトしてから、サインインし直す。

オーディオインターフェース使える

Surface Pro 5 には、バスパワータイプの USB ポートハブ「Anker A7516」を接続し、2 つのデバイスを常時接続している。

どちらのデバイスも正常に動作している。非常に快適だ。電圧が足りなくなったことは一度もない。

今まで使っていた PC「Aspire V5-171」は、US-122mkII との相性が最高ではなく、たとえば、ある条件を満たすと、スリープ復帰後にブルースクリーンが生じたり、再起動しないと US-122mkII が動作しなくなったりしていた。

Surface Pro 5 と US-122mkII の相性は最高で、問題が起こったことは一度もない。

Bluetooth マウス使える

他に接続しているデバイスは、マウス「Logicool M336」だ。正常に動作している。こちらは、Bluetooth タイプなので、USB ポートを塞ぐことはない。

BitLocker 回復キーは控えよう

Windows 10 Pro では BitLocker が使える。BitLocker による保護を有効にする際は、「回復キー」を PC の外部(SD や紙など)に、確実に保存しておく必要がある。UEFI 設定を変更した後、Windows 起動時に、一度だけ、回復キーを求められたことがある。

デカすぎる解像度だが安心

画面の大きさは 12.3 インチで、解像度は 2736x1824px だ。等倍表示すると、あまりにもドットが小さく、1px の線は産毛のような細さだ。黒色なのに灰色に感じられるほどだ。ただし、表示スケールの設定がデフォルトで 200% になっており、Explorer のアイコンやフリーソフトGUI など、ほぼあらゆるものが 2 倍に拡大されて表示されている。

Web ブラウザ JavaScript window.screen オブジェクトにおいても、availWidth, availHeight がそれぞれ実際値の半分になっており、1368x912px と見なしたレンダリングをしてくれている。

古いソフトウェアの中には、この表示スケール拡大で良好に変換されないものもあり、ゴマ粒のように小さすぎて読むのが難しいフォントで表示されてしまうこともある。そういう時は、そのソフトのファイルプロパティで、「高 DPI スケール設定の上書き」を行えば、概ね対応できる。