クリップボードの収集
クリップボードの内容を常に収集して保存するようなアプリケーションを作りたい場合、以下の方法があります。
前者は、XFIXES 拡張機能が実装される前の古いやり方です。
所有者のクライアントが終了しても、データを維持できるのが利点ですが、この場合は、特定のターゲットしかサポートされなくなるので、元のセレクション所有者による自由なターゲット変換が行えなくなります。
XFIXES 拡張機能を使う場合、所有者は変更されないので、所有者による自由なターゲット変換が行えます。
拡張機能が使える場合は、こちらを使った方が良いでしょう。
- セレクション所有者が変更されたら、データを要求して保存した後、自身が所有者になる。
以降、データの要求はこのアプリケーションで処理する。 - XFIXES 拡張機能で、セレクション所有者が変更された時のイベントを受信し、クリップボードデータが変更されたら、常にデータを要求して保存する。
セレクション所有者は変更されないので、クリップボードは引き続き元のクライアントが管理する。
前者は、XFIXES 拡張機能が実装される前の古いやり方です。
所有者のクライアントが終了しても、データを維持できるのが利点ですが、この場合は、特定のターゲットしかサポートされなくなるので、元のセレクション所有者による自由なターゲット変換が行えなくなります。
XFIXES 拡張機能を使う場合、所有者は変更されないので、所有者による自由なターゲット変換が行えます。
拡張機能が使える場合は、こちらを使った方が良いでしょう。
プログラム
XFIXES 拡張機能で、CLIPBOARD セレクションの所有者が変わった時のイベントを受け取ります。
閉じるボタンで終了します。
<X11/extensions/Xfixes.h> のインクルードと、-lXfixes のリンクが必要です。
<d20-xfixes.c>
クリップボードにコピーした時、イベントが来るのを確認してください。
所有者ウィンドウの破棄や、そのクライアントの終了時にもイベントが来ます。
XFIXES 拡張機能には、コアプロトコルで実現できない、ちょっとした機能が集まっています。
セレクション関連以外にも、いくつか機能があります。
XFixesQueryExtension は、XFIXES 拡張機能がサポートされている場合、True が返ります。
引数にはそれぞれ、拡張機能のイベントタイプのベース値と、エラーコードのベース値が返ります。
XFixesSelectSelectionInput は、ウィンドウに対して、セレクション関連のイベントマスクを選択します。
selection は、対象のセレクションを指定します。
eventMask は、以下の値です。
実際のイベントタイプは、XFixesQueryExtension() で取得した event_base + XFixesSelectionNotify です。
閉じるボタンで終了します。
<X11/extensions/Xfixes.h> のインクルードと、-lXfixes のリンクが必要です。
$ cc -o run d20-xfixes.c util.c -lX11 -lXfixes
<d20-xfixes.c>
#include <stdio.h> #include <X11/Xlib.h> #include <X11/extensions/Xfixes.h> #include "util.h" int main(int argc,char **argv) { Display *disp; Window win; XEvent ev; int event_base,error_base; XFixesSelectionNotifyEvent *pev; disp = XOpenDisplay(NULL); if(!disp) return 1; set_display(disp); if(!XFixesQueryExtension(disp, &event_base, &error_base)) { printf("unsupported XFIXES\n"); XCloseDisplay(disp); return 1; } win = create_test_window2(disp, 200, 200, 0, ButtonPress); //イベント選択 XFixesSelectSelectionInput(disp, win, GET_ATOM("CLIPBOARD"), XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask); //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); if(event_quit(&ev)) break; if(ev.type == event_base + XFixesSelectionNotify) { pev = (XFixesSelectionNotifyEvent *)&ev; printf("[XFixesSelectionNotify] subtype(%d) owner(0x%lx) " "timestamp(0x%lx) selection_timestamp(0x%lx)\n", pev->subtype, pev->owner, pev->timestamp, pev->selection_timestamp); } } XCloseDisplay(disp); return 0; }
クリップボードにコピーした時、イベントが来るのを確認してください。
所有者ウィンドウの破棄や、そのクライアントの終了時にもイベントが来ます。
XFIXES 拡張機能には、コアプロトコルで実現できない、ちょっとした機能が集まっています。
セレクション関連以外にも、いくつか機能があります。
関数
Bool XFixesQueryExtension(Display *dpy, int *event_base_return, int *error_base_return); void XFixesSelectSelectionInput(Display *dpy, Window win, Atom selection, unsigned long eventMask);
XFixesQueryExtension は、XFIXES 拡張機能がサポートされている場合、True が返ります。
引数にはそれぞれ、拡張機能のイベントタイプのベース値と、エラーコードのベース値が返ります。
XFixesSelectSelectionInput は、ウィンドウに対して、セレクション関連のイベントマスクを選択します。
selection は、対象のセレクションを指定します。
eventMask は、以下の値です。
XFixesSetSelectionOwnerNotifyMask | XSetSelectionOwner() で所有者が変化した時。 subtype = XFixesSetSelectionOwnerNotify |
---|---|
XFixesSelectionWindowDestroyNotifyMask | 所有者ウィンドウが破棄された時。 subtype = XFixesSelectionWindowDestroyNotify |
XFixesSelectionClientCloseNotifyMask | 所有者のクライアントが閉じられた時。 subtype = XFixesSelectionClientCloseNotify |
XFixesSelectionNotify イベント
typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; int subtype; Window owner; Atom selection; Time timestamp; Time selection_timestamp; } XFixesSelectionNotifyEvent;
実際のイベントタイプは、XFixesQueryExtension() で取得した event_base + XFixesSelectionNotify です。
subtype | 何が原因で所有者が変更されたか。 イベントマスクの表をご覧ください。 |
---|---|
owner | 現在の所有者。または None |
timestamp | イベントが生成された時間 |
selection_timestamp | XSetSelectionOwner() 時に渡されたタイムスタンプ。 ウィンドウ破棄や閉じられた時は 0。 |
クリップボードマネージャ
クリップボード所有者のクライアントが終了する時、「クリップボードマネージャ」にデータを引き渡すことで、クライアントが終了した後も、引き続き同じクリップボードデータを貼り付けることができるようになります。
ここで言うところのクリップボードマネージャは、クリップボードデータを収集するような機能があるわけではなく、単に破棄されようとしているクリップボードデータを引き受ける役目を持っています。
※クリップボードマネージャと謳われているようなアプリは、基本的に、クリップボードデータを常に収集したりするような機能があるだけで、以下で説明しているような、CLIPBOARD_MANAGER セレクションを実装しているものではありません。
ここで言うところのクリップボードマネージャは、クリップボードデータを収集するような機能があるわけではなく、単に破棄されようとしているクリップボードデータを引き受ける役目を持っています。
※クリップボードマネージャと謳われているようなアプリは、基本的に、クリップボードデータを常に収集したりするような機能があるだけで、以下で説明しているような、CLIPBOARD_MANAGER セレクションを実装しているものではありません。
CLIPBOARD_MANAGER セレクション
クリップボードマネージャは、CLIPBOARD_MANAGER セレクションの所有権を持っています。
XGetSelectionOwner() で CLIPBOARD_MANAGER の所有者を取得した時、戻り値が None でなければ、クリップボードマネージャが存在します。
クリップボードマネージャは、SAVE_TARGETS ターゲットの変換に対応している必要があります。
XGetSelectionOwner() で CLIPBOARD_MANAGER の所有者を取得した時、戻り値が None でなければ、クリップボードマネージャが存在します。
クリップボードマネージャは、SAVE_TARGETS ターゲットの変換に対応している必要があります。
SAVE_TARGETS ターゲット
終了時に、クリップボードデータをウィンドウマネージャに引き渡す CLIPBOARD 所有者は、TARGETS ターゲットが要求された時に、SAVE_TARGETS をリストに含める必要があります。
これは、CLIPBOARD_MANAGER によって、ただの目印として使われます。
これは、CLIPBOARD_MANAGER によって、ただの目印として使われます。
手順
クリップボード所有者のクライアントが終了する時、以下の手順で、データをクリップボードマネージャに引き渡します。
ただし、その前に、データの保存先のプロパティに対して、クリップボードマネージャに保存したい各ターゲットのアトムのリストをセットします (type = "ATOM", format = 32)。
文字列なら、UTF8_STRING や STRING です。CLIPBOARD 所有者が変換可能なターゲットである必要があります。
XConvertSelection() 時に property 引数を None にした場合は、クリップボードマネージャが CLIPBOARD セレクションに対して TARGETS を要求し、不要なターゲットを除外した上で、現在の所有者でサポートされているターゲットが選択されます。
CLIPBOARD 所有者は、type = "ATOM", format = 32 のデータをプロパティにセットします。
2つの値をペアにします。最初の値はターゲットのアトムで、2番目の値は整数です。
データを保存する各ターゲットと、そのデータの変換後のバイトサイズを指定します。
副作用ターゲット (実際のデータはなく、type = "NULL" で長さ 0 のプロパティがセットされる) の場合は -1、変換後のサイズが決められない場合は 0 を指定します。
これにより、クリップボードマネージャが大きなサイズのデータを保存したくない場合、そのターゲットの保存を拒否するか、ユーザーに対して、保存の可否を確認することがあります。
これにより、クリップボードマネージャで、各ターゲットのデータが保存されます。
その後、クリップボードマネージャーが CLIPBOARD セレクションの所有権を取得します。
以降は、クリップボードマネージャがクリップボードデータを所持する形になります。
元のクリップボード所有者が SelectionNotify イベントを受け取った時、データはすべて変換されたということになるので、その後、クライアントを終了させることができます。
(データの転送が終わらないうちに終了してはいけません)
※クリップボードマネージャは、各ターゲットタイプごとにデータを保持するため、柔軟なデータ変換は行えません。
文字列 (UTF8_STRING) など、一般的なデータを保存する時だけ使用してください。
クリップボードマネージャが存在するか
XGetSelectionOwner() で CLIPBOARD_MANAGER セレクションの所有者がいるかどうかを確認し、所有者がいない場合は、何もせずに終了します。SAVE_TARGETS ターゲットを要求
XConvertSelection() で、CLIPBOARD_MANAGER セレクションに対して、SAVE_TARGETS ターゲットの変換を要求します。ただし、その前に、データの保存先のプロパティに対して、クリップボードマネージャに保存したい各ターゲットのアトムのリストをセットします (type = "ATOM", format = 32)。
文字列なら、UTF8_STRING や STRING です。CLIPBOARD 所有者が変換可能なターゲットである必要があります。
XConvertSelection() 時に property 引数を None にした場合は、クリップボードマネージャが CLIPBOARD セレクションに対して TARGETS を要求し、不要なターゲットを除外した上で、現在の所有者でサポートされているターゲットが選択されます。
TARGET_SIZES ターゲット
CLIPBOARD 所有者が、TARGETS で TARGET_SIZES ターゲットをサポートしている場合、クリップボードマネージャは、CLIPBOARD セレクションに対して TARGET_SIZES ターゲットを要求します。CLIPBOARD 所有者は、type = "ATOM", format = 32 のデータをプロパティにセットします。
2つの値をペアにします。最初の値はターゲットのアトムで、2番目の値は整数です。
データを保存する各ターゲットと、そのデータの変換後のバイトサイズを指定します。
副作用ターゲット (実際のデータはなく、type = "NULL" で長さ 0 のプロパティがセットされる) の場合は -1、変換後のサイズが決められない場合は 0 を指定します。
これにより、クリップボードマネージャが大きなサイズのデータを保存したくない場合、そのターゲットの保存を拒否するか、ユーザーに対して、保存の可否を確認することがあります。
データの変換
その後、クリップボードマネージャは、CLIPBOARD セレクションに対して、SAVE_TARGETS で取得したそれぞれのターゲットのデータ変換を要求します。これにより、クリップボードマネージャで、各ターゲットのデータが保存されます。
その後、クリップボードマネージャーが CLIPBOARD セレクションの所有権を取得します。
以降は、クリップボードマネージャがクリップボードデータを所持する形になります。
元のクリップボード所有者が SelectionNotify イベントを受け取った時、データはすべて変換されたということになるので、その後、クライアントを終了させることができます。
(データの転送が終わらないうちに終了してはいけません)
※クリップボードマネージャは、各ターゲットタイプごとにデータを保持するため、柔軟なデータ変換は行えません。
文字列 (UTF8_STRING) など、一般的なデータを保存する時だけ使用してください。