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,
Color ink, int n, const int *dx) {
GuiLock __;
DTIMING("DrawText");
LLOG("DrawText " << ToUtf8(WString(text, n)) << " color:" << ink << " font:" << font);
//TODO - X11 seems to crash when displaying too long strings (?)
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)
{
GuiLock __;
DTIMING("PAINT IMAGE");
x += w.GetOffset().x;
y += w.GetOffset().y;
Size sz = img.GetSize();

View file

@ -142,6 +142,23 @@ String Ctrl::Xclipboard::Read(int fmt, int selection, int property)
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()
{
static Xclipboard xc;
@ -200,21 +217,6 @@ WString ReadClipboardUnicodeText()
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)
{
GuiLock __;
@ -222,11 +224,11 @@ bool Ctrl::ClipHas(int type, const char *fmt)
if(strcmp(fmt, "files") == 0)
fmt = "text/uri-list";
if(type == 0)
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "CLIPBOARD");
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), false);
if(type == 2) {
if(sel_ctrl)
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;
}
@ -318,7 +320,7 @@ void Append(VectorMap<String, ClipData>& data, const WString& text) // optimize
bool IsClipboardAvailable(const char *fmt)
{
GuiLock __;
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), "CLIPBOARD");
return Ctrl::xclipboard().IsAvailable(XAtom(fmt), false);
}
bool IsClipboardAvailableText()

View file

@ -70,11 +70,14 @@ public:
VectorMap<int, ClipData> data;
String formats[2]; // 0 CLIPBOARD, 1 PRIMARY
String Read(int fmt, int selection, int property);
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);
Xclipboard();

View file

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

View file

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