xrdpとmacOSの組み合わせでキーボード判定

xrdpの環境にmacOS端末でログインすると結構な確率でキーボード判定がおかしくなりませんか?

macOSでRDPするにはMicrosoft謹製のwindows.appを使うわけなのですが、どうもxrdpに送られてくるキーボードレイアウトの情報がおかしい、というか無いに等しい。

これがWindows機ならばある程度の種類を送ってきてくれるので、それに適したレイアウトで操作できるわけなのですが、Windows.appから送られてくる情報は常に0x00000000。

キーマップは0x00000411.tomlをコピーして0x00000000.tomlを作ってしまうことで誤魔化しは効くものの、どういうわけかusキー・日本語キーの判定は接続するたびに違っていたりで、少なくとも私の個人持ちのMacBookではUSキー判定されたり日本語キー判定されたり。

接続自体はスムーズなのに、いざ「@」や「¥」を入力しようとすると違うレイアウトになっていることに気づいて接続し直す羽目になるのがどうも憂鬱。

・というわけでもう作っちゃえ

というわけで、GUIで設定変更できるツールをさくさくっと。

ついでに接続してきた端末名や解像度と設定内容を記録しておいて、自動実行でも設定できるように。

[ keyboard-switcher.sh ]

#!/bin/bash

# --- 実行モード判定 ---
AUTO_MODE=false

if [[ "$1" == "--auto" ]]; then
    AUTO_MODE=true
fi

# --- 設定保存先・識別情報取得 ---
CONFIG_DIR="$HOME/.config/keyboard_layouts"
AUTOSTART_DIR="$HOME/.config/autostart"
AUTOSTART_FILE="$AUTOSTART_DIR/keyboard-switcher.desktop"
CURRENT_UID=$(id -u)

mkdir -p "$CONFIG_DIR"
mkdir -p "$AUTOSTART_DIR"

resolution=$(xdpyinfo | grep dimensions | awk '{print $2}')
hostname=$(xprop -root | grep Xdpy | cut -d\" -f2 | cut -d@ -f2)

[[ -z "$resolution" ]] && resolution="unknown_resolution"
[[ -z "$hostname" ]] && hostname="unknown_host"

safe_id=$(echo "${hostname}_${resolution}" | tr -cs '[:alnum:]_-' '_')
CONFIG_FILE="$CONFIG_DIR/${safe_id}.conf"

# --- 現在のキーボードレイアウトを取得 ---
current_layout=$(setxkbmap -query | grep layout | awk '{print $2}')

# --- 自動モード: GUIなしで適用する ---
if $AUTO_MODE; then
    if [[ -f "$CONFIG_FILE" ]]; then
        saved_layout=$(cat "$CONFIG_FILE")
        if [[ "$saved_layout" == "us" || "$saved_layout" == "jp" ]]; then
            setxkbmap "$saved_layout"
            echo "[INFO] 自動実行: '${saved_layout}' を適用しました (${safe_id})"
            if ! ps -u $CURRENT_UID -o comm | grep -q "^fcitx5$"; then
               fcitx5 -d
            fi
            exit 0
        fi
    fi
    echo "[INFO] 自動実行: 設定が見つからなかったため変更なし"
    exit 0
fi

# --- 手動実行: GUIで選択 ---
us_selected=FALSE
jp_selected=FALSE

if [[ -f "$CONFIG_FILE" ]]; then
    saved_layout=$(cat "$CONFIG_FILE")
    if [[ "$saved_layout" == "us" ]]; then
        us_selected=TRUE
    elif [[ "$saved_layout" == "jp" ]]; then
        jp_selected=TRUE
    fi
else
    if [[ "$current_layout" == "jp" ]]; then
        jp_selected=TRUE
    else
        us_selected=TRUE
    fi
fi

layout=$(GSK_RENDERER=cairo zenity --list \
    --title="キーボードレイアウトの選択" \
    --text="使用するキーボードレイアウトを選んでください\n(端末: ${hostname}, 解像度: ${resolution})" \
    --radiolist \
    --column="選択" --column="レイアウト" \
    $us_selected "us (英語)" \
    $jp_selected "jp (日本語)" \
    FALSE "削除(設定をリセット)")

# --- ユーザーが削除を選択した場合 ---
if [[ "$layout" == "削除(設定をリセット)" ]]; then
    GSK_RENDERER=cairo zenity --question --title="設定削除" --text="自動実行設定とキーボード設定を削除しますか?"
    if [[ $? -eq 0 ]]; then
        rm -f "$AUTOSTART_FILE"
        rm -rf "$CONFIG_DIR"
        GSK_RENDERER=cairo zenity --info --text="削除が完了しました。"
        echo "[INFO] 削除が完了しました。"
    else
        echo "[INFO] 削除をキャンセルしました。"
    fi
    exit 0
fi

# --- キーボードレイアウトの適用 ---
if [[ "$layout" == "us (英語)" ]]; then
    setxkbmap us
    echo "us" > "$CONFIG_FILE"
    if ! ps -u $CURRENT_UID -o comm | grep -q "^fcitx5$"; then
        fcitx5 -d
    fi
elif [[ "$layout" == "jp (日本語)" ]]; then
    setxkbmap jp
    echo "jp" > "$CONFIG_FILE"
    if ! ps -u $CURRENT_UID -o comm | grep -q "^fcitx5$"; then
        fcitx5 -d
    fi
else
    GSK_RENDERER=cairo zenity --info --text="レイアウトが選択されませんでした。"
    exit 1
fi

# --- 自動起動用のデスクトップエントリーを作成 ---
# Exec行はスクリプト配置により適宜書き換え /usr/local/bin/ 推奨
cat <<EOF > "$AUTOSTART_FILE"
[Desktop Entry]
Type=Application
Exec=$HOME/keyboard-switcher.sh --auto
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
EOF

GSK_RENDERER=cairo zenity --info --text="設定内容を保存し自動実行を設定しました (${AUTOSTART_FILE})"
echo "[INFO] 設定内容を保存し自動実行を設定しました (${AUTOSTART_FILE})"

と、こんな感じで。

配置はどこでも良いけれど、実行するためには chmod +x keyboard-switcher.sh を忘れずに。

・こまかいところ

仕様的には

・自動実行時+保存情報あり:保存された情報で設定して静かに終了
・自動実行時+保存情報なし:なにもせず静かに終了
・手動実行時+保存情報あり:GUIを開き保存された情報を表示
・手動実行時+保存情報なし:GUIを開く

と言う感じで、保存情報は

~/.config/keyboard_layouts/ 以下に「ホスト名_解像度.conf」の設定ファイルを作って記憶。

ただ、実際に動かしてみるとhost名が取得できなくて unknown_host_1440x900_.confみたいな形になってしまいましたが、実用上支障なしということで。

書き忘れるところだった。
自動実行時というのをどう判定するねん、という点は –auto というオプションを付けることで判断します。

・せっかく作ったならメニューへ

せっかくGUIなツールなのに起動がいちいちCLIではもったいないので、デスクトップエントリーに以下のように追加。

[ /usr/share/applications/keyboard-switcher.desktop ]

[Desktop Entry]
Name=キーボードレイアウト切替
Exec=/usr/local/bin/keyboard-switcher.sh ←ここは環境にあわせてフルpathを!
Type=Application
Terminal=false
Icon=input-keyboard
Categories=Settings;Utility;

これでちょっと楽ができるかな?


安心安全安価なSDカードデータ復元・HDDデータ復元は
長年の信頼と実績の『株式会社パソコントラブル救助隊』へ。
https://hqsecure.net/

iCloud Driveの同期が遅い時や除外表示の時の対処方法

Always keep on this device

いままでにも何度か挑戦しては撤退を繰り返していた各種のクラウドドライブの活用ですが、昨年末に再トライするしはじめました。

もうタイトルに書いちゃってますが、今回候補にあげたのは
・One Drive (現在の契約容量 1TB)
・iCloud Drive (現在の契約容量 200GB)
の2つがもともとの候補でした。

環境としてはWindowsパソコンに事務作業用の主だったファイルがあり、外出先ではMacやらiPhoneやらiPadという環境なのでどちらでも似たような環境構築はできるかなと。
実際、現在のWindows上のiCloud DriveもOne Driveと同じCloud Syncエンジンを使用しているとのことなのでシステム寄りの部分でも似たようなものということになります。

ただ、過去に何度かトライしている段階では試験運用中に最新ファイルが消失したりなにかと不整合が起こって、結局どれが最新ファイルなのかずっと人間側が追いかけねばならないというとてもめんどくさいことになった記憶が二の足を踏ませ続けてくれます。

■さてどちらを選ぶ?

One DriveもiCloud Driveも使い方は大差なく、同期したいフォルダを設定してあげるだけで自動的にクラウドとの同期がはじまります。(きっとたいていの場合には何も気にすることなくすぐにクラウド環境を使いこなせるようになることでしょう)

手元の環境でも双方とも試してみたんですが、結局のところ後述するようなトラブルを先に解消できたのがiCloud Drive側だったというだけの理由で今回はiCloud Driveでの運用に一本化することにしました。

■今回、運用にあたって工夫したこと

まずは「クラウドにしかファイルが存在しない」という状況にしてしまわないように設定して運用します。
そのためにまず同期を行うフォルダに「Always keep on this device 」にチェックを入れておくことで中身のファイル丸ごとWindows側にファイル実体が保存された状態で維持されるようになります。

同期が完了したフォルダやファイルの「状態」プロパティ表示にはチェックマークがつきますが、このチェックマークのアイコンが緑地に白のチェックマークになります(通常、クラウドにしか最新ファイルを維持しない設定の場合には白地に緑のチェックマークになります)

状態表示アイコンの違い

ちなみに「状態」プロパティが”雲形”のアイコンの場合にはクラウドにしかファイルが無い状態、”丸い矢印”のアイコンの場合には同期処理中という意味になります。

■でもクラウドが信頼できない場合

ローカルストレージにも常にファイルが存在している状態にしたものの、これでもまだクラウドの不整合などで最新ファイルが行方不明になったり古いファイルが壊れたりした場合の手間を回避したい場合、ローカル内でさらに別フォルダへとバックアップコピーをとっておくこともできます。

■もっとも手軽な手順はXCOPYコマンドを使用すること

昔ながらのバッチファイルに仕上げて適当なタイミングで自動実行するようにしてもよし、気づいたときに手動実行してもよし、ともなくフォルダ丸ごと任意の時点のコピーが手元に存在するという安心感は大きいものです。(実際にそのコピーに頼るシーンが来ないことを祈りますが)

XCOPYコマンドを使って保存しておきたいフォルダを丸ごと同期外のフォルダにコピーしておく手が使えます。ただし「Always keep on this device」のチェックが有効で全てのファイルがローカル上に存在しているということが大前提になります。もし同期中などでファイルが存在しない場合には予期せぬ動作になってしまうこともあり得るので実行タイミングにはご注意を。

xcopy “sourcedir” “destinationdir” /d /e /h

上述のコマンドのうち”sourcedir”はiCloud Driveで同期対象にしているフォルダ名を記述し、”destinationdir”にはバックアップコピー用に別のドライブを指定しています。

■なかなか同期が終わらない

iCloud Driveへの同期をはじめたのが12月1日。
その時点で同期ファイル数はおよそ60,000ファイルほど。容量としてはざっと40GB前後といったところでしたのでのんびりと同期が済むのを待ちます。ひたすら待ちます。

・・・まちました、10日ほど。

・・・さらに待ちました、通算20日ほど。

ですが、待てど暮らせど終わらない。
残りの同期数を見ても少しずつしか減らない。逆にすこしずつは減っているので処理が止まっているわけでもないという面倒な状態へと陥りつつある予感がします。

アップデート中

■待っていてもなかなか進まないので

同期ができていないファイルを一つ一つチェックしてみても規則性はまったく見当たらないものの、なんとかできそうな予感がしてきました。

まずはブラウザでiCloud.comにアクセスし、ブラウザを経由してiCloud Driveへと同期できていないファイルを1つずつ手動でアップロードします。
残念ながらフォルダごと手動でアップすることはできないようなので、ファイル単位で少しずつ試すしかありません。(幸いなことにディレクトリでコピーされていないものはなかったので、その中身の数ファイルをアップするという作業だけで済んだのはラッキーだったのかもしれません)

そうすると、機嫌の良いときはブラウザでアップしたファイルが即座にExplorer上にも反映されて同期マークがついたりするのですが、機嫌が悪いときはいつまでたっても反映されなかったり、あげくのはてには同期マークが消え「除外」となってたり・・・

状態表示が「除外」になる件についてはググってもろくな回答が得られなかったのでひとまず諦めてそのまま放置して様子をみることにしましたが、気がつけばいつのまにか同期されていたので何もせず焦らずひたすら待てばよいのでしょう、きっと。

■ようやく同期完了

やっとおわった!
開始からほぼ1ヶ月になる12月の末、ふと同期のステータスを見てみるとアップデート完了と表示されている(^^)
フォルダ内のチェックマーク表示をみてもすべて同期完了をしめす表示になっているので一安心。それにしても同期にはかなりの時間がかかるものなのだなあと思い知らされた出来事なのでありました。

アップデート完了

■その後

同期が完了してそこそこリアルタイムでファイルの更新ができるようになったのは宜しいんですが、いつの間にかMacBookのFinderがバッテリーを激しく消耗するようになってしまってました。

ただ、これもMacの再起動ですぐに収まってくれたので一時的な重たい処理があったのかもしれません。あまり神経質になるのもよろしくないですね。気にせず気持ちよく仕事しましょう。


消えちゃった動画や写真データの復元は
安心安全な『株式会社パソコントラブル救助隊』へ。
https://hqsecure.net/