251 lines
8.9 KiB
Markdown
251 lines
8.9 KiB
Markdown
# Clipboard 機能設計書
|
||
|
||
## 概要
|
||
VRCT のクリップボード機能は、VRChat 内でのテキスト送信効率を向上させるため、翻訳結果をクリップボードにコピーし、ペースト機能を提供します。
|
||
|
||
## 主要機能
|
||
|
||
### 1. テキストコピー
|
||
- 翻訳結果をシステムクリップボードにコピー
|
||
- 複数のバックエンド対応(Windows clip、pyperclip、tkinter)
|
||
- UTF-16LE BOM 対応でクロスプラットフォーム互換性を確保
|
||
|
||
### 2. テキストペースト
|
||
- `Ctrl+V` による自動ペースト
|
||
- VRChat ウィンドウの自動フォーカス
|
||
- カウントダウン機能で準備時間を確保
|
||
|
||
### 3. VR アプリ検出
|
||
- OpenVR を用いた VRChat アプリ名の自動検出
|
||
- バックグラウンドスレッドによる非同期監視
|
||
- SteamVR の起動を待機・検出
|
||
|
||
## クラス設計
|
||
|
||
### Clipboard クラス
|
||
```python
|
||
class Clipboard:
|
||
def __init__(self)
|
||
def enable(self) -> None
|
||
def disable(self) -> None
|
||
def copy(self, message: str) -> bool
|
||
def paste(self, window_name: str|None = None, countdown: int = 0) -> bool
|
||
```
|
||
|
||
#### メンバ変数
|
||
- `is_enabled`: クリップボード機能の有効/無効状態
|
||
- `app_name`: VRChat アプリケーション名(自動検出)
|
||
- `_vr_monitor_thread`: SteamVR 監視スレッド
|
||
- `_stop_monitoring`: スレッド停止フラグ
|
||
|
||
### 主要メソッド
|
||
|
||
#### `__init__()`
|
||
初期化処理:
|
||
1. VR 監視スレッドを起動
|
||
2. SteamVR の起動を待機(10秒間隔で確認)
|
||
|
||
#### `enable()`
|
||
クリップボード機能を有効化:
|
||
1. `is_enabled` フラグを True に設定
|
||
2. `_initialize()` を呼び出し VR 監視を再開
|
||
|
||
#### `disable()`
|
||
クリップボード機能を無効化:
|
||
1. `is_enabled` フラグを False に設定
|
||
2. `_stop_monitoring` フラグを設定しスレッド停止
|
||
3. スレッド終了を待機(タイムアウト 1秒)
|
||
|
||
#### `copy(message: str) -> bool`
|
||
**パラメータ:**
|
||
- `message`: クリップボードにコピーするテキスト
|
||
|
||
**戻り値:**
|
||
- `True`: コピー成功
|
||
- `False`: コピー失敗または無効状態
|
||
|
||
**動作:**
|
||
1. `is_enabled` チェック
|
||
2. `copy_to_clipboard()` 関数を呼び出し
|
||
|
||
#### `paste(window_name: str|None = None, countdown: int = 0) -> bool`
|
||
**パラメータ:**
|
||
- `window_name`: フォーカス対象ウィンドウ(タイトル部分一致 or プロセス名)
|
||
- `countdown`: ペースト前の待機秒数
|
||
|
||
**戻り値:**
|
||
- `True`: ペースト送信成功
|
||
- `False`: ペースト失敗または無効状態
|
||
|
||
**動作:**
|
||
1. `is_enabled` チェック
|
||
2. `window_name` 未指定時は自動検出した VRChat アプリ名を使用
|
||
3. Windows 環境下でウィンドウをフォーカス(以下の順序)
|
||
- ウィンドウタイトルで部分一致検索
|
||
- マッチしない場合、プロセス名で検索
|
||
4. ウィンドウフォーカス成功時、`paste_via_pyautogui()` でペースト実行
|
||
|
||
### 内部メソッド
|
||
|
||
#### `_initialize()`
|
||
VR 監視スレッドの初期化:
|
||
1. `_stop_monitoring` を False に設定
|
||
2. デーモンスレッドで `_monitor_steamvr()` を起動
|
||
|
||
#### `_monitor_steamvr()`
|
||
バックグラウンドスレッド処理:
|
||
1. 10秒間隔で `checkSteamvrRunning()` を確認
|
||
2. SteamVR 検出時に `_setup_vr_app_name()` を呼び出し
|
||
3. `_stop_monitoring` が True になるまで監視
|
||
|
||
#### `_setup_vr_app_name()`
|
||
OpenVR を用いた VR アプリ名検出:
|
||
1. OpenVR 初期化(`VRApplication_Background` モード)
|
||
2. すべてのアプリケーション情報を取得
|
||
3. `steam.app` で始まるキーを検索し `app_name` に設定
|
||
4. 例外発生時は `app_name` を None に設定
|
||
|
||
## 支援関数
|
||
|
||
### `checkSteamvrRunning() -> bool`
|
||
SteamVR が起動しているかを確認
|
||
- プロセス名 `vrmonitor.exe`(Windows)または `vrmonitor`(その他)を検索
|
||
|
||
### `copy_to_clipboard(text: str) -> bool`
|
||
複数バックエンド対応のコピー関数
|
||
1. Windows: `clip` コマンド + UTF-16LE BOM
|
||
2. 汎用: `pyperclip` ライブラリ
|
||
3. フォールバック: `tkinter` 使用
|
||
|
||
### `paste_via_pyautogui(countdown: int = 0) -> bool`
|
||
PyAutoGUI を用いたペースト
|
||
- `pyautogui.hotkey('ctrl', 'v')` で Ctrl+V を送信
|
||
- カウントダウン実行
|
||
|
||
### ウィンドウ検索関数(Windows のみ)
|
||
|
||
#### `find_windows_by_title_substring(substring: str) -> list`
|
||
ウィンドウタイトルで部分一致検索
|
||
|
||
#### `find_windows_by_process_name(proc_name: str) -> list`
|
||
プロセス名でウィンドウを検索
|
||
|
||
#### `focus_window(hwnd) -> bool`
|
||
指定ウィンドウをアクティブ化
|
||
|
||
## 依存ライブラリ
|
||
|
||
| ライブラリ | 用途 | オプション |
|
||
|-----------|------|----------|
|
||
| pyautogui | キー入力シミュレーション | 必須(ペースト機能) |
|
||
| pyperclip | クリップボード操作 | オプション(フォールバック) |
|
||
| tkinter | クリップボード操作 | オプション(フォールバック) |
|
||
| openvr | VR アプリ名検出 | 必須 |
|
||
| psutil | プロセス検索 | 必須 |
|
||
|
||
## 動作フロー
|
||
|
||
### 初期化フロー
|
||
```
|
||
Clipboard.__init__()
|
||
└─ _initialize()
|
||
└─ スレッド起動: _monitor_steamvr()
|
||
└─ 10秒間隔で SteamVR 監視
|
||
└─ 検出時: _setup_vr_app_name()
|
||
└─ VRChat アプリ名を app_name に設定
|
||
```
|
||
|
||
### コピー・ペーストフロー
|
||
```
|
||
Model.setCopyToClipboard(text)
|
||
└─ clipboard.copy(text)
|
||
└─ copy_to_clipboard(text) [成功/失敗]
|
||
|
||
Model.setPasteFromClipboard()
|
||
└─ clipboard.paste(window_name=self.app_name)
|
||
└─ find_windows_by_title_substring() [Windows]
|
||
or find_windows_by_process_name()
|
||
└─ focus_window(hwnd)
|
||
└─ paste_via_pyautogui(countdown)
|
||
```
|
||
|
||
## 有効/無効制御
|
||
|
||
### コンフィグ設定
|
||
- `config.ENABLE_CLIPBOARD`: True/False でクリップボード機能を制御
|
||
- Controller 側で `config.ENABLE_CLIPBOARD` をチェックし、True の場合のみ `setCopyToClipboard()` と `setPasteFromClipboard()` を呼び出し
|
||
|
||
### 制御メソッド(Clipboard クラス内部)
|
||
```python
|
||
# 有効化(内部API、通常は使用しない)
|
||
clipboard.enable()
|
||
|
||
# 無効化(内部API、通常は使用しない)
|
||
clipboard.disable()
|
||
```
|
||
|
||
**注意**: 現在の実装では、Controller は `clipboard.enable()/disable()` を呼び出さず、`config.ENABLE_CLIPBOARD` フラグのみで制御します。Clipboard インスタンスは常に初期化されており、VR 監視スレッドも稼働し続けます。
|
||
|
||
### API エンドポイント(Controller)
|
||
```
|
||
/get/data/clipboard - 現在の有効/無効状態を取得
|
||
/set/enable/clipboard - config.ENABLE_CLIPBOARD を True に設定
|
||
/set/disable/clipboard - config.ENABLE_CLIPBOARD を False に設定
|
||
```
|
||
|
||
## エラーハンドリング
|
||
|
||
### 失敗時の動作
|
||
- コピー失敗: `False` を返却、リトライなし
|
||
- ペースト失敗: `False` を返却、リトライなし
|
||
- VR アプリ名検出失敗: `app_name` を None に設定、現在フォーカス中のウィンドウにペースト
|
||
|
||
### 例外処理
|
||
- OpenVR 初期化失敗時は `app_name = None` に設定
|
||
- プロセス検索例外も握りつぶし
|
||
- ウィンドウフォーカス失敗はログのみ出力
|
||
|
||
## パフォーマンス考慮
|
||
|
||
### スレッド設計
|
||
- VR 監視スレッドは **デーモンスレッド**(アプリ終了時の強制停止可能)
|
||
- 10秒間隔で監視(CPU 負荷最小化)
|
||
|
||
### クリップボード操作
|
||
- コピー・ペースト操作はメインスレッド実行
|
||
- ウィンドウフォーカス後に 0.2秒スリープで安定性向上
|
||
|
||
## セキュリティ・プライバシー
|
||
|
||
### データ保護
|
||
- クリップボードデータは一切ログ出力されない
|
||
- VR アプリ名のみ app_name として保持
|
||
|
||
### OS 依存性
|
||
- Windows: `clip` コマンド、ctypes によるウィンドウ制御
|
||
- その他 OS: pyperclip/tkinter フォールバック、自動フォーカス機能なし
|
||
|
||
## 制限事項・既知の問題
|
||
|
||
1. **非 Windows 環境**
|
||
- ウィンドウフォーカス機能は Windows のみ対応
|
||
- その他 OS では現在フォーカス中のウィンドウにペースト
|
||
|
||
2. **VRChat アプリ名検出**
|
||
- OpenVR が利用可能で SteamVR が起動している必要がある
|
||
- `steam.app` 接頭辞のアプリのみ認識
|
||
|
||
3. **PyAutoGUI の制限**
|
||
- キー入力シミュレーションはOS依存
|
||
- 一部アプリケーションではセキュリティ制限により失敗する可能性あり
|
||
|
||
4. **クリップボードバッファ**
|
||
- Ctrl+V 送信のタイミングはユーザー判断(カウントダウン機能あり)
|
||
- クリップボード内容の永続化はしない
|
||
|
||
## 参考資料
|
||
|
||
- [PyAutoGUI 公式ドキュメント](https://pyautogui.readthedocs.io/)
|
||
- [OpenVR Python バインディング](https://github.com/ValvePython/openvr)
|
||
- [psutil ドキュメント](https://psutil.readthedocs.io/)
|