Release v0.6.39: isolate history UI and image clipboard memory

This commit is contained in:
2026-11-04 19:40:54 +02:00
parent fa2f6d2ce4
commit 00c9cb0ee4
22 changed files with 747 additions and 49 deletions
+108
View File
@@ -0,0 +1,108 @@
// Run with the resident stopped. The suite restores the original clipboard.
#include "storage.h"
#include "ocr.h"
#include <psapi.h>
#include <iostream>
#include <algorithm>
static void require(bool ok, const char *name) {
if (!ok) throw std::runtime_error(name);
std::cout << "PASS " << name << '\n';
}
static HWND owner() {
return CreateWindowExW(0,L"STATIC",L"Snapshot test",0,0,0,0,0,HWND_MESSAGE,nullptr,nullptr,nullptr);
}
static void child(const std::wstring &argument) {
auto exe=ks::executable(), command=L"\""+exe+L"\" "+argument;
STARTUPINFOW startup{sizeof(startup)}; startup.dwFlags=STARTF_USESHOWWINDOW; startup.wShowWindow=SW_HIDE;
PROCESS_INFORMATION p{};
if(!CreateProcessW(exe.c_str(),command.data(),nullptr,nullptr,FALSE,CREATE_NO_WINDOW,nullptr,nullptr,&startup,&p))
throw std::runtime_error("start fixture process");
CloseHandle(p.hThread);
auto wait=WaitForSingleObject(p.hProcess,30000); DWORD code=1;
if(wait!=WAIT_OBJECT_0) {TerminateProcess(p.hProcess,1);WaitForSingleObject(p.hProcess,INFINITE);}
GetExitCodeProcess(p.hProcess,&code); CloseHandle(p.hProcess);
if(code) throw std::runtime_error("fixture process failed");
}
static size_t resident() {
ks::Bytes b(1024*1024); auto ws=reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(b.data());
if(!QueryWorkingSet(GetCurrentProcess(),ws,DWORD(b.size()))) throw std::runtime_error("working set query");
size_t pages=0; for(size_t i=0;i<ws->NumberOfEntries;++i) if(!ws->WorkingSetInfo[i].Shared) ++pages;
return pages*4096;
}
static void verifyImage(HWND w) {
auto c=ks::readClipboard(w,64*1024*1024);
for(auto id:{CF_DIB,CF_DIBV5}) {
auto it=std::find_if(c.formats.begin(),c.formats.end(),[&](const auto &f){return f.id==UINT(id);});
require(it!=c.formats.end(),"restores image representation");
auto pixels=ks::decodeImage(ks::Clip{{*it}});
require(pixels.width==1566 && pixels.height==1068 && pixels.bgra.front()==77,"preserves image pixels");
}
require(c.text()==L"Snapshot mixed text","preserves accompanying text");
}
int main(int argc,char**argv) {
CoInitializeEx(nullptr,COINIT_MULTITHREADED);
HWND w=owner(); int result=0; ks::Clip original; bool saved=false;
try {
std::string mode=argc>1?argv[1]:"suite";
if(mode=="publish") {
ks::Pixels p{1566,1068,ks::Bytes(1566*1068*4,255)}; p.bgra[0]=77;
auto dib=ks::imageDib(p); auto v5=dib;
BITMAPV5HEADER h{}; h.bV5Size=sizeof(h); h.bV5Width=p.width; h.bV5Height=-p.height;
h.bV5Planes=1; h.bV5BitCount=32; h.bV5Compression=BI_BITFIELDS;
h.bV5RedMask=0xff0000; h.bV5GreenMask=0xff00; h.bV5BlueMask=0xff; h.bV5AlphaMask=0xff000000;
v5.id=CF_DIBV5; v5.data.resize(sizeof(h)+p.bgra.size());
memcpy(v5.data.data(),&h,sizeof(h)); memcpy(v5.data.data()+sizeof(h),p.bgra.data(),p.bgra.size());
auto clip=ks::textClip(L"Snapshot mixed text"); clip.formats.push_back(dib); clip.formats.push_back(v5);
ks::writeClipboard(w,clip);
} else if(mode=="old" || mode=="new") {
child(L"publish"); auto baseline=resident();
if(mode=="old") {
auto clip=ks::readClipboard(w,64*1024*1024);
ks::writeClipboard(w,ks::textClip(L"temporary")); ks::writeClipboard(w,clip);
} else {
ks::ClipboardSnapshot snapshot; snapshot.capture(w,GetClipboardSequenceNumber());
auto seq=ks::writeClipboard(w,ks::textClip(L"temporary"));
require(snapshot.restore(w,seq)==GetClipboardSequenceNumber(),"helper returns current sequence");
}
auto after=resident();
auto report="baseline="+std::to_string(baseline)+" after="+std::to_string(after)+" delta="+std::to_string(int64_t(after)-int64_t(baseline))+"\n";
auto path=std::filesystem::path(ks::executable()).parent_path()/(L"snapshot-memory-"+ks::wide(mode)+L".txt");
ks::atomicWrite(path,ks::Bytes(report.begin(),report.end()));
verifyImage(w); // Only after measuring: verification intentionally reads images.
} else {
original=ks::readClipboard(w,128*1024*1024); saved=true;
child(L"old"); child(L"new"); child(L"publish");
auto seq=GetClipboardSequenceNumber();
auto selected=ks::readClipboard(w,32*1024*1024,ks::ClipboardRead::Selection);
require(selected.formats.size()==1 && selected.text()==L"Snapshot mixed text","selection read excludes image data");
bool rejected=false;
try {ks::readClipboard(w,32*1024*1024,ks::ClipboardRead::WithoutImages,seq);} catch(...) {rejected=true;}
require(rejected,"resident rejects images before reading their data");
{
ks::ClipboardSnapshot snapshot; snapshot.capture(w,seq);
auto stale=ks::writeClipboard(w,ks::textClip(L"temporary"));
ks::writeClipboard(w,ks::textClip(L"external copy")); rejected=false;
try {snapshot.restore(w,stale);} catch(...) {rejected=true;}
require(rejected && ks::readClipboard(w).text()==L"external copy","stale image restore preserves external copy");
}
{
ks::ClipboardSnapshot snapshot; snapshot.capture(w,GetClipboardSequenceNumber());
auto temporary=ks::writeClipboard(w,ks::textClip(L"temporary"));
snapshot.restore(w,temporary);
require(ks::readClipboard(w).text()==L"external copy","text snapshot fast path restores clipboard");
}
{
ks::Clip empty; auto seqEmpty=ks::writeClipboard(w,empty);
ks::ClipboardSnapshot snapshot; snapshot.capture(w,seqEmpty);
snapshot.restore(w,ks::writeClipboard(w,ks::textClip(L"temporary")));
require(ks::readClipboard(w).formats.empty(),"empty clipboard restored");
}
auto marked=ks::textClip(L"line"); marked.formats.push_back({0,L"MSDEVLineSelect",ks::Bytes{1}});
ks::writeClipboard(w,marked);
require(ks::readClipboard(w,1024,ks::ClipboardRead::Selection).formats.size()==2,"selection reader preserves editor guard metadata");
std::cout<<"PASS clipboard snapshot suite\n";
}
} catch(const std::exception &e) {std::cerr<<"FAIL "<<e.what()<<'\n';result=1;}
if(saved) try {ks::writeClipboard(w,original);} catch(...) {result=1;}
DestroyWindow(w); CoUninitialize(); return result;
}