CtrlCore: X11 Optimized clipboard handling

This commit is contained in:
Mirek Fidler 2026-01-13 09:46:59 +01:00
parent f07e8ae05c
commit 32a6d9eb56
8 changed files with 66 additions and 27 deletions

View file

@ -0,0 +1,10 @@
uses
CtrlLib;
file
main.cpp;
mainconfig
"" = "GUI",
"" = "GUI X11";

View file

@ -0,0 +1,24 @@
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct MyApp : TopWindow {
void Paint(Draw& w) override {
w.DrawRect(GetSize(), White());
for(int i = 0; i < 100; i++) {
{
RTIMING("IsClipboardAvailableText");
IsClipboardAvailableText();
}
{
RTIMING("IsClipboardAvailableImage");
IsClipboardAvailableImage();
}
}
}
};
GUI_APP_MAIN
{
MyApp().Run();
}

View file

@ -157,7 +157,6 @@ INITBLOCK {
void SystemDraw::DrawTextOp(int x, int y, int angle, const wchar *text, Font font, void SystemDraw::DrawTextOp(int x, int y, int angle, const wchar *text, Font font,
Color ink, int n, const int *dx) { Color ink, int n, const int *dx) {
GuiLock __; GuiLock __;
DTIMING("DrawText");
LLOG("DrawText " << ToUtf8(WString(text, n)) << " color:" << ink << " font:" << font); LLOG("DrawText " << ToUtf8(WString(text, n)) << " color:" << ink << " font:" << font);
//TODO - X11 seems to crash when displaying too long strings (?) //TODO - X11 seems to crash when displaying too long strings (?)
int ox = x + actual_offset.x; int ox = x + actual_offset.x;

View file

@ -152,7 +152,6 @@ static XPicture sGetSolidFill(Color c)
void ImageSysData::Paint(SystemDraw& w, int x, int y, const Rect& src, Color c) void ImageSysData::Paint(SystemDraw& w, int x, int y, const Rect& src, Color c)
{ {
GuiLock __; GuiLock __;
DTIMING("PAINT IMAGE");
x += w.GetOffset().x; x += w.GetOffset().x;
y += w.GetOffset().y; y += w.GetOffset().y;
Size sz = img.GetSize(); Size sz = img.GetSize();

View file

@ -142,6 +142,23 @@ String Ctrl::Xclipboard::Read(int fmt, int selection, int property)
return Null; return Null;
} }
bool Ctrl::Xclipboard::IsAvailable(int fmt, bool primary)
{
GuiLock __;
if(data.GetCount())
return data.Find(fmt) >= 0;
String& formats = this->formats[primary];
if(formats.IsVoid())
formats = Read(XAtom("TARGETS"), XAtom(primary ? "PRIMARY" : "CLIPBOARD"), XAtom("CLIPDATA"));
int c = formats.GetCount() / sizeof(Atom);
const Atom *m = (Atom *) ~formats;
for(int i = 0; i < c; i++) {
if(m[i] == (dword)fmt)
return true;
}
return false;
}
Ctrl::Xclipboard& Ctrl::xclipboard() Ctrl::Xclipboard& Ctrl::xclipboard()
{ {
static Xclipboard xc; static Xclipboard xc;
@ -200,21 +217,6 @@ WString ReadClipboardUnicodeText()
return ToUtf32(ReadClipboard("UTF8_STRING")); return ToUtf32(ReadClipboard("UTF8_STRING"));
} }
bool Ctrl::Xclipboard::IsAvailable(int fmt, const char *type)
{
GuiLock __;
if(data.GetCount())
return data.Find(fmt) >= 0;
String formats = Read(XAtom("TARGETS"), XAtom(type), XAtom("CLIPDATA"));
int c = formats.GetCount() / sizeof(Atom);
const Atom *m = (Atom *) ~formats;
for(int i = 0; i < c; i++) {
if(m[i] == (dword)fmt)
return true;
}
return false;
}
bool Ctrl::ClipHas(int type, const char *fmt) bool Ctrl::ClipHas(int type, const char *fmt)
{ {
GuiLock __; GuiLock __;
@ -222,11 +224,11 @@ bool Ctrl::ClipHas(int type, const char *fmt)
if(strcmp(fmt, "files") == 0) if(strcmp(fmt, "files") == 0)
fmt = "text/uri-list"; fmt = "text/uri-list";
if(type == 0) if(type == 0)
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "CLIPBOARD"); return Ctrl::xclipboard().IsAvailable(XAtom(fmt), false);
if(type == 2) { if(type == 2) {
if(sel_ctrl) if(sel_ctrl)
return sel_formats.Find(fmt) >= 0; return sel_formats.Find(fmt) >= 0;
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "PRIMARY"); return Ctrl::xclipboard().IsAvailable(XAtom(fmt), true);
} }
return drop_formats.Find(fmt) >= 0; return drop_formats.Find(fmt) >= 0;
} }
@ -318,7 +320,7 @@ void Append(VectorMap<String, ClipData>& data, const WString& text) // optimize
bool IsClipboardAvailable(const char *fmt) bool IsClipboardAvailable(const char *fmt)
{ {
GuiLock __; GuiLock __;
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "CLIPBOARD"); return Ctrl::xclipboard().IsAvailable(XAtom(fmt), false);
} }
bool IsClipboardAvailableText() bool IsClipboardAvailableText()

View file

@ -70,11 +70,14 @@ public:
VectorMap<int, ClipData> data; VectorMap<int, ClipData> data;
String formats[2]; // 0 CLIPBOARD, 1 PRIMARY
String Read(int fmt, int selection, int property); String Read(int fmt, int selection, int property);
void Write(int fmt, const ClipData& data); void Write(int fmt, const ClipData& data);
bool IsAvailable(int fmt, const char *type); bool IsAvailable(int fmt, bool primary);
void Clear() { data.Clear(); } void InvalidateFormats() { formats[0] = String::GetVoid(); formats[1] = String::GetVoid(); }
void Clear() { data.Clear(); InvalidateFormats(); }
void Request(XSelectionRequestEvent *se); void Request(XSelectionRequestEvent *se);
Xclipboard(); Xclipboard();

View file

@ -109,10 +109,12 @@ void Ctrl::EventProc(XWindow& w, XEvent *event)
LTIMING("XUserInput"); LTIMING("XUserInput");
switch(event->type) { switch(event->type) {
case FocusIn: case FocusIn:
xclipboard().InvalidateFormats();
if(w.xic) if(w.xic)
XSetICFocus(w.xic); XSetICFocus(w.xic);
break; break;
case FocusOut: case FocusOut:
xclipboard().InvalidateFormats();
if(w.xic) if(w.xic)
XUnsetICFocus(w.xic); XUnsetICFocus(w.xic);
break; break;

View file

@ -43,24 +43,24 @@ void TopWindow::EventProc(XWindow& w, XEvent *event)
if(event->xclient.message_type == XAtom("WM_PROTOCOLS")) { if(event->xclient.message_type == XAtom("WM_PROTOCOLS")) {
Atom a = event->xclient.data.l[0]; Atom a = event->xclient.data.l[0];
if(a == XAtom("WM_DELETE_WINDOW") && IsEnabled()) { if(a == XAtom("WM_DELETE_WINDOW") && IsEnabled()) {
DLOG("DELETE_WINDOW " << Name()); LLOG("DELETE_WINDOW " << Name());
WhenClose(); WhenClose();
return; return;
} }
if(a == XAtom("WM_TAKE_FOCUS")) { if(a == XAtom("WM_TAKE_FOCUS")) {
DLOG("TAKE_FOCUS serial: " << event->xclient.serial); LLOG("TAKE_FOCUS serial: " << event->xclient.serial);
Xeventtime = event->xclient.data.l[1]; Xeventtime = event->xclient.data.l[1];
TakeFocus(); TakeFocus();
return; return;
} }
if(a == XAtom("_NET_WM_PING")) { if(a == XAtom("_NET_WM_PING")) {
DLOG("_NET_WM_PING"); LLOG("_NET_WM_PING");
XEvent ev = *event; XEvent ev = *event;
ev.xclient.window = Xroot; ev.xclient.window = Xroot;
XSendEvent(Xdisplay, Xroot, 0, SubstructureRedirectMask|SubstructureNotifyMask, &ev); XSendEvent(Xdisplay, Xroot, 0, SubstructureRedirectMask|SubstructureNotifyMask, &ev);
return; return;
} }
DLOG("Unknown WM_PROTOCOLS: " << XAtomName(a)); LLOG("Unknown WM_PROTOCOLS: " << XAtomName(a));
} }
} }
else else