@@ -205,6 +205,12 @@ void Vt102Emulation::addToCurrentToken(wchar_t cc)
205205{
206206 tokenBuffer[tokenBufferPos] = cc;
207207 tokenBufferPos = qMin (tokenBufferPos+1 ,MAX_TOKEN_LENGTH-1 );
208+
209+ // ========== OSC52: Check accumulated buffer size ==========
210+ if (_osc52InProgress && tokenBufferPos >= MAX_TOKEN_LENGTH - 100 ) {
211+ handleOSC52Chunk ();
212+ }
213+ // ==========================================================
208214}
209215
210216// Character Class flags used while decoding
@@ -278,13 +284,30 @@ void Vt102Emulation::initTokenizer()
278284#define CNTL (c ) ((c)-' @' )
279285#define ESC 27
280286#define DEL 127
287+ #define BEL 7
281288
282289// process an incoming unicode character
283290void Vt102Emulation::receiveChar (wchar_t cc)
284291{
285292 if (cc == DEL)
286293 return ; // VT100: ignore.
287294
295+ // ========== OSC52: Direct path for subsequent chunks ==========
296+ // If OSC52 is in progress and past first chunk, directly accumulate
297+ if (_osc52InProgress && cc != BEL) {
298+ addToCurrentToken (cc);
299+ // Chunk boundary check is handled inside addToCurrentToken()
300+ return ;
301+ }
302+ // ========== OSC52: Check for BEL end ==========
303+ // CRITICAL: Remove Xpe check - BEL alone should end OSC52 sequence
304+ if (cc == BEL && _osc52InProgress) {
305+ handleOSC52End (cc);
306+ resetTokenizer ();
307+ return ;
308+ }
309+ // =======================================================
310+
288311 if (ces (CTL))
289312 {
290313 // ignore control characters in the text part of Xpe (aka OSC) "ESC]"
@@ -317,8 +340,26 @@ void Vt102Emulation::receiveChar(wchar_t cc)
317340 if (lec (1 ,0 ,ESC)) { return ; }
318341 if (lec (1 ,0 ,ESC+128 )) { s[0 ] = ESC; receiveChar (' [' ); return ; }
319342 if (les (2 ,1 ,GRP)) { return ; }
320- if (Xte ) { processWindowAttributeChange (); resetTokenizer (); return ; }
321- if (Xpe ) { prevCC = cc; return ; }
343+ // ========== OSC: Unified handling ==========
344+ if (Xpe && !_osc52InProgress) {
345+ // Check 1: OSC52 start detection
346+ if (isOSC52Start ()) {
347+ startOSC52 ();
348+ // Don't return - continue to accumulate
349+ }
350+ // Check 2: OSC end (BEL or ST)
351+ else if (cc == BEL || (prevCC == ESC && cc == 0x5C )) {
352+ processWindowAttributeChange ();
353+ resetTokenizer ();
354+ return ;
355+ }
356+ // Check 3: Normal OSC - accumulate
357+ else {
358+ prevCC = cc;
359+ return ;
360+ }
361+ }
362+ // ============================================
322363 if (lec (3 ,2 ,' ?' )) { return ; }
323364 if (lec (3 ,2 ,' >' )) { return ; }
324365 if (lec (3 ,2 ,' !' )) { return ; }
@@ -917,6 +958,127 @@ void Vt102Emulation::reportTerminalParms(int p)
917958 sendString (tmp);
918959}
919960
961+ // ========== OSC52 Clipboard Implementation ==========
962+
963+ bool Vt102Emulation::isOSC52Start () const
964+ {
965+ // Check if tokenBuffer starts with "ESC ] 5 2 ;"
966+ // tokenBuffer layout: [0]=ESC, [1]=']', [2]='5', [3]='2', [4]=';'
967+ return (tokenBufferPos >= 5 &&
968+ tokenBuffer[2 ] == L' 5' &&
969+ tokenBuffer[3 ] == L' 2' &&
970+ tokenBuffer[4 ] == L' ;' );
971+ }
972+
973+ char Vt102Emulation::extractOSC52Target () const
974+ {
975+ // tokenBuffer layout: ESC ] 5 2 ; target ; base64...
976+ // ↑
977+ // position 5
978+ if (tokenBufferPos > 5 ) {
979+ return static_cast <char >(tokenBuffer[5 ]);
980+ }
981+ return ' c' ; // Default to CLIPBOARD
982+ }
983+
984+ QString Vt102Emulation::extractOSC52Data () const
985+ {
986+ // tokenBuffer layout: ESC ] 5 2 ; target ; base64...
987+ // ↑
988+ // position 7 (data start for first chunk)
989+ // For subsequent chunks, data starts at position 0
990+ int dataStart = _osc52IsFirstChunk ? 7 : 0 ;
991+ if (tokenBufferPos > dataStart) {
992+ return QString::fromWCharArray (tokenBuffer + dataStart, tokenBufferPos - dataStart);
993+ }
994+ return QString ();
995+ }
996+
997+ void Vt102Emulation::startOSC52 ()
998+ {
999+ _osc52InProgress = true ;
1000+ _osc52Target = extractOSC52Target ();
1001+ _osc52DataBuffer.clear ();
1002+ _osc52IsFirstChunk = true ; // Mark as first chunk
1003+
1004+ qInfo () << " OSC52: Start, target=" << _osc52Target;
1005+ }
1006+
1007+ void Vt102Emulation::handleOSC52End (wchar_t cc)
1008+ {
1009+ // cc should be BEL (7)
1010+ Q_ASSERT (cc == BEL);
1011+ Q_UNUSED (cc);
1012+
1013+ // Extract base64 data from current tokenBuffer
1014+ // For the final chunk, skip BEL character at the end
1015+ QString base64Data;
1016+ if (_osc52IsFirstChunk) {
1017+ // Single chunk OSC52: skip header (7 chars) and BEL (1 char)
1018+ if (tokenBufferPos > 8 ) {
1019+ base64Data = QString::fromWCharArray (tokenBuffer + 7 , tokenBufferPos - 8 );
1020+ }
1021+ } else {
1022+ // Multi-chunk OSC52 final chunk: tokenBuffer contains ONLY base64 data
1023+ // tokenBufferPos includes the BEL character, so subtract 1
1024+ if (tokenBufferPos > 1 ) {
1025+ base64Data = QString::fromWCharArray (tokenBuffer, tokenBufferPos - 1 );
1026+ }
1027+ }
1028+
1029+ // Accumulate to buffer
1030+ _osc52DataBuffer += base64Data;
1031+
1032+ // ========== OSC52: Check final buffer size ==========
1033+ if (_osc52DataBuffer.size () >= MAX_OSC52_BUFFER) {
1034+ qWarning () << " OSC52: Buffer overflow (>=" << MAX_OSC52_BUFFER
1035+ << " bytes), rejecting" ;
1036+ _osc52DataBuffer.clear ();
1037+ _osc52InProgress = false ;
1038+ _osc52IsFirstChunk = false ;
1039+ return ;
1040+ }
1041+ // ====================================================
1042+
1043+ // Emit signal with complete data
1044+ emit osc52ClipboardRequest (_osc52Target, _osc52DataBuffer);
1045+
1046+ qInfo () << " OSC52: Complete, sent" << _osc52DataBuffer.size () << " bytes" ;
1047+
1048+ // Clear state
1049+ _osc52DataBuffer.clear ();
1050+ _osc52InProgress = false ;
1051+ _osc52IsFirstChunk = false ;
1052+ }
1053+
1054+ void Vt102Emulation::handleOSC52Chunk ()
1055+ {
1056+ // ========== OSC52: Check buffer size limit ==========
1057+ if (_osc52DataBuffer.size () >= MAX_OSC52_BUFFER) {
1058+ qWarning () << " OSC52: Buffer overflow (>=" << MAX_OSC52_BUFFER
1059+ << " bytes), rejecting" ;
1060+ _osc52DataBuffer.clear ();
1061+ _osc52InProgress = false ;
1062+ _osc52IsFirstChunk = false ;
1063+ return ;
1064+ }
1065+ // ====================================================
1066+
1067+ // Extract current chunk from tokenBuffer
1068+ QString base64Data = extractOSC52Data ();
1069+ _osc52DataBuffer += base64Data;
1070+
1071+ // Mark as not first chunk for subsequent chunks
1072+ _osc52IsFirstChunk = false ;
1073+
1074+ // ========== Add resetTokenizer ==========
1075+ resetTokenizer (); // 清空 tokenBuffer,准备接收新数据
1076+ // ========================================
1077+ // Don't clear state, continue receiving
1078+ }
1079+
1080+ // ====================================================
1081+
9201082void Vt102Emulation::reportStatus ()
9211083{
9221084 sendString (" \033 [0n" ); // VT100. Device status report. 0 = Ready.
0 commit comments