およそ20年前の技術であるが、日本語情報が全然なかったのでメモがてらまとめた
動作は確認した(on Xubuntu 20.04)が完全に準拠してる保証はないのでご注意
別のアプリケーション(ソース、主にファイルマネージャーとか)から
ファイルを送られる(ターゲット)のアプリケーションを想定、
ソース側の説明は簡素化
参考文献
仕様: Drag-and-Drop Protocol for the X Window System
https://freedesktop.org/wiki/Specifications/XDND/
参考にした実装: SDLのソース(主にこの辺)
https://github.com/libsdl-org/SDL/blob/main/src/video/x11/SDL_x11events.c
実際の実装
処理の流れ
Step 0: (ウィンドウ起動時)
起動時にXChangePropertyでXdndAwareを指定し、XDNDプロトコルをサポートすることをアナウンス
このときプロトコルバージョンも指定する
なおプロトコルバージョン5しか考慮していないので互換性に欠けるかもしれない
仕様上クライアントは3〜最新のすべてに対応しなければならない:
- バージョン5においてXdndFinishedの受け入れるフラグを立てる必要がある(逆に言うと以前では無視されるはず)
- バージョン4においてXdndProxyが実装されたがこれはソースに対してなのかターゲットに対してなのか不明
Step 1: (ソースウィンドウ上でマウスを押す)
(ドラッグを始めるとソースでXdndSelectionの所有権を得る)
Step 2: (マウス押したままターゲットウィンドウにカーソルが乗る、初回のみ)
ターゲットのウィンドウにドラッグしているマウスカーソルが入るとターゲットがXdndEnterメッセージを受け取る
メッセージにはドラッグされた種類(ファイルならtext/uri-listとか、テキストならTEXTとかUTF8_STRINGとか)が3つまで乗る
種類が4つ以上ある場合はメッセージには載せられないため、(「3つを超える」フラグが立って、種類自体は空)
XGetWindowPropertyでソースからXdndTypeListを取得する
手元の例として、thunarからファイルを持ってくると以下がメッセージに乗って来る
- text/uri-list
mousepadからテキストを持ってくると以下がWindowProperty経由で来る
- GTK_TEXT_BUFFER_CONTENTS
- application/x-gtk-text-buffer-rich-text
- UTF8_STRING
- COMPOUND_TEXT
- TEXT
- STRING
- text/plain;charset=utf-8
- text/plain
この時点でソースウィンドウIDと必要なタイプ(例えばファイルならtext/uri-list)が届いているかを保持する
Step 3: (マウス押したままターゲットウィンドウ上でカーソルが動く度、ただしStep 4でメッセージを返さないと連続して来ない)
(ソースがXdndPositionメッセージでマウス座標とユーザが要求したアクションXdndAction〜を送る)
Step 4および5: (Step 3に対して毎度ターゲットからソースへメッセージ)
ターゲットがXdndPositionメッセージを受け取り(Step 4)、
ドロップを受け入れる場合はXdndStatusメッセージにアクションと受け入れるフラグと長方形を送る(Step 5)
(アクションはいくつか種類があるがここではXdndActionCopyのみ扱う)
(なお長方形の使い方がよくわからない、指定すると毎度XdndPositionが来なくなるっぽいが広範囲を指定しても特に変化がない)
Step 6: (Step 5に対して)
(ソースがXdndStatusメッセージを受け取る、(長方形がなかったり)長方形から出たら再度Step 3に進む)
Step 7a:(マウスがターゲットウィンドウを離れる)
(ソースがXdndLeaveメッセージをターゲットに送る)
Step 7b:(マウスがターゲットウィンドウ上でボタンを離す、受け入れるフラグが立っている)
(ソースがXdndDropメッセージをターゲットに送る)
Step 7c:(マウスがターゲットウィンドウ上でボタンを離す、受け入れるフラグが立っていない)
(ソースがXdndLeaveメッセージをターゲットに送る)
Step 7d:(マウスがターゲットウィンドウ上でボタンを離す、ソースがXdndStatusを受け取っていない)
(ソースがXdndLeaveメッセージをターゲットに送る)
Step 8a: (ターゲットがXdndLeaveメッセージを受信)
ターゲットはキャッシュしたデータを捨てる
Step 8b: (ターゲットがXdndDropメッセージを受信、それを受け入れる)
ターゲットはXdndSelectionとXdndDropに同梱されたタイムスタンプでXConvertSelectionを呼び出し、
SelectionNotifyイベントが届いた際にXGetWindowPropertyでデータを取得した後、
XdndFinishedをソースに送る(SelectionNotifyにはソースウィンドウIDが載ってないのでXdndEnterで収集した値を使う)
取得したデータ自体はtext/uri-listに則ったフォーマットになっている
(ローカルファイルであれば"file:///path/to/file\r\n"、複数なら連結される)
Step 8c: (ターゲットがXdndDropメッセージを受信、それを受け入れない)
ターゲットはXdndFinishedをソースに送る(ソースはXdndLeaveとして扱う)
感想とか
リファレンスコードが全然見つからず、SDLのコードが大変参考になりました
(過去の経緯で仕方ないかもしれないが)Xlibが32bit値をlongで扱っているのですこし厄介でした