GNU/Linux >> Belajar Linux >  >> Linux

C++ Dapatkan string dari Clipboard di Linux

X11 menggunakan protokol clipboard sisi aplikasi multi-buffer multi-format asinkron yang fleksibel.

Sebagian besar toolkit menerapkannya (gtk_clipboard_get() GTK , QApplication::clipboard() Qt , clipboard_get Tk). Namun Anda dapat melakukannya secara manual dengan X11 API, misalnya, jika Anda tidak menggunakan toolkit, atau jika Anda harus meneruskan data dalam jumlah besar melalui buffer clipboard tanpa menyimpan semuanya di memori pada saat yang bersamaan.

Teori

Mungkin ada banyak buffer, tetapi Anda hanya perlu mengetahui dua hal:

  • CLIPBOARD adalah buffer eksplisit yang biasa:Anda menyalin sesuatu di sana dengan menu Edit/Salin, dan menempelkannya dengan menu Edit/Tempel.
  • PRIMARY seleksi adalah fitur pemilihan tetikus implisit:teks masuk ke dalamnya saat dipilih dengan kursor tetikus, dan ditempelkan dari situ saat klik tengah di bidang masukan teks.

Pemilihan utama tidak memerlukan penekanan tombol, jadi berguna untuk menyalin fragmen kecil di antara jendela yang bersebelahan. Fitur ini sebagian besar khusus untuk unix, tetapi saya telah melihat dempul, trillian, dan beberapa aplikasi gtk yang menirunya di OS Windows. Firefox juga memiliki fitur "Tempel &Buka" saat mengeklik tengah ruang non-interaktif kosong pada halaman.

Untuk mengoptimalkan hal-hal yang ada di sisi aplikasi buffer:alih-alih mendorong seluruh papan klip/pilihan ke server setiap kali berubah, aplikasi hanya memberi tahu server "Saya memilikinya". Untuk mendapatkan buffer, Anda meminta pemiliknya untuk memberikan isinya kepada Anda. Dengan cara ini, bahkan buffer besar tidak memerlukan sumber daya hingga benar-benar diminta.

Saat meminta buffer, Anda meminta pemilik untuk format tertentu yang Anda butuhkan. Misalnya gambar yang disalin dari browser seamonkey (klik kanan gambar dan tekan "Salin Gambar") dapat direpresentasikan dalam format yang berbeda. Itu akan muncul sebagai URL gambar jika Anda menempelkannya di terminal. Itu akan menjadi gambar yang dimuat dari URL itu jika Anda menempelkannya di penulis libreoffice. Dan itu akan menjadi gambar itu sendiri jika disisipkan di gimp. Itu berfungsi karena seamonkey pintar dan menyediakan setiap aplikasi dengan format yang diminta:string teks untuk terminal, html untuk libreoffice, dan data gambar untuk gimp. Untuk meminta format teks, Anda akan meminta UTF8_STRING format dengan fallback ke STRING .

Saat Anda meminta aplikasi lain untuk menyiapkan buffer, dan itu mungkin membutuhkan waktu, permintaannya asinkron :pemilik menyiapkan buffer, menyimpannya di lokasi tertentu (properti jendela digunakan sebagai penyimpanan sementara) dan memberi tahu Anda dengan SelectionNotify acara setelah selesai.

Jadi untuk mendapatkan buffer:

  • pilih nama buffer (CLIPBOARD , PRIMARY ), format(UTF8_STRING , STRING ) dan properti jendela untuk menyimpan hasil ke
  • panggil XConvertSelection() untuk meminta buffer
  • tunggu SelectionNotify acara
  • membaca konten penyangga dari properti jendela

Penerapan naif

// 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;
}

Ini akan bekerja untuk banyak kasus sederhana. Satu hal yang hilang di sini adalah dukungan untuk pembacaan buffer besar secara bertahap. Ayo tambahkan!

Buffer besar

Beberapa aplikasi mungkin ingin menyalin/menempel 100 gigabyte log teks. Dan X11 memungkinkan itu! Namun data harus diteruskan secara bertahap, dipecah menjadi beberapa bagian.

Jika buffer yang diminta terlalu besar, alih-alih menyimpannya ke properti jendela, pemilik menyetel properti dengan format INCR . Jika Anda menghapusnya, pemilik menganggap Anda telah membacanya, dan meletakkan potongan berikutnya di properti yang sama. Itu berlanjut sampai potongan terakhir dibaca dan dihapus. Akhirnya pemilik menyetel properti berukuran 0 untuk menandai akhir data.

Jadi untuk membaca buffer besar Anda menghapus INCR properti dan tunggu hingga properti muncul kembali (PropertyNotify acara, status ==PropertyNewValue ), baca dan hapus, tunggu hingga muncul kembali, dan seterusnya hingga muncul dengan ukuran nol.

// 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;
}

Misalnya xsel alat menggunakan INCR transfer untuk buffer yang lebih besar dari 4000. Menurut ICCCM, terserah aplikasi untuk memilih batas ukuran yang masuk akal.

Kode yang sama berfungsi untuk PRIMARY pilihan. Ganti "CLIPBOARD" dengan "PRIMARY" untuk mencetak PRIMARY konten pilihan.

Referensi

  • Ringkasan X Selections oleh Jamie Zawinski
  • Panduan Pemrograman Xlib - Pilihan
  • ICCCM - Transfer Data Besar dan protokol INCR
  • https://github.com/exebook/x11clipboard - minimal XCopy() dan XPaste() implementasi
  • xsel dan xclip sumber
  • Seleksi Sekunder - sejarah dan gagasan oleh Charles Lindsey

Apakah Anda mencoba menemukan bukan kode terlebih dahulu tetapi program dengan implementasi? Saya melakukannya untuk Anda dan menemukan banyak implementasi yang menggunakan panggilan X11 langsung. Saya pikir yang paling berharga adalah ini, tetapi Anda juga dapat membaca ini. Temukan saja program apa saja dan cari sumbernya. Coba lihat di wikipedia aplikasi apa yang menggunakan sistem clipboard/seleksi x11.

Program berikut secara khusus beroperasi pada mekanisme transfer data:

xcutsel mentransfer data dari pilihan untuk memotong buffer atau sebaliknya

xclipboard , glipper (Gnome), parcellite (LXDE), dan klipper (KDE) adalah pengelola clipboard, mungkin wmcliphist serta xcb menunjukkan konten dari cut buffer dan memungkinkan pengguna untuk memanipulasinya xpilihan,

xclip , xsel dan xcopy adalah program baris perintah yang menyalin data ke atau dari pilihan X. xcopy memiliki opsi verbositas yang membantu men-debug masalah Xselection. parcellite juga memiliki kemampuan untuk membaca dari dan menulis ke pilihan X tertentu dari baris perintah.

synergy adalah alat lintas platform yang memungkinkan Anda berbagi papan klip di beberapa komputer yang menjalankan beberapa sistem operasi

xfce4-clipman-plugin adalah "plugin riwayat papan klip untuk panel Xfce4" dan juga pengelola papan klip xtranslate mencari kata-kata di Xselection dalam kamus multibahasa autocutsel menyinkronkan buffer potong dan buffer pilihan

Singkatnya, secara teori, X11 memiliki 2 "papan klip":sebenarnya keyboard dan untuk pilihan - teks yang Anda pilih segera dapat ditempelkan di mana pun Anda inginkan dengan menekan tombol mouse tengah sementara "papan ketik" sebenarnya dibuat untuk tujuan papan klip utama/default sebagai bertukar dengan berbagai jenis objek.

P.S. Saya tidak akan bekerja dengan x11 lagi setelah pengalaman saya. Selamat menikmati :)


Linux
  1. Dapatkan bilangan bulat langit-langit dari angka di linux (BASH)

  2. Cara mendapatkan total penggunaan cpu di Linux menggunakan C++

  3. gambar linux dari clipboard

  1. Bagaimana cara mendapatkan nama host dari IP (Linux)?

  2. Shell Linux mendapatkan nilai bidang dari file yml

  3. Bagaimana cara mendapatkan netmask dari bash?

  1. Linux – Bisakah Kita Mendapatkan Informasi Kompilator Dari Biner Elf?

  2. Perintah ls Linux

  3. C++ dapatkan nama distribusi linux\versi