LoginSignup
3
1

More than 1 year has passed since last update.

X11でのドラッグアンドドロップ実装メモ

Posted at

およそ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

実際の実装

xdndfile.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で扱っているのですこし厄介でした

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1