GNU/Linux >> Linux Esercitazione >  >> Linux

C++ Ottieni una stringa dagli Appunti su Linux

X11 utilizza un protocollo di appunti lato applicazione asincrono multiformato multi-buffer flessibile.

La maggior parte dei toolkit lo hanno implementato (gtk_clipboard_get() di GTK , Qt è QApplication::clipboard() , clipboard_get di Tk). Ma puoi farlo manualmente con l'API X11, ad esempio, se non stai utilizzando i toolkit o se devi passare una grande quantità di dati attraverso il buffer degli appunti senza tenerli tutti in memoria allo stesso tempo.

Teoria

Potrebbero esserci molti buffer, ma devi conoscerne solo due:

  • CLIPBOARD è il solito buffer esplicito:copi le cose lì con il menu Modifica/Copia e le incolli con il menu Modifica/Incolla.
  • PRIMARY la selezione è una funzione implicita di selezione del mouse:il testo viene inserito quando viene selezionato con il cursore del mouse e viene incollato da esso facendo clic con il pulsante centrale del mouse nei campi di immissione del testo.

La selezione primaria non richiede pressioni di tasti, quindi è utile per copiare piccoli frammenti tra finestre che si trovano una accanto all'altra. Questa funzione è per lo più specifica per Unix, ma ho visto putty, trillian e alcune app gtk che la emulano su sistema operativo Windows. Anche firefox ha la funzione "Incolla e vai" quando si fa clic con il pulsante centrale su uno spazio vuoto non interattivo della pagina.

Per ottimizzare le cose quelle sono lato applicazione buffer:invece di inviare interi appunti/selezione al server ogni volta che cambia, l'applicazione dice semplicemente al server "lo possiedo". Per ottenere il buffer chiedi al proprietario di darti il ​​suo contenuto. In questo modo anche un buffer di grandi dimensioni non richiede risorse fino a quando non viene effettivamente richiesto.

Quando richiedi il buffer chiedi al proprietario un formato specifico di cui hai bisogno. Ad esempio, un'immagine copiata dal browser seamonkey (fare clic con il pulsante destro del mouse su un'immagine e premere "Copia immagine") può essere rappresentata in diversi formati. Apparirebbe come URL dell'immagine se lo incolli nel terminale. Diventerebbe un'immagine caricata da quell'URL se la incolli in libreoffice writer. E sarebbe l'immagine stessa se incollata in gimp. Funziona perché seamonkey è intelligente e fornisce a ogni applicazione il formato richiesto:stringa di testo per terminale, html per libreoffice e dati immagine per gimp. Per richiedere il formato del testo devi chiedere UTF8_STRING formato con fallback a STRING .

Poiché chiedi a un'altra applicazione di preparare il buffer e ciò potrebbe richiedere del tempo, la richiesta è asincrona :il proprietario prepara il buffer, lo salva in una posizione specificata (la proprietà della finestra viene utilizzata come memoria temporanea) e ti avvisa con SelectionNotify evento al termine.

Quindi, per ottenere il buffer:

  • scegli il nome del buffer (CLIPBOARD , PRIMARY ), format(UTF8_STRING , STRING ) e una proprietà della finestra in cui archiviare il risultato
  • chiama il XConvertSelection() per richiedere il buffer
  • aspetta SelectionNotify evento
  • legge il contenuto del buffer dalla proprietà della finestra

Implementazione ingenua

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

Questo funzionerà per molti casi semplici. Una cosa che manca qui è il supporto per la lettura incrementale di buffer di grandi dimensioni. Aggiungiamolo!

Grandi buffer

Alcune app potrebbero voler copiare/incollare 100 gigabyte di registri di testo. E X11 lo permette! Ma i dati devono essere passati in modo incrementale, suddivisi in blocchi.

Se il buffer richiesto è troppo grande, invece di memorizzarlo nella proprietà della finestra, il proprietario imposta una proprietà di formato INCR . Se lo elimini, il proprietario presuppone che tu l'abbia letto e inserisce il blocco successivo nella stessa proprietà. Ciò continua fino a quando l'ultimo blocco non viene letto ed eliminato. Infine il proprietario imposta la proprietà di dimensione 0 per contrassegnare la fine dei dati.

Quindi per leggere un buffer di grandi dimensioni elimini INCR property e attendi che la proprietà appaia di nuovo (PropertyNotify evento, stato ==PropertyNewValue ), leggilo ed eliminalo, attendi che appaia di nuovo e così via finché non appare con dimensione zero.

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

Ad esempio xsel lo strumento usa INCR trasferimento per buffer superiori a 4000. Secondo ICCCM, spetta all'applicazione scegliere un limite di dimensione ragionevole.

Lo stesso codice funziona per PRIMARY selezione. Sostituisci "CLIPBOARD" con "PRIMARY" per stampare PRIMARY contenuto della selezione.

Riferimenti

  • Riepilogo di X Selections di Jamie Zawinski
  • Manuale di programmazione Xlib - Selezioni
  • ICCCM - Trasferimenti di dati di grandi dimensioni e protocollo INCR
  • https://github.com/exebook/x11clipboard - minimo XCopy() e XPaste() implementazioni
  • xsel e xclip fonti
  • The Secondary Selection - storia e idee di Charles Lindsey

Hai provato a trovare prima non un codice ma un programma con un'implementazione? L'ho fatto per te e ho trovato molte implementazioni che utilizzano chiamate X11 dirette. Penso che il più prezioso sia questo, ma potresti anche leggere questo. Basta trovare qualsiasi programma e cercare le fonti. Prova a cercare su wikipedia quali applicazioni utilizzano il sistema x11 clipboard/selection.

I seguenti programmi operano specificamente sui meccanismi di trasferimento dei dati:

xcutsel trasferisce i dati dalle selezioni ai buffer tagliati o viceversa

xclipboard , glipper (Gnomo), parcellite (LXDE) e klipper (KDE) sono gestori di appunti, forse wmcliphist anche xcb mostra il contenuto dei buffer tagliati e permette all'utente di manipolarli xselezione,

xclip , xsel e xcopy sono programmi a riga di comando che copiano i dati da o verso la selezione X. xcopy ha un'opzione di verbosità che aiuta a eseguire il debug dei problemi di Xselection. parcellite ha anche la capacità di leggere e scrivere su selezioni X specifiche dalla riga di comando.

synergy è uno strumento multipiattaforma che ti consente di condividere gli appunti tra più computer che eseguono più sistemi operativi

xfce4-clipman-plugin è un "plugin della cronologia degli appunti per il pannello Xfce4" e anche un gestore degli appunti xtranslate cerca le parole nella selezione X in un dizionario multilingue autocutsel sincronizza il buffer di taglio e il buffer di selezione

In breve, in teoria, X11 ha 2 "appunti":in realtà una tastiera e per le selezioni - il testo che hai selezionato immediatamente può essere incollato ovunque tu voglia premendo il pulsante centrale del mouse mentre la "tastiera" vera e propria è creata per gli scopi degli appunti principali/predefiniti come scambio di diversi tipi di oggetti.

P.S. Non lavorerei più con x11 dopo la mia esperienza. Divertiti :)


Linux
  1. Ottieni il numero intero massimo dal numero in Linux (BASH)

  2. Come ottenere l'utilizzo totale della CPU in Linux usando C++

  3. Come ottenere il nome host dall'IP (Linux)?

  4. La shell Linux ottiene il valore di un campo da un file yml

  5. Come visualizzare determinate righe da un file di testo in Linux?

Visualizza citazioni casuali dalla riga di comando in Linux

Wikit:ottieni i riepiloghi di Wikipedia dalla riga di comando in Linux

Come ottenere notizie istantaneamente dalla riga di comando in Linux

Come ottenere il nome del file dal percorso completo in Linux

Ottieni la risoluzione dello schermo dalla riga di comando per Linux Desktop

Come ottenere netmask da bash?