GNU/Linux >> Znalost Linux >  >> Linux

C++ Získejte řetězec ze schránky na Linuxu

X11 používá flexibilní víceformátový asynchronní protokol schránky na straně aplikace s více vyrovnávací pamětí.

Většina sad nástrojů jej má implementován (GTK gtk_clipboard_get() , Qt QApplication::clipboard() , Tk's clipboard_get). Ale můžete to udělat ručně pomocí X11 API, například pokud nepoužíváte sady nástrojů nebo pokud musíte předávat velké množství dat přes vyrovnávací paměť schránky, aniž byste je všechna současně uchovávali v paměti.

Teorie

Může existovat mnoho vyrovnávacích pamětí, ale potřebujete vědět pouze o dvou:

  • CLIPBOARD je obvyklá explicitní vyrovnávací paměť:věci tam zkopírujete pomocí nabídky Úpravy/Kopírovat a vložíte pomocí nabídky Úpravy/Vložit.
  • PRIMARY výběr je implicitní funkce výběru myší:text se do něj dostane při výběru kurzorem myši a vloží se z něj po kliknutí prostředním tlačítkem do polí pro zadávání textu.

Primární výběr nepotřebuje žádné stisknutí kláves, takže je užitečný pro kopírování malých fragmentů mezi okny, která jsou vedle sebe. Tato funkce je většinou specifická pro unix, ale viděl jsem aplikace Putty, Trillian a některé gtk, které ji emulovaly v OS Windows. Firefox má také funkci „Paste &Go“ při kliknutí prostředním tlačítkem na prázdné neinteraktivní místo na stránce.

Chcete-li optimalizovat věci, ty jsou na straně aplikace buffery:místo toho, aby se celá schránka/výběr vkládala na server pokaždé, když se to změnilo, aplikace jen sdělí serveru "já to vlastním". Chcete-li získat vyrovnávací paměť, požádáte vlastníka, aby vám poskytl její obsah. Tímto způsobem ani velká vyrovnávací paměť nezabere žádné prostředky, dokud není skutečně požadována.

Při požadavku na vyrovnávací paměť požádáte vlastníka o konkrétní formát, který potřebujete. Například obrázek zkopírovaný z prohlížeče seamonkey (klikněte pravým tlačítkem na obrázek a stiskněte "Kopírovat obrázek") může být reprezentován v různých formátech. Pokud ji vložíte do terminálu, zobrazí se jako adresa URL obrázku. Pokud jej vložíte do libreoffice Writer, stane se z něj obrázek načtený z této adresy URL. A byl by to samotný obrázek, kdyby byl vložen do gimpu. To funguje, protože seamonkey je chytrý a poskytuje každé aplikaci formát, který požaduje:textový řetězec pro terminál, html pro libreoffice a obrazová data pro gimp. Chcete-li požádat o formát textu, požádejte o UTF8_STRING formát s nouzovou možností STRING .

Když požádáte jinou aplikaci o přípravu vyrovnávací paměti, což může nějakou dobu trvat, požadavek je asynchronní :vlastník připraví vyrovnávací paměť, uloží ji na určené místo (vlastnost okna se používá jako dočasné úložiště) a upozorní vás pomocí SelectionNotify událost, až bude hotovo.

Takže získat vyrovnávací paměť:

  • zvolte název vyrovnávací paměti (CLIPBOARD , PRIMARY ), format(UTF8_STRING , STRING ) a vlastnost okna pro uložení výsledku
  • zavolejte na číslo XConvertSelection() požádat o vyrovnávací paměť
  • počkej na SelectionNotify událost
  • číst obsah vyrovnávací paměti z vlastnosti okna

Naivní implementace

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

To bude fungovat pro mnoho jednoduchých případů. Jedna věc zde chybí je podpora pro inkrementální čtení velkých vyrovnávacích pamětí. Pojďme to přidat!

Velké vyrovnávací paměti

Některé aplikace mohou chtít zkopírovat/vložit 100 gigabajtů textových protokolů. A X11 to umožňuje! Data však musí být předávána postupně, rozdělená na části.

Pokud je požadovaná vyrovnávací paměť příliš velká, namísto jejího uložení do vlastnosti okna vlastník nastaví vlastnost formátu INCR . Pokud jej smažete, vlastník předpokládá, že jste si jej přečetli, a vloží další blok do stejné vlastnosti. To pokračuje, dokud není přečten a odstraněn poslední blok. Nakonec vlastník nastaví vlastnost velikosti 0 pro označení konce dat.

Takže pro čtení velkého bufferu smažete INCR vlastnost a počkejte, až se vlastnost znovu objeví (PropertyNotify událost, stav ==PropertyNewValue ), přečtěte si jej a odstraňte, počkejte, až se znovu objeví, a tak dále, dokud se neobjeví s nulovou velikostí.

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

Například xsel nástroj používá INCR přenos pro buffery větší než 4000. Podle ICCCM je na aplikaci, aby zvolila rozumný limit velikosti.

Stejný kód funguje pro PRIMARY výběr. Chcete-li vytisknout PRIMARY, nahraďte „SCHRÁNKA“ výrazem „PRIMARY“. obsah výběru.

Odkazy

  • Shrnutí X výběrů od Jamieho Zawinského
  • Příručka k programování Xlib – výběry
  • ICCCM – Velké datové přenosy a protokol INCR
  • https://github.com/exebook/x11clipboard – minimální XCopy() a XPaste() implementace
  • xsel a xclip zdroje
  • Sekundární výběr – historie a myšlenky Charlese Lindseyho

Snažili jste se nejprve najít ne kód, ale program s implementací? Udělal jsem to za vás a našel jsem spoustu implementací, které používají přímá volání X11. Myslím, že nejcennější je toto, ale také si to můžete přečíst. Stačí najít jakýkoli program a hledat zdroje. Zkuste se podívat na wikipedii, jaké aplikace používají x11 systém schránky/výběru.

Následující programy specificky fungují na mechanismech přenosu dat:

xcutsel přenáší data z výběrů do vyrovnávací paměti nebo naopak

xclipboard , glipper (Gnome), parcellite (LXDE) a klipper (KDE) jsou správci schránky, možná wmcliphist také xcb zobrazuje obsah vyrovnávacích pamětí řezu a umožňuje uživateli manipulovat s nimi xselection,

xclip , xsel a xcopy jsou programy příkazového řádku, které kopírují data do nebo z výběru X. xcopy má možnost upovídanosti, která pomáhá ladit problémy Xselection. parcellite má také schopnost číst a zapisovat do konkrétních X výběrů z příkazového řádku.

synergy je multiplatformní nástroj, který vám umožňuje sdílet schránku mezi více počítači s více operačními systémy

xfce4-clipman-plugin je "plugin pro historii schránky pro Xfce4panel" a také správce schránky xtranslate vyhledává slova v Xselection ve vícejazyčném slovníku autocutsel synchronizuje vyrovnávací paměť a vyrovnávací paměť pro výběr

Stručně řečeno, teoreticky má X11 2 „schránky“:ve skutečnosti klávesnici a pro výběry – text, který jste vybrali, lze vložit kamkoli chcete stisknutím prostředního tlačítka myši, zatímco skutečná „klávesnice“ je vytvořena pro účely hlavní/výchozí schránky jako výměna různými druhy objektů.

P.S. Po mých zkušenostech bych už s x11 nepracoval. Užijte si to :)


Linux
  1. Jak získat celkové využití CPU v Linuxu pomocí C++

  2. Jak získat uživatelské jméno v C/C++ v Linuxu?

  3. Linuxový shell získá hodnotu pole ze souboru yml

  1. Získejte rozlišení displeje z příkazového řádku pro Linux Desktop

  2. Jak získat masku sítě z bash?

  3. Jak zobrazit určité řádky z textového souboru v Linuxu?

  1. Jak získat geolokaci z příkazového řádku v Linuxu

  2. Linux – Můžeme získat informace o kompilátoru z binárního souboru Elf?

  3. Příkaz Linux ls