Mac のマウスで Windows を操作する。ソフトウェアKVM「crossMouse」を Rustツールで公開

はじめに
当てはまるのは私たちのようなエンジニアや一部のギークな人たちに限定されるかと思いますが、Mac と Windows を並べて使っているという方向けの便利ツールのご紹介です。
机の上にマウスとキーボードが 2 セット並び、切り替えるたびに手を持ち替え、コピーしたテキストは Slack の自分宛て DM を経由して移動する——この地味なストレスを消すために、社内向けのソフトウェア KVM「crossMouse」を Rust で開発しました。
本記事では、なぜ既存ツールではなく自作という選択をしたのか、どんな機能を実装したのか、同じ「2 台持ち」の環境で消耗している方の参考になれば幸いです。
crossMouse とは
crossMouse とは、Mac に接続した 1 組のマウス・キーボードで、隣に置いた Windows PC をそのまま操作できるソフトウェア KVM ツールです。Mac の画面端をカーソルが越えると、そのまま Windows の画面にカーソルが現れ、以降の入力はすべて Windows へ転送されます。逆方向に画面端を越えれば Mac に戻ります。ケーブルの差し替えもボタンの切り替えも不要で、操作対象の切り替えは「カーソルを動かす」だけです。
Windowsのマウスを握ればこれまで通り操作をすることができるため、コントロールを握って離さない、といったこともありません。
| 項目 | 内容 |
|---|---|
| できること | Mac のマウス・キーボードで Windows PC を操作 クリップボードの双方向同期 共有クリップボード履歴 暗号化通信 |
| 構成 | Mac 側サーバ + Windows 側クライアント(LAN 経由) |
| 実装言語 | Rust(依存を最小化した自作 FFI) |
| 切り替え操作 | 画面端をカーソルが越えるだけ(ホットキー不要) |
| セキュリティ | Noise プロトコルによる通信暗号化(パスフレーズ方式) |
| 設定ファイル | なし(コマンドライン引数のみ) |
背景:なぜ自作したのか?
2 台持ちの「入力デバイス 2 セット問題」
開発は Mac、動作確認や特定業務は Windows、という環境は珍しくありません。しかし机の上でマウスとキーボードが 2 セット占有されるのは、物理的にも認知的にもコストです。手を持ち替えた瞬間に思考が途切れますし、そもそも作業スペースが狭くなります。
加えて厄介なのが、テキストの移動です。Mac でコピーした URL を Windows に貼りたいだけなのに、チャットツールや共有フォルダを経由する。1 回あたり数十秒の作業でも、1 日に何度も発生すれば無視できない時間になります。
既存のソフトウェア KVM に感じた惜しさ
この領域には Synergy や Barrier といった定番ツールがあり、実際に長く使われています。ただ、自分たちの用途で使ってみると、次の点が惜しく感じました。
- 機能が多く、設定項目も多い。「Mac 1 台と Windows 1 台をつなぐ」だけの用途にはオーバースペックで、GUI の設定画面を往復することになる
- クリップボード連携が「ついで」の機能である。同期はできても、履歴を跨いで使うといった体験までは踏み込んでいない
- ブラックボックスである。挙動が想定と違ったとき、原因を追って自分で直すことができない
「必要な機能だけを、自分たちで直せる形で持つ」。この方針なら、むしろ作ったほうが早いのではないか——という判断で開発が始まりました。結果として、後述する「共有クリップボード履歴」という、既存ツールにはない体験まで作り込むことができました。
特徴 1:画面端を越えるだけ。設定ファイルはゼロ
crossMouse の操作体系は極端にシンプルです。覚えるべき操作は「カーソルを画面端まで動かす」ことだけで、切り替え用のホットキーはありません。Windows PC を Mac の左に置いているなら、カーソルを左端まで押し込むと Windows 側に移ります。
Windows 側のクライアントは、起動すると LAN に UDP ブロードキャストを投げて Mac 側のサーバを自動的に見つけます。IP アドレスを調べて入力する初期設定は、基本的に不要です。
また、リモート操作中に何かが固まっても戻れなくならないよう、フェイルセーフも用意しました。押せば即座にローカル操作へ復帰します。接続が切れた場合も自動でローカルに戻り、Windows 側にキーが押しっぱなしで残らないよう、押下中のキーを解放するイベントを合成してから離脱します。
特徴 2:両側マルチモニターに対応
実際のデスク環境は「Mac に外部ディスプレイ 1 枚、Windows にもディスプレイ 2 枚」といった構成になりがちです。crossMouse は両側のマルチモニターに対応しており、Mac 側は全ディスプレイを合わせた一番外側の端で越境し、Windows 側は仮想デスクトップ全体(全モニター)を自由に移動できます。
地味に効いているのが縦方向のマッピングです。境界を接するモニター同士で高さの比率を計算して座標を変換するため、解像度が違う画面を並べていても、カーソルは「同じ高さ」から出てきます。画面端の上のほうで越えたのに、反対側では中央から現れる、といった違和感がありません。
特徴 3:クリップボードは「常時」双方向同期
クリップボード同期は、画面をまたいだ瞬間だけ実行する実装が一般的です。crossMouse では途中から方針を変え、約 0.3 秒間隔で両マシンのクリップボードを監視し、変化した時点で即座に相手側へ反映する常時同期にしました。
ポイントは監視のコストです。毎回クリップボードの本文を読み出すのは無駄が多いため、macOS では NSPasteboard の changeCount、Windows では GetClipboardSequenceNumber という「変更回数のカウンタ」だけを比較し、数値が変わったときだけ本文を読む方式にしています。おかげで常時監視でも負荷はほぼ発生しません。
結果として、Mac でコピーしたテキストは、カーソルを動かすまでもなく Windows 側に届いています。「コピーしてから移動する」という手順を意識しなくてよくなったのは、想像以上に快適でした。
特徴 4:2 台で共有するクリップボード履歴(Clippy 風)

今回いちばん作り込んだのが、この機能です。Mac と Windows の両方でコピーしたテキストが、1 本の共有履歴に最大 50 件まで自動で蓄積されます。
呼び出しは Cmd + Shift + V。カーソルがある側の画面の、カーソル位置にミニメニューが開きます。Mac 側にいるときは Mac の画面に、Windows を操作中なら Windows の画面に出るので、視線を動かす必要がありません。
50 件を 1 画面に並べると縦に長くなりすぎるため、表示は1 ページ 10 件のページ単位にしました。操作は次のとおりです。
| キー | 動作 |
|---|---|
| ↑ ↓ | ページ内で選択を移動(端で折り返し) |
| ← → / PageUp・PageDown | ページを切り替え |
| 0 〜 9 | その番号の項目を即ペースト(各行の先頭に番号を表示) |
| Enter | 選択中の項目をペースト |
| Esc / 再度 Cmd + Shift + V | メニューを閉じる |
使いやすさのポイントは「選んだら即ペースト」という 1 アクション化です。Enter または数字キーを押した瞬間にメニューが閉じ、貼り付けまで完了します。
もう一点、地味ですが重要なのがポップアップがフォーカスを奪わないことです。メニューはキーボード操作専用の非アクティブウィンドウとして表示され、貼り付け先のアプリはアクティブなまま。だからメニューを閉じた次の瞬間に、狙ったエディタへ確実にペーストできます。メニュー表示中に関係ないキーを押した場合は、メニューが閉じてそのキー入力はアプリにそのまま届くので、入力の邪魔にもなりません。
特徴 5:通信を暗号化できる
ソフトウェア KVM は、性質上キーボード入力がそのままネットワークを流れます。パスワードを打てば、それも流れます。LAN 内だから安全、という前提に寄りかかるのは避けたいところです。
そこで crossMouse では、Noise プロトコル(X25519 + ChaCha20-Poly1305 + BLAKE2s)による暗号化を実装しました。両側で同じパスフレーズを指定するだけで、以降の全通信が暗号化されます。パスフレーズを知らない相手はハンドシェイクを通過できないため、認証も同時に成立します(前方秘匿性あり、改ざんやリプレイは切断として検出)。
パスフレーズはコマンドライン引数でも渡せますが、シェル履歴やプロセス一覧に残るのは避けたいので、指定がなければ起動時に非表示入力で尋ねる方式を標準にしています。
技術構成
常駐して全キー入力を扱うツールなので、「軽いこと」「余計な依存を持たないこと」を最優先に技術選定しました。
| カテゴリ | 選定技術 | 理由 |
|---|---|---|
| 言語 | Rust | 常駐プロセスでの安定性、ランタイム不要の単一バイナリ、Windows へのクロスコンパイルが容易 |
| 構成 | 3 クレート構成(protocol / server-mac / client-win) | ワイヤプロトコルとキーマップを共有し、両バイナリの実装ズレを防ぐ |
| 入力捕捉(Mac) | CGEventTap | OS レベルで入力を横取りでき、リモート中は Mac 側にイベントを流さない制御が可能 |
| 入力注入(Windows) | SendInput | Windows 標準の入力注入 API。マウス・キーボード・ホイールを一貫して扱える |
| 転送 | TCP + bincode(TCP_NODELAY 有効) | 順序保証が必要な入力イベント向き。バッファリングによる遅延を避ける |
| サーバ発見 | UDP ブロードキャスト | IP 手入力なしで接続。マルチ NIC 環境向けにサブネット指定ブロードキャストも併用 |
| 暗号化 | Noise プロトコル(snow) | 証明書運用が不要で、パスフレーズだけで認証と暗号化を両立できる |
キーマップは macOS のキーコードから Windows の仮想キーコードへ変換するテーブルを自前で持っており、JIS 配列も考慮しています。英数キー・かなキーを Windows の IME オン/オフに割り当て、円記号や「ろ」キーも変換対象です。Command キーはデフォルトで Ctrl として送るため、Cmd + C / Cmd + V の指の動きを変えずに Windows を操作できます。
実装で踏み抜いた落とし穴 3 選
同種のツールを作る方の役に立ちそうな 3 つを共有します。
1. macOS のカーソル移動が、次のイベントの「移動量」に混入する
最初に実装した版では、Windows 側に移った瞬間にカーソルが跳ね返って Mac に戻る、という現象が高速で繰り返されました。原因は CGWarpMouseCursorPosition で、プログラムからカーソルを移動させた分のジャンプ量が、次のマウスイベントの移動量に加算されて届くという挙動でした。巨大な移動量が入力されるので、仮想カーソルが一気に反対側の端まで飛び、そのまま「戻る」判定になっていたわけです。
対策は 2 つ。越境時にカーソルを退避させる処理をやめ、代わりにカーソルとマウスの関連付けを外して画面端に固定したままにすること。そして、人間の操作ではありえない大きさの移動量を破棄することです。あわせて、越境の判定自体も移動量を見るのをやめ、「カーソルの位置が端に達したか」だけで判定するよう変更しました。macOS ではカーソルが画面端に張り付くと移動量が信用できなくなるためです。
2. NSMenu を出したらプロセスが落ちた
クリップボード履歴のメニューは、当初 macOS 標準の NSMenu で実装していました。ところが選択して閉じた直後、サーバのメインループが終了してプロセスが落ちるという事象が発生しました。メニューのモーダル追跡が終わるときに、AppKit がアプリのメインループごと停止させるのが原因です。
最終的に NSMenu の採用をやめ、フォーカスを奪わない自前のパネル + キー操作は入力捕捉側で処理する構成に作り替えました。モーダルループを一切使わないため、この問題は構造的に起こりません。副次的に「貼り付け先のフォーカスが移らない」という利点も得られ、結果的にはこちらが正解でした。保険として、メインループが何らかの理由で停止しても自動的に再開するガードも入れています。
3. Windows のクリップボードは「取り合い」になる
履歴からのペーストが Windows 側で無反応になる、という報告を受けて調べたところ、Windows のクリップボードはシステム全体で排他的なリソースであることが効いていました。自分自身の監視スレッドや、常駐しているクリップボード管理ソフトがクリップボードを開いている一瞬に書き込むと、失敗して何も起きないのです。
対策として、プロセス内のクリップボード操作をすべて直列化したうえで、書き込みは短い間隔でリトライする方式にしました。加えて、貼り付け直前に「押されたままの修飾キー」を強制的に解放しています。Cmd + Shift + V で呼び出す都合上、Shift が押しっぱなしのまま Ctrl + V を送ると Ctrl + Shift + V になってしまい、アプリによっては別の動作になるためです。成功・失敗をログに明示するようにもしたので、環境依存の問題が起きても切り分けられます。
使い方は 3 ステップ
実際の起動手順はこれだけです。
1. Mac 側でサーバを起動(初回は「アクセシビリティ」と「入力監視」の権限付与が必要です)
# Windows PC を Mac の右側に置いている場合
./crossmouse-mac --edge right
# 起動時に共有パスフレーズを聞かれます(暗号化しない場合は空 Enter)
2. Windows 側でクライアントを起動(同じパスフレーズを入力)
.\crossmouse-win.exe
# 自動発見が使えない環境では接続先を直接指定
.\crossmouse-win.exe --connect 192.168.1.10
3. カーソルを画面端の外へ動かす。以上です。設定ファイルもプロファイルもありません。
現時点での制限
「必要な機能だけ」を方針にしているため、意図的に対応していない領域があります。導入を検討される場合の判断材料として明記しておきます。
- 操作の方向は Mac → Windows の一方向(Windows 側のキーボードで Mac を操作することはできません)
- 接続できる Windows PC は同時に 1 台(両側のマルチモニターには対応)
- クリップボード同期はプレーンテキストのみ(画像・ファイルは対象外)
- マシンをまたぐファイルのドラッグ & ドロップは非対応
よくある質問
Q. ソフトウェア KVM とは何ですか?
A. 1 組のキーボード・マウスで複数のパソコンを操作する仕組みを KVM(Keyboard / Video / Mouse)と呼びます。従来は専用の切替器(ハードウェア)が使われてきましたが、ソフトウェア KVM は同じことをネットワーク経由のソフトウェアだけで実現します。crossMouse はこのソフトウェア KVM にあたり、機器の追加購入もケーブルの差し替えも必要ありません。
Q. Mac と Windows でクリップボードを共有できますか?
A. できます。crossMouse は約 0.3 秒間隔で両マシンのクリップボードを監視し、テキストをコピーすると自動的にもう一方へ同期します。さらに、両マシンのコピー履歴を最大 50 件まとめて保持し、Cmd + Shift + V でどちらの画面からでも呼び出して貼り付けられます。
Q. Barrier や Synergy と何が違いますか?
A. 対応 OS の広さや機能の多さでは既存ツールに軍配が上がります。crossMouse は「Mac 1 台と Windows 1 台」という構成に絞り、設定ファイルを持たない代わりに、2 台で共有するクリップボード履歴や、パスフレーズだけで完結する通信暗号化といった体験面を厚くしているのが違いです。自社開発なので、必要になった機能をその場で足せる点も実務上のメリットになっています。
Q. 通信のセキュリティは大丈夫ですか?
A. パスフレーズを設定すれば、Noise プロトコル(X25519 + ChaCha20-Poly1305)で全通信が暗号化され、パスフレーズを知らない相手は接続できません。設定しない場合は平文通信となるため、起動時に警告を表示する仕様にしています。加えて、接続を許可する IP を限定するオプションも用意しています。
Q. 遅延は気になりませんか?
A. 入力イベントは TCP_NODELAY を有効にした接続で即時送信し、バッファリングによる待ちが発生しない設計にしています。また、送信キューが詰まった場合はマウス移動イベントを間引くことで、操作感が「重くなる」より「わずかに粗くなる」方向に倒しています。
まとめ
crossMouse は、「Mac と Windows を並べて使う」という限定的な課題に対して、必要な機能だけを実装したソフトウェア KVM です。画面端を越えるだけの切り替え、常時双方向のクリップボード同期、2 台で共有するクリップボード履歴、そしてパスフレーズだけで完結する通信暗号化——いずれも、日々の小さなストレスを消すことに全振りしています。
作ってみて改めて感じたのは、「毎日 100 回使うものは、5 秒の短縮でも効く」ということです。既製品で 8 割満たされている領域でも、残りの 2 割を自分たちで作れると体験は大きく変わります。OS のネイティブ API と向き合う場面は確かに骨が折れましたが、その分だけ「思ったとおりに動く道具」が手に入りました。
Infigate では、こうした業務ツールの内製から、Web アプリケーション・業務システムの開発までを手掛けています。
実際にCrossMouseを使いたいという場合も、全く別の問題を抱えていて「既製ツールでは痒いところに手が届かない」という場合でも、お気軽にご相談ください。