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()
aXPaste()
implementace xsel
axclip
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) aklipper
(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
axcopy
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 :)