GNU/Linux >> Linux の 問題 >  >> Linux

C++ Linux でクリップボードから文字列を取得する

X11 は、柔軟なマルチバッファ マルチフォーマットの非同期アプリケーション側クリップボード プロトコルを使用します。

ほとんどのツールキットに実装されています (GTK の gtk_clipboard_get() 、Qt の QApplication::clipboard() 、Tk の clipboard_get)。ただし、たとえば、ツールキットを使用していない場合や、大量のデータを同時にメモリに保持せずにクリップボード バッファーを介して渡す必要がある場合は、X11 API を使用して手動で行うことができます。

理論

多くのバッファーが存在する可能性がありますが、知っておく必要があるのは次の 2 つだけです。

  • CLIPBOARD は通常の明示的なバッファです。編集/コピー メニューでコピーし、編集/貼り付けメニューで貼り付けます。
  • PRIMARY 選択は、暗黙的なマウス選択機能です。マウス カーソルで選択するとテキストが挿入され、テキスト入力フィールドを中クリックするとそこから貼り付けられます。

一次選択ではキーを押す必要がないため、隣接するウィンドウ間で小さなフラグメントをコピーする場合に便利です。この機能は主に UNIX 固有のものですが、Windows OS でエミュレートするパテ、トリリアン、およびいくつかの gtk アプリを見てきました。また、Firefox には、ページの空白の非インタラクティブ スペースを中クリックすると、「貼り付け &実行」機能があります。

アプリケーション側のものを最適化するには バッファ:変更されるたびにクリップボード/選択範囲全体をサーバーにプッシュする代わりに、アプリケーションはサーバーに「私はそれを所有しています」と伝えるだけです。バッファーを取得するには、所有者にその内容を提供するように依頼します。この方法では、実際にリクエストされるまで、大きなバッファーでもリソースを消費しません。

バッファーを要求するときは、所有者に必要な特定の形式を尋ねます。たとえば、seamonkey ブラウザからコピーされた画像 (画像を右クリックして [画像のコピー] を押す) は、さまざまな形式で表すことができます。ターミナルに貼り付けると、画像の URL として表示されます。 libreoffice writer に貼り付けると、その URL から読み込まれた画像になります。そして、gimpに貼り付けた場合、それは画像そのものになります。これが機能するのは、seamonkey がスマートで、各アプリケーションが要求する形式 (端末用のテキスト文字列、libreoffice 用の html、gimp 用の画像データ) を各アプリケーションに提供するためです。テキスト形式を要求するには、UTF8_STRING を要求します STRING にフォールバックする形式 .

別のアプリケーションにバッファの準備を依頼すると、時間がかかる場合がありますが、リクエストは非同期です :所有者はバッファを準備し、指定された場所に保存し (window プロパティは一時的なストレージとして使用されます)、SelectionNotify で通知します

バッファを取得するには:

  • バッファ名を選択 (CLIPBOARDPRIMARY )、フォーマット(UTF8_STRINGSTRING ) および結果を保存するウィンドウ プロパティ
  • XConvertSelection() に電話する バッファをリクエストする
  • SelectionNotify を待つ イベント
  • ウィンドウ プロパティからバッファ コンテンツを読み取る

素朴な実装

// gcc -o xclipget xclipget.c -lX11
#include <stdio.h>
#include <limits.h>
#include <X11/Xlib.h>

Bool PrintSelection(Display *display, Window window, const char *bufname, const char *fmtname)
{
  char *result;
  unsigned long ressize, restail;
  int resbits;
  Atom bufid = XInternAtom(display, bufname, False),
       fmtid = XInternAtom(display, fmtname, False),
       propid = XInternAtom(display, "XSEL_DATA", False),
       incrid = XInternAtom(display, "INCR", False);
  XEvent event;

  XConvertSelection(display, bufid, fmtid, propid, window, CurrentTime);
  do {
    XNextEvent(display, &event);
  } while (event.type != SelectionNotify || event.xselection.selection != bufid);

  if (event.xselection.property)
  {
    XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, False, AnyPropertyType,
      &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);

    if (fmtid == incrid)
      printf("Buffer is too large and INCR reading is not implemented yet.\n");
    else
      printf("%.*s", (int)ressize, result);

    XFree(result);
    return True;
  }
  else // request failed, e.g. owner can't convert to the target format
    return False;
}

int main()
{
  Display *display = XOpenDisplay(NULL);
  unsigned long color = BlackPixel(display, DefaultScreen(display));
  Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0, 1,1, 0, color, color);
  Bool result = PrintSelection(display, window, "CLIPBOARD", "UTF8_STRING") ||
                PrintSelection(display, window, "CLIPBOARD", "STRING");
  XDestroyWindow(display, window);
  XCloseDisplay(display);
  return !result;
}

これは、多くの単純なケースで機能します。ここで欠けていることの 1 つは、大きなバッファーのインクリメンタル読み取りのサポートです。追加しましょう!

大きなバッファ

一部のアプリでは、100 ギガバイトのテキスト ログをコピーして貼り付けたい場合があります。そしてX11はそれを可能にします!ただし、データは段階的に渡され、チャンクに分割される必要があります。

要求されたバッファが大きすぎる場合、ウィンドウ プロパティに格納する代わりに、所有者は INCR の形式のプロパティを設定します。 .削除すると、所有者はそれを読んだものと見なし、次のチャンクを同じプロパティに入れます。これは、最後のチャンクが読み取られて削除されるまで続きます。最後に、所有者はデータの終わりを示すためにサイズ 0 のプロパティを設定します。

したがって、大きなバッファを読み取るには、INCR を削除します プロパティを取得し、プロパティが再び表示されるまで待ちます (PropertyNotify イベント、状態 ==PropertyNewValue )、それを読んで削除し、再び表示されるまで待ちます。というように、サイズがゼロになるまで続けます。

// gcc -o xclipget xclipget.c -lX11
#include <stdio.h>
#include <limits.h>
#include <X11/Xlib.h>

Bool PrintSelection(Display *display, Window window, const char *bufname, const char *fmtname)
{
  char *result;
  unsigned long ressize, restail;
  int resbits;
  Atom bufid = XInternAtom(display, bufname, False),
       fmtid = XInternAtom(display, fmtname, False),
       propid = XInternAtom(display, "XSEL_DATA", False),
       incrid = XInternAtom(display, "INCR", False);
  XEvent event;

  XSelectInput (display, window, PropertyChangeMask);
  XConvertSelection(display, bufid, fmtid, propid, window, CurrentTime);
  do {
    XNextEvent(display, &event);
  } while (event.type != SelectionNotify || event.xselection.selection != bufid);

  if (event.xselection.property)
  {
    XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, True, AnyPropertyType,
      &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);
    if (fmtid != incrid)
      printf("%.*s", (int)ressize, result);
    XFree(result);

    if (fmtid == incrid)
      do {
        do {
          XNextEvent(display, &event);
        } while (event.type != PropertyNotify || event.xproperty.atom != propid || event.xproperty.state != PropertyNewValue);

        XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, True, AnyPropertyType,
          &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result);
        printf("%.*s", (int)ressize, result);
        XFree(result);
      } while (ressize > 0);

    return True;
  }
  else // request failed, e.g. owner can't convert to the target format
    return False;
}

int main()
{
  Display *display = XOpenDisplay(NULL);
  unsigned long color = BlackPixel(display, DefaultScreen(display));
  Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0, 1,1, 0, color, color);
  Bool result = PrintSelection(display, window, "CLIPBOARD", "UTF8_STRING") ||
                PrintSelection(display, window, "CLIPBOARD", "STRING");
  XDestroyWindow(display, window);
  XCloseDisplay(display);
  return !result;
}

例:xsel ツールは INCR を使用します 4000 を超えるバッファーの転送。ICCCM によると、適切なサイズ制限を選択するのはアプリケーション次第です。

PRIMARY でも同じコードが機能します 選択。 PRIMARY を印刷するには、「CLIPBOARD」を「PRIMARY」に置き換えます 選択内容。

参考文献

  • Jamie Zawinski による X セレクションの要約
  • Xlib プログラミング マニュアル - 選択項目
  • ICCCM - 大容量データ転送と INCR プロトコル
  • https://github.com/exebook/x11clipboard - 最小限の XCopy()XPaste() 実装
  • xselxclip ソース
  • 二次選考 - Charles Lindsey による歴史とアイデア

最初にコードではなく、実装のあるプログラムを見つけようとしましたか?私はあなたのためにそれを行い、X11 の直接呼び出しを使用する多くの実装を見つけました。私はこれが最も価値があると思いますが、これも読んでください。プログラムを見つけて、ソースを探すだけです。ウィキペディアで、x11 クリップボード/選択システムを使用するアプリケーションを調べてみてください。

<ブロック引用>

次のプログラムは、特にデータ転送メカニズムで動作します:

xcutsel 選択範囲からカット バッファに、またはその逆にデータを転送します

xclipboardglipper (Gnome)、parcellite (LXDE)、および klipper (KDE) reclipboard マネージャー、たぶん wmcliphist 同様に xcb カット バッファの内容を表示し、ユーザーがそれらを操作できるようにします xselection、

xclipxsel そして xcopy Xセレクションとの間でデータをコピーするコマンドラインプログラムです。 xcopy には、Xselection の問題をデバッグするのに役立つ冗長オプションがあります。 parcellite には、コマンド ラインから特定の X 選択を読み書きする機能もあります。

synergy は、複数のオペレーティング システムを実行している複数のコンピュータでクリップボードを共有できるクロス プラットフォーム ツールです

xfce4-clipman-plugin 「Xfce4panel 用のクリップボード履歴プラグイン」であり、クリップボード マネージャーでもあります xtranslate は Xselection 内の単語を多言語辞書で検索します

まもなく、理論的には、X11 には 2 つの「クリップボード」があります。実際にはキーボードと選択用です。実際の「キーボード」はメイン/デフォルトのクリップボードの目的で作成されますが、マウスの中ボタンを押すと、選択したテキストをすぐに好きな場所に貼り付けることができます。異なる種類のオブジェクトによる交換。

追記私の経験の後、私はもう x11 を使用しません。お楽しみください:)


Linux
  1. Linux(BASH)で数値から上限整数を取得する

  2. C++ を使用して Linux で合計 CPU 使用率を取得する方法

  3. クリップボードからの Linux イメージ

  1. Linuxでコマンドラインからジオロケーションを取得する方法

  2. Linuxシェルはymlファイルからフィールドの値を取得します

  3. Linuxでテキストファイルから特定の行を表示するには?

  1. Linux – Elfバイナリからコンパイラ情報を取得できますか?

  2. Linuxlsコマンド

  3. C++ は Linux ディストリビューション名\バージョンを取得します